diff --git a/backend/package-lock.json b/backend/package-lock.json index 73ac899..bec85ee 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -18,7 +18,6 @@ "express-session": "^1.17.3", "keycloak-connect": "^23.0.7", "mssql": "^11.0.1", - "multer": "^1.4.5-lts.1", "mysql2": "^3.0.0" }, "devDependencies": { @@ -26,7 +25,6 @@ "@types/cors": "^2.8.0", "@types/express": "^4.17.0", "@types/express-session": "^1.17.7", - "@types/multer": "^1.4.7", "@types/node": "^20.0.0", "nodemon": "^3.0.0", "prisma": "^5.0.0", @@ -34,87 +32,9 @@ "typescript": "^5.0.0" } }, - "node_modules/@azure-rest/core-client": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@azure-rest/core-client/-/core-client-2.5.1.tgz", - "integrity": "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.1.2", - "@azure/core-auth": "^1.10.0", - "@azure/core-rest-pipeline": "^1.22.0", - "@azure/core-tracing": "^1.3.0", - "@typespec/ts-http-runtime": "^0.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-auth": { - "version": "1.10.1", - "resolved": "https://registry.npmmirror.com/@azure/core-auth/-/core-auth-1.10.1.tgz", - "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.1.2", - "@azure/core-util": "^1.13.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@azure/core-client": { - "version": "1.10.1", - "resolved": "https://registry.npmmirror.com/@azure/core-client/-/core-client-1.10.1.tgz", - "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", - "license": "MIT", - "peer": true, - "dependencies": { - "@azure/abort-controller": "^2.1.2", - "@azure/core-auth": "^1.10.0", - "@azure/core-rest-pipeline": "^1.22.0", - "@azure/core-tracing": "^1.3.0", - "@azure/core-util": "^1.13.0", - "@azure/logger": "^1.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@azure/core-http-compat": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/@azure/core-http-compat/-/core-http-compat-2.3.2.tgz", - "integrity": "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.1.2" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@azure/core-client": "^1.10.0", - "@azure/core-rest-pipeline": "^1.22.0" - } - }, "node_modules/@azure/core-lro": { "version": "2.7.2", - "resolved": "https://registry.npmmirror.com/@azure/core-lro/-/core-lro-2.7.2.tgz", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", "license": "MIT", "dependencies": { @@ -127,10 +47,10 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-paging": { - "version": "1.6.2", - "resolved": "https://registry.npmmirror.com/@azure/core-paging/-/core-paging-1.6.2.tgz", - "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "node_modules/@azure/core-lro/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", "license": "MIT", "dependencies": { "tslib": "^2.6.2" @@ -139,12 +59,172 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.23.0", - "resolved": "https://registry.npmmirror.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.23.0.tgz", - "integrity": "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==", + "node_modules/@azure/core-lro/node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-lro/node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-lro/node_modules/@typespec/ts-http-runtime": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.6.tgz", + "integrity": "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-lro/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/core-lro/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@azure/core-lro/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/core-lro/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/core-lro/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@azure/keyvault-common": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.1.0.tgz", + "integrity": "sha512-aCDidWuKY06LWQ4x7/8TIXK6iRqTaRWRL3t7T+LC+j1b07HtoIsOxP/tU90G4jCSBn5TAyUTCtA4MS/y5Hudaw==", + "license": "MIT", + "dependencies": { + "@azure-rest/core-client": "^2.3.3", + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.10.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-common/node_modules/@azure-rest/core-client": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.7.0.tgz", + "integrity": "sha512-rL0lJqh1E8HLXNgjIw8cRyGAV/v+m6p1xRu/8OhsnmN8XHhwkyYJkAoGM+zrew96v7jZYPmVfy7pv7v4Iccfsg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.24.0", + "@azure/core-tracing": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-common/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-common/node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-common/node_modules/@azure/core-rest-pipeline": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.24.0.tgz", + "integrity": "sha512-PpLsoDQ3AMmKZ0VU+0GrmqMxgp/sExjlVm4R+nLWngeoEGAzOIPVifaxKGU5gMv+nWELUoHfvrolWD+ZS/nFJg==", "license": "MIT", - "peer": true, "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", @@ -158,9 +238,9 @@ "node": ">=20.0.0" } }, - "node_modules/@azure/core-tracing": { + "node_modules/@azure/keyvault-common/node_modules/@azure/core-tracing": { "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", "license": "MIT", "dependencies": { @@ -170,9 +250,9 @@ "node": ">=20.0.0" } }, - "node_modules/@azure/core-util": { + "node_modules/@azure/keyvault-common/node_modules/@azure/core-util": { "version": "1.13.1", - "resolved": "https://registry.npmmirror.com/@azure/core-util/-/core-util-1.13.1.tgz", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", "license": "MIT", "dependencies": { @@ -184,73 +264,9 @@ "node": ">=20.0.0" } }, - "node_modules/@azure/identity": { - "version": "4.13.0", - "resolved": "https://registry.npmmirror.com/@azure/identity/-/identity-4.13.0.tgz", - "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.9.0", - "@azure/core-client": "^1.9.2", - "@azure/core-rest-pipeline": "^1.17.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.11.0", - "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^4.2.0", - "@azure/msal-node": "^3.5.0", - "open": "^10.1.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@azure/keyvault-common": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", - "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.10.0", - "@azure/logger": "^1.1.4", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/keyvault-keys": { - "version": "4.10.0", - "resolved": "https://registry.npmmirror.com/@azure/keyvault-keys/-/keyvault-keys-4.10.0.tgz", - "integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==", - "license": "MIT", - "dependencies": { - "@azure-rest/core-client": "^2.3.3", - "@azure/abort-controller": "^2.1.2", - "@azure/core-auth": "^1.9.0", - "@azure/core-http-compat": "^2.2.0", - "@azure/core-lro": "^2.7.2", - "@azure/core-paging": "^1.6.2", - "@azure/core-rest-pipeline": "^1.19.0", - "@azure/core-tracing": "^1.2.0", - "@azure/core-util": "^1.11.0", - "@azure/keyvault-common": "^2.0.0", - "@azure/logger": "^1.1.4", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/logger": { + "node_modules/@azure/keyvault-common/node_modules/@azure/logger": { "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/@azure/logger/-/logger-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", "license": "MIT", "dependencies": { @@ -261,41 +277,305 @@ "node": ">=20.0.0" } }, - "node_modules/@azure/msal-browser": { - "version": "4.29.0", - "resolved": "https://registry.npmmirror.com/@azure/msal-browser/-/msal-browser-4.29.0.tgz", - "integrity": "sha512-/f3eHkSNUTl6DLQHm+bKecjBKcRQxbd/XLx8lvSYp8Nl/HRyPuIPOijt9Dt0sH50/SxOwQ62RnFCmFlGK+bR/w==", + "node_modules/@azure/keyvault-common/node_modules/@typespec/ts-http-runtime": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.6.tgz", + "integrity": "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==", "license": "MIT", "dependencies": { - "@azure/msal-common": "15.15.0" + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-common/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/keyvault-common/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@azure/keyvault-common/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/keyvault-common/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/keyvault-common/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@azure/keyvault-keys": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.10.2.tgz", + "integrity": "sha512-VmUSLbXRAbSzDD8grXHGPaknYs0SKr3yuf6U+d4XMpX4XuVYskNqbTTwXce0zR1LyxfTZm9rWEBcvs3vdYwCmQ==", + "license": "MIT", + "dependencies": { + "@azure-rest/core-client": "^2.3.3", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-lro": "^2.7.2", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.0", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/keyvault-common": "^2.1.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure-rest/core-client": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.7.0.tgz", + "integrity": "sha512-rL0lJqh1E8HLXNgjIw8cRyGAV/v+m6p1xRu/8OhsnmN8XHhwkyYJkAoGM+zrew96v7jZYPmVfy7pv7v4Iccfsg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.24.0", + "@azure/core-tracing": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure/core-rest-pipeline": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.24.0.tgz", + "integrity": "sha512-PpLsoDQ3AMmKZ0VU+0GrmqMxgp/sExjlVm4R+nLWngeoEGAzOIPVifaxKGU5gMv+nWELUoHfvrolWD+ZS/nFJg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/@typespec/ts-http-runtime": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.6.tgz", + "integrity": "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@azure/keyvault-keys/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@azure/msal-browser": { + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.25.0.tgz", + "integrity": "sha512-7GyXzDUrdLG5zPDCYJbQdJqbl9a2U9SWUSC4lxr7cXjkBq6diTfWcJsi0UjdGWlqy+EevQg9dSC4g3s3BEp19g==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.14.2" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "15.15.0", - "resolved": "https://registry.npmmirror.com/@azure/msal-common/-/msal-common-15.15.0.tgz", - "integrity": "sha512-/n+bN0AKlVa+AOcETkJSKj38+bvFs78BaP4rNtv3MJCmPH0YrHiskMRe74OhyZ5DZjGISlFyxqvf9/4QVEi2tw==", + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.2.tgz", + "integrity": "sha512-HOBR0Gfo39TMrkxNG8k6cfjRkrRjXZc3JQs3ebMWQc9S4cihuknMYra3sgmzbZB752oQBqBN9+WF7c826p2vIQ==", "license": "MIT", "engines": { "node": ">=0.8.0" } }, - "node_modules/@azure/msal-node": { - "version": "3.8.8", - "resolved": "https://registry.npmmirror.com/@azure/msal-node/-/msal-node-3.8.8.tgz", - "integrity": "sha512-+f1VrJH1iI517t4zgmuhqORja0bL6LDQXfBqkjuMmfTYXTQQnh1EvwwxO3UbKLT05N0obF72SRHFrC1RBDv5Gg==", - "license": "MIT", - "dependencies": { - "@azure/msal-common": "15.15.0", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -311,7 +591,7 @@ }, "node_modules/@fast-csv/format": { "version": "4.3.5", - "resolved": "https://registry.npmmirror.com/@fast-csv/format/-/format-4.3.5.tgz", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", "license": "MIT", "dependencies": { @@ -325,13 +605,13 @@ }, "node_modules/@fast-csv/format/node_modules/@types/node": { "version": "14.18.63", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-14.18.63.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", "license": "MIT" }, "node_modules/@fast-csv/parse": { "version": "4.3.6", - "resolved": "https://registry.npmmirror.com/@fast-csv/parse/-/parse-4.3.6.tgz", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", "license": "MIT", "dependencies": { @@ -346,7 +626,7 @@ }, "node_modules/@fast-csv/parse/node_modules/@types/node": { "version": "14.18.63", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-14.18.63.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", "license": "MIT" }, @@ -361,9 +641,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -380,13 +660,13 @@ }, "node_modules/@js-joda/core": { "version": "5.7.0", - "resolved": "https://registry.npmmirror.com/@js-joda/core/-/core-5.7.0.tgz", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.7.0.tgz", "integrity": "sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg==", "license": "BSD-3-Clause" }, "node_modules/@prisma/client": { "version": "5.22.0", - "resolved": "https://registry.npmmirror.com/@prisma/client/-/client-5.22.0.tgz", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", "hasInstallScript": true, "license": "Apache-2.0", @@ -404,14 +684,14 @@ }, "node_modules/@prisma/debug": { "version": "5.22.0", - "resolved": "https://registry.npmmirror.com/@prisma/debug/-/debug-5.22.0.tgz", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "5.22.0", - "resolved": "https://registry.npmmirror.com/@prisma/engines/-/engines-5.22.0.tgz", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", "devOptional": true, "hasInstallScript": true, @@ -425,14 +705,14 @@ }, "node_modules/@prisma/engines-version": { "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", - "resolved": "https://registry.npmmirror.com/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "5.22.0", - "resolved": "https://registry.npmmirror.com/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", "devOptional": true, "license": "Apache-2.0", @@ -444,7 +724,7 @@ }, "node_modules/@prisma/get-platform": { "version": "5.22.0", - "resolved": "https://registry.npmmirror.com/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", "devOptional": true, "license": "Apache-2.0", @@ -454,28 +734,21 @@ }, "node_modules/@tediousjs/connection-string": { "version": "0.5.0", - "resolved": "https://registry.npmmirror.com/@tediousjs/connection-string/-/connection-string-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz", "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==", "license": "MIT" }, "node_modules/@testim/chrome-version": { "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@testim/chrome-version/-/chrome-version-1.1.4.tgz", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", "integrity": "sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==", "license": "MIT", "optional": true }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmmirror.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT", - "optional": true - }, "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", + "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", "dev": true, "license": "MIT" }, @@ -540,7 +813,7 @@ }, "node_modules/@types/express": { "version": "4.17.25", - "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.25.tgz", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "license": "MIT", @@ -552,9 +825,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.7", - "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", - "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", "dev": true, "license": "MIT", "dependencies": { @@ -565,9 +838,9 @@ } }, "node_modules/@types/express-session": { - "version": "1.18.2", - "resolved": "https://registry.npmmirror.com/@types/express-session/-/express-session-1.18.2.tgz", - "integrity": "sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.19.0.tgz", + "integrity": "sha512-GbypG0bog68UbOq2tSAp7SclvCUm3ha1uDi58OPRGK1NfRvCIu7Gz0M7fTGtpNG1T9a29GpuurQj9zEcT/lMXQ==", "dev": true, "license": "MIT", "dependencies": { @@ -588,44 +861,33 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/multer": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", - "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, "node_modules/@types/node": { - "version": "20.19.25", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.25.tgz", - "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } }, "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", "dev": true, "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", - "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true, "license": "MIT" }, "node_modules/@types/readable-stream": { - "version": "4.0.23", - "resolved": "https://registry.npmmirror.com/@types/readable-stream/-/readable-stream-4.0.23.tgz", - "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.25.tgz", + "integrity": "sha512-7GsxL8Wd5X3jh73/qgW7WCnbBvGSdD8U3E4Jiuus3/8I+PN20DtlkJtwFhr/sImXfDDQ2Z03i/9wNiIF3TVSsw==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -633,7 +895,7 @@ }, "node_modules/@types/send": { "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/@types/send/-/send-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", @@ -664,33 +926,9 @@ "@types/node": "*" } }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typespec/ts-http-runtime": { - "version": "0.3.4", - "resolved": "https://registry.npmmirror.com/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.4.tgz", - "integrity": "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ==", - "license": "MIT", - "dependencies": { - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/abort-controller": { "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", "dependencies": { @@ -702,7 +940,7 @@ }, "node_modules/accepts": { "version": "1.3.8", - "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { @@ -714,9 +952,9 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", "dev": true, "license": "MIT", "bin": { @@ -727,9 +965,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "dev": true, "license": "MIT", "dependencies": { @@ -739,15 +977,54 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "node_modules/adm-zip": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.1.tgz", + "integrity": "sha512-Xwrja8nx9e5o2N1my4DsKCeKpdrnACyr1wtbPxBDgGzKzKyE9kRtBFA8mWldI+RVlD7CBZNWY/wQ2+ydwOR6kQ==", "license": "MIT", + "optional": true, "engines": { - "node": ">= 14" + "node": ">=14.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -762,15 +1039,9 @@ "node": ">= 8" } }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, "node_modules/archiver": { "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/archiver/-/archiver-5.3.2.tgz", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", "license": "MIT", "dependencies": { @@ -788,7 +1059,7 @@ }, "node_modules/archiver-utils": { "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "license": "MIT", "dependencies": { @@ -807,18 +1078,40 @@ "node": ">= 6" } }, - "node_modules/archiver/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/archiver-utils/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, "node_modules/arg": { @@ -828,15 +1121,52 @@ "dev": true, "license": "MIT" }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asn1.js": { "version": "5.4.1", - "resolved": "https://registry.npmmirror.com/asn1.js/-/asn1.js-5.4.1.tgz", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "license": "MIT", "dependencies": { @@ -848,7 +1178,7 @@ }, "node_modules/ast-types": { "version": "0.13.4", - "resolved": "https://registry.npmmirror.com/ast-types/-/ast-types-0.13.4.tgz", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "license": "MIT", "optional": true, @@ -861,20 +1191,44 @@ }, "node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT", "optional": true }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/aws-ssl-profiles": { "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", "license": "MIT", "engines": { @@ -882,15 +1236,16 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", "license": "MIT", "optional": true, "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/balanced-match": { @@ -901,7 +1256,7 @@ }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { @@ -920,9 +1275,9 @@ "license": "MIT" }, "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmmirror.com/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "license": "MIT", "optional": true, "engines": { @@ -937,7 +1292,7 @@ }, "node_modules/big-integer": { "version": "1.6.52", - "resolved": "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.52.tgz", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", "license": "Unlicense", "engines": { @@ -946,7 +1301,7 @@ }, "node_modules/binary": { "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/binary/-/binary-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", "license": "MIT", "dependencies": { @@ -972,7 +1327,7 @@ }, "node_modules/bl": { "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", "dependencies": { @@ -981,75 +1336,46 @@ "readable-stream": "^3.4.0" } }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/bluebird": { "version": "3.4.7", - "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", "license": "MIT" }, "node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.8", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.16.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -1071,13 +1397,13 @@ }, "node_modules/brorand": { "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/brorand/-/brorand-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "license": "MIT" }, "node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "funding": [ { @@ -1101,7 +1427,7 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "license": "MIT", "engines": { @@ -1110,19 +1436,13 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, "node_modules/buffer-indexof-polyfill": { "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", "license": "MIT", "engines": { @@ -1131,16 +1451,16 @@ }, "node_modules/buffers": { "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/buffers/-/buffers-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "engines": { "node": ">=0.2.0" } }, "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" @@ -1152,29 +1472,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { @@ -1187,7 +1514,7 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { @@ -1203,7 +1530,7 @@ }, "node_modules/chainsaw": { "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/chainsaw/-/chainsaw-0.1.0.tgz", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", "license": "MIT/X11", "dependencies": { @@ -1239,31 +1566,31 @@ } }, "node_modules/chromedriver": { - "version": "142.0.1", - "resolved": "https://registry.npmmirror.com/chromedriver/-/chromedriver-142.0.1.tgz", - "integrity": "sha512-Cgm5rPQ1aNdkoA4wiwJiKweQErbv+8w5xnN5SA5EIAxN0bcZzWrbcFtwlaKG3DUcJJYQKKArLe4vg+s85wmhfQ==", + "version": "155.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-155.0.0.tgz", + "integrity": "sha512-db0fFnj6q4a0dqsTnkVqinnP4HrZdRIPpfYxTAEsoMCcA5dIN/Ximzx4+RLjudjBZNYGwUjY7PnqcF/6KuUd0w==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, "dependencies": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.12.0", - "compare-versions": "^6.1.0", - "extract-zip": "^2.0.1", - "proxy-agent": "^6.4.0", - "proxy-from-env": "^1.1.0", - "tcp-port-used": "^1.0.2" + "adm-zip": "^0.6.0", + "axios": "^1.19.0", + "compare-versions": "^6.1.1", + "proxy-agent": "^8.0.2", + "proxy-from-env": "^2.1.0", + "tcp-port-used": "^1.0.3" }, "bin": { "chromedriver": "bin/chromedriver" }, "engines": { - "node": ">=20" + "node": ">=22" } }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "optional": true, @@ -1276,7 +1603,7 @@ }, "node_modules/commander": { "version": "11.1.0", - "resolved": "https://registry.npmmirror.com/commander/-/commander-11.1.0.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "license": "MIT", "engines": { @@ -1285,14 +1612,14 @@ }, "node_modules/compare-versions": { "version": "6.1.1", - "resolved": "https://registry.npmmirror.com/compare-versions/-/compare-versions-6.1.1.tgz", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", "license": "MIT", "optional": true }, "node_modules/compress-commons": { "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/compress-commons/-/compress-commons-4.1.2.tgz", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", "license": "MIT", "dependencies": { @@ -1305,44 +1632,15 @@ "node": ">= 10" } }, - "node_modules/compress-commons/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "node_modules/content-disposition": { "version": "0.5.4", - "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", "dependencies": { @@ -1354,7 +1652,7 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { @@ -1362,18 +1660,18 @@ } }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, "node_modules/core-util-is": { @@ -1383,9 +1681,9 @@ "license": "MIT" }, "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -1393,11 +1691,15 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/crc-32": { "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "license": "Apache-2.0", "bin": { @@ -1409,7 +1711,7 @@ }, "node_modules/crc32-stream": { "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/crc32-stream/-/crc32-stream-4.0.3.tgz", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", "license": "MIT", "dependencies": { @@ -1420,20 +1722,6 @@ "node": ">= 10" } }, - "node_modules/crc32-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -1442,49 +1730,92 @@ "license": "MIT" }, "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-8.0.0.tgz", + "integrity": "sha512-6UHfyCux51b8PTGDgveqtz1tvphBku5DrMKKJbFAZAJOI2zsjDpDoYE1+QGj7FOMS4BdTFNJsJiR3zEB0xH0yQ==", "license": "MIT", "optional": true, "engines": { - "node": ">= 14" + "node": ">= 20" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", "license": "MIT" }, "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "ms": "2.0.0" } }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "license": "MIT", "optional": true }, "node_modules/default-browser": { - "version": "5.5.0", - "resolved": "https://registry.npmmirror.com/default-browser/-/default-browser-5.5.0.tgz", - "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", @@ -1499,7 +1830,7 @@ }, "node_modules/default-browser-id": { "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/default-browser-id/-/default-browser-id-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "license": "MIT", "engines": { @@ -1509,9 +1840,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "license": "MIT", "engines": { @@ -1521,10 +1869,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-7.0.1.tgz", + "integrity": "sha512-ABErK0IefDSyHjlPH7WUEenIAX2rPPnrDcDM+TS3z3+zu9TfyKKi07BQM+8rmxpdE2y1v5fjjdoAS/x4D2U60w==", "license": "MIT", "optional": true, "dependencies": { @@ -1533,12 +1898,15 @@ "esprima": "^4.0.1" }, "engines": { - "node": ">= 14" + "node": ">= 20" + }, + "peerDependencies": { + "quickjs-wasi": "^2.2.0" } }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "optional": true, @@ -1546,18 +1914,9 @@ "node": ">=0.4.0" } }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { @@ -1566,7 +1925,7 @@ }, "node_modules/destroy": { "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "license": "MIT", "engines": { @@ -1575,9 +1934,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1586,7 +1945,7 @@ }, "node_modules/dotenv": { "version": "16.6.1", - "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "license": "BSD-2-Clause", "engines": { @@ -1598,7 +1957,7 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { @@ -1612,16 +1971,52 @@ }, "node_modules/duplexer2": { "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/duplexer2/-/duplexer2-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "license": "BSD-3-Clause", "dependencies": { "readable-stream": "^2.0.2" } }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", "dependencies": { @@ -1630,13 +2025,13 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/elliptic": { "version": "6.6.1", - "resolved": "https://registry.npmmirror.com/elliptic/-/elliptic-6.6.1.tgz", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "license": "MIT", "dependencies": { @@ -1651,7 +2046,7 @@ }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { @@ -1660,16 +2055,102 @@ }, "node_modules/end-of-stream": { "version": "1.4.5", - "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "license": "MIT", "dependencies": { "once": "^1.4.0" } }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { @@ -1678,7 +2159,7 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { @@ -1686,9 +2167,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -1699,10 +2180,9 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", - "optional": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -1713,15 +2193,35 @@ "node": ">= 0.4" } }, + "node_modules/es-to-primitive": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/escodegen": { "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "license": "BSD-2-Clause", "optional": true, @@ -1743,7 +2243,7 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "license": "BSD-2-Clause", "optional": true, @@ -1757,7 +2257,7 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "license": "BSD-2-Clause", "optional": true, @@ -1767,7 +2267,7 @@ }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", "optional": true, @@ -1777,7 +2277,7 @@ }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { @@ -1786,7 +2286,7 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "engines": { @@ -1795,7 +2295,7 @@ }, "node_modules/events": { "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", "engines": { @@ -1804,7 +2304,7 @@ }, "node_modules/exceljs": { "version": "4.4.0", - "resolved": "https://registry.npmmirror.com/exceljs/-/exceljs-4.4.0.tgz", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", "license": "MIT", "dependencies": { @@ -1822,54 +2322,40 @@ "node": ">=8.3.0" } }, - "node_modules/exceljs/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmmirror.com/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz", + "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.13", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.16.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -1883,93 +2369,31 @@ } }, "node_modules/express-session": { - "version": "1.18.2", - "resolved": "https://registry.npmmirror.com/express-session/-/express-session-1.18.2.tgz", - "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.19.0.tgz", + "integrity": "sha512-0csaMkGq+vaiZTmSMMGkfdCOabYv192VbytFypcvI0MANrp+4i/7yEkJ0sbAEhycQjntaKGzYfjfXQyVb7BHMA==", "license": "MIT", "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.7", - "debug": "2.6.9", + "cookie": "~0.7.2", + "cookie-signature": "~1.0.7", + "debug": "~2.6.9", "depd": "~2.0.0", "on-headers": "~1.1.0", "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", + "safe-buffer": "~5.2.1", "uid-safe": "~2.1.5" }, "engines": { "node": ">= 0.8.0" - } - }, - "node_modules/express-session/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express-session/node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/express-session/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express-session/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "license": "BSD-2-Clause", - "optional": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/fast-csv": { "version": "4.3.6", - "resolved": "https://registry.npmmirror.com/fast-csv/-/fast-csv-4.3.6.tgz", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", "license": "MIT", "dependencies": { @@ -1980,16 +2404,6 @@ "node": ">=10.0.0" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "license": "MIT", - "optional": true, - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2004,42 +2418,27 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "statuses": "2.0.1", + "statuses": "~2.0.2", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz", + "integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==", "funding": [ { "type": "individual", @@ -2057,18 +2456,33 @@ } } }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "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", "optional": true, "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" @@ -2085,7 +2499,7 @@ }, "node_modules/fresh": { "version": "0.5.2", - "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", "engines": { @@ -2094,13 +2508,13 @@ }, "node_modules/fs-constants": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, @@ -2108,7 +2522,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -2121,7 +2534,7 @@ }, "node_modules/fstream": { "version": "1.0.12", - "resolved": "https://registry.npmmirror.com/fstream/-/fstream-1.0.12.tgz", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "deprecated": "This package is no longer supported.", "license": "ISC", @@ -2137,25 +2550,66 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/generate-function": { "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", "license": "MIT", "dependencies": { "is-property": "^1.0.2" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { @@ -2179,7 +2633,7 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { @@ -2190,42 +2644,68 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "license": "MIT", - "optional": true, "dependencies": { - "pump": "^3.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmmirror.com/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-8.0.1.tgz", + "integrity": "sha512-/5N/P4Lrh0p/mDwlDRi7Y1+P2o/OyzZI3l6Iz1Ov6XXwwm1y3RlZLuo3gVgML99djrEDtV980bBxSuOeHLk8ww==", "license": "MIT", "optional": true, "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", + "basic-ftp": "^5.3.1", + "data-uri-to-buffer": "8.0.0", "debug": "^4.3.4" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, + "node_modules/get-uri/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -2255,9 +2735,25 @@ "node": ">= 6" } }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { @@ -2269,10 +2765,22 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2283,9 +2791,36 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { @@ -2297,10 +2832,9 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", - "optional": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -2313,7 +2847,7 @@ }, "node_modules/hash.js": { "version": "1.1.7", - "resolved": "https://registry.npmmirror.com/hash.js/-/hash.js-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "license": "MIT", "dependencies": { @@ -2322,9 +2856,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/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" @@ -2335,7 +2869,7 @@ }, "node_modules/hmac-drbg": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "license": "MIT", "dependencies": { @@ -2345,50 +2879,117 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz", + "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==", "license": "MIT", + "optional": true, "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, "dependencies": { - "agent-base": "^7.1.2", + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", "debug": "4" }, "engines": { - "node": ">= 14" + "node": ">= 6" } }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, "node_modules/iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { @@ -2400,7 +3001,7 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { @@ -2427,13 +3028,13 @@ }, "node_modules/immediate": { "version": "3.0.6", - "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "license": "MIT" }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "license": "ISC", @@ -2444,14 +3045,28 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz", + "integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==", "license": "MIT", "optional": true, "engines": { @@ -2459,13 +3074,13 @@ } }, "node_modules/ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", "license": "MIT", "optional": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/ipaddr.js": { @@ -2477,6 +3092,57 @@ "node": ">= 0.10" } }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2490,9 +3156,70 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "license": "MIT", "bin": { @@ -2505,6 +3232,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2515,6 +3257,40 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2530,7 +3306,7 @@ }, "node_modules/is-inside-container": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "license": "MIT", "dependencies": { @@ -2546,6 +3322,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2556,22 +3356,174 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-property": { "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-property/-/is-property-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", "license": "MIT" }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-url": { "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/is-url/-/is-url-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", "license": "MIT", "optional": true }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-wsl": { "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.1.tgz", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", "license": "MIT", "dependencies": { @@ -2585,35 +3537,35 @@ } }, "node_modules/is2": { - "version": "2.0.9", - "resolved": "https://registry.npmmirror.com/is2/-/is2-2.0.9.tgz", - "integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz", + "integrity": "sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==", "license": "MIT", "optional": true, "dependencies": { "deep-is": "^0.1.3", - "ip-regex": "^4.1.0", - "is-url": "^1.2.4" + "ip-regex": "^2.1.0", + "is-url": "^1.2.2" }, "engines": { "node": ">=v0.10.0" } }, "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "license": "MIT" }, "node_modules/js-md4": { "version": "0.3.2", - "resolved": "https://registry.npmmirror.com/js-md4/-/js-md4-0.3.2.tgz", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", "license": "MIT" }, "node_modules/jsonwebtoken": { "version": "9.0.3", - "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "dependencies": { @@ -2633,10 +3585,16 @@ "npm": ">=6" } }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.2.tgz", + "integrity": "sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==", "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { "lie": "~3.3.0", @@ -2645,9 +3603,45 @@ "setimmediate": "^1.0.5" } }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/jwa": { "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/jwa/-/jwa-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { @@ -2658,7 +3652,7 @@ }, "node_modules/jwk-to-pem": { "version": "2.0.7", - "resolved": "https://registry.npmmirror.com/jwk-to-pem/-/jwk-to-pem-2.0.7.tgz", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.7.tgz", "integrity": "sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ==", "license": "Apache-2.0", "dependencies": { @@ -2669,7 +3663,7 @@ }, "node_modules/jws": { "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { @@ -2679,7 +3673,7 @@ }, "node_modules/keycloak-connect": { "version": "23.0.7", - "resolved": "https://registry.npmmirror.com/keycloak-connect/-/keycloak-connect-23.0.7.tgz", + "resolved": "https://registry.npmjs.org/keycloak-connect/-/keycloak-connect-23.0.7.tgz", "integrity": "sha512-7Coz2m/ar3QPYIPRhTr+sAuJB0BQn4r0vw+vsStRj0LpMVn2Uokgb2ufb9+OnTMIR3kjFLjQEJO3rtxBQJWSPA==", "license": "Apache-2.0", "dependencies": { @@ -2694,7 +3688,7 @@ }, "node_modules/lazystream": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/lazystream/-/lazystream-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "license": "MIT", "dependencies": { @@ -2704,9 +3698,45 @@ "node": ">= 0.6.3" } }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/lie": { "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "license": "MIT", "dependencies": { @@ -2715,138 +3745,139 @@ }, "node_modules/listenercount": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/listenercount/-/listenercount-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", "license": "ISC" }, "node_modules/lodash.defaults": { "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "license": "MIT" }, "node_modules/lodash.difference": { "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/lodash.difference/-/lodash.difference-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", "license": "MIT" }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "license": "MIT" }, "node_modules/lodash.flatten": { "version": "4.4.0", - "resolved": "https://registry.npmmirror.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "license": "MIT" }, "node_modules/lodash.groupby": { "version": "4.6.0", - "resolved": "https://registry.npmmirror.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", "license": "MIT" }, "node_modules/lodash.isfunction": { "version": "3.0.9", - "resolved": "https://registry.npmmirror.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, "node_modules/lodash.isnil": { "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.isundefined": { "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, "node_modules/lodash.union": { "version": "4.6.0", - "resolved": "https://registry.npmmirror.com/lodash.union/-/lodash.union-4.6.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", "license": "MIT" }, "node_modules/lodash.uniq": { "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "license": "MIT" }, "node_modules/long": { "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/long/-/long-5.3.2.tgz", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, "node_modules/lru-cache": { "version": "7.18.3", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "license": "ISC", + "optional": true, "engines": { "node": ">=12" } }, "node_modules/lru.min": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.2.tgz", - "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.5.tgz", + "integrity": "sha512-5J9ysMYUpYIg9RF2vJpy9SinEmSviFSe0GyPpCQ4L5QSkLAgeLXlTAOu2ZwWUU5m+0SBl6gUU1R1ZQB3aKypfA==", "license": "MIT", "engines": { "bun": ">=1.0.0", @@ -2867,7 +3898,7 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { @@ -2876,7 +3907,7 @@ }, "node_modules/media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { @@ -2885,7 +3916,7 @@ }, "node_modules/merge-descriptors": { "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "license": "MIT", "funding": { @@ -2894,7 +3925,7 @@ }, "node_modules/methods": { "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", "engines": { @@ -2903,7 +3934,7 @@ }, "node_modules/mime": { "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", "bin": { @@ -2915,7 +3946,7 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { @@ -2924,7 +3955,7 @@ }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { @@ -2936,20 +3967,20 @@ }, "node_modules/minimalistic-assert": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "license": "ISC" }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -2980,15 +4011,15 @@ } }, "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/mssql": { - "version": "11.0.1", - "resolved": "https://registry.npmmirror.com/mssql/-/mssql-11.0.1.tgz", - "integrity": "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-11.0.2.tgz", + "integrity": "sha512-x3Rdo3aR7/gQgpcVWA5EexMTd/BtHzdOkv94MTvV137UglFOZUJER6wRlq2Fl4R0F7JqBpDYyV4hX1kMTP0j7A==", "license": "MIT", "dependencies": { "@tediousjs/connection-string": "^0.5.0", @@ -3005,49 +4036,54 @@ "node": ">=18" } }, - "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", + "node_modules/mssql/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">= 6.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, + "node_modules/mssql/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/mysql2": { - "version": "3.15.3", - "resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.15.3.tgz", - "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.24.5.tgz", + "integrity": "sha512-X6Ujsr2QSkkLpkQGjxzpKRAPn9nu4axpR63ntBzquFVEvPOArgbUQ1sJjFKI7hnaYtiVZxa17Z7q18KSubW0IQ==", "license": "MIT", "dependencies": { - "aws-ssl-profiles": "^1.1.1", - "denque": "^2.1.0", + "aws-ssl-profiles": "^1.1.2", "generate-function": "^2.3.1", - "iconv-lite": "^0.7.0", - "long": "^5.2.1", - "lru.min": "^1.0.0", - "named-placeholders": "^1.1.3", - "seq-queue": "^0.0.5", - "sqlstring": "^2.3.2" + "iconv-lite": "^0.7.3", + "long": "^5.3.2", + "lru.min": "^1.1.4", + "named-placeholders": "^1.1.6", + "sql-escaper": "^1.5.1" }, "engines": { "node": ">= 8.0" + }, + "peerDependencies": { + "@types/node": ">= 8" } }, "node_modules/mysql2/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -3061,26 +4097,26 @@ } }, "node_modules/named-placeholders": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.3.tgz", - "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", "license": "MIT", "dependencies": { - "lru-cache": "^7.14.1" + "lru.min": "^1.1.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=8.0.0" } }, "node_modules/native-duplexpair": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/native-duplexpair/-/native-duplexpair-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==", "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { @@ -3088,9 +4124,9 @@ } }, "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", "license": "MIT", "optional": true, "engines": { @@ -3098,16 +4134,16 @@ } }, "node_modules/nodemon": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", - "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", "dev": true, "license": "MIT", "dependencies": { "chokidar": "^3.5.2", "debug": "^4", "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", + "minimatch": "^10.2.1", "pstree.remy": "^1.1.8", "semver": "^7.5.3", "simple-update-notifier": "^2.0.0", @@ -3126,6 +4162,70 @@ "url": "https://opencollective.com/nodemon" } }, + "node_modules/nodemon/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3146,7 +4246,7 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { @@ -3156,9 +4256,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { @@ -3170,7 +4299,7 @@ }, "node_modules/on-headers": { "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { @@ -3179,7 +4308,7 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { @@ -3188,7 +4317,7 @@ }, "node_modules/open": { "version": "10.2.0", - "resolved": "https://registry.npmmirror.com/open/-/open-10.2.0.tgz", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "license": "MIT", "dependencies": { @@ -3204,49 +4333,120 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "node_modules/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", "license": "MIT", - "optional": true, "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmmirror.com/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "node_modules/pac-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-9.1.0.tgz", + "integrity": "sha512-1aU+1mpj3DrQPfo3gh+3Gap3G5x+axnMx1P/y0ZF2ch7kb2meyOCAH8K2k9d27ROsTE7TnAerzxqF9aon2jqnA==", "license": "MIT", "optional": true, "dependencies": { - "degenerator": "^5.0.0", + "agent-base": "9.0.0", + "debug": "^4.3.4", + "get-uri": "8.0.1", + "http-proxy-agent": "9.1.0", + "https-proxy-agent": "9.1.0", + "pac-resolver": "9.0.1", + "quickjs-wasi": "^2.2.0", + "socks-proxy-agent": "10.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/pac-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/pac-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/pac-resolver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-9.0.1.tgz", + "integrity": "sha512-lJbS008tmkj08VhoM8Hzuv/VE5tK9MS0OIQ/7+s0lIF+BYhiQWFYzkSpML7lXs9iBu2jfmzBTLzhe9n6BX+dYw==", + "license": "MIT", + "optional": true, + "dependencies": { + "degenerator": "7.0.1", "netmask": "^2.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 20" + }, + "peerDependencies": { + "quickjs-wasi": "^2.2.0" } }, "node_modules/pako": { "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { @@ -3255,7 +4455,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "license": "MIT", "engines": { @@ -3263,22 +4463,15 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT", - "optional": true - }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -3288,14 +4481,22 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prisma": { "version": "5.22.0", - "resolved": "https://registry.npmmirror.com/prisma/-/prisma-5.22.0.tgz", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/engines": "5.22.0" }, @@ -3311,7 +4512,7 @@ }, "node_modules/process": { "version": "0.11.10", - "resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", "engines": { @@ -3325,9 +4526,9 @@ "license": "MIT" }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -3335,35 +4536,110 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-8.0.2.tgz", + "integrity": "sha512-idLLRewuemWd7GH/BDJzGiB0dWGfT2SQs3jy6NtZtGWU9uPTTSdeC1/cdbqLwgzhfv027daGFuXX426e2Eg20A==", "license": "MIT", "optional": true, "dependencies": { - "agent-base": "^7.1.2", + "agent-base": "9.0.0", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", + "http-proxy-agent": "9.1.0", + "https-proxy-agent": "9.1.0", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" + "pac-proxy-agent": "9.1.0", + "proxy-from-env": "^2.0.0", + "socks-proxy-agent": "10.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, - "node_modules/proxy-from-env": { + "node_modules/proxy-agent-negotiate": { "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz", + "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "kerberos": "^2.0.0" + }, + "peerDependenciesMeta": { + "kerberos": { + "optional": true + } + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT", "optional": true }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -3371,24 +4647,14 @@ "dev": true, "license": "MIT" }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "optional": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -3397,9 +4663,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quickjs-wasi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-2.2.0.tgz", + "integrity": "sha512-zQxXmQMrEoD3S+jQdYsloq4qAuaxKFHZj6hHqOYGwB2iQZH+q9e/lf5zQPXCKOk0WJuAjzRFbO4KwHIp2D05Iw==", + "license": "MIT", + "optional": true + }, "node_modules/random-bytes": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/random-bytes/-/random-bytes-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", "license": "MIT", "engines": { @@ -3408,7 +4681,7 @@ }, "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { @@ -3416,44 +4689,37 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, "node_modules/readdir-glob": { "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "license": "Apache-2.0", "dependencies": { @@ -3461,9 +4727,9 @@ } }, "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3471,7 +4737,7 @@ }, "node_modules/readdir-glob/node_modules/minimatch": { "version": "5.1.9", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "license": "ISC", "dependencies": { @@ -3494,15 +4760,57 @@ "node": ">=8.10.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/rfdc": { "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, "node_modules/rimraf": { "version": "2.7.1", - "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.7.1.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "license": "ISC", @@ -3515,7 +4823,7 @@ }, "node_modules/run-applescript": { "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.1.0.tgz", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "license": "MIT", "engines": { @@ -3525,9 +4833,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { @@ -3545,15 +4872,48 @@ ], "license": "MIT" }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/saxes": { "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/saxes/-/saxes-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", "license": "ISC", "dependencies": { @@ -3564,9 +4924,9 @@ } }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3576,94 +4936,117 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmmirror.com/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmmirror.com/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" - }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT" }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -3675,13 +5058,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -3692,7 +5075,7 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { @@ -3710,7 +5093,7 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { @@ -3742,7 +5125,7 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "license": "MIT", "optional": true, @@ -3752,13 +5135,13 @@ } }, "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", "license": "MIT", "optional": true, "dependencies": { - "ip-address": "^10.0.1", + "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -3767,23 +5150,58 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-10.1.0.tgz", + "integrity": "sha512-WlMj/67cEJ6MDI1OcsnjuYKDNDoyPCCYZ249kuuXPiMDw9F8PXkVaQ7YWu3siTydfQ/4BEZcvGzu+aYvz7dDCQ==", "license": "MIT", "optional": true, "dependencies": { - "agent-base": "^7.1.2", + "agent-base": "9.0.0", "debug": "^4.3.4", "socks": "^2.8.3" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 20" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", "optional": true, @@ -3793,50 +5211,112 @@ }, "node_modules/sprintf-js": { "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "license": "BSD-3-Clause" }, - "node_modules/sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "node_modules/sql-escaper": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.5.2.tgz", + "integrity": "sha512-6CKD38c31SENivxOADeMNLdukOnUxUcflKtzVWzace7Riv1v7cAEym5Cx9Q7gYZ3ezIJI7ZpqecQq8cqNeSSFg==", "license": "MIT", "engines": { - "node": ">= 0.6" + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/streamsearch": { + "node_modules/stop-iteration-iterator": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/supports-color": { "version": "5.5.0", @@ -3853,7 +5333,7 @@ }, "node_modules/tar-stream": { "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "license": "MIT", "dependencies": { @@ -3867,43 +5347,29 @@ "node": ">=6" } }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/tarn": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/tarn/-/tarn-3.0.2.tgz", - "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.1.2.tgz", + "integrity": "sha512-3RTvqKZcK/17jnJ8rMKFXbyNogywTs1z0gVPPwFsJGX46rkmUHOdIaSQ/aVO1rS7nH+soiXiWk7rvUXxndm8Dg==", "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/tcp-port-used": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz", - "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.3.tgz", + "integrity": "sha512-4CEQ3qRJYo+mtEbJ+OoQu3dF4TDkwaO3RDVC4UzP5cpAOIUWwuwPjD7sdxDFFqsMUjsXVVYBMlg/boAaloThMA==", "license": "MIT", "optional": true, "dependencies": { "debug": "4.3.1", - "is2": "^2.0.6" + "is2": "2.0.1" } }, "node_modules/tcp-port-used/node_modules/debug": { "version": "4.3.1", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.1.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "license": "MIT", "optional": true, @@ -3921,14 +5387,14 @@ }, "node_modules/tcp-port-used/node_modules/ms": { "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT", "optional": true }, "node_modules/tedious": { "version": "18.6.2", - "resolved": "https://registry.npmmirror.com/tedious/-/tedious-18.6.2.tgz", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-18.6.2.tgz", "integrity": "sha512-g7jC56o3MzLkE3lHkaFe2ZdOVFBahq5bsB60/M4NYUbocw/MCrS89IOEQUFr+ba6pb8ZHczZ/VqCyYeYq0xBAg==", "license": "MIT", "dependencies": { @@ -3947,9 +5413,177 @@ "node": ">=18" } }, + "node_modules/tedious/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/core-client": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.2.tgz", + "integrity": "sha512-1D2LpsU7y9xrqKjdIbsB7PlrRePw0xsVV8p+AKTlzITrWmscajryfJCdDJB/oGwvDI5HmRo04eMMADB67uwAwQ==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/core-rest-pipeline": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.24.0.tgz", + "integrity": "sha512-PpLsoDQ3AMmKZ0VU+0GrmqMxgp/sExjlVm4R+nLWngeoEGAzOIPVifaxKGU5gMv+nWELUoHfvrolWD+ZS/nFJg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/@azure/msal-common": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.13.0.tgz", + "integrity": "sha512-rOAy0KUcyBbdwVJ+f3uPpthXatFLLZN+/KWAsTLzk1aB23Xl9DRmmXYwSvBFOZyXj4jUQQ5FKxxRkhAFW1fOow==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/tedious/node_modules/@azure/msal-node": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.6.0.tgz", + "integrity": "sha512-uFY9NxrWHw8PwZx7gAX6PDn+9vdfS05+levc/kwkx77IkjfaldnQbbcQzzDIZ5Hq5Zdr6/z92oAIoRWKp6MnOA==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.13.0", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tedious/node_modules/@typespec/ts-http-runtime": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.6.tgz", + "integrity": "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tedious/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/tedious/node_modules/bl": { "version": "6.1.6", - "resolved": "https://registry.npmmirror.com/bl/-/bl-6.1.6.tgz", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", "license": "MIT", "dependencies": { @@ -3961,7 +5595,7 @@ }, "node_modules/tedious/node_modules/buffer": { "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { @@ -3983,9 +5617,52 @@ "ieee754": "^1.2.1" } }, + "node_modules/tedious/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/tedious/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/tedious/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/tedious/node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { @@ -3995,10 +5672,16 @@ "node": ">=0.10.0" } }, + "node_modules/tedious/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/tedious/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.8.0.tgz", + "integrity": "sha512-2jhTTU3Eilp/2DMT71gbcoqC35eQHasMJH0RHkYxwlxIv3+FR6Y5xAFZIwEGLxKkjZYEFlsiMNIdwUqjK2lLHQ==", "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", @@ -4011,19 +5694,10 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/tedious/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "license": "MIT", "engines": { "node": ">=14.14" @@ -4044,7 +5718,7 @@ }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { @@ -4062,12 +5736,22 @@ } }, "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "license": "MIT/X11", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.10.tgz", + "integrity": "sha512-pd6fq5qmiFuPA3TpdqBCqJXlOrSdCSt0l9IZDg83Vbf4x3m/X18prEwMMVM5LXp9vGL9NxjeiDGBrTasqyngXQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-to-primitive": "^1.3.4", + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.6", + "which-typed-array": "^1.1.24" + }, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/ts-node": { @@ -4116,13 +5800,13 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/type-is": { "version": "1.6.18", - "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { @@ -4133,11 +5817,100 @@ "node": ">= 0.6" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.5.tgz", + "integrity": "sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray.prototype.slice": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.6.tgz", + "integrity": "sha512-r+7YvGfM5TkTiDQ5EqWi/Z4L4KP3jUI+YCmTtWg1tmTHUw6xladLJqzR2heMaHBIM0TTFLZmn/EkIaEHYs/bIA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "math-intrinsics": "^1.1.0", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-offset": "^1.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/typescript": { "version": "5.9.3", @@ -4145,7 +5918,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4156,7 +5928,7 @@ }, "node_modules/uid-safe": { "version": "2.1.5", - "resolved": "https://registry.npmmirror.com/uid-safe/-/uid-safe-2.1.5.tgz", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "license": "MIT", "dependencies": { @@ -4166,6 +5938,24 @@ "node": ">= 0.8" } }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -4175,13 +5965,13 @@ }, "node_modules/undici-types": { "version": "6.21.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { @@ -4190,7 +5980,7 @@ }, "node_modules/unzipper": { "version": "0.10.14", - "resolved": "https://registry.npmmirror.com/unzipper/-/unzipper-0.10.14.tgz", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", "license": "MIT", "dependencies": { @@ -4206,6 +5996,42 @@ "setimmediate": "~1.0.4" } }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4214,7 +6040,7 @@ }, "node_modules/utils-merge": { "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "license": "MIT", "engines": { @@ -4222,12 +6048,16 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { @@ -4246,15 +6076,100 @@ "node": ">= 0.8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.24.tgz", + "integrity": "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/wsl-utils": { "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/wsl-utils/-/wsl-utils-0.1.0.tgz", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", "license": "MIT", "dependencies": { @@ -4269,30 +6184,10 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "license": "MIT", - "optional": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -4305,7 +6200,7 @@ }, "node_modules/zip-stream": { "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/zip-stream/-/zip-stream-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", "license": "MIT", "dependencies": { @@ -4319,7 +6214,7 @@ }, "node_modules/zip-stream/node_modules/archiver-utils": { "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-3.0.4.tgz", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", "license": "MIT", "dependencies": { @@ -4337,20 +6232,6 @@ "engines": { "node": ">= 10" } - }, - "node_modules/zip-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } } } } diff --git a/backend/package.json b/backend/package.json index 915918b..cd896ce 100644 --- a/backend/package.json +++ b/backend/package.json @@ -27,7 +27,6 @@ "express-session": "^1.17.3", "keycloak-connect": "^23.0.7", "mssql": "^11.0.1", - "multer": "^1.4.5-lts.1", "mysql2": "^3.0.0" }, "devDependencies": { @@ -35,11 +34,26 @@ "@types/cors": "^2.8.0", "@types/express": "^4.17.0", "@types/express-session": "^1.17.7", - "@types/multer": "^1.4.7", "@types/node": "^20.0.0", "nodemon": "^3.0.0", "prisma": "^5.0.0", "ts-node": "^10.9.0", "typescript": "^5.0.0" + }, + "overrides": { + "exceljs": { + "uuid": "11.1.1" + }, + "@azure-rest/core-client": "2.7.0", + "@azure/abort-controller": "2.1.2", + "@azure/core-auth": "1.10.1", + "@azure/core-client": "1.10.2", + "@azure/core-paging": "1.6.2", + "@azure/core-rest-pipeline": "1.24.0", + "@azure/core-tracing": "1.3.1", + "@azure/core-util": "1.13.1", + "@azure/identity": "4.13.1", + "@azure/logger": "1.3.0", + "@typespec/ts-http-runtime": "0.3.6" } } diff --git a/backend/prisma/migrations/20261007_maintenance_original_tree_snapshot/migration.sql b/backend/prisma/migrations/20261007_maintenance_original_tree_snapshot/migration.sql new file mode 100644 index 0000000..ce456c7 --- /dev/null +++ b/backend/prisma/migrations/20261007_maintenance_original_tree_snapshot/migration.sql @@ -0,0 +1,4 @@ +-- Nullable: legacy maintenance records have no reliable pre-maintenance snapshot. +-- Do not backfill guessed asset statuses. +ALTER TABLE `maintenance_record` + ADD COLUMN `original_tree_snapshot` LONGTEXT NULL; diff --git a/backend/prisma/migrations/20261009_print_claim_recovery/migration.sql b/backend/prisma/migrations/20261009_print_claim_recovery/migration.sql new file mode 100644 index 0000000..b04dbfe --- /dev/null +++ b/backend/prisma/migrations/20261009_print_claim_recovery/migration.sql @@ -0,0 +1,8 @@ +-- Add claim fencing and result verification without guessing historical delivery. +ALTER TABLE `print_terminal` ADD COLUMN `protocol_version` INTEGER NOT NULL DEFAULT 1; +ALTER TABLE `print_job` + ADD COLUMN `claim_token` VARCHAR(64) NULL, + ADD COLUMN `lease_expires_at` DATETIME(3) NULL, + ADD COLUMN `result_confirmed_by` INTEGER NULL, + ADD COLUMN `result_confirmed_at` DATETIME(3) NULL; +CREATE INDEX `print_job_status_lease_expires_at_idx` ON `print_job` (`status`, `lease_expires_at`); diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 88fd2f4..e77ff32 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -191,6 +191,7 @@ model PrintTerminal { platform String @db.VarChar(30) machine_name String @db.VarChar(100) status String @default("offline") @db.VarChar(20) + protocol_version Int @default(1) is_default Boolean @default(false) last_seen_at DateTime? selected_printer_name String? @db.VarChar(150) @@ -241,6 +242,10 @@ model PrintJob { payload_json String @db.Text error_message String? @db.Text claimed_at DateTime? + claim_token String? @db.VarChar(64) + lease_expires_at DateTime? + result_confirmed_by Int? + result_confirmed_at DateTime? started_at DateTime? completed_at DateTime? attempt_count Int @default(0) @@ -252,6 +257,7 @@ model PrintJob { requester User @relation("PrintJobRequester", fields: [requested_by], references: [id]) @@index([status, created_at]) + @@index([status, lease_expires_at]) @@index([target_terminal_id, status, created_at]) @@index([requested_by]) @@index([claimed_by_terminal_id]) @@ -798,6 +804,7 @@ model MaintenanceRecord { actual_end_date DateTime? // 实际完成日期 status String @default("in_progress") @db.VarChar(20) // in_progress/completed/cancelled repair_result String? @db.Text // 维修结果 + original_tree_snapshot String? @db.LongText // 送修前整棵资产树状态和关系快照 created_by Int created_at DateTime @default(now()) updated_at DateTime @updatedAt diff --git a/backend/src/controllers/__tests__/batchController.return-concurrency.test.ts b/backend/src/controllers/__tests__/batchController.return-concurrency.test.ts index c28b98f..6651b13 100644 --- a/backend/src/controllers/__tests__/batchController.return-concurrency.test.ts +++ b/backend/src/controllers/__tests__/batchController.return-concurrency.test.ts @@ -171,7 +171,7 @@ test('总量 2 并发借出 B 先提交后归还 A,使用锁后库存恢复为 unit_price: 100 } const borrowRecords = [ - { id: 401, equipment_id: lockedRoot.id, status: '已借出', person_id: 1 }, + { id: 401, equipment_id: lockedRoot.id, status: '已借出', person_id: 1, children_snapshot: JSON.stringify([{ id: mountedChild.id, parent_id: lockedRoot.id }]) }, { id: 402, equipment_id: lockedRoot.id, status: '已借出', person_id: 2 } ] const rootUpdates: any[] = [] diff --git a/backend/src/controllers/__tests__/batchImportController.validation.test.ts b/backend/src/controllers/__tests__/batchImportController.validation.test.ts new file mode 100644 index 0000000..5c70637 --- /dev/null +++ b/backend/src/controllers/__tests__/batchImportController.validation.test.ts @@ -0,0 +1,67 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import prisma from '../../utils/prisma' +import { batchImportEquipments } from '../batchImportController' + +test('Excel import preserves source row, rejects invalid quantities/prices without creating equipment, and retains legacy row fallback', async () => { + const originals = [prisma.assetType.findMany, prisma.department.findMany, prisma.storageLocation.findMany, prisma.equipment.create, prisma.operationLog.create] + let creates = 0 + ;(prisma.assetType.findMany as any) = async () => [{ id: 1, name: '电脑', can_be_child: false }] + ;(prisma.department.findMany as any) = async () => [] + ;(prisma.storageLocation.findMany as any) = async () => [] + ;(prisma.equipment.create as any) = async () => { creates++; return {} } + ;(prisma.operationLog.create as any) = async () => ({}) + const state = { code: 200, body: null as any } + const res = { locals: { user: { id: 9, role: 'it' } }, status(code: number) { state.code = code; return this }, json(body: any) { state.body = body; return this } } + try { + const quantities = [null, 0, -1, 1.5, 'abc', '', true] + const equipments = quantities.map((quantity, index) => ({ equipment_name: '电脑', equipment_type: '电脑', total_quantity: quantity, source_row: index + 10 })) + equipments.push({ equipment_name: '电脑', equipment_type: '电脑', total_quantity: 1, unit_price: -2, source_row: 27 } as any) + equipments.push({ equipment_name: '电脑', equipment_type: '电脑', total_quantity: 1, unit_price: 'bad-price', source_row: 28 } as any) + equipments.push({ equipment_name: '电脑', equipment_type: '电脑', total_quantity: 1, unit_price: 'Infinity', source_row: 29 } as any) + equipments.push({ equipment_name: '', equipment_type: '电脑', total_quantity: 1, source_row: 2.5 } as any) + await batchImportEquipments({ body: { equipments }, ip: 'mock' } as any, res as any) + assert.equal(state.code, 200) + assert.equal(creates, 0) + assert.equal(state.body.data.failed, 11) + assert.deepEqual(state.body.data.errors.map((item: any) => item.row), [10, 11, 12, 13, 14, 15, 16, 27, 28, 29, 12]) + assert.match(state.body.data.errors[0].message, /数量.*整数/) + assert.match(state.body.data.errors[7].message, /单价/) + } finally { + ;[prisma.assetType.findMany, prisma.department.findMany, prisma.storageLocation.findMany, prisma.equipment.create, prisma.operationLog.create] = originals as any + } +}) + +test('valid positive quantity and zero price retain exact stock/value while child types still require one item', async () => { + const targets: Array<[any, string, any]> = [ + [prisma.assetType, 'findMany', async () => [{ id: 1, name: '电脑', can_be_child: false }, { id: 2, name: '内存', can_be_child: true }]], + [prisma.assetType, 'findFirst', async () => ({ id: 1, short_name: 'PC' })], + [prisma.department, 'findMany', async () => []], + [prisma.storageLocation, 'findMany', async () => []], + [prisma.equipment, 'findMany', async () => []], + [prisma.equipment, 'findUnique', async () => null], + [prisma.operationLog, 'create', async () => ({})] + ] + const captured: any[] = [] + targets.push([prisma.equipment, 'create', async (args: any) => { captured.push(args.data); return args.data }]) + const original = targets.map(([object, key]) => object[key]) + targets.forEach(([object, key, fn]) => { object[key] = fn }) + const state = { body: null as any } + const res = { locals: { user: { id: 9, role: 'it' } }, json(body: any) { state.body = body; return this }, status() { return this } } + try { + await batchImportEquipments({ ip: 'mock', body: { equipments: [ + { equipment_name: '办公电脑', equipment_type: '电脑', total_quantity: '2', unit_price: 0, source_row: 6 }, + { equipment_name: '多数量配件', equipment_type: '内存', total_quantity: 2, source_row: 19 } + ] } } as any, res as any) + assert.equal(state.body.data.success, 1) + assert.equal(state.body.data.failed, 1) + assert.equal(state.body.data.errors[0].row, 19) + assert.match(state.body.data.errors[0].message, /子资产.*数量必须为 1/) + assert.equal(captured.length, 1) + assert.equal(captured[0].total_quantity, 2) + assert.equal(captured[0].remaining_quantity, 2) + assert.equal(captured[0].available_value, 0) + } finally { + targets.forEach(([object, key], index) => { object[key] = original[index] }) + } +}) diff --git a/backend/src/controllers/__tests__/borrowController.return-concurrency.test.ts b/backend/src/controllers/__tests__/borrowController.return-concurrency.test.ts index 5c8dcda..b7913f2 100644 --- a/backend/src/controllers/__tests__/borrowController.return-concurrency.test.ts +++ b/backend/src/controllers/__tests__/borrowController.return-concurrency.test.ts @@ -44,7 +44,7 @@ test('单笔归还省略部门动作或显式 clear 均按入仓规则清空部 equipment_id: root.id, person_id: 5, status: '已借出', - children_snapshot: null + children_snapshot: JSON.stringify([{ id: child.id, parent_id: root.id }]) } const rootUpdates: any[] = [] const childUpdates: any[] = [] @@ -271,7 +271,7 @@ test('行政可在报废前将唯一未归还资产快捷入库,并保留正 assert.equal(state.body.message, '资产已入库,可以继续申请报废') assert.equal(state.body.data.return_condition, '损坏') assert.equal(borrowRecord.status, '已归还') - assert.equal(rootUpdates.at(-1)?.data.status, '可用') + assert.equal(rootUpdates.at(-1)?.data.status, '已损坏') assert.equal(rootUpdates.at(-1)?.data.department_id, null) assert.deepEqual(movementReasons, ['报废前快捷入库']) } finally { diff --git a/backend/src/controllers/__tests__/borrowController.return-integrity.test.ts b/backend/src/controllers/__tests__/borrowController.return-integrity.test.ts new file mode 100644 index 0000000..35f9f34 --- /dev/null +++ b/backend/src/controllers/__tests__/borrowController.return-integrity.test.ts @@ -0,0 +1,129 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import prisma from '../../utils/prisma' +import { returnEquipment, scanReturn, scanReturnBatch } from '../borrowController' +import { batchReturn } from '../batchController' + +const now = new Date('2026-10-09T02:00:00Z') +const root = { + id: 10, equipment_code: 'ROOT', equipment_name: '主机', equipment_model: null, + equipment_type: '电脑', parent_id: null, department_id: 3, storage_location_id: null, + status: '已借出', mount_status: 'none', total_quantity: 1, remaining_quantity: 0, + unit_price: 100, updated_at: now +} +const child = { ...root, id: 11, equipment_code: 'CHILD', parent_id: 10, mount_status: 'mounted', unit_price: 20 } +const record = { id: 5, equipment_id: 10, equipment_code: 'ROOT', person_id: 8, status: '已借出', children_snapshot: null, updated_at: now } + +const run = async (handler: any, tree: any[], snapshot: string | null, body: Record) => { + const originals = [prisma.equipment.findUnique, prisma.borrowRecord.findFirst, prisma.borrowRecord.findMany, prisma.$transaction, prisma.operationLog.create] + const borrowed: any = { ...record, children_snapshot: snapshot } + let committed = tree.map((item) => ({ ...item })) + const movements: any[] = [] + let writes = 0 + ;(prisma.equipment.findUnique as any) = async () => ({ ...committed[0] }) + ;(prisma.borrowRecord.findFirst as any) = async () => ({ ...borrowed }) + ;(prisma.borrowRecord.findMany as any) = async () => [{ ...borrowed }] + ;(prisma.operationLog.create as any) = async () => ({}) + ;(prisma.$transaction as any) = async (callback: any) => { + const working = committed.map((item) => ({ ...item })) + const tx = { + $queryRaw: async (query: any) => { + if (query.sql.includes('WHERE equipment.id = ?')) return working.filter((item) => item.id === query.values[0]) + if (query.sql.includes('WHERE equipment.parent_id IN')) return working.filter((item) => query.values.includes(item.parent_id)) + return [] + }, + equipment: { + findUnique: async ({ where }: any) => ({ ...working.find((item) => item.id === where.id) }), + findMany: async ({ where }: any) => working.filter((item) => where.parent_id.in.includes(item.parent_id)).map((item) => ({ ...item })), + updateMany: async ({ where, data }: any) => { writes++; Object.assign(working.find((item) => item.id === where.id)!, data); return { count: 1 } }, + update: async ({ where, data }: any) => { writes++; Object.assign(working.find((item) => item.id === where.id)!, data); return {} } + }, + department: { findUnique: async () => ({ name: '部门' }) }, + borrowRecord: { updateMany: async ({ data }: any) => { writes++; Object.assign(borrowed, data); return { count: 1 } } }, + assetTransferRecord: { create: async ({ data }: any) => { movements.push(data); return { id: 1, items: [] } } } + } + const result = await callback(tx) + committed = working + return result + } + const state = { code: 200, body: null as any } + const res = { + locals: { user: { id: 9, role: 'it', username: 'it' } }, + status(code: number) { state.code = code; return this }, + json(payload: any) { state.body = payload; return this } + } + try { + await handler({ body: { equipment_id: 10, equipment_code: 'ROOT', equipment_ids: [10], equipment_codes: ['ROOT'], borrow_record_id: 5, ...body }, ip: 'mock' }, res) + return { state, committed, borrowed, movements, writes } + } finally { + ;[prisma.equipment.findUnique, prisma.borrowRecord.findFirst, prisma.borrowRecord.findMany, prisma.$transaction, prisma.operationLog.create] = originals as any + } +} + +test('single, scan and batch damaged/lost returns preserve physical stock and never create available value', async () => { + for (const handler of [returnEquipment, scanReturn, scanReturnBatch, batchReturn]) { + for (const condition of ['损坏', '丢失']) { + const result = await run(handler, [root], null, { return_condition: condition }) + assert.equal(result.state.code, 200) + if (handler === scanReturnBatch || handler === batchReturn) assert.equal(result.state.body.data.success_count, 1) + assert.equal(result.borrowed.status, '已归还') + assert.equal(result.borrowed.return_condition, condition) + assert.equal(result.committed[0].status, condition === '损坏' ? '已损坏' : '已丢失') + assert.equal(result.committed[0].remaining_quantity, condition === '损坏' ? 1 : 0) + assert.equal(result.committed[0].available_value, 0) + assert.equal(result.committed[0].borrowed_value, 0) + assert.equal(result.movements[0].action_type, condition === '丢失' ? 'loss' : 'return') + if (condition === '丢失') assert.match(result.movements[0].reason, /未入仓/) + } + } +}) + +test('non-normal return of multi-quantity asset preserves active borrow and inventory with explicit conflict', async () => { + for (const handler of [returnEquipment, scanReturn, scanReturnBatch, batchReturn]) { + for (const condition of ['损坏', '丢失']) { + const multi = { ...root, total_quantity: 2 } + const result = await run(handler, [multi], null, { return_condition: condition }) + if (handler === returnEquipment || handler === scanReturn) { + assert.equal(result.state.code, 409) + assert.match(result.state.body.message, /多数量.*原借用单保持未归还/) + } else { + assert.equal(result.state.body.data.failed_count, 1) + assert.match(result.state.body.data.results[0].message, /多数量/) + } + assert.equal(result.borrowed.status, '已借出') + assert.deepEqual(result.committed, [multi]) + assert.equal(result.writes, 0) + assert.equal(result.movements.length, 0) + } + } +}) + +test('empty borrowed-child snapshot detects new child; boolean or stale token cannot bypass confirmation', async () => { + for (const handler of [returnEquipment, scanReturn]) { + const first = await run(handler, [root, child], null, { return_condition: '正常', confirm_children_diff: true }) + assert.equal(first.state.body.data.need_confirm, true) + assert.equal(first.state.body.data.children_diff.added[0].id, child.id) + assert.equal(first.writes, 0) + assert.equal(first.movements.length, 0) + const token = first.state.body.data.confirmation_token + const changed = await run(handler, [root, { ...child, updated_at: new Date(now.getTime() + 1000) }], null, { confirmation_token: token }) + assert.equal(changed.state.body.data.need_confirm, true) + assert.equal(changed.writes, 0) + assert.notEqual(changed.state.body.data.confirmation_token, token) + const accepted = await run(handler, [root, child], null, { confirmation_token: token, return_condition: '损坏' }) + assert.equal(accepted.borrowed.status, '已归还') + assert.deepEqual(accepted.committed.map((item) => item.status), ['已损坏', '已损坏']) + assert.ok(accepted.committed.every((item) => item.available_value === 0)) + } +}) + +test('batch paths reject added child without writes and direct operator to single return', async () => { + for (const handler of [scanReturnBatch, batchReturn]) { + const result = await run(handler, [root, child], null, { return_condition: '正常' }) + assert.equal(result.state.body.data.failed_count, 1) + assert.match(result.state.body.data.results[0].message, /单个.*归还确认/) + assert.equal(result.writes, 0) + assert.equal(result.movements.length, 0) + assert.equal(result.borrowed.status, '已借出') + } +}) diff --git a/backend/src/controllers/__tests__/disposalController.state-machine.test.ts b/backend/src/controllers/__tests__/disposalController.state-machine.test.ts index b7cd3f2..4c51f6f 100644 --- a/backend/src/controllers/__tests__/disposalController.state-machine.test.ts +++ b/backend/src/controllers/__tests__/disposalController.state-machine.test.ts @@ -59,6 +59,35 @@ const createResponse = (role = 'it') => { return { res, state } } +test('已丢失主资产或配件不能申请报废,写入前拒绝且不改待报废状态', async () => { + const originalTransaction = prisma.$transaction + try { + for (const rootIsLost of [true, false]) { + const root = rootIsLost ? { ...rootEquipment, status: '已丢失', remaining_quantity: 0 } : rootEquipment + const child = rootIsLost ? childEquipment : { ...childEquipment, status: '已丢失', remaining_quantity: 0 } + let writes = 0 + const children = (ids: number[]) => ids.includes(root.id) ? [child] : [] + ;(prisma.$transaction as any) = async (callback: any) => callback({ + $queryRaw: async (query: any) => query.sql.includes('WHERE equipment.id = ?') ? [root] + : query.sql.includes('WHERE equipment.parent_id IN') ? children(query.values) : [], + equipment: { + findUnique: async () => root, + findMany: async (args: any) => children(args.where.parent_id.in), + updateMany: async () => { writes++; return { count: 1 } } + }, + disposalRecord: { create: async () => { writes++; return { id: 1 } } } + }) + const { res, state } = createResponse() + await createDisposal({ body: { equipment_id: root.id, item_ids: [root.id, child.id], disposal_reason: '丢失后报废', disposal_method: '报废' } } as any, res as any) + assert.equal(state.statusCode, 422) + assert.match(state.body.message, /已丢失.*不能申请报废/) + assert.equal(writes, 0) + } + } finally { + ;(prisma.$transaction as any) = originalTransaction + } +}) + test('报废流程钉钉正文不重复项目标题、时间优先且字段间保留空行', () => { const cases = [ { @@ -430,7 +459,7 @@ test('行政已处理列表包含全部已审批、已驳回和已完成记录 ) assert.equal(state.statusCode, 200) - assert.deepEqual(capturedWhere.AND[0], { + assert.deepEqual(capturedWhere.AND[1], { status: { in: ['approved', 'rejected', 'completed'] } }) assert.doesNotMatch(JSON.stringify(capturedWhere), /approved_by|completed_by/) @@ -464,7 +493,7 @@ test('行政可以查询自己发起的报废流程', async () => { ) assert.equal(state.statusCode, 200) - assert.deepEqual(capturedWhere.AND[0], { applied_by: 6 }) + assert.ok(capturedWhere.AND.some((condition: any) => condition.applied_by === 6)) } finally { ;(prisma.disposalRecord.findMany as any) = originalFindMany ;(prisma.disposalRecord.count as any) = originalCount @@ -493,7 +522,7 @@ test('IT即使请求all范围也只能查询本人发起的报废记录', async ) assert.equal(state.statusCode, 200) - assert.deepEqual(capturedWhere.AND[0], { applied_by: 6 }) + assert.ok(capturedWhere.AND.some((condition: any) => condition.applied_by === 6)) } finally { ;(prisma.disposalRecord.findMany as any) = originalFindMany ;(prisma.disposalRecord.count as any) = originalCount diff --git a/backend/src/controllers/__tests__/disposalController.visibility.test.ts b/backend/src/controllers/__tests__/disposalController.visibility.test.ts new file mode 100644 index 0000000..46d7e28 --- /dev/null +++ b/backend/src/controllers/__tests__/disposalController.visibility.test.ts @@ -0,0 +1,74 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import prisma from '../../utils/prisma' +import { getDisposalDetail, getDisposalList, getEquipmentDisposalHistory } from '../disposalController' + +const response = (role: string) => { + const state = { code: 200, body: null as any } + const res = { + locals: { user: { id: 6, role } }, + status(code: number) { state.code = code; return this }, + json(body: any) { state.body = body; return this } + } + return { state, res } +} + +test('disposal history limits IT to applicant across root and child equipment records', async () => { + const original = prisma.disposalRecord.findMany + const records = [{ id: 1, applied_by: 6 }, { id: 2, applied_by: 99 }] + const queries: any[] = [] + ;(prisma.disposalRecord.findMany as any) = async (args: any) => { + queries.push(args.where) + const userId = args.where.AND[0].applied_by + return records.filter((item) => !userId || item.applied_by === userId) + } + try { + for (const role of ['it', 'administrative', 'admin']) { + const { state, res } = response(role) + await getEquipmentDisposalHistory({ params: { equipment_id: '10' } } as any, res as any) + assert.equal(state.code, 200) + assert.deepEqual(state.body.data.map((item: any) => item.id), role === 'it' ? [1] : [1, 2]) + } + assert.deepEqual(queries[0].AND, [ + { applied_by: 6 }, + { OR: [{ equipment_id: 10 }, { items: { some: { equipment_id: 10 } } }] } + ]) + } finally { + ;(prisma.disposalRecord.findMany as any) = original + } +}) + +test('disposal detail permits IT own record and rejects another applicant', async () => { + const original = prisma.disposalRecord.findUnique + ;(prisma.disposalRecord.findUnique as any) = async (args: any) => ({ + id: args.where.id, applied_by: args.where.id === 1 ? 6 : 99, status: 'pending' + }) + try { + for (const [role, id, code] of [['it', '1', 200], ['it', '2', 403], ['admin', '2', 200], ['administrative', '2', 200]] as const) { + const { res, state } = response(role) + await getDisposalDetail({ params: { id } } as any, res as any) + assert.equal(state.code, code) + if (code === 403) assert.equal(state.body.data, null) + } + } finally { + ;(prisma.disposalRecord.findUnique as any) = original + } +}) + +test('disposal read endpoints reject unsupported roles and malformed IDs before DB access', async () => { + for (const handler of [getDisposalList, getDisposalDetail, getEquipmentDisposalHistory]) { + const { res, state } = response('user') + await handler({ query: {}, params: { id: '1', equipment_id: '1' } } as any, res as any) + assert.equal(state.code, 403) + } + for (const handler of [getDisposalDetail, getEquipmentDisposalHistory]) { + for (const value of ['0', '-1', 'NaN', '9007199254740992', ['1', '2']]) { + const { res, state } = response('it') + await handler({ params: { id: value, equipment_id: value } } as any, res as any) + assert.equal(state.code, 422) + } + } + const { res, state } = response('administrative') + await getDisposalList({ query: { scope: 'all' } } as any, res as any) + assert.equal(state.code, 403) +}) diff --git a/backend/src/controllers/__tests__/equipmentController.update-status.test.ts b/backend/src/controllers/__tests__/equipmentController.update-status.test.ts index dde7030..3bb1e95 100644 --- a/backend/src/controllers/__tests__/equipmentController.update-status.test.ts +++ b/backend/src/controllers/__tests__/equipmentController.update-status.test.ts @@ -49,6 +49,39 @@ const currentEquipment = { updated_at: new Date('2026-07-14T01:00:00.000Z') } +test('已丢失资产基础编辑保留状态与零库存,禁止增总数量生成虚假库存', async () => { + const originalFindUnique = prisma.equipment.findUnique + const originalTransaction = prisma.$transaction + const lostEquipment = { ...currentEquipment, status: '已丢失', remaining_quantity: 0, unit_price: 100, total_value: 100 } + let updateData: any + let writes = 0 + ;(prisma.equipment.findUnique as any) = async () => lostEquipment + ;(prisma.$transaction as any) = async (callback: any) => callback({ + $queryRawUnsafe: async () => [], + operationLog: { create: async () => ({ id: 1 }) }, + equipment: { + updateMany: async (args: any) => { writes++; updateData = args.data; return { count: 1 } }, + count: async () => 0 + } + }) + try { + const metadata = createMockResponse() + await updateEquipment({ params: { id: '903' }, body: { equipment_name: '丢失主机', total_quantity: 1, unit_price: 120 } } as any, metadata.res as any) + assert.equal(metadata.responseBody.statusCode, 200) + assert.equal(updateData.status, '已丢失') + assert.equal(updateData.remaining_quantity, 0) + assert.equal(updateData.available_value, 0) + assert.equal(updateData.borrowed_value, 0) + const quantity = createMockResponse() + await updateEquipment({ params: { id: '903' }, body: { total_quantity: 2 } } as any, quantity.res as any) + assert.equal(quantity.responseBody.statusCode, 409) + assert.equal(writes, 1) + } finally { + ;(prisma.equipment.findUnique as any) = originalFindUnique + ;(prisma.$transaction as any) = originalTransaction + } +}) + test('普通编辑资产时不应把待报废资产冲回可用', async () => { const originalFindUnique = prisma.equipment.findUnique const originalTransaction = prisma.$transaction diff --git a/backend/src/controllers/__tests__/healthController.test.ts b/backend/src/controllers/__tests__/healthController.test.ts new file mode 100644 index 0000000..74ba678 --- /dev/null +++ b/backend/src/controllers/__tests__/healthController.test.ts @@ -0,0 +1,64 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import prisma from '../../utils/prisma' +import { createHealthController } from '../healthController' + +const response = () => { + const state = { code: 0, body: null as any } + const res = { + status(code: number) { state.code = code; return this }, + json(body: any) { state.body = body; return this } + } + return { state, res } +} + +test('health verifies business tables and maintenance migration even with empty tables', async () => { + const original = prisma.$queryRaw + let queryText = '' + ;(prisma.$queryRaw as any) = async (parts: any) => { + queryText = parts.join('') + return [] + } + try { + const { res, state } = response() + await createHealthController()({} as any, res as any) + assert.equal(state.code, 200) + assert.equal(state.body.status, 'ok') + for (const table of ['equipment', 'maintenance_record', 'web_session', 'original_tree_snapshot', + 'claim_token', 'lease_expires_at', 'result_confirmed_by', 'result_confirmed_at', 'protocol_version']) { + assert.ok(queryText.includes(table)) + } + assert.match(queryText, /WHERE 1 = 0/) + } finally { + ;(prisma.$queryRaw as any) = original + } +}) + +test('health database failure returns sanitized 503', async () => { + const { res, state } = response() + await createHealthController(async () => { throw new Error('mysql://secret-password@host/db') })({} as any, res as any) + assert.equal(state.code, 503) + assert.equal(state.body.status, 'error') + assert.doesNotMatch(JSON.stringify(state.body), /secret|mysql|host/) +}) + +test('timed out health probe is shared until DB query finishes, then retries', async () => { + let calls = 0 + let finish!: () => void + const pending = new Promise((resolve) => { finish = resolve }) + const handler = createHealthController(async () => { calls += 1; if (calls === 1) await pending }, 10) + const responses = [response(), response(), response()] + await Promise.all(responses.map(({ res }) => handler({} as any, res as any))) + assert.equal(calls, 1) + assert.ok(responses.every(({ state }) => state.code === 503)) + const subsequent = response() + await handler({} as any, subsequent.res as any) + assert.equal(calls, 1) + assert.equal(subsequent.state.code, 503) + finish() + await new Promise((resolve) => setImmediate(resolve)) + const recovered = response() + await handler({} as any, recovered.res as any) + assert.equal(calls, 2) + assert.equal(recovered.state.code, 200) +}) diff --git a/backend/src/controllers/__tests__/maintenanceController.concurrency.test.ts b/backend/src/controllers/__tests__/maintenanceController.concurrency.test.ts index 6374cfb..b588ed4 100644 --- a/backend/src/controllers/__tests__/maintenanceController.concurrency.test.ts +++ b/backend/src/controllers/__tests__/maintenanceController.concurrency.test.ts @@ -31,7 +31,9 @@ const equipment = { status: '可用', total_quantity: 1, remaining_quantity: 1, - unit_price: 1000 + unit_price: 1000, + mount_status: 'none', + updated_at: new Date('2026-10-07T00:00:00Z') } test('并发送修未抢到资产状态时不创建第二条维修单', async () => { @@ -40,6 +42,7 @@ test('并发送修未抢到资产状态时不创建第二条维修单', async () let acquireWhere: any = null const tx = { + $queryRaw: async (query: any) => query.sql.includes('WHERE equipment.id = ?') ? [equipment] : [], equipment: { findUnique: async () => equipment, findMany: async () => [], @@ -80,7 +83,9 @@ test('并发送修未抢到资产状态时不创建第二条维修单', async () status: '可用', parent_id: null, remaining_quantity: 1, - department_id: 3 + department_id: 3, + mount_status: 'none', + updated_at: equipment.updated_at }) } finally { ;(prisma.$transaction as any) = originalTransaction @@ -92,6 +97,7 @@ test('重复完成维修时用 in_progress 条件阻止再次覆盖资产', asyn let maintenanceWhere: any = null const tx = { + $queryRaw: async (query: any) => query.sql.includes('WHERE equipment.id = ?') ? [{ ...equipment, status: '维护中' }] : [], maintenanceRecord: { findUnique: async () => ({ id: 20, @@ -107,6 +113,7 @@ test('重复完成维修时用 in_progress 条件阻止再次覆盖资产', asyn }, equipment: { findUnique: async () => ({ ...equipment, status: '维护中' }), + findMany: async () => [], updateMany: async () => { equipmentUpdated = true return { count: 1 } @@ -123,7 +130,7 @@ test('重复完成维修时用 in_progress 条件阻止再次覆盖资产', asyn ) assert.equal(responseBody.statusCode, 409) - assert.deepEqual(maintenanceWhere, { id: 20, status: 'in_progress' }) + assert.deepEqual(maintenanceWhere, { id: 20, status: 'in_progress', updated_at: undefined }) assert.equal(equipmentUpdated, false) } finally { ;(prisma.$transaction as any) = originalTransaction diff --git a/backend/src/controllers/__tests__/maintenanceController.snapshot.test.ts b/backend/src/controllers/__tests__/maintenanceController.snapshot.test.ts new file mode 100644 index 0000000..410cc81 --- /dev/null +++ b/backend/src/controllers/__tests__/maintenanceController.snapshot.test.ts @@ -0,0 +1,187 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import prisma from '../../utils/prisma' +import { cancelMaintenance, completeMaintenance, createMaintenance } from '../maintenanceController' +import { buildMaintenanceSnapshot } from '../../utils/maintenanceSnapshot' + +const root = { + id: 10, equipment_code: 'ROOT', equipment_name: '主机', equipment_model: null, + equipment_type: '电脑', parent_id: null, department_id: 1, storage_location_id: null, + status: '已损坏', mount_status: 'none', total_quantity: 2, remaining_quantity: 1, + unit_price: 100, updated_at: new Date('2026-10-07T00:00:00Z') +} +const child = { ...root, id: 11, equipment_code: 'CHILD', status: '可用', parent_id: 10, mount_status: 'mounted' } +const grandchild = { ...child, id: 12, equipment_code: 'GRANDCHILD', parent_id: 11, status: '已损坏' } +const originalTree = [root, child, grandchild] +const snapshot = buildMaintenanceSnapshot(originalTree as any) +const response = () => { + const state = { code: 200, body: null as any } + const res = { + locals: { user: { id: 6, username: 'test' } }, + status(code: number) { state.code = code; return this }, + json(body: any) { state.body = body; return this } + } + return { state, res } +} + +const harness = async ( + tree: any[], + record: any, + operation: 'create' | 'cancel' | 'complete', + failedEquipmentId?: number +) => { + const originalTransaction = prisma.$transaction + const originalLog = prisma.operationLog.create + let committedTree = tree.map((item) => ({ ...item })) + let committedRecord = { ...record } + let options: any + const writes: any[] = [] + const locks: string[] = [] + let created: any + ;(prisma.$transaction as any) = async (callback: any, txOptions: any) => { + options = txOptions + const working = tree.map((item) => ({ ...item })) + let workingRecord = { ...record } + const tx = { + $queryRaw: async (query: any) => { + locks.push(query.sql) + if (query.sql.includes('WHERE equipment.id = ?')) return working.filter((item) => item.id === query.values[0]) + if (query.sql.includes('WHERE equipment.parent_id IN')) return working.filter((item) => query.values.includes(item.parent_id)) + return [] + }, + equipment: { + findUnique: async ({ where }: any) => working.find((item) => item.id === where.id), + findMany: async ({ where }: any) => working.filter((item) => where.parent_id.in.includes(item.parent_id)), + updateMany: async (args: any) => { + writes.push(args) + if (args.where.id === failedEquipmentId) return { count: 0 } + Object.assign(working.find((item) => item.id === args.where.id), args.data) + return { count: 1 } + } + }, + borrowRecord: { count: async () => 0 }, + disposalItem: { count: async () => 0 }, + maintenanceRecord: { + count: async () => 0, + findUnique: async () => workingRecord, + create: async ({ data }: any) => { created = data; return { id: 20, ...data } }, + updateMany: async ({ data }: any) => { workingRecord = { ...workingRecord, ...data }; return { count: 1 } } + } + } + const result = await callback(tx) + committedTree = working + committedRecord = workingRecord + return result + } + ;(prisma.operationLog.create as any) = async () => ({}) + try { + const { state, res } = response() + if (operation === 'create') { + await createMaintenance({ body: { equipment_id: 10, issue_description: '故障' } } as any, res as any) + } else if (operation === 'cancel') { + await cancelMaintenance({ params: { id: '20' }, body: { reason: '取消' } } as any, res as any) + } else { + await completeMaintenance({ params: { id: '20' }, body: { actual_cost: 0, repair_result: '已修复' } } as any, res as any) + } + return { state, committedTree, committedRecord, writes, options, created, locks } + } finally { + ;(prisma.$transaction as any) = originalTransaction + ;(prisma.operationLog.create as any) = originalLog + } +} + +const record = { id: 20, equipment_id: 10, equipment_code: 'ROOT', status: 'in_progress', original_tree_snapshot: snapshot, updated_at: root.updated_at } +const maintenanceTree = originalTree.map((item) => ({ ...item, status: '维护中' })) + +test('sending entire asset tree to maintenance preserves each original status and inventory', async () => { + const result = await harness(originalTree, {}, 'create') + assert.equal(result.state.code, 200) + assert.equal(result.options.isolationLevel, 'ReadCommitted') + assert.deepEqual(JSON.parse(result.created.original_tree_snapshot).assets, JSON.parse(snapshot).assets) + assert.ok(result.committedTree.every((item) => item.status === '维护中' && item.remaining_quantity === 1)) + assert.equal(result.writes.length, 3) +}) + +test('cancel restores mixed damaged/available states for every descendant and recomputes values', async () => { + const result = await harness(maintenanceTree, record, 'cancel') + assert.equal(result.state.code, 200) + assert.equal(result.committedRecord.status, 'cancelled') + assert.equal(result.options.isolationLevel, 'ReadCommitted') + assert.deepEqual(result.committedTree.map((item) => item.status), ['已损坏', '可用', '已损坏']) + assert.match(result.locks[0], /FROM maintenance_record.*FOR UPDATE/) + assert.match(result.locks[1], /FROM equipment.*FOR UPDATE/) + assert.deepEqual(result.committedTree.map((item) => item.available_value), [0, 100, 0]) + assert.deepEqual(result.committedTree.map((item) => item.borrowed_value), [0, 100, 0]) + assert.deepEqual(result.writes.map((item) => item.where.parent_id), [null, 10, 11]) + assert.ok(result.writes.every((item) => item.where.status === '维护中' && item.where.updated_at)) +}) + +test('legacy or corrupt snapshots return explicit manual verification conflict without writes', async () => { + for (const value of [null, '', '{', '{}', JSON.stringify({ version: 1, assets: [JSON.parse(snapshot).assets[0], JSON.parse(snapshot).assets[0]] })]) { + const result = await harness(maintenanceTree, { ...record, original_tree_snapshot: value }, 'cancel') + assert.equal(result.state.code, 409) + assert.match(result.state.body.message, /人工核实/) + assert.equal(result.writes.length, 0) + assert.equal(result.committedRecord.status, 'in_progress') + } +}) + +test('changed asset tree, inventory or maintenance status prevents cancellation', async () => { + const cases = [ + [...maintenanceTree, { ...child, id: 13, status: '维护中' }], + maintenanceTree.slice(0, 2), + maintenanceTree.map((item) => item.id === 12 ? { ...item, parent_id: 10 } : item), + maintenanceTree.map((item) => item.id === 11 ? { ...item, status: '可用' } : item), + maintenanceTree.map((item) => item.id === 11 ? { ...item, remaining_quantity: 2 } : item) + ] + for (const tree of cases) { + const result = await harness(tree, record, 'cancel') + assert.equal(result.state.code, 409) + assert.equal(result.writes.length, 0) + } + const completed = await harness(maintenanceTree, { ...record, status: 'completed' }, 'cancel') + assert.equal(completed.state.code, 409) + assert.equal(completed.writes.length, 0) +}) + +test('conditional descendant write conflict rolls back both asset tree and maintenance record', async () => { + const result = await harness(maintenanceTree, record, 'cancel', 11) + assert.equal(result.state.code, 409) + assert.deepEqual(result.committedTree, maintenanceTree) + assert.equal(result.committedRecord.status, 'in_progress') +}) + +test('completion locks whole asset tree after maintenance row and conditionally repairs all descendants', async () => { + const result = await harness(maintenanceTree, record, 'complete') + assert.equal(result.state.code, 200) + assert.equal(result.options.isolationLevel, 'ReadCommitted') + assert.match(result.locks[0], /FROM maintenance_record.*FOR UPDATE/) + assert.match(result.locks[1], /FROM equipment.*FOR UPDATE/) + assert.deepEqual(result.committedTree.map((item) => item.status), ['可用', '可用', '可用']) + assert.equal(result.committedRecord.status, 'completed') + assert.equal(result.writes.length, 3) + assert.deepEqual(result.writes.map((item) => item.where.parent_id), [null, 10, 11]) + assert.ok(result.writes.every((item) => item.where.status === '维护中' && item.where.updated_at && item.where.unit_price)) +}) + +test('completion rejects removed, reparented, independently borrowed or changed-stock descendants without overwrite', async () => { + for (const tree of [ + maintenanceTree.slice(0, 2), + maintenanceTree.map((item) => item.id === 12 ? { ...item, parent_id: 10 } : item), + maintenanceTree.map((item) => item.id === 11 ? { ...item, status: '已借出' } : item), + maintenanceTree.map((item) => item.id === 11 ? { ...item, remaining_quantity: 0 } : item) + ]) { + const result = await harness(tree, record, 'complete') + assert.equal(result.state.code, 409) + assert.equal(result.writes.length, 0) + assert.equal(result.committedRecord.status, 'in_progress') + assert.deepEqual(result.committedTree, tree) + } +}) + +test('completion descendant CAS failure rolls back earlier repaired rows and completed record', async () => { + const result = await harness(maintenanceTree, record, 'complete', 11) + assert.equal(result.state.code, 409) + assert.deepEqual(result.committedTree, maintenanceTree) + assert.equal(result.committedRecord.status, 'in_progress') +}) diff --git a/backend/src/controllers/__tests__/mountController.concurrency.test.ts b/backend/src/controllers/__tests__/mountController.concurrency.test.ts index 7e1e3b8..09e3aba 100644 --- a/backend/src/controllers/__tests__/mountController.concurrency.test.ts +++ b/backend/src/controllers/__tests__/mountController.concurrency.test.ts @@ -288,3 +288,30 @@ test('卸载接口拒绝直接把配件设为维修中或待报废', async () => ;(prisma.$transaction as any) = originalTransaction } }) + +test('maintenance parent rejects mounting and unmounting before any child or history writes', async () => { + const originalTransaction = prisma.$transaction + let writes = 0 + ;(prisma.$transaction as any) = async (callback: any) => callback({ + $queryRaw: async () => [], + equipment: { + findUnique: async ({ where }: any) => where.id === parent.id + ? { ...parent, status: '维护中' } + : { ...child, parent_id: parent.id, status: '维护中' }, + updateMany: async () => { writes++; return { count: 1 } }, + update: async () => { writes++; return {} } + }, + mountHistory: { create: async () => { writes++; return {} } } + }) + try { + for (const handler of [mountChild, unmountChild]) { + const { res, state } = createResponse() + await handler({ params: { parentId: String(parent.id) }, body: { child_id: child.id } } as any, res as any) + assert.equal(state.statusCode, 409) + assert.match(state.body.message, /先完成或取消维修/) + } + assert.equal(writes, 0) + } finally { + ;(prisma.$transaction as any) = originalTransaction + } +}) diff --git a/backend/src/controllers/__tests__/statisticsController.dashboard.test.ts b/backend/src/controllers/__tests__/statisticsController.dashboard.test.ts index 1571e10..3104440 100644 --- a/backend/src/controllers/__tests__/statisticsController.dashboard.test.ts +++ b/backend/src/controllers/__tests__/statisticsController.dashboard.test.ts @@ -20,13 +20,15 @@ test('首页统计应排除已报废库存、完整覆盖在册状态并使用 { status: '可用', _sum: { total_quantity: 1122, remaining_quantity: 1122, total_value: 424805, borrowed_value: 0, available_value: 424805 } }, { status: '已借出', _sum: { total_quantity: 4507, remaining_quantity: 0, total_value: 2848258.44, borrowed_value: 2848258.44, available_value: 0 } }, { status: '待报废', _sum: { total_quantity: 1, remaining_quantity: 1, total_value: 0, borrowed_value: 0, available_value: 0 } }, + { status: '已丢失', _sum: { total_quantity: 3, remaining_quantity: 0, total_value: 300, borrowed_value: 0, available_value: 0 } }, { status: '已报废', _sum: { total_quantity: 127, remaining_quantity: 127, total_value: 83257, borrowed_value: 0, available_value: 0 } } ] } if (args.by.includes('status')) { return [ { equipment_type: '显示器', status: '可用', _sum: { total_quantity: 10, remaining_quantity: 10 } }, - { equipment_type: '显示器', status: '待报废', _sum: { total_quantity: 1, remaining_quantity: 1 } } + { equipment_type: '显示器', status: '待报废', _sum: { total_quantity: 1, remaining_quantity: 1 } }, + { equipment_type: '仅丢失资产类型', status: '已丢失', _sum: { total_quantity: 3, remaining_quantity: 0 } } ] } return [{ equipment_type: '显示器', _sum: { remaining_quantity: 8 } }] @@ -47,13 +49,16 @@ test('首页统计应排除已报废库存、完整覆盖在册状态并使用 total_value_with_children: 0 } ] - ;(prisma.equipment.aggregate as any) = async () => ({ - _count: { id: 2057 }, - _sum: { total_quantity: 2057 } - }) + ;(prisma.equipment.aggregate as any) = async (args: any) => { + assert.deepEqual(args.where.status.notIn, ['已报废', '已丢失']) + return { _count: { id: 2057 }, _sum: { total_quantity: 2057 } } + } ;(prisma.equipment.count as any) = async () => 3 ;(prisma.borrowRecord.findMany as any) = async (args: any) => { borrowRanges.push(args.where.borrow_time || args.where.return_time) + if (args.where.return_time) { + assert.deepEqual(args.where.return_condition, { not: '丢失' }) + } return args.where.borrow_time ? [{ children_snapshot: '[{"unit_price":20}]', equipment: { unit_price: 100 } }] : [] @@ -82,13 +87,20 @@ test('首页统计应排除已报废库存、完整覆盖在册状态并使用 assert.equal(state.body.data.borrowed_equipment, 4507) assert.equal(state.body.data.pending_disposal_equipment, 1) assert.equal(state.body.data.disposed_equipment, 127) + assert.equal(state.body.data.lost_equipment, 3) + assert.equal(state.body.data.other_equipment, 0) + const lostOnlyType = state.body.data.inventory_by_type.find((row: any) => row.equipment_type === '仅丢失资产类型') + assert.equal(lostOnlyType.total_quantity, 0) + assert.equal(lostOnlyType.lost_quantity, 3) + assert.equal(lostOnlyType.other_quantity, 0) assert.equal(state.body.data.borrowed_without_active_record, 3) assert.equal(state.body.data.today_borrow, 1) assert.equal(state.body.data.today_return, 0) assert.equal(state.body.data.today_borrow_value, 120) assert.equal(state.body.data.business_timezone, 'Asia/Shanghai') - assert.equal(state.body.data.inventory_scope, 'current_excluding_disposed') - assert.equal(state.body.data.total_value, 3273063.44) + assert.equal(state.body.data.inventory_scope, 'current_excluding_disposed_and_lost') + assert.equal(state.body.data.value_scope, 'book_value_excluding_disposed_including_lost') + assert.equal(state.body.data.total_value, 3273363.44) assert.equal(state.body.data.borrowed_value, 2848258.44) assert.equal( state.body.data.available_equipment + diff --git a/backend/src/controllers/batchController.ts b/backend/src/controllers/batchController.ts index 51acfd2..2f83d92 100644 --- a/backend/src/controllers/batchController.ts +++ b/backend/src/controllers/batchController.ts @@ -1,6 +1,8 @@ import { Request, Response } from 'express' import { Prisma } from '@prisma/client' import prisma from '../utils/prisma' +import { parseBusinessDate } from '../utils/businessDate' +import { getReturnChildrenDiff } from '../utils/returnSnapshot' import { successResponse, errorResponse } from '../utils/response' import { logOperation } from './operationLogController' import { assertReturnStorageDepartmentContract } from '../utils/borrowDepartment' @@ -8,7 +10,9 @@ import { buildSyncedBusinessDepartmentWhere } from '../utils/organization' import { borrowAssetTree, finalizeNormalReturnState, - recordReturnToStorage, + loadAndLockAssetTree, + createAssetMovementRecord, + assertReturnConditionSupported, transferAssetTree } from '../services/assetMovementService' @@ -24,7 +28,7 @@ const buildBorrowNotes = (notes?: string, isLongTerm?: boolean) => { const batchBorrowTransaction = async ( equipmentId: number, person: any, - expectedReturnTime?: string, + expectedReturnTime?: Date | null, isLongTerm?: boolean, notes?: string ) => { @@ -35,7 +39,7 @@ const batchBorrowTransaction = async ( person, expectedReturnTime: isLongTerm ? null - : (expectedReturnTime ? new Date(expectedReturnTime) : null), + : (expectedReturnTime || null), isLongTerm, notes: finalNotes }), @@ -51,10 +55,17 @@ const batchReturnTransaction = async ( operatedBy: number ) => { return prisma.$transaction(async (tx) => { - const { items } = await recordReturnToStorage(tx, { - rootEquipmentId: equipment.id, + const items = await loadAndLockAssetTree(tx, equipment.id) + assertReturnConditionSupported(items, returnCondition) + if (getReturnChildrenDiff(items, borrowRecord)) { + throw Object.assign(new Error('配件与借出时不一致,请使用单个归还确认'), { status: 409 }) + } + await createAssetMovementRecord(tx, { + items, + actionType: returnCondition === '丢失' ? 'loss' : 'return', + toDepartmentId: null, operatedBy, - reason: notes || '批量归还入仓' + reason: returnCondition === '丢失' ? `丢失登记(未入仓)${notes ? `;${notes}` : ''}` : notes || '批量归还入仓' }) const currentRoot = items[0] const updatedRecord = await tx.borrowRecord.updateMany({ @@ -75,8 +86,8 @@ const batchReturnTransaction = async ( throw new Error('该借用记录已归还,请刷新后重试') } - await finalizeNormalReturnState(tx, currentRoot) - }) + await finalizeNormalReturnState(tx, currentRoot, returnCondition, items) + }, { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted }) } // 批量借出 @@ -88,6 +99,7 @@ export const batchBorrow = async (req: Request, res: Response) => { return errorResponse(res, '请选择要借出的资产') } + const expectedReturn = is_long_term ? null : parseBusinessDate(expected_return_time, '预计归还时间') const person = await prisma.person.findUnique({ where: { id: Number(person_id) } }) if (!person || person.status !== 'active') { return errorResponse(res, '使用者不存在或已禁用') @@ -127,7 +139,7 @@ export const batchBorrow = async (req: Request, res: Response) => { await batchBorrowTransaction( equipment.id, person, - expected_return_time, + expectedReturn, is_long_term, notes ) @@ -158,7 +170,7 @@ export const batchBorrow = async (req: Request, res: Response) => { successResponse(res, { success_count: successCount, failed_count: failedCount, results }, '批量借出完成') } catch (error: any) { - errorResponse(res, error.message || '批量借出失败') + errorResponse(res, error.message || '批量借出失败', error.status || 400) } } @@ -213,7 +225,7 @@ export const batchReturn = async (req: Request, res: Response) => { await batchReturnTransaction(equipment, borrowRecord, return_condition, notes, operatedBy) - results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'success', message: '归还成功' }) + results.push({ equipment_id: equipmentId, equipment_code: equipment.equipment_code, status: 'success', message: return_condition === '丢失' ? '丢失已登记(未入仓)' : '归还成功' }) successCount++ } catch (error: any) { results.push({ equipment_id: equipmentId, status: 'failed', message: error.message }) diff --git a/backend/src/controllers/batchImportController.ts b/backend/src/controllers/batchImportController.ts index 4bbf644..b7dd599 100644 --- a/backend/src/controllers/batchImportController.ts +++ b/backend/src/controllers/batchImportController.ts @@ -6,6 +6,7 @@ import { calculateEquipmentValues } from '../utils/assetValue' import { buildSyncedBusinessDepartmentWhere } from '../utils/organization' interface PersonImportData { + source_row?: number nickname: string real_name: string job_number?: string @@ -17,12 +18,13 @@ interface PersonImportData { } interface EquipmentImportData { + source_row?: number equipment_name: string equipment_type: string equipment_model?: string department?: string storage_location?: string - unit_price?: number + unit_price?: number | string total_quantity: number supplier?: string purchase_date?: string @@ -35,6 +37,9 @@ const normalizeOptionalText = (value: unknown) => { return text || null } +const getSourceRow = (value: unknown, index: number) => + typeof value === 'number' && Number.isSafeInteger(value) && value >= 2 ? value : index + 2 + // 批量导入使用者 export const batchImportPersons = async (req: Request, res: Response) => { try { @@ -56,7 +61,7 @@ export const batchImportPersons = async (req: Request, res: Response) => { for (let i = 0; i < persons.length; i++) { const person = persons[i] - const rowNum = i + 2 // Excel行号从2开始(第1行是表头) + const rowNum = getSourceRow(person?.source_row, i) try { // 验证必填字段 @@ -173,11 +178,11 @@ export const batchImportEquipments = async (req: Request, res: Response) => { for (let i = 0; i < equipments.length; i++) { const eq = equipments[i] - const rowNum = i + 2 + const rowNum = getSourceRow(eq?.source_row, i) try { // 验证必填字段 - if (!eq.equipment_name || !eq.equipment_type || !eq.total_quantity) { + if (!eq || !normalizeOptionalText(eq.equipment_name) || !normalizeOptionalText(eq.equipment_type)) { results.errors.push({ row: rowNum, message: '资产名称、资产类型、数量为必填项' }) results.failed++ continue @@ -190,8 +195,22 @@ export const batchImportEquipments = async (req: Request, res: Response) => { results.failed++ continue } + const quantityInput = eq.total_quantity + const totalQuantity = typeof quantityInput === 'number' || (typeof quantityInput === 'string' && String(quantityInput).trim()) + ? Number(quantityInput) : NaN + if (!Number.isSafeInteger(totalQuantity) || totalQuantity <= 0) { + results.errors.push({ row: rowNum, message: '数量必须是大于0的整数' }) + results.failed++ + continue + } + const unitPrice = eq.unit_price === undefined || eq.unit_price === null ? 0 : Number(eq.unit_price) + if (!Number.isFinite(unitPrice) || unitPrice < 0) { + results.errors.push({ row: rowNum, message: '单价必须是大于或等于0的有效数字' }) + results.failed++ + continue + } - if (assetType.can_be_child && Number(eq.total_quantity) !== 1) { + if (assetType.can_be_child && totalQuantity !== 1) { results.errors.push({ row: rowNum, message: `子资产类型 "${eq.equipment_type}" 导入时数量必须为 1` }) results.failed++ continue @@ -228,8 +247,6 @@ export const batchImportEquipments = async (req: Request, res: Response) => { const equipmentCode = await generateEquipmentCode(eq.equipment_type, prisma, locationId) // 计算价值 - const unitPrice = Number(eq.unit_price) || 0 - const totalQuantity = Number(eq.total_quantity) || 1 const values = calculateEquipmentValues({ unitPrice, totalQuantity, diff --git a/backend/src/controllers/borrowController.ts b/backend/src/controllers/borrowController.ts index 5219743..dd3f9a7 100644 --- a/backend/src/controllers/borrowController.ts +++ b/backend/src/controllers/borrowController.ts @@ -1,7 +1,8 @@ import { Request, Response } from 'express' import { Prisma } from '@prisma/client' -import { createHash } from 'crypto' import prisma from '../utils/prisma' +import { parseBusinessDate } from '../utils/businessDate' +import { getReturnChildrenDiff, buildReturnConfirmationToken } from '../utils/returnSnapshot' import { successResponse, errorResponse } from '../utils/response' import { getDisplayEquipmentValue @@ -14,7 +15,7 @@ import { } from '../utils/equipmentSearchPriority' import { logOperation } from './operationLogController' import { - buildRecursiveBorrowSnapshot, + assertReturnConditionSupported, borrowAssetTree, createAssetMovementRecord, finalizeBorrowAnomalyReturnState, @@ -30,23 +31,8 @@ const parseNumber = (value: any): number | undefined => { return Number.isNaN(num) ? undefined : num } -const parseDate = (value?: any): Date | null => { - if (!value) return null - const date = new Date(value) - return Number.isNaN(date.getTime()) ? null : date -} - -const parseBorrowAnomalyBorrowTime = (value?: any): Date | null => { - if (!value || typeof value !== 'string') return null - const normalizedValue = value.trim() - if (!normalizedValue) return null - - if (/^\d{4}-\d{2}-\d{2}$/.test(normalizedValue)) { - return parseDate(`${normalizedValue}T00:00:00`) - } - - return parseDate(normalizedValue) -} +const parseDate = (value?: unknown) => parseBusinessDate(value, '预计归还时间') +const parseBorrowAnomalyBorrowTime = (value?: unknown) => parseBusinessDate(value, '借出日期') const equipmentInclude = { storageLocation: { @@ -271,27 +257,11 @@ const findActiveBorrowRecord = async ( } const getChildrenDiff = async (equipmentId: number, borrowRecord: any) => { - if (!borrowRecord?.children_snapshot) { - return null - } - - const borrowedChildren = JSON.parse(borrowRecord.children_snapshot) as any[] const currentTree = await prisma.$transaction( (tx) => loadAssetTree(tx, equipmentId), { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted } ) - const currentChildren = buildRecursiveBorrowSnapshot(currentTree) - - const borrowedIds = new Set(borrowedChildren.map((child) => child.id)) - const currentIds = new Set(currentChildren.map((child) => child.id)) - const removed = borrowedChildren.filter((child) => !currentIds.has(child.id)) - const added = currentChildren.filter((child) => !borrowedIds.has(child.id)) - - if (removed.length === 0 && added.length === 0) { - return null - } - - return { removed, added } + return getReturnChildrenDiff(currentTree, borrowRecord) } const returnEquipmentInTransaction = async ( @@ -299,16 +269,29 @@ const returnEquipmentInTransaction = async ( borrowRecord: any, returnCondition: string, notes?: string, - operatedBy?: number + operatedBy?: number, + confirmationToken?: string ) => { return prisma.$transaction(async (tx) => { if (!operatedBy) { throw new Error('未获取到归还操作人') } - const { items } = await recordReturnToStorage(tx, { - rootEquipmentId: equipment.id, + const items = await loadAndLockAssetTree(tx, equipment.id) + assertReturnConditionSupported(items, returnCondition) + const childrenDiff = getReturnChildrenDiff(items, borrowRecord) + const currentToken = buildReturnConfirmationToken(items, borrowRecord) + if (childrenDiff && confirmationToken !== currentToken) { + return { need_confirm: true as const, confirmation_token: currentToken, children_diff: childrenDiff } + } + const finalNotes = childrenDiff + ? [notes, `配件变更:移除${childrenDiff.removed.length}个,新增${childrenDiff.added.length}个`].filter(Boolean).join(';') + : notes + await createAssetMovementRecord(tx, { + items, + actionType: returnCondition === '丢失' ? 'loss' : 'return', + toDepartmentId: null, operatedBy, - reason: notes || '正常归还入仓' + reason: returnCondition === '丢失' ? `丢失登记(未入仓)${finalNotes ? `;${finalNotes}` : ''}` : finalNotes || '正常归还入仓' }) const currentRoot = items[0] const returnTime = new Date() @@ -322,7 +305,7 @@ const returnEquipmentInTransaction = async ( return_time: returnTime, status: '已归还', return_condition: returnCondition, - notes + notes: finalNotes } }) @@ -330,15 +313,17 @@ const returnEquipmentInTransaction = async ( throw new Error('该借用记录已归还,请刷新后重试') } - await finalizeNormalReturnState(tx, currentRoot) + await finalizeNormalReturnState(tx, currentRoot, returnCondition, items) return { + need_confirm: false as const, + children_diff: childrenDiff, id: borrowRecord.id, person_id: borrowRecord.person_id, return_time: returnTime, status: '已归还' } - }) + }, { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted }) } type DisposalReturnBorrowRecord = { @@ -349,71 +334,8 @@ type DisposalReturnBorrowRecord = { updated_at: Date } -const getLockedDisposalChildrenDiff = ( - items: Awaited>, - borrowRecord: DisposalReturnBorrowRecord -) => { - const currentChildren = buildRecursiveBorrowSnapshot(items) - let borrowedChildren: Array<{ - id: number - parent_id?: number | null - parent_equipment_id?: number | null - equipment_code?: string - equipment_name?: string - }> = [] - if (borrowRecord.children_snapshot) { - try { - const parsed = JSON.parse(borrowRecord.children_snapshot) - if (!Array.isArray(parsed)) throw new Error('配件快照格式错误') - borrowedChildren = parsed - } catch { - throw new Error('借用记录的配件快照无效,无法安全快捷入库,请先使用正式归还流程') - } - } - - const borrowedById = new Map(borrowedChildren.map((child) => [Number(child.id), child])) - const currentById = new Map(currentChildren.map((child) => [child.id, child])) - const getParentId = (child: { parent_id?: number | null; parent_equipment_id?: number | null }) => - child.parent_id ?? child.parent_equipment_id ?? null - const samePlacement = ( - left: { parent_id?: number | null; parent_equipment_id?: number | null }, - right: { parent_id?: number | null; parent_equipment_id?: number | null } - ) => getParentId(left) === getParentId(right) - - const removed = borrowedChildren.filter((child) => { - const current = currentById.get(Number(child.id)) - return !current || !samePlacement(child, current) - }) - const added = currentChildren.filter((child) => { - const borrowed = borrowedById.get(child.id) - return !borrowed || !samePlacement(borrowed, child) - }) - - return removed.length === 0 && added.length === 0 ? null : { removed, added } -} - -const buildDisposalReturnConfirmationToken = ( - items: Awaited>, - borrowRecord: DisposalReturnBorrowRecord -) => createHash('sha256').update(JSON.stringify({ - borrow_record: { - id: borrowRecord.id, - equipment_id: borrowRecord.equipment_id, - updated_at: borrowRecord.updated_at.getTime(), - children_snapshot: borrowRecord.children_snapshot - }, - asset_tree: items.map((item) => ({ - id: item.id, - parent_id: item.parent_id, - department_id: item.department_id, - status: item.status, - mount_status: item.mount_status, - total_quantity: item.total_quantity, - remaining_quantity: item.remaining_quantity, - unit_price: item.unit_price.toString(), - updated_at: item.updated_at.getTime() - })) -})).digest('hex') +const getLockedDisposalChildrenDiff = getReturnChildrenDiff +const buildDisposalReturnConfirmationToken = buildReturnConfirmationToken const returnEquipmentBeforeDisposalInTransaction = async (input: { equipmentId: number @@ -487,7 +409,7 @@ const returnEquipmentBeforeDisposalInTransaction = async (input: { throw new Error('该借用记录已归还,请刷新后重试') } - const returnState = await finalizeNormalReturnState(tx, root) + const returnState = await finalizeNormalReturnState(tx, root, input.returnCondition, items) return { need_confirm: false as const, equipment_id: root.id, @@ -781,7 +703,7 @@ export const scanBorrowBatch = async (req: Request, res: Response) => { body: req.body, user: res.locals.user }) - errorResponse(res, error.message || '批量出库失败') + errorResponse(res, error.message || '批量出库失败', error.status || 400) } } @@ -795,7 +717,7 @@ export const returnEquipment = async (req: Request, res: Response) => { person_id, return_condition = '正常', notes, - confirm_children_diff + confirmation_token } = req.body as Record try { @@ -826,37 +748,27 @@ export const returnEquipment = async (req: Request, res: Response) => { return errorResponse(res, '未找到对应的借用记录') } - const childrenDiff = await getChildrenDiff(equipment.id, borrowRecord) - if (childrenDiff && !confirm_children_diff) { - return successResponse(res, { - need_confirm: true, - children_diff: childrenDiff, - message: '配件与借出时不一致,请确认后继续' - }, '配件差异提醒') - } - - const finalNotes = childrenDiff - ? [notes, `配件变更:移除${childrenDiff.removed.length}个,新增${childrenDiff.added.length}个`] - .filter(Boolean) - .join(';') - : notes - - await returnEquipmentInTransaction( + const updatedRecord = await returnEquipmentInTransaction( equipment, borrowRecord, return_condition, - finalNotes, - Number(res.locals.user?.id) + notes, + Number(res.locals.user?.id), + typeof confirmation_token === 'string' ? confirmation_token : undefined ) + if (updatedRecord.need_confirm) { + return successResponse(res, updatedRecord, '配件差异提醒') + } + successResponse( res, { id: borrowRecord.id, return_time: new Date(), - children_diff: childrenDiff + children_diff: updatedRecord.children_diff }, - '归还成功' + return_condition === '丢失' ? '丢失已登记(未入仓)' : '归还成功' ) } catch (error: any) { console.error('[归还资产失败]', { @@ -865,7 +777,7 @@ export const returnEquipment = async (req: Request, res: Response) => { body: req.body, user: res.locals.user }) - errorResponse(res, error.message || '归还资产失败') + errorResponse(res, error.message || '归还资产失败', error.status || 400) } } @@ -925,7 +837,7 @@ export const scanReturn = async (req: Request, res: Response) => { notes, person_id, borrow_record_id, - confirm_children_diff + confirmation_token } = req.body as Record try { @@ -955,29 +867,19 @@ export const scanReturn = async (req: Request, res: Response) => { return errorResponse(res, '未找到对应的借用记录,请选择正确的使用者', 404) } - const childrenDiff = await getChildrenDiff(equipment.id, borrowRecord) - if (childrenDiff && !confirm_children_diff) { - return successResponse(res, { - need_confirm: true, - children_diff: childrenDiff, - message: '配件与借出时不一致,请确认后继续' - }, '配件差异提醒') - } - - const finalNotes = childrenDiff - ? [notes, `配件变更:移除${childrenDiff.removed.length}个,新增${childrenDiff.added.length}个`] - .filter(Boolean) - .join(';') - : notes - const updatedRecord = await returnEquipmentInTransaction( equipment, borrowRecord, return_condition, - finalNotes, - Number(res.locals.user?.id) + notes, + Number(res.locals.user?.id), + typeof confirmation_token === 'string' ? confirmation_token : undefined ) + if (updatedRecord.need_confirm) { + return successResponse(res, updatedRecord, '配件差异提醒') + } + successResponse( res, { @@ -988,7 +890,7 @@ export const scanReturn = async (req: Request, res: Response) => { status: '已归还', person_id: updatedRecord.person_id }, - '入库成功' + return_condition === '丢失' ? '丢失已登记(未入仓)' : '入库成功' ) } catch (error: any) { console.error('[扫码入库失败]', { @@ -997,7 +899,7 @@ export const scanReturn = async (req: Request, res: Response) => { body: req.body, user: res.locals.user }) - errorResponse(res, error.message || '入库失败') + errorResponse(res, error.message || '入库失败', error.status || 400) } } @@ -1063,7 +965,7 @@ export const scanReturnBatch = async (req: Request, res: Response) => { continue } - await returnEquipmentInTransaction( + const returned = await returnEquipmentInTransaction( equipment, borrowRecord, return_condition, @@ -1071,7 +973,12 @@ export const scanReturnBatch = async (req: Request, res: Response) => { Number(res.locals.user?.id) ) - results.push({ equipment_code: code, status: 'success', message: '入库成功' }) + if (returned.need_confirm) { + results.push({ equipment_code: code, status: 'failed', message: '配件与借出时不一致,请使用单个扫码归还确认' }) + failedCount++ + continue + } + results.push({ equipment_code: code, status: 'success', message: return_condition === '丢失' ? '丢失已登记(未入仓)' : '入库成功' }) successCount++ } catch (error: any) { results.push({ diff --git a/backend/src/controllers/disposalController.ts b/backend/src/controllers/disposalController.ts index 2e0381b..53711d0 100644 --- a/backend/src/controllers/disposalController.ts +++ b/backend/src/controllers/disposalController.ts @@ -21,7 +21,7 @@ class DisposalConflictError extends Error { status = 409 } -const blockedStatuses = new Set(['已借出', '维护中', '待报废', '已报废']) +const blockedStatuses = new Set(['已借出', '维护中', '待报废', '已报废', '已丢失']) const disposalInclude = { equipment: true, @@ -207,6 +207,16 @@ const getCurrentUser = (res: Response) => ({ role: String(res.locals.user?.role || '') }) +const getDisposalVisibility = (res: Response): Prisma.DisposalRecordWhereInput => { + const user = getCurrentUser(res) + if (!Number.isSafeInteger(user.id) || user.id <= 0) { + throw Object.assign(new Error('用户未登录'), { status: 401 }) + } + if (user.role === 'admin' || user.role === 'administrative') return {} + if (user.role === 'it') return { applied_by: user.id } + throw Object.assign(new Error('无权查看报废流程'), { status: 403 }) +} + const DISPOSAL_STATUSES = ['pending', 'approved', 'rejected', 'completed'] as const const parseListPositiveInteger = ( @@ -215,9 +225,15 @@ const parseListPositiveInteger = ( fallback: number, max?: number ) => { - if (value === undefined || value === null || value === '') return fallback + if (value === undefined || value === null || value === '') { + if (fallback > 0) return fallback + throw Object.assign(new Error(`${fieldName}无效`), { status: 422 }) + } + if ((typeof value !== 'string' && typeof value !== 'number') || !/^\d+$/.test(String(value))) { + throw Object.assign(new Error(`${fieldName}无效`), { status: 422 }) + } const parsed = Number(value) - if (!Number.isInteger(parsed) || parsed <= 0 || (max !== undefined && parsed > max)) { + if (!Number.isSafeInteger(parsed) || parsed <= 0 || (max !== undefined && parsed > max)) { throw Object.assign(new Error(`${fieldName}无效`), { status: 422 }) } return parsed @@ -425,21 +441,11 @@ export const getDisposalList = async (req: Request, res: Response) => { return errorResponse(res, '报废列表范围无效', 422) } - const summaryConditions: Prisma.DisposalRecordWhereInput[] = [] - - if (currentUser.role === 'it') { - summaryConditions.push({ applied_by: currentUser.id }) - } else if (currentUser.role === 'administrative') { - if (resolvedScope === 'mine') { - summaryConditions.push({ applied_by: currentUser.id }) - } else if (resolvedScope === 'pending') { - summaryConditions.push({ status: 'pending' }) - } else if (resolvedScope === 'processed') { - summaryConditions.push({ status: { in: ['approved', 'rejected', 'completed'] } }) - } else { - return errorResponse(res, '行政仅可查看本人发起、全部待审批和全部已处理报废流程', 403) - } - } else if (currentUser.role === 'admin') { + const summaryConditions: Prisma.DisposalRecordWhereInput[] = [getDisposalVisibility(res)] + if (currentUser.role === 'administrative' && resolvedScope === 'all') { + return errorResponse(res, '行政仅可查看本人发起、全部待审批和全部已处理报废流程', 403) + } + if (currentUser.role !== 'it') { if (resolvedScope === 'mine') { summaryConditions.push({ applied_by: currentUser.id }) } else if (resolvedScope === 'pending') { @@ -447,8 +453,6 @@ export const getDisposalList = async (req: Request, res: Response) => { } else if (resolvedScope === 'processed') { summaryConditions.push({ status: { in: ['approved', 'rejected', 'completed'] } }) } - } else { - return errorResponse(res, '无权查看报废流程', 403) } const normalizedStatus = String(status || '').trim() @@ -530,18 +534,19 @@ export const getDisposalList = async (req: Request, res: Response) => { export const getDisposalDetail = async (req: Request, res: Response) => { try { + const visibility = getDisposalVisibility(res) + const disposalId = parseListPositiveInteger(req.params.id, '报废记录ID', 0) const disposal = await prisma.disposalRecord.findUnique({ - where: { id: Number(req.params.id) }, + where: { id: disposalId }, include: disposalInclude }) if (!disposal) return errorResponse(res, '报废记录不存在', 404) - const currentUser = getCurrentUser(res) - if (currentUser.role === 'it' && disposal.applied_by !== currentUser.id) { + if (visibility.applied_by && disposal.applied_by !== visibility.applied_by) { return errorResponse(res, '无权查看该报废申请', 403) } successResponse(res, withHistoricalDisposalNotes(disposal), '获取成功') } catch (error: any) { - errorResponse(res, error.message || '获取报废记录详情失败') + errorResponse(res, error.message || '获取报废记录详情失败', error.status || 500) } } @@ -763,13 +768,14 @@ export const completeDisposal = async (req: Request, res: Response) => { export const getEquipmentDisposalHistory = async (req: Request, res: Response) => { try { - const equipmentId = Number(req.params.equipment_id) + const visibility = getDisposalVisibility(res) + const equipmentId = parseListPositiveInteger(req.params.equipment_id, '资产ID', 0) const history = await prisma.disposalRecord.findMany({ where: { - OR: [ + AND: [visibility, { OR: [ { equipment_id: equipmentId }, { items: { some: { equipment_id: equipmentId } } } - ] + ] }] }, orderBy: { created_at: 'desc' }, include: { @@ -782,6 +788,6 @@ export const getEquipmentDisposalHistory = async (req: Request, res: Response) = }) successResponse(res, history, '获取成功') } catch (error: any) { - errorResponse(res, error.message || '获取报废历史失败') + errorResponse(res, error.message || '获取报废历史失败', error.status || 500) } } diff --git a/backend/src/controllers/equipmentController.ts b/backend/src/controllers/equipmentController.ts index 350b34b..3c10576 100644 --- a/backend/src/controllers/equipmentController.ts +++ b/backend/src/controllers/equipmentController.ts @@ -376,7 +376,7 @@ const ensureEquipmentCodeHasNoBusinessHistory = async (equipmentId: number, clie } } -const EDIT_LOCKED_STATUSES = new Set(['维护中', '待报废', '已报废', '已损坏']) +const EDIT_LOCKED_STATUSES = new Set(['维护中', '待报废', '已报废', '已损坏', '已丢失']) const resolveEditableEquipmentStatus = ({ currentStatus, @@ -1211,6 +1211,9 @@ export const updateEquipment = async (req: Request, res: Response) => { ) { return errorResponse(res, '资产总数量必须是大于或等于 1 的整数', 422) } + if (currentEquipment.status === '已丢失' && finalTotalQuantity !== currentEquipment.total_quantity) { + return errorResponse(res, '已丢失资产不能修改库存数量,请先人工核实台账', 409) + } const finalUnitPrice = unit_price !== undefined ? Number(unit_price) : Number(currentEquipment.unit_price) // 已借数量 = 原总数 - 原剩余数(保证不为负) const borrowedQuantity = Math.max(currentEquipment.total_quantity - currentEquipment.remaining_quantity, 0) diff --git a/backend/src/controllers/healthController.ts b/backend/src/controllers/healthController.ts new file mode 100644 index 0000000..5c4b50c --- /dev/null +++ b/backend/src/controllers/healthController.ts @@ -0,0 +1,51 @@ +import type { Request, Response } from 'express' +import prisma from '../utils/prisma' + +const probeDatabase = async () => { + // Referencing the new column also detects a missing maintenance migration. + await prisma.$queryRaw` + SELECT + (SELECT COUNT(id) FROM equipment WHERE 1 = 0) AS equipment_ready, + (SELECT COUNT(original_tree_snapshot) FROM maintenance_record WHERE 1 = 0) AS maintenance_ready, + (SELECT COUNT(claim_token) + COUNT(lease_expires_at) + COUNT(result_confirmed_by) + COUNT(result_confirmed_at) FROM print_job WHERE 1 = 0) AS print_job_ready, + (SELECT COUNT(protocol_version) FROM print_terminal WHERE 1 = 0) AS print_terminal_ready, + (SELECT COUNT(sid) FROM web_session WHERE 1 = 0) AS session_ready + ` +} + +export const createHealthController = ( + probe: () => Promise = probeDatabase, + timeoutMs = 3000 +) => { + let inFlight: Promise | null = null + + const check = () => { + if (inFlight) return inFlight + let timer: NodeJS.Timeout + const pending = Promise.resolve().then(probe) + const result = Promise.race([ + pending.then(() => true, () => false), + new Promise((resolve) => { + timer = setTimeout(() => resolve(false), timeoutMs) + }) + ]).finally(() => clearTimeout(timer)) + inFlight = result + // A timed-out query cannot be cancelled by Prisma. Retain its shared failed + // result until it settles so repeated health calls cannot fill the DB pool. + const release = () => { + if (inFlight === result) inFlight = null + } + pending.then(release, release) + return result + } + + return async (_req: Request, res: Response) => { + const healthy = await check() + res.status(healthy ? 200 : 503).json({ + status: healthy ? 'ok' : 'error', + message: healthy ? 'Server is ready' : 'Service is unavailable' + }) + } +} + +export const getHealth = createHealthController() diff --git a/backend/src/controllers/maintenanceController.ts b/backend/src/controllers/maintenanceController.ts index c4cc61b..e301504 100644 --- a/backend/src/controllers/maintenanceController.ts +++ b/backend/src/controllers/maintenanceController.ts @@ -1,9 +1,11 @@ import { Request, Response } from 'express' +import { Prisma } from '@prisma/client' import prisma from '../utils/prisma' import { successResponse, errorResponse } from '../utils/response' import { logOperation } from './operationLogController' -import { calculateEquipmentValues, syncMountedChildrenState } from '../utils/assetValue' -import { loadAssetTree } from '../services/assetMovementService' +import { calculateEquipmentValues } from '../utils/assetValue' +import { loadAndLockAssetTree } from '../services/assetMovementService' +import { buildMaintenanceSnapshot, validateMaintenanceSnapshot } from '../utils/maintenanceSnapshot' class MaintenanceConflictError extends Error { status = 409 @@ -18,9 +20,15 @@ const parseListPositiveInteger = ( fallback: number, max?: number ) => { - if (value === undefined || value === null || value === '') return fallback + if (value === undefined || value === null || value === '') { + if (fallback > 0) return fallback + throw Object.assign(new Error(`${fieldName}无效`), { status: 422 }) + } + if ((typeof value !== 'string' && typeof value !== 'number') || !/^\d+$/.test(String(value))) { + throw Object.assign(new Error(`${fieldName}无效`), { status: 422 }) + } const parsed = Number(value) - if (!Number.isInteger(parsed) || parsed <= 0 || (max !== undefined && parsed > max)) { + if (!Number.isSafeInteger(parsed) || parsed <= 0 || (max !== undefined && parsed > max)) { throw Object.assign(new Error(`${fieldName}无效`), { status: 422 }) } return parsed @@ -41,7 +49,7 @@ export const createMaintenance = async (req: Request, res: Response) => { } const result = await prisma.$transaction(async (tx) => { - const tree = await loadAssetTree(tx, equipmentId) + const tree = await loadAndLockAssetTree(tx, equipmentId) const equipment = tree[0] if (equipment.parent_id) { throw Object.assign(new Error('该子资产当前已挂载,请先卸载后再送修'), { status: 422 }) @@ -82,30 +90,32 @@ export const createMaintenance = async (req: Request, res: Response) => { throw new MaintenanceConflictError('资产树已有进行中的维修记录,请刷新后查看') } - const values = calculateEquipmentValues({ - unitPrice: Number(equipment.unit_price), - totalQuantity: Number(equipment.total_quantity), - remainingQuantity: Number(equipment.remaining_quantity), - status: '维护中' - }) - - const acquired = await tx.equipment.updateMany({ - where: { - id: equipment.id, - status: equipment.status, - parent_id: null, - remaining_quantity: equipment.remaining_quantity, - department_id: equipment.department_id - }, - data: { - status: '维护中', - total_value: values.total_value, - borrowed_value: values.borrowed_value, - available_value: values.available_value + if (tree.some((item) => !['可用', '已损坏'].includes(item.status))) { + throw new MaintenanceConflictError('资产原状态无效,请人工核实后送修') + } + // All rows are locked by loadAndLockAssetTree; conditional writes still reject stale state. + for (const item of tree) { + const values = calculateEquipmentValues({ + unitPrice: Number(item.unit_price), + totalQuantity: item.total_quantity, + remainingQuantity: item.remaining_quantity, + status: '维护中' + }) + const acquired = await tx.equipment.updateMany({ + where: { + id: item.id, + status: item.status, + parent_id: item.parent_id, + remaining_quantity: item.remaining_quantity, + department_id: item.department_id, + mount_status: item.mount_status, + updated_at: item.updated_at + }, + data: { status: '维护中', ...values } + }) + if (acquired.count !== 1) { + throw new MaintenanceConflictError('资产状态、库存或归属已变化,请刷新后重试') } - }) - if (acquired.count !== 1) { - throw new MaintenanceConflictError('资产状态、库存或归属已变化,请刷新后重试') } const created = await tx.maintenanceRecord.create({ @@ -116,14 +126,13 @@ export const createMaintenance = async (req: Request, res: Response) => { repair_vendor, estimated_cost: Number(estimated_cost) || 0, expected_end_date: expected_end_date ? new Date(expected_end_date) : null, - created_by: userId + created_by: userId, + original_tree_snapshot: buildMaintenanceSnapshot(tree) } }) - await syncMountedChildrenState(tx, equipment.id, '维护中') - return { maintenance: created, equipment } - }) + }, { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted }) // 记录操作日志 const username = res.locals.user?.username || res.locals.user?.name || 'unknown' @@ -286,35 +295,31 @@ export const completeMaintenance = async (req: Request, res: Response) => { try { const { id } = req.params const { actual_cost, repair_result } = req.body - const maintenanceId = Number(id) + const maintenanceId = parseListPositiveInteger(id, '维修记录ID', 0) const maintenance = await prisma.$transaction(async (tx) => { - const currentMaintenance = await tx.maintenanceRecord.findUnique({ - where: { id: maintenanceId } - }) + await tx.$queryRaw(Prisma.sql`SELECT id FROM maintenance_record WHERE id = ${maintenanceId} FOR UPDATE`) + const currentMaintenance = await tx.maintenanceRecord.findUnique({ where: { id: maintenanceId } }) if (!currentMaintenance) { throw Object.assign(new Error('维修记录不存在'), { status: 404 }) } - - const equipment = await tx.equipment.findUnique({ - where: { id: currentMaintenance.equipment_id } - }) - if (!equipment) { - throw Object.assign(new Error('资产不存在'), { status: 404 }) + if (currentMaintenance.status !== 'in_progress') { + throw new MaintenanceConflictError('该维修记录已被处理,请刷新后查看') + } + const tree = await loadAndLockAssetTree(tx, currentMaintenance.equipment_id) + if (tree[0].parent_id !== null || tree.some((item) => item.status !== '维护中')) { + throw new MaintenanceConflictError('资产树已不处于维修状态,不能覆盖当前状态') + } + // Legacy records can be completed after whole-tree checks, but cannot be + // cancelled without original statuses. New records must match their tree. + if (currentMaintenance.original_tree_snapshot) { + validateMaintenanceSnapshot(currentMaintenance.original_tree_snapshot, tree, currentMaintenance.equipment_id) } - - const values = calculateEquipmentValues({ - unitPrice: Number(equipment.unit_price), - totalQuantity: Number(equipment.total_quantity), - remainingQuantity: Number(equipment.remaining_quantity), - status: '可用' - }) const updatedMaintenance = await tx.maintenanceRecord.updateMany({ - where: { id: maintenanceId, status: 'in_progress' }, + where: { id: maintenanceId, status: 'in_progress', updated_at: currentMaintenance.updated_at }, data: { status: 'completed', - actual_cost: - actual_cost !== undefined ? Number(actual_cost) : currentMaintenance.estimated_cost, + actual_cost: actual_cost !== undefined ? Number(actual_cost) : currentMaintenance.estimated_cost, actual_end_date: new Date(), repair_result } @@ -322,27 +327,26 @@ export const completeMaintenance = async (req: Request, res: Response) => { if (updatedMaintenance.count !== 1) { throw new MaintenanceConflictError('该维修记录已被处理,请刷新后查看') } - - const updatedEquipment = await tx.equipment.updateMany({ - where: { - id: currentMaintenance.equipment_id, - status: '维护中', - parent_id: null - }, - data: { - status: '可用', - total_value: values.total_value, - borrowed_value: values.borrowed_value, - available_value: values.available_value + for (const item of tree) { + const values = calculateEquipmentValues({ + unitPrice: Number(item.unit_price), totalQuantity: item.total_quantity, + remainingQuantity: item.remaining_quantity, status: '可用' + }) + const updated = await tx.equipment.updateMany({ + where: { + id: item.id, status: '维护中', parent_id: item.parent_id, + total_quantity: item.total_quantity, remaining_quantity: item.remaining_quantity, + department_id: item.department_id, mount_status: item.mount_status, + unit_price: item.unit_price, updated_at: item.updated_at + }, + data: { status: '可用', ...values } + }) + if (updated.count !== 1) { + throw new MaintenanceConflictError('资产状态、库存或挂载关系已变化,不能覆盖当前状态') } - }) - if (updatedEquipment.count !== 1) { - throw new MaintenanceConflictError('资产已不处于维修状态,不能覆盖当前状态') } - - await syncMountedChildrenState(tx, equipment.id, '可用') return currentMaintenance - }) + }, { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted }) // 记录操作日志 const userId = res.locals.user?.id @@ -368,9 +372,12 @@ export const cancelMaintenance = async (req: Request, res: Response) => { try { const { id } = req.params const { reason } = req.body - const maintenanceId = Number(id) + const maintenanceId = parseListPositiveInteger(id, '维修记录ID', 0) const maintenance = await prisma.$transaction(async (tx) => { + // Completion acquires the maintenance row before equipment writes. Use + // the same order to prevent completion/cancellation from deadlocking. + await tx.$queryRaw(Prisma.sql`SELECT id FROM maintenance_record WHERE id = ${maintenanceId} FOR UPDATE`) const currentMaintenance = await tx.maintenanceRecord.findUnique({ where: { id: maintenanceId } }) @@ -378,47 +385,50 @@ export const cancelMaintenance = async (req: Request, res: Response) => { throw Object.assign(new Error('维修记录不存在'), { status: 404 }) } - const equipment = await tx.equipment.findUnique({ - where: { id: currentMaintenance.equipment_id } - }) - if (!equipment) { - throw Object.assign(new Error('资产不存在'), { status: 404 }) + if (currentMaintenance.status !== 'in_progress') { + throw new MaintenanceConflictError('该维修记录已被处理,请刷新后查看') } - - const values = calculateEquipmentValues({ - unitPrice: Number(equipment.unit_price), - totalQuantity: Number(equipment.total_quantity), - remainingQuantity: Number(equipment.remaining_quantity), - status: '可用' - }) + const tree = await loadAndLockAssetTree(tx, currentMaintenance.equipment_id) + const snapshot = validateMaintenanceSnapshot( + currentMaintenance.original_tree_snapshot, + tree, + currentMaintenance.equipment_id + ) const updatedMaintenance = await tx.maintenanceRecord.updateMany({ - where: { id: maintenanceId, status: 'in_progress' }, + where: { id: maintenanceId, status: 'in_progress', updated_at: currentMaintenance.updated_at }, data: { status: 'cancelled', repair_result: reason || '已取消' } }) if (updatedMaintenance.count !== 1) { throw new MaintenanceConflictError('该维修记录已被处理,请刷新后查看') } - const updatedEquipment = await tx.equipment.updateMany({ - where: { - id: currentMaintenance.equipment_id, - status: '维护中', - parent_id: null - }, - data: { - status: '可用', - total_value: values.total_value, - borrowed_value: values.borrowed_value, - available_value: values.available_value + for (const item of tree) { + const previous = snapshot.find((entry) => entry.id === item.id)! + const values = calculateEquipmentValues({ + unitPrice: Number(item.unit_price), + totalQuantity: item.total_quantity, + remainingQuantity: previous.remaining_quantity, + status: previous.status + }) + const restored = await tx.equipment.updateMany({ + where: { + id: item.id, + status: '维护中', + parent_id: previous.parent_id, + total_quantity: item.total_quantity, + remaining_quantity: item.remaining_quantity, + department_id: item.department_id, + mount_status: item.mount_status, + updated_at: item.updated_at + }, + data: { status: previous.status, remaining_quantity: previous.remaining_quantity, ...values } + }) + if (restored.count !== 1) { + throw new MaintenanceConflictError('资产已不处于原维修状态,不能覆盖当前状态') } - }) - if (updatedEquipment.count !== 1) { - throw new MaintenanceConflictError('资产已不处于维修状态,不能覆盖当前状态') } - - await syncMountedChildrenState(tx, equipment.id, '可用') return currentMaintenance - }) + }, { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted }) // 记录操作日志 const userId = res.locals.user?.id diff --git a/backend/src/controllers/mountController.ts b/backend/src/controllers/mountController.ts index 8d8271f..b5940d9 100644 --- a/backend/src/controllers/mountController.ts +++ b/backend/src/controllers/mountController.ts @@ -17,7 +17,7 @@ import { type TransactionClient = Prisma.TransactionClient type MountLifecycleStatus = '可用' | '已借出' | '维护中' | '待报废' | '已报废' -const PARENT_MOUNTABLE_STATUSES = new Set(['可用', '已借出', '维护中']) +const PARENT_MOUNTABLE_STATUSES = new Set(['可用', '已借出']) class MountOperationError extends Error { statusCode: number @@ -30,6 +30,9 @@ class MountOperationError extends Error { } const ensureParentMountOperable = (equipment: { status: string }, label: string) => { + if (equipment.status === '维护中') { + throw new MountOperationError(`${label}正在维修,请先完成或取消维修后再挂载或卸载配件`, 409) + } if (!PARENT_MOUNTABLE_STATUSES.has(equipment.status as MountLifecycleStatus)) { throw new MountOperationError(`${label}当前状态不可挂载或更换配件`) } diff --git a/backend/src/controllers/printController.ts b/backend/src/controllers/printController.ts index a0888d2..4f98536 100644 --- a/backend/src/controllers/printController.ts +++ b/backend/src/controllers/printController.ts @@ -6,6 +6,8 @@ import { createPrintTerminalPairCode as createPrintTerminalPairCodeService, heartbeatPrintTerminalAgent, getDefaultPrintTerminalReadiness, + getPrintAgentJobState, + resolveUnknownPrintJob, isPrintServiceError, listPrintJobs, listPrintTerminals, @@ -195,7 +197,8 @@ export const heartbeatPrintAgent = async (req: Request, res: Response) => { machine_name: req.body?.machine_name, selected_printer_name: req.body?.selected_printer_name, printer_snapshot: req.body?.printer_snapshot, - label_profile: req.body?.label_profile + label_profile: req.body?.label_profile, + protocol_version: req.body?.protocol_version }) successResponse(res, result, '心跳已接收') @@ -211,7 +214,7 @@ export const claimPrintAgentJob = async (req: Request, res: Response) => { } try { - const result = await claimNextPrintJob(terminalId) + const result = await claimNextPrintJob(terminalId, req.body?.protocol_version) successResponse(res, result, result.message) } catch (error) { handlePrintServiceError(res, error, '领取打印任务失败') @@ -233,6 +236,7 @@ export const updatePrintAgentJobStatus = async (req: Request, res: Response) => try { const result = await updateClaimedPrintJobStatus(terminalId, jobId, { status: req.body?.status, + claim_token: req.body?.claim_token, error_message: req.body?.error_message, retryable: req.body?.retryable }) @@ -243,6 +247,30 @@ export const updatePrintAgentJobStatus = async (req: Request, res: Response) => } } +export const readPrintAgentJobState = async (req: Request, res: Response) => { + const terminalId = getCurrentTerminalId(res) + const jobId = parsePositiveInt(req.params.id) + if (!terminalId) return errorResponse(res, '打印终端未认证', 401) + if (!jobId) return errorResponse(res, '打印任务编号无效', 422) + try { + successResponse(res, await getPrintAgentJobState(terminalId, jobId), '获取任务状态成功') + } catch (error) { + handlePrintServiceError(res, error, '获取任务状态失败') + } +} + +export const resolvePrintJob = async (req: Request, res: Response) => { + const userId = getCurrentUserId(res) + const jobId = parsePositiveInt(req.params.id) + if (!userId) return errorResponse(res, '当前登录用户无效', 401) + if (!jobId) return errorResponse(res, '打印任务编号无效', 422) + try { + successResponse(res, await resolveUnknownPrintJob(jobId, userId, req.body || {}), '核实结果已保存') + } catch (error) { + handlePrintServiceError(res, error, '保存核实结果失败') + } +} + export const printAgentTestPrint = async (req: Request, res: Response) => { const terminalId = getCurrentTerminalId(res) if (!terminalId) { diff --git a/backend/src/controllers/statisticsController.ts b/backend/src/controllers/statisticsController.ts index 8ab67f3..62d3274 100644 --- a/backend/src/controllers/statisticsController.ts +++ b/backend/src/controllers/statisticsController.ts @@ -19,6 +19,7 @@ type InventoryByTypeRow = { maintenance_quantity: number pending_disposal_quantity: number damaged_quantity: number + lost_quantity: number other_quantity: number mountable_available_quantity: number is_child_type: boolean @@ -122,7 +123,7 @@ export const getDashboardStats = async (req: Request, res: Response) => { prisma.equipment.aggregate({ where: { parent_id: null, - status: { not: '已报废' } + status: { notIn: ['已报废', '已丢失'] } }, _count: { id: true @@ -164,7 +165,8 @@ export const getDashboardStats = async (req: Request, res: Response) => { return_time: { gte: todayRange.start, lt: todayRange.end - } + }, + return_condition: { not: '丢失' } }, select: { children_snapshot: true, @@ -237,6 +239,7 @@ export const getDashboardStats = async (req: Request, res: Response) => { maintenance_equipment: maintenanceEquipmentQuantity, pending_disposal_equipment: inventorySummary.pendingDisposal, damaged_equipment: inventorySummary.damaged, + lost_equipment: inventorySummary.lost, other_equipment: inventorySummary.other, disposed_equipment: inventorySummary.disposed, borrowed_without_active_record: borrowedWithoutActiveRecord, @@ -252,7 +255,8 @@ export const getDashboardStats = async (req: Request, res: Response) => { today_return_value: todayReturnValue, generated_at: generatedAt.toISOString(), business_timezone: DASHBOARD_TIME_ZONE, - inventory_scope: 'current_excluding_disposed', + inventory_scope: 'current_excluding_disposed_and_lost', + value_scope: 'book_value_excluding_disposed_including_lost', inventory_by_type: Array.from( inventoryByType.reduce((map, item) => { const equipmentType = item.equipment_type || '其他' @@ -264,6 +268,7 @@ export const getDashboardStats = async (req: Request, res: Response) => { maintenance_quantity: 0, pending_disposal_quantity: 0, damaged_quantity: 0, + lost_quantity: 0, other_quantity: 0, mountable_available_quantity: mountableInventoryByTypeMap.get(equipmentType) || 0, is_child_type: childTypeNames.has(equipmentType) @@ -282,6 +287,7 @@ export const getDashboardStats = async (req: Request, res: Response) => { current.maintenance_quantity += breakdown.maintenance current.pending_disposal_quantity += breakdown.pendingDisposal current.damaged_quantity += breakdown.damaged + current.lost_quantity += breakdown.lost current.other_quantity += breakdown.other map.set(equipmentType, current) @@ -289,7 +295,7 @@ export const getDashboardStats = async (req: Request, res: Response) => { }, new Map()).values() ) .sort((a, b) => b.total_quantity - a.total_quantity) - .filter((item) => item.total_quantity > 0) + .filter((item) => item.total_quantity > 0 || item.lost_quantity > 0) }, '获取成功') } catch (error: any) { errorResponse(res, error.message || '获取统计数据失败') @@ -388,6 +394,7 @@ export const getValueStatistics = async (req: Request, res: Response) => { available_value: summary.available_value, maintenance_value: summary.maintenance_value, other_value: otherValue, + value_scope: 'book_value_excluding_disposed_including_lost', value_by_type: valueByType, top_equipment: topEquipment }, '获取成功') diff --git a/backend/src/index.ts b/backend/src/index.ts index d56e3df..f92687b 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,6 +1,7 @@ import 'dotenv/config' import path from 'path' import express from 'express' +import { getHealth } from './controllers/healthController' import cors from 'cors' import session from 'express-session' import equipmentRoutes from './routes/equipment' @@ -41,6 +42,7 @@ import { isCorsOriginAllowed } from './utils/corsPolicy' import { unsafeOriginGuard } from './middleware/originGuard' import { assertSupportedAuthMode, resolveSessionSecret } from './utils/authMode' import { persistentSessionStore, startSessionCleanupWorker } from './utils/sessionStore' +import { parseStandardJsonBody, parseStandardFormBody } from './utils/requestBody' const app = express() const PORT = process.env.PORT || 3000 @@ -57,8 +59,8 @@ app.use(cors({ credentials: true })) app.use(unsafeOriginGuard) -app.use(express.json({ limit: '50mb' })) -app.use(express.urlencoded({ extended: true, limit: '50mb' })) +app.use(parseStandardJsonBody) +app.use(parseStandardFormBody) app.use( session({ secret: SESSION_SECRET, @@ -112,9 +114,7 @@ app.use('/api/dingtalk', dingtalkRoutes) app.use('/downloads', printDownloadRoutes) // Health check -app.get('/api/health', (req, res) => { - res.json({ status: 'ok', message: 'Server is running' }) -}) +app.get('/api/health', getHealth) // Error handling middleware app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => { diff --git a/backend/src/routes/printAgent.ts b/backend/src/routes/printAgent.ts index d56ed08..39d54f9 100644 --- a/backend/src/routes/printAgent.ts +++ b/backend/src/routes/printAgent.ts @@ -4,7 +4,8 @@ import { heartbeatPrintAgent, pairPrintAgent, printAgentTestPrint, - updatePrintAgentJobStatus + updatePrintAgentJobStatus, + readPrintAgentJobState } from '../controllers/printController' import { protectPrintAgent } from '../middleware/printAgentAuth' @@ -17,6 +18,7 @@ router.use(protectPrintAgent()) router.post('/heartbeat', heartbeatPrintAgent) router.post('/jobs/claim', claimPrintAgentJob) router.post('/jobs/:id/status', updatePrintAgentJobStatus) +router.get('/jobs/:id/state', readPrintAgentJobState) router.post('/test-print', printAgentTestPrint) export default router diff --git a/backend/src/routes/printJobs.ts b/backend/src/routes/printJobs.ts index 51838fc..3642202 100644 --- a/backend/src/routes/printJobs.ts +++ b/backend/src/routes/printJobs.ts @@ -2,15 +2,18 @@ import { Router } from 'express' import { cancelPrintJob, createAssetLabelPrintJob, - getPrintJobs + getPrintJobs, + resolvePrintJob } from '../controllers/printController' import { requirePermission } from '../middleware/auth' import { PERMISSIONS } from '../utils/permissions' +import { parsePrintLabelJsonBody } from '../utils/requestBody' const router = Router() -router.post('/asset-labels', requirePermission(PERMISSIONS.ASSETS_PRINT_LABELS), createAssetLabelPrintJob) +router.post('/asset-labels', requirePermission(PERMISSIONS.ASSETS_PRINT_LABELS), parsePrintLabelJsonBody, createAssetLabelPrintJob) router.get('/', requirePermission(PERMISSIONS.SYSTEM_SETTINGS), getPrintJobs) router.patch('/:id/cancel', requirePermission(PERMISSIONS.ASSETS_PRINT_LABELS), cancelPrintJob) +router.post('/:id/resolve', requirePermission(PERMISSIONS.SYSTEM_SETTINGS), resolvePrintJob) export default router diff --git a/backend/src/services/__tests__/assetMovementService.concurrency.test.ts b/backend/src/services/__tests__/assetMovementService.concurrency.test.ts index b8b8a9f..e09b555 100644 --- a/backend/src/services/__tests__/assetMovementService.concurrency.test.ts +++ b/backend/src/services/__tests__/assetMovementService.concurrency.test.ts @@ -1,5 +1,7 @@ import test from 'node:test' import assert from 'node:assert/strict' +import prisma from '../../utils/prisma' +import { batchTransfer } from '../../controllers/batchController' import { borrowAssetTree, @@ -38,6 +40,45 @@ const childEquipment = { updated_at: now } +test('lost roots or descendants reject department transfer through service and batch API before any writes', async () => { + const originalTransaction = prisma.$transaction + const originalDepartmentFindFirst = prisma.department.findFirst + try { + ;(prisma.department.findFirst as any) = async () => ({ id: 8, name: '目标部门' }) + for (const rootIsLost of [true, false]) { + const root = rootIsLost ? { ...rootEquipment, status: '已丢失', remaining_quantity: 0 } : rootEquipment + const child = rootIsLost ? childEquipment : { ...childEquipment, status: '已丢失', remaining_quantity: 0 } + let writes = 0 + const children = (ids: number[]) => ids.includes(root.id) ? [child] : [] + const client = { + $queryRaw: async (query: any) => query.sql.includes('WHERE equipment.id = ?') ? [root] + : query.sql.includes('WHERE equipment.parent_id IN') ? children(query.values) : [], + equipment: { + findUnique: async () => root, + findMany: async (args: any) => children(args.where.parent_id.in), + updateMany: async () => { writes++; return { count: 1 } } + }, + borrowRecord: { count: async () => 0 }, + assetTransferRecord: { create: async () => { writes++; return {} } } + } + await assert.rejects(transferAssetTree(client as any, { + rootEquipmentId: root.id, actionType: 'transfer', toDepartmentId: 8, operatedBy: 9, reason: '部门调整' + }), /已丢失.*不能调拨/) + ;(prisma.$transaction as any) = async (callback: any) => callback(client) + const state = { body: null as any } + const response = { locals: { user: { id: 9, role: 'it' } }, status() { return this }, json(body: any) { state.body = body; return this } } + await batchTransfer({ body: { equipment_ids: [root.id], department_id: 8, reason: '部门调整' } } as any, response as any) + assert.equal(state.body.data.success_count, 0) + assert.equal(state.body.data.failed_count, 1) + assert.match(state.body.data.results[0].message, /已丢失.*不能调拨/) + assert.equal(writes, 0) + } + } finally { + ;(prisma.$transaction as any) = originalTransaction + ;(prisma.department.findFirst as any) = originalDepartmentFindFirst + } +}) + test('初读资产树后并发挂载新配件时调拨中止且不拆分部门', async () => { let mountedAfterInitialRead = false let movementWriteCount = 0 diff --git a/backend/src/services/__tests__/businessLocks.integration.test.ts b/backend/src/services/__tests__/businessLocks.integration.test.ts new file mode 100644 index 0000000..8f85a69 --- /dev/null +++ b/backend/src/services/__tests__/businessLocks.integration.test.ts @@ -0,0 +1,221 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { randomUUID } from 'node:crypto' +import type { Person, InventoryTaskParticipant, InventoryTaskItem } from '@prisma/client' + +// Explicit opt-in and a narrow database allowlist, before importing any client. +// Do not load .env or use the application entry point in this test. +const enabled = process.env.RUN_BUSINESS_DB_TESTS === 'true' + +const assertLocalDatabase = (raw: string) => { + const url = new URL(raw) + assert.ok(url.protocol === 'mysql:' && ['127.0.0.1', 'localhost'].includes(url.hostname) + && url.port === '13309' && url.pathname === '/equipment_audit' && !url.search && !url.hash, + 'Business integration tests require localhost:13309/equipment_audit without URL overrides') +} + +test('business integration database guard rejects remote, other-port, other-database and socket overrides before client loading', () => { + for (const target of [ + 'mysql://unused@example.invalid:13309/equipment_audit', + 'mysql://unused@127.0.0.1:3306/equipment_audit', + 'mysql://unused@127.0.0.1:13309/other_database', + 'mysql://unused@localhost:13309/equipment_audit?socket=/tmp/another.sock' + ]) assert.throws(() => assertLocalDatabase(target), /require localhost/) + assertLocalDatabase('mysql://unused@localhost:13309/equipment_audit') +}) + +test('business locks and exceptional returns in isolated local MySQL', { skip: !enabled }, async (t) => { + assertLocalDatabase(process.env.DATABASE_URL || '') + + const prisma = (await import('../../utils/prisma')).default + const { Prisma } = await import('@prisma/client') + const inventory = await import('../inventoryService') + const maintenance = await import('../../controllers/maintenanceController') + const { scanReturn } = await import('../../controllers/borrowController') + const prefix = `biz-${randomUUID()}` + const userIds: number[] = [] + const personIds: number[] = [] + const equipmentIds: number[] = [] + const taskIds: number[] = [] + let equipmentSequence = 0 + + const response = (userId: number) => { + const state = { status: 200, body: null as any } + const res = { + locals: { user: { id: userId, username: prefix, name: prefix, role: 'it' } }, + status(code: number) { state.status = code; return this }, + json(body: any) { state.body = body; return this } + } + return { state, res } + } + const createEquipment = async (extra: Record = {}) => { + const row = await prisma.equipment.create({ data: { + equipment_code: `${prefix}-${++equipmentSequence}`, equipment_name: prefix, equipment_type: '业务测试', + total_quantity: 1, remaining_quantity: 1, unit_price: 100, + total_value: 100, available_value: 100, status: '可用', ...extra + } }) + equipmentIds.push(row.id) + return row + } + + // Hold the real InnoDB row while both requests start, then release them to + // contend for it. All operations settle before the fixture is cleaned up. + const contend = async (table: 'inventory_task' | 'maintenance_record', id: number, operations: Array<() => Promise>) => { + let acquired!: () => void + let release!: () => void + const ready = new Promise((resolve) => { acquired = resolve }) + const gate = new Promise((resolve) => { release = resolve }) + const blocker = prisma.$transaction(async (tx) => { + await tx.$queryRaw(table === 'inventory_task' + ? Prisma.sql`SELECT id FROM inventory_task WHERE id = ${id} FOR UPDATE` + : Prisma.sql`SELECT id FROM maintenance_record WHERE id = ${id} FOR UPDATE`) + acquired() + await gate + }, { timeout: 10000 }) + await Promise.race([ready, blocker.then(() => { throw new Error('Lock fixture ended before acquiring row') })]) + let results: Promise[]> | undefined + try { + results = Promise.allSettled(operations.map((operation) => operation())) + await new Promise((resolve) => setTimeout(resolve, 40)) + } finally { + release() + await blocker + } + return results! + } + + try { + const user = await prisma.user.create({ data: { username: prefix, name: prefix, password: 'local-test-only-not-a-login', role: 'it' } }) + userIds.push(user.id) + const people: Person[] = [] + for (let i = 0; i < 2; i++) { + const person = await prisma.person.create({ data: { nickname: `${prefix}-${i}`, real_name: `${prefix}-${i}`, status: 'active' } }) + personIds.push(person.id) + people.push(person) + } + + await t.test('different-item participant scans and admin checks retain every aggregate; closed task cannot assign', async () => { + const task = await prisma.inventoryTask.create({ data: { + title: prefix, public_token: prefix, created_by: user.id, + participants_count: 2, items_count: 5, unassigned_count: 1 + } }) + taskIds.push(task.id) + const participants: InventoryTaskParticipant[] = [] + for (const person of people) participants.push(await prisma.inventoryTaskParticipant.create({ data: { + task_id: task.id, person_id: person.id, person_name: person.real_name, person_nickname: person.nickname + } })) + const items: InventoryTaskItem[] = [] + for (let i = 0; i < 5; i++) { + const equipment = await createEquipment() + items.push(await prisma.inventoryTaskItem.create({ data: { + task_id: task.id, equipment_id: equipment.id, equipment_code: equipment.equipment_code, + equipment_name: equipment.equipment_name, equipment_type: equipment.equipment_type, + participant_id: i < 4 ? participants[i % 2].id : null, is_unassigned: i === 4 + } })) + } + const scanned = await contend('inventory_task', task.id, [ + () => inventory.submitInventoryScan(task.id, participants[0].id, items[0].equipment_code), + () => inventory.submitInventoryScan(task.id, participants[1].id, items[1].equipment_code) + ]) + assert.ok(scanned.every((result) => result.status === 'fulfilled'), JSON.stringify(scanned)) + assert.equal((await prisma.inventoryTask.findUniqueOrThrow({ where: { id: task.id } })).completed_count, 2) + const checked = await contend('inventory_task', task.id, [ + () => inventory.submitAdminInventoryScan(task.id, items[2].equipment_code, user.id), + () => inventory.submitAdminInventoryScan(task.id, items[3].equipment_code, user.id) + ]) + assert.ok(checked.every((result) => result.status === 'fulfilled'), JSON.stringify(checked)) + const aggregate = await prisma.inventoryTask.findUniqueOrThrow({ where: { id: task.id } }) + assert.deepEqual([aggregate.items_count, aggregate.completed_count, aggregate.exception_count, aggregate.unassigned_count], [5, 4, 0, 1]) + assert.equal(await prisma.inventoryTaskItem.count({ where: { task_id: task.id, check_status: 'checked' } }), 4) + assert.equal(await prisma.inventoryTaskAction.count({ where: { task_id: task.id } }), 4) + const storedParticipants = await prisma.inventoryTaskParticipant.findMany({ where: { task_id: task.id } }) + assert.ok(storedParticipants.every((participant) => participant.status === 'completed')) + await inventory.closeInventoryTask(task.id, user.id) + await assert.rejects(inventory.assignUnassignedTaskItem(task.id, items[4].id, people[0].id), /已关闭/) + assert.equal((await prisma.inventoryTaskItem.findUniqueOrThrow({ where: { id: items[4].id } })).is_unassigned, true) + assert.equal(await prisma.inventoryTaskAction.count({ where: { task_id: task.id } }), 5) + assert.equal((await prisma.inventoryTask.findUniqueOrThrow({ where: { id: task.id } })).status, 'closed') + }) + + await t.test('completion and cancellation contend on one maintenance row; one winner commits a consistent asset tree', async () => { + for (let round = 0; round < 4; round++) { + const root = await createEquipment({ status: '已损坏', available_value: 0 }) + const child = await createEquipment({ parent_id: root.id, mount_status: 'mounted' }) + const created = response(user.id) + await maintenance.createMaintenance({ ip: '127.0.0.1', body: { equipment_id: root.id, issue_description: prefix, estimated_cost: 10 } } as any, created.res as any) + assert.equal(created.state.status, 200, JSON.stringify(created.state.body)) + const maintenanceId = Number(created.state.body.data.id) + const completed = response(user.id) + const cancelled = response(user.id) + const operations = [ + async () => maintenance.completeMaintenance({ ip: '127.0.0.1', params: { id: String(maintenanceId) }, body: { repair_result: prefix } } as any, completed.res as any), + async () => maintenance.cancelMaintenance({ ip: '127.0.0.1', params: { id: String(maintenanceId) }, body: { reason: prefix } } as any, cancelled.res as any) + ] + await contend('maintenance_record', maintenanceId, round % 2 ? operations.reverse() : operations) + assert.deepEqual([completed.state.status, cancelled.state.status].sort(), [200, 409]) + const record = await prisma.maintenanceRecord.findUniqueOrThrow({ where: { id: maintenanceId } }) + const rows = await prisma.equipment.findMany({ where: { id: { in: [root.id, child.id] } }, orderBy: { id: 'asc' } }) + assert.deepEqual(rows.map((row) => row.status), record.status === 'completed' ? ['可用', '可用'] : ['已损坏', '可用']) + assert.equal(rows[1].parent_id, root.id) + assert.ok(rows.every((row) => row.remaining_quantity === 1 && Number(row.borrowed_value) === 0)) + assert.equal(Number(rows[0].available_value), record.status === 'completed' ? 100 : 0) + assert.equal(Number(rows[1].available_value), 100) + assert.equal(record.status, completed.state.status === 200 ? 'completed' : 'cancelled') + } + }) + + await t.test('real damaged and lost return transactions never recreate available stock or value', async () => { + for (const condition of ['损坏', '丢失']) { + const equipment = await createEquipment({ status: '已借出', remaining_quantity: 0, available_value: 0, borrowed_value: 100 }) + const borrow = await prisma.borrowRecord.create({ data: { + equipment_id: equipment.id, equipment_code: equipment.equipment_code, person_id: people[0].id, + person_name: people[0].real_name, person_nickname: people[0].nickname + } }) + const returned = response(user.id) + await scanReturn({ ip: '127.0.0.1', body: { equipment_code: equipment.equipment_code, borrow_record_id: borrow.id, return_condition: condition } } as any, returned.res as any) + assert.equal(returned.state.status, 200, JSON.stringify(returned.state.body)) + assert.equal(returned.state.body.data.need_confirm, undefined) + const stored = await prisma.equipment.findUniqueOrThrow({ where: { id: equipment.id } }) + assert.equal(stored.status, condition === '损坏' ? '已损坏' : '已丢失') + assert.equal(stored.remaining_quantity, condition === '损坏' ? 1 : 0) + assert.equal(Number(stored.available_value), 0) + assert.equal(Number(stored.borrowed_value), 0) + assert.equal(Number(stored.total_value), 100) + const storedBorrow = await prisma.borrowRecord.findUniqueOrThrow({ where: { id: borrow.id } }) + assert.equal(storedBorrow.status, '已归还') + assert.equal(storedBorrow.return_condition, condition) + const movements = await prisma.assetTransferRecord.findMany({ where: { root_equipment_id: equipment.id } }) + assert.equal(movements.length, 1) + assert.equal(movements[0].action_type, condition === '丢失' ? 'loss' : 'return') + } + }) + } finally { + try { + // Exact fixture IDs only; cascading rows belong to these newly created IDs. + await prisma.$transaction(async (tx) => { + await tx.inventoryTask.deleteMany({ where: { id: { in: taskIds } } }) + await tx.maintenanceRecord.deleteMany({ where: { equipment_id: { in: equipmentIds } } }) + await tx.borrowRecord.deleteMany({ where: { equipment_id: { in: equipmentIds } } }) + await tx.assetTransferRecord.deleteMany({ where: { root_equipment_id: { in: equipmentIds } } }) + await tx.operationLog.deleteMany({ where: { user_id: { in: userIds } } }) + await tx.equipment.updateMany({ where: { id: { in: equipmentIds } }, data: { parent_id: null } }) + await tx.equipment.deleteMany({ where: { id: { in: equipmentIds } } }) + await tx.user.deleteMany({ where: { id: { in: userIds } } }) + await tx.person.deleteMany({ where: { id: { in: personIds } } }) + }, { timeout: 10000 }) + const leftovers = await Promise.all([ + prisma.equipment.count({ where: { id: { in: equipmentIds } } }), + prisma.inventoryTask.count({ where: { id: { in: taskIds } } }), + prisma.user.count({ where: { id: { in: userIds } } }), + prisma.person.count({ where: { id: { in: personIds } } }), + prisma.maintenanceRecord.count({ where: { equipment_id: { in: equipmentIds } } }), + prisma.borrowRecord.count({ where: { equipment_id: { in: equipmentIds } } }), + prisma.assetTransferRecord.count({ where: { root_equipment_id: { in: equipmentIds } } }), + prisma.operationLog.count({ where: { user_id: { in: userIds } } }) + ]) + assert.deepEqual(leftovers, Array(8).fill(0), 'All sample rows and derived records must be removed') + } finally { + await prisma.$disconnect() + } + } +}) diff --git a/backend/src/services/__tests__/hrSyncService.source-safety.test.ts b/backend/src/services/__tests__/hrSyncService.source-safety.test.ts new file mode 100644 index 0000000..9f986be --- /dev/null +++ b/backend/src/services/__tests__/hrSyncService.source-safety.test.ts @@ -0,0 +1,59 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import sql from 'mssql' +import prisma from '../../utils/prisma' +import { runHrSync } from '../hrSyncService' + +test('empty or sharply reduced successful source stops before organizational/person writes and releases sync guard', async () => { + const originals = [sql.connect, prisma.department.findMany, prisma.person.findMany, prisma.$transaction, prisma.operationLog.create] + const envKeys = ['SQLSERVER_HOST', 'SQLSERVER_DATABASE', 'SQLSERVER_USER', 'SQLSERVER_PASSWORD', 'HR_SYNC_MAX_DEACTIVATION_RATIO', 'HR_SYNC_MAX_DEACTIVATION_COUNT'] + const originalEnv = envKeys.map((key) => process.env[key]) + let rows: any[] = [] + let sourceRelease: (() => void) | undefined + let sourceWait: Promise | undefined + let closed = 0 + let failureJobs = 0 + let domainWrites = 0 + for (const key of envKeys.slice(0, 4)) process.env[key] = 'mock-only' + delete process.env.HR_SYNC_MAX_DEACTIVATION_RATIO + delete process.env.HR_SYNC_MAX_DEACTIVATION_COUNT + const mutations = [prisma.department.create, prisma.department.update, prisma.department.updateMany, prisma.person.create, prisma.person.update, prisma.person.updateMany, prisma.person.deleteMany] + const mutationObjects: any[] = [prisma.department, prisma.department, prisma.department, prisma.person, prisma.person, prisma.person, prisma.person] + const mutationKeys = ['create', 'update', 'updateMany', 'create', 'update', 'updateMany', 'deleteMany'] + mutationObjects.forEach((object, index) => { object[mutationKeys[index]] = async () => { domainWrites++; throw new Error('domain write must not occur') } }) + ;(prisma.department.findMany as any) = async () => [] + ;(prisma.person.findMany as any) = async () => Array.from({ length: 10 }, (_, index) => ({ + id: index + 1, source_userid: `u${index}`, nickname: '员工', real_name: '员工', status: 'active', + position: null, department: '研发部', department_sync_bucket: 'formal', main_department_id: null, + mainDepartment: null, personDepartments: [] + })) + ;(sql.connect as any) = async () => ({ + request: () => ({ query: async () => { if (sourceWait) await sourceWait; return { recordset: rows } } }), + close: async () => { closed++ } + }) + ;(prisma.$transaction as any) = async (callback: any) => callback({ + syncJob: { create: async ({ data }: any) => { assert.equal(data.status, 'failed'); failureJobs++; return { id: 1 } } }, + user: { findMany: async () => [] } + }) + ;(prisma.operationLog.create as any) = async () => ({}) + try { + await assert.rejects(runHrSync('mock-test'), /源在职人员为空/) + assert.equal(domainWrites, 0) + rows = [{ sourceUserId: 'u0', employeeName: '员工', registerDept: '研发部', mainDepts: '研发部', employeeStatus: '2', registerJobNumber: '001' }] + await assert.rejects(runHrSync('mock-test'), /预计停用 9\/10 人/) + assert.equal(domainWrites, 0) + rows = [] + sourceWait = new Promise((resolve) => { sourceRelease = resolve }) + const first = runHrSync('mock-test') + await assert.rejects(runHrSync('mock-second'), /正在进行/) + sourceRelease!() + await assert.rejects(first, /源在职人员为空/) + assert.equal(closed, 3) + assert.equal(failureJobs, 3) + assert.equal(domainWrites, 0) + } finally { + ;[sql.connect, prisma.department.findMany, prisma.person.findMany, prisma.$transaction, prisma.operationLog.create] = originals as any + mutationObjects.forEach((object, index) => { object[mutationKeys[index]] = mutations[index] }) + envKeys.forEach((key, index) => { if (originalEnv[index] === undefined) delete process.env[key]; else process.env[key] = originalEnv[index] }) + } +}) diff --git a/backend/src/services/__tests__/inventoryService.concurrency.test.ts b/backend/src/services/__tests__/inventoryService.concurrency.test.ts new file mode 100644 index 0000000..4ad1325 --- /dev/null +++ b/backend/src/services/__tests__/inventoryService.concurrency.test.ts @@ -0,0 +1,131 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import prisma from '../../utils/prisma' +import { assignUnassignedTaskItem, closeInventoryTask, submitInventoryException, submitInventoryScan } from '../inventoryService' + +const withDatabase = async (run: (database: any) => Promise) => { + const originals = [prisma.$transaction, prisma.inventoryTaskItem.findFirst, prisma.person.findUnique] + let state: any = { + task: { id: 7, status: 'active', completed_count: 0, exception_count: 0 }, + items: [ + { id: 1, task_id: 7, participant_id: 101, equipment_code: 'A', check_status: 'pending', is_unassigned: false }, + { id: 2, task_id: 7, participant_id: 102, equipment_code: 'B', check_status: 'pending', is_unassigned: false }, + { id: 3, task_id: 7, participant_id: null, equipment_code: 'C', check_status: 'pending', is_unassigned: true } + ], + participants: [{ id: 101, task_id: 7, person_id: 1 }, { id: 102, task_id: 7, person_id: 2 }], + actions: [] + } + const clone = (value: any) => JSON.parse(JSON.stringify(value)) + let lockQueue = Promise.resolve() + let activeLocks = 0 + let maxLocks = 0 + let writes = 0 + const matches = (item: any, where: any) => Object.entries(where).every(([key, value]) => item[key] === value) + ;(prisma.inventoryTaskItem.findFirst as any) = async ({ where }: any) => clone(state.items.find((item: any) => matches(item, where)) || null) + ;(prisma.person.findUnique as any) = async ({ where }: any) => ({ id: where.id, real_name: `人员${where.id}`, nickname: '人员', source_userid: `u-${where.id}`, status: 'active', main_department_id: null }) + ;(prisma.$transaction as any) = async (callback: any) => { + let working: any = null + let release: (() => void) | undefined + const current = () => { assert.ok(working, 'all transaction reads/writes must follow task FOR UPDATE'); return working } + const tx = { + $queryRaw: async (query: any) => { + assert.match(query.sql, /FROM inventory_task.*FOR UPDATE/) + assert.equal(query.values[0], 7) + const previous = lockQueue + lockQueue = new Promise((resolve) => { release = resolve }) + await previous + activeLocks++; maxLocks = Math.max(maxLocks, activeLocks) + // MySQL RR snapshots start only after the task row lock is acquired. + working = clone(state) + return [{ id: 7, status: working.task.status }] + }, + inventoryTaskItem: { + findFirst: async ({ where }: any) => current().items.find((item: any) => matches(item, where)) || null, + findMany: async ({ where }: any) => current().items.filter((item: any) => matches(item, where)), + count: async ({ where }: any) => current().items.filter((item: any) => matches(item, where)).length, + update: async ({ where, data }: any) => { writes++; const item = current().items.find((item: any) => item.id === where.id); Object.assign(item, data); return item } + }, + inventoryTaskParticipant: { + findFirst: async ({ where }: any) => current().participants.find((item: any) => matches(item, where)) || null, + findUnique: async ({ where }: any) => current().participants.find((item: any) => item.id === where.id), + findMany: async () => current().participants, + count: async () => current().participants.length, + update: async ({ where, data }: any) => { writes++; Object.assign(current().participants.find((item: any) => item.id === where.id), data); return {} }, + create: async ({ data }: any) => { writes++; const participant = { id: 200 + current().participants.length, ...data }; current().participants.push(participant); return participant } + }, + inventoryTask: { update: async ({ data }: any) => { writes++; Object.assign(current().task, data); return current().task } }, + inventoryTaskAction: { create: async ({ data }: any) => { writes++; if (database.rejectAction) throw new Error('mock action failure'); current().actions.push(data); return {} } } + } + try { + const result = await callback(tx) + state = working + return result + } finally { + if (release) { activeLocks--; release() } + } + } + const database = { + get state() { return state }, + get writes() { return writes }, + get maxLocks() { return maxLocks }, + rejectAction: false + } + try { await run(database) } finally { + ;[prisma.$transaction, prisma.inventoryTaskItem.findFirst, prisma.person.findUnique] = originals as any + } +} + +test('concurrent participants serialize task snapshots and retain both checked count and audit actions', async () => { + await withDatabase(async (database) => { + await Promise.all([submitInventoryScan(7, 101, 'A'), submitInventoryScan(7, 102, 'B')]) + assert.equal(database.maxLocks, 1) + assert.equal(database.state.items.filter((item: any) => item.check_status === 'checked').length, 2) + assert.equal(database.state.task.completed_count, 2) + assert.equal(database.state.task.exception_count, 0) + assert.equal(database.state.actions.length, 2) + }) +}) + +test('closed task rejects assignment, participant scans and exceptions without mutations', async () => { + await withDatabase(async (database) => { + await closeInventoryTask(7, 9) + const before = structuredClone(database.state) + const writes = database.writes + await assert.rejects(assignUnassignedTaskItem(7, 3, 8), /任务已关闭/) + await assert.rejects(submitInventoryScan(7, 101, 'A'), /任务已关闭/) + await assert.rejects(submitInventoryException(7, 101, 1, 'not_found'), /任务已关闭/) + assert.deepEqual(database.state, before) + assert.equal(database.writes, writes) + }) +}) + +test('close racing a scan is serialized; no result can mutate after closure', async () => { + await withDatabase(async (database) => { + const results = await Promise.allSettled([closeInventoryTask(7, 9), submitInventoryScan(7, 101, 'A')]) + assert.equal(results[0].status, 'fulfilled') + assert.equal(results[1].status, 'rejected') + assert.equal(database.state.task.status, 'closed') + assert.equal(database.state.items[0].check_status, 'pending') + assert.equal(database.state.task.completed_count, 0) + }) +}) + +test('concurrent assignment of the same unassigned item has one winner and no orphan participant', async () => { + await withDatabase(async (database) => { + const results = await Promise.allSettled([assignUnassignedTaskItem(7, 3, 8), assignUnassignedTaskItem(7, 3, 9)]) + assert.equal(results.filter((result) => result.status === 'fulfilled').length, 1) + assert.equal(results.filter((result) => result.status === 'rejected').length, 1) + assert.equal(database.state.participants.length, 3) + assert.equal(database.state.items[2].is_unassigned, false) + assert.equal(database.state.actions.length, 1) + }) +}) + +test('inventory item, counters and audit action commit together; failed action rolls back result', async () => { + await withDatabase(async (database) => { + const before = structuredClone(database.state) + database.rejectAction = true + await assert.rejects(submitInventoryScan(7, 101, 'A'), /mock action failure/) + assert.deepEqual(database.state, before) + }) +}) diff --git a/backend/src/services/__tests__/printClaimRecovery.integration.test.ts b/backend/src/services/__tests__/printClaimRecovery.integration.test.ts new file mode 100644 index 0000000..8ffc06e --- /dev/null +++ b/backend/src/services/__tests__/printClaimRecovery.integration.test.ts @@ -0,0 +1,166 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { randomUUID } from 'crypto' +import { promises as fs } from 'fs' +import path from 'path' +import express from 'express' +import prisma from '../../utils/prisma' +import { + claimNextPrintJob, expireStalePrintClaims, resolveUnknownPrintJob, + updateClaimedPrintJobStatus +} from '../printService' +import { getHealth } from '../../controllers/healthController' +import { PNG_ONE, makePng } from './printFixture' + +// Opt-in only. Refuse to run against any shared or production database. +const enabled = process.env.RUN_PRINT_DB_TESTS === 'true' +test('打印领取恢复在独立MySQL库中的并发与幂等验证', { skip: !enabled }, async (t) => { + const url = new URL(process.env.DATABASE_URL || '') + assert.ok(url.protocol === 'mysql:' && ['127.0.0.1', 'localhost'].includes(url.hostname) + && url.port === '13309' && url.pathname === '/equipment_audit' && !url.search && !url.hash, + '只允许本机专用equipment_audit测试库') + const suffix = randomUUID().slice(0, 8) + const user = await prisma.user.create({ data: { username: `audit-${suffix}`, name: '测试样本', password: 'not-a-real-password' } }) + const terminal = await prisma.printTerminal.create({ data: { + name: `Audit ${suffix}`, platform: 'test', machine_name: 'test-only', status: 'online', protocol_version: 2, + terminal_token_hash: randomUUID(), selected_printer_name: 'Test printer', last_seen_at: new Date(), + label_profile_json: JSON.stringify({ paper_size: '60x40mm', width_mm: 60, height_mm: 40, orientation: 'landscape', scale_mode: 'fit' }) + } }) + const createJob = (data: Record = {}) => prisma.printJob.create({ data: { + target_terminal_id: terminal.id, requested_by: user.id, + payload_json: JSON.stringify({ job_type: 'asset_label', generated_at: new Date().toISOString(), equipment_ids: [1], + items: [], rendered_labels: [{ equipment_id: 1, image_data_url: PNG_ONE }] }), ...data + } }) + try { + await t.test('旧助手缺协议能力时不能领取,两个新请求只有一个领取成功', async () => { + const job = await createJob() + await assert.rejects(claimNextPrintJob(terminal.id), /更新打印助手/) + const claims = await Promise.all([claimNextPrintJob(terminal.id, 2), claimNextPrintJob(terminal.id, 2)]) + const winners = claims.filter(claim => claim.job) + assert.equal(winners.length, 1) + assert.equal(winners[0].job?.id, job.id) + assert.ok(winners[0].job?.claim_token) + const stored = await prisma.printJob.findUniqueOrThrow({ where: { id: job.id } }) + assert.equal(stored.attempt_count, 1) + assert.ok(stored.lease_expires_at && stored.lease_expires_at > new Date()) + }) + await t.test('旧token不能回写;同token完成重复提交成功,晚到续期不能倒退', async () => { + const job = await createJob({ status: 'printing', claim_token: suffix, claimed_by_terminal_id: terminal.id, + lease_expires_at: new Date(Date.now() + 120000) }) + await assert.rejects(updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'completed', claim_token: 'old' }), /凭据/) + await Promise.all([ + updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'completed', claim_token: suffix }), + updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'completed', claim_token: suffix }) + ]) + await assert.rejects(updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'printing', claim_token: suffix }), /结束/) + assert.equal((await prisma.printJob.findUniqueOrThrow({ where: { id: job.id } })).status, 'completed') + }) + await t.test('超时和旧无租约任务进入待核实,不能自动重排;同token晚到完成仍接收', async () => { + const job = await createJob({ status: 'claimed', claim_token: `${suffix}-expired`, claimed_by_terminal_id: terminal.id, + lease_expires_at: new Date(Date.now() - 1000) }) + const legacy = await createJob({ status: 'printing', claimed_by_terminal_id: terminal.id }) + await expireStalePrintClaims() + for (const id of [job.id, legacy.id]) { + assert.equal((await prisma.printJob.findUniqueOrThrow({ where: { id } })).status, 'result_unknown') + } + assert.equal((await claimNextPrintJob(terminal.id, 2)).job, null) + await updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'completed', claim_token: job.claim_token! }) + assert.equal((await prisma.printJob.findUniqueOrThrow({ where: { id: job.id } })).status, 'completed') + }) + await t.test('打印中失败即使retryable也不能整批自动重派', async () => { + const job = await createJob({ status: 'printing', claim_token: `${suffix}-partial`, claimed_by_terminal_id: terminal.id, + lease_expires_at: new Date(Date.now() + 120000) }) + const result = await updateClaimedPrintJobStatus(terminal.id, job.id, { + status: 'failed', claim_token: job.claim_token!, retryable: true, error_message: 'partial output' + }) + assert.equal(result.job.status, 'result_unknown') + assert.equal((await claimNextPrintJob(terminal.id, 2)).job, null) + }) + await t.test('人工处理需明确确认,两个核实请求只有一个成功,旧token不能覆盖重派', async () => { + const token = `${suffix}-resolve` + const job = await createJob({ status: 'result_unknown', claim_token: token, claimed_by_terminal_id: terminal.id }) + await assert.rejects(resolveUnknownPrintJob(job.id, user.id, { outcome: 'not_printed', expected_claim_token: token }), /核实/) + const results = await Promise.allSettled([ + resolveUnknownPrintJob(job.id, user.id, { confirmed: true, outcome: 'not_printed', expected_claim_token: token }), + resolveUnknownPrintJob(job.id, user.id, { confirmed: true, outcome: 'cancel', expected_claim_token: token }) + ]) + assert.equal(results.filter(result => result.status === 'fulfilled').length, 1) + await assert.rejects(updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'completed', claim_token: token }), /凭据/) + const final = await prisma.printJob.findUniqueOrThrow({ where: { id: job.id } }) + assert.ok(['queued', 'cancelled'].includes(final.status)) + assert.equal(final.result_confirmed_by, user.id) + assert.ok(final.result_confirmed_at) + await prisma.printJob.delete({ where: { id: job.id } }) + }) + await t.test('领取后的成品图读取失败保留凭据并条件标记failed', async () => { + const job = await createJob({ payload_json: JSON.stringify({ job_type: 'asset_label', items: [], equipment_ids: [1], + rendered_label_files: [{ equipment_id: 1, storage_key: `missing-${suffix}/label.png`, content_type: 'image/png' }] }) }) + const result = await claimNextPrintJob(terminal.id, 2) + assert.equal(result.job, null) + assert.match(result.message, /成品标签图读取失败/) + const final = await prisma.printJob.findUniqueOrThrow({ where: { id: job.id } }) + assert.equal(final.status, 'failed') + assert.ok(final.claim_token) + assert.equal(final.lease_expires_at, null) + }) + await t.test('待核实保留PNG,人工确认未出纸后原图重派,完成后才清理', async () => { + const root = path.resolve(__dirname, '../../../uploads/print-jobs') + const batch = `test-${randomUUID()}` + const directory = path.resolve(root, batch) + const relative = path.relative(root, directory) + assert.ok(relative && !relative.startsWith('..') && !path.isAbsolute(relative)) + const imagePath = path.join(directory, 'label.png') + await fs.mkdir(directory, { recursive: true }) + await fs.writeFile(imagePath, makePng()) + try { + const token = `${suffix}-image` + const job = await createJob({ status: 'result_unknown', claim_token: token, claimed_by_terminal_id: terminal.id, + payload_json: JSON.stringify({ job_type: 'asset_label', items: [], equipment_ids: [1], + rendered_label_files: [{ equipment_id: 1, storage_key: `${batch}/label.png`, content_type: 'image/png' }] }) }) + await updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'result_unknown', claim_token: token }) + await fs.access(imagePath) + await resolveUnknownPrintJob(job.id, user.id, { confirmed: true, outcome: 'not_printed', expected_claim_token: token }) + await fs.access(imagePath) + const claim = await claimNextPrintJob(terminal.id, 2) + assert.equal(claim.job?.id, job.id) + assert.ok(claim.job?.claim_token && claim.job.claim_token !== token) + const payload = claim.job?.payload + assert.ok(payload && 'rendered_labels' in payload) + assert.equal(payload.rendered_labels?.[0].image_data_url, PNG_ONE) + await assert.rejects(updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'completed', claim_token: token }), /凭据/) + await fs.access(imagePath) + await updateClaimedPrintJobStatus(terminal.id, job.id, { status: 'completed', claim_token: claim.job!.claim_token! }) + await assert.rejects(fs.access(imagePath)) + } finally { await fs.rm(directory, { recursive: true, force: true }) } + }) + await t.test('人工确认已出纸可完成历史无token任务,重复人工决定拒绝', async () => { + const job = await createJob({ status: 'result_unknown' }) + await resolveUnknownPrintJob(job.id, user.id, { confirmed: true, outcome: 'printed', expected_claim_token: null }) + const final = await prisma.printJob.findUniqueOrThrow({ where: { id: job.id } }) + assert.equal(final.status, 'completed') + assert.equal(final.result_confirmed_by, user.id) + await assert.rejects(resolveUnknownPrintJob(job.id, user.id, { confirmed: true, outcome: 'not_printed', expected_claim_token: null }), /状态已变化/) + }) + await t.test('真实GET /api/health能够验证迁移后的业务列和会话表', async () => { + // The web-session table is managed outside the Prisma schema. The fixture + // has only the column used by readiness; no login or real session is used. + await prisma.$executeRawUnsafe('CREATE TABLE IF NOT EXISTS web_session (sid VARCHAR(128) PRIMARY KEY)') + const app = express() + app.get('/api/health', getHealth) + const server = app.listen(0, '127.0.0.1') + try { + await new Promise((resolve) => server.listening ? resolve() : server.once('listening', resolve)) + const address = server.address() + assert.ok(address && typeof address !== 'string') + const response = await fetch(`http://127.0.0.1:${address.port}/api/health`) + assert.equal(response.status, 200) + assert.equal((await response.json() as { status: string }).status, 'ok') + } finally { await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())) } + }) + } finally { + await prisma.printJob.deleteMany({ where: { requested_by: user.id, target_terminal_id: terminal.id } }) + await prisma.printTerminal.delete({ where: { id: terminal.id } }) + await prisma.user.delete({ where: { id: user.id } }) + await prisma.$disconnect() + } +}) diff --git a/backend/src/services/__tests__/printFixture.ts b/backend/src/services/__tests__/printFixture.ts new file mode 100644 index 0000000..9dcfe7b --- /dev/null +++ b/backend/src/services/__tests__/printFixture.ts @@ -0,0 +1,30 @@ +import { deflateSync } from 'zlib' + +export const pngChunk = (type: string, content: Buffer) => { + const chunk = Buffer.alloc(content.length + 12) + chunk.writeUInt32BE(content.length, 0) + chunk.write(type, 4, 4, 'ascii') + content.copy(chunk, 8) + let crc = 0xffffffff + for (const value of chunk.subarray(4, chunk.length - 4)) { + crc ^= value + for (let bit = 0; bit < 8; bit++) crc = (crc >>> 1) ^ ((crc & 1) ? 0xedb88320 : 0) + } + chunk.writeUInt32BE((crc ^ 0xffffffff) >>> 0, chunk.length - 4) + return chunk +} + +export const makePng = (width = 1, height = 1, pixels = Buffer.from([0, 0, 0, 0, 255])) => { + const header = Buffer.alloc(13) + header.writeUInt32BE(width, 0) + header.writeUInt32BE(height, 4) + header[8] = 8 + header[9] = 6 + return Buffer.concat([ + Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), + pngChunk('IHDR', header), pngChunk('IDAT', deflateSync(pixels)), pngChunk('IEND', Buffer.alloc(0)) + ]) +} + +export const PNG_ONE = `data:image/png;base64,${makePng().toString('base64')}` +export const PNG_TWO = `data:image/png;base64,${makePng(1, 1, Buffer.from([0, 255, 0, 0, 255])).toString('base64')}` diff --git a/backend/src/services/__tests__/printPng.test.ts b/backend/src/services/__tests__/printPng.test.ts new file mode 100644 index 0000000..848b742 --- /dev/null +++ b/backend/src/services/__tests__/printPng.test.ts @@ -0,0 +1,45 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { randomBytes } from 'crypto' +import { sanitizeRenderedLabelImages } from '../printService' +import { validateLabelPng, MAX_LABEL_IMAGE_BYTES } from '../../utils/labelPng' +import { makePng, PNG_ONE } from './printFixture' + +test('真实PNG可打印,假Base64、截断、CRC错误、尺寸炸弹均拒绝', () => { + validateLabelPng(makePng()) + assert.throws(() => validateLabelPng(Buffer.from('fake image')), /PNG/) + assert.throws(() => validateLabelPng(makePng().subarray(0, -1)), /不完整/) + const bad = makePng() + bad[40] ^= 1 + assert.throws(() => validateLabelPng(bad), /校验/) + assert.throws(() => validateLabelPng(makePng(100000, 100000)), /尺寸/) + assert.throws(() => validateLabelPng(makePng(1, 1, Buffer.alloc(1_000_000))), /压缩数据/) + assert.throws(() => validateLabelPng(makePng(1, 1, Buffer.from([5, 0, 0, 0, 255]))), /过滤/) +}) + +test('拒绝每张和任务数量预算超限,保存前完成检查', () => { + assert.throws(() => validateLabelPng(Buffer.alloc(MAX_LABEL_IMAGE_BYTES + 1)), /2 MB/) + assert.throws(() => sanitizeRenderedLabelImages(Array.from({ length: 101 }, (_, i) => ({ + equipment_id: i + 1, image_data_url: PNG_ONE + })), Array.from({ length: 101 }, (_, i) => i + 1)), /100/) +}) + +test('成品图重复编号、缺少或多余资产都拒绝', () => { + const entry = { equipment_id: 1, image_data_url: PNG_ONE } + assert.throws(() => sanitizeRenderedLabelImages([entry, entry], [1, 2]), /重复/) + assert.throws(() => sanitizeRenderedLabelImages([entry], [1, 2]), /数量/) + assert.throws(() => sanitizeRenderedLabelImages([entry], [2]), /缺少/) +}) + +test('每张均为有效且未超限PNG时,整批解码总量仍受20MB预算限制', () => { + const width = 1024, height = 256, rowBytes = width * 4 + 1 + const pixels = randomBytes(rowBytes * height) + for (let row = 0; row < height; row++) pixels[row * rowBytes] = 0 + const image = makePng(width, height, pixels) + assert.ok(image.length < MAX_LABEL_IMAGE_BYTES) + validateLabelPng(image) + const imageDataUrl = `data:image/png;base64,${image.toString('base64')}` + assert.throws(() => sanitizeRenderedLabelImages(Array.from({ length: 21 }, (_, i) => ({ + equipment_id: i + 1, image_data_url: imageDataUrl + })), Array.from({ length: 21 }, (_, i) => i + 1)), /总大小超限/) +}) diff --git a/backend/src/services/__tests__/printService.test.ts b/backend/src/services/__tests__/printService.test.ts index 0bcfb4d..7985096 100644 --- a/backend/src/services/__tests__/printService.test.ts +++ b/backend/src/services/__tests__/printService.test.ts @@ -1,5 +1,6 @@ import test from 'node:test' import assert from 'node:assert/strict' +import { PNG_ONE, PNG_TWO } from './printFixture' import { normalizeLabelProfile, planPrintTerminalAdminStatusChange, @@ -306,11 +307,11 @@ test('sanitizeRenderedLabelImages 会按 equipment_ids 顺序重排网页生成 [ { equipment_id: 2, - image_data_url: 'data:image/png;base64,ZmFrZS1pbWFnZS0y' + image_data_url: PNG_TWO }, { equipment_id: 1, - image_data_url: 'data:image/png;base64,ZmFrZS1pbWFnZS0x' + image_data_url: PNG_ONE } ], [1, 2] @@ -319,11 +320,11 @@ test('sanitizeRenderedLabelImages 会按 equipment_ids 顺序重排网页生成 assert.deepEqual(renderedLabels, [ { equipment_id: 1, - image_data_url: 'data:image/png;base64,ZmFrZS1pbWFnZS0x' + image_data_url: PNG_ONE }, { equipment_id: 2, - image_data_url: 'data:image/png;base64,ZmFrZS1pbWFnZS0y' + image_data_url: PNG_TWO } ]) }) @@ -335,7 +336,7 @@ test('sanitizeRenderedLabelImages 在缺少网页成品图时直接报错,避 [ { equipment_id: 1, - image_data_url: 'data:image/png;base64,ZmFrZS1pbWFnZS0x' + image_data_url: PNG_ONE } ], [1, 2] diff --git a/backend/src/services/assetMovementService.ts b/backend/src/services/assetMovementService.ts index e83cca0..98590e9 100644 --- a/backend/src/services/assetMovementService.ts +++ b/backend/src/services/assetMovementService.ts @@ -622,7 +622,7 @@ export const createAssetMovementRecord = async ( client: TransactionClient, input: { items: AssetTreeItem[] - actionType: 'transfer' | 'idle' | 'return' + actionType: 'transfer' | 'idle' | 'return' | 'loss' toDepartmentId: number | null toDepartmentName?: string | null operatedBy: number @@ -677,7 +677,7 @@ export const transferAssetTree = async ( } const blocked = items.find((item) => - ['已借出', '维护中', '待报废', '已报废'].includes(item.status) + ['已借出', '维护中', '待报废', '已报废', '已丢失'].includes(item.status) ) if (blocked) { throw new Error(`资产 ${blocked.equipment_code} 当前为${blocked.status},不能调拨`) @@ -751,15 +751,17 @@ export const recordReturnToStorage = async ( rootEquipmentId: number operatedBy: number reason?: string | null + returnCondition?: string } ) => { const items = await loadAndLockAssetTree(client, input.rootEquipmentId) + assertReturnConditionSupported(items, input.returnCondition || '正常') const movement = await createAssetMovementRecord(client, { items, - actionType: 'return', + actionType: input.returnCondition === '丢失' ? 'loss' : 'return', toDepartmentId: null, operatedBy: input.operatedBy, - reason: input.reason || '正常归还入仓' + reason: input.returnCondition === '丢失' ? `丢失登记(未入仓)${input.reason ? `;${input.reason}` : ''}` : input.reason || '正常归还入仓' }) return { items, movement } } @@ -769,7 +771,7 @@ const updateReturnedRootWithSnapshot = async ( root: AssetTreeItem, input: { remainingQuantity: number - status: '可用' | '已借出' + status: '可用' | '已借出' | '已损坏' | '已丢失' departmentId: number | null } ) => { @@ -805,10 +807,23 @@ const updateReturnedRootWithSnapshot = async ( } } +export const assertReturnConditionSupported = (items: AssetTreeItem[], returnCondition: string) => { + if (!['正常', '损坏', '丢失'].includes(returnCondition)) { + throw Object.assign(new Error('归还状况无效'), { status: 422 }) + } + if (returnCondition !== '正常' && items.some((item) => item.total_quantity !== 1)) { + throw new AssetTreeConflictError('多数量资产无法准确记录单件损坏或丢失,请先人工核实并拆分台账;原借用单保持未归还') + } +} + export const finalizeNormalReturnState = async ( client: TransactionClient, - root: AssetTreeItem + root: AssetTreeItem, + returnCondition = '正常', + lockedItems?: AssetTreeItem[] ) => { + const items = lockedItems || (returnCondition === '正常' ? [root] : await loadAndLockAssetTree(client, root.id)) + assertReturnConditionSupported(items, returnCondition) if (root.parent_id !== null) { throw new AssetTreeConflictError('挂载中的子资产不能单独归还') } @@ -821,6 +836,15 @@ export const finalizeNormalReturnState = async ( throw new AssetTreeConflictError('资产状态与当前库存不一致,请先核查') } + if (returnCondition !== '正常') { + const status = returnCondition === '损坏' ? '已损坏' as const : '已丢失' as const + const remainingQuantity = returnCondition === '损坏' ? 1 : 0 + for (const item of items) { + await updateReturnedRootWithSnapshot(client, item, { remainingQuantity, status, departmentId: null }) + } + return { remainingQuantity, borrowedQuantity: 0, status, departmentId: null } + } + const remainingQuantity = root.remaining_quantity + 1 const borrowedQuantity = root.total_quantity - remainingQuantity const status = remainingQuantity > 0 ? '可用' : '已借出' diff --git a/backend/src/services/hrSyncService.ts b/backend/src/services/hrSyncService.ts index 50f712a..79686d1 100644 --- a/backend/src/services/hrSyncService.ts +++ b/backend/src/services/hrSyncService.ts @@ -1,4 +1,5 @@ import prisma from '../utils/prisma' +import { assertHrSyncSourceSafe } from '../utils/hrSyncSafety' import sql from 'mssql' import { HR_BUSINESS_DEPARTMENT_SOURCE_SYSTEM, @@ -1823,10 +1824,14 @@ export const previewHrSync = async () => { } } +let hrSyncRunning = false + export const runHrSync = async ( operator = 'system-sync', triggerMode: SyncTriggerMode = inferTriggerMode(operator) ) => { + if (hrSyncRunning) throw Object.assign(new Error('花名册同步正在进行,请稍后再试'), { status: 409 }) + hrSyncRunning = true let pool: any = null try { @@ -1838,6 +1843,11 @@ export const runHrSync = async ( pool = await sql.connect(getSqlServerConfig()) const personCollection = await getSourcePersons(pool) const persons = personCollection.persons + assertHrSyncSourceSafe( + persons.map((person) => person.sourceUserId), + [...beforePersonMap.values()].filter((person) => person.status === 'active').map((person) => person.sourceUserId), + { maxRatio: process.env.HR_SYNC_MAX_DEACTIVATION_RATIO, maxCount: process.env.HR_SYNC_MAX_DEACTIVATION_COUNT } + ) const businessDepartments = buildBusinessDepartmentCandidates(persons) const departmentResult = await syncDepartments(businessDepartments) @@ -1938,8 +1948,10 @@ export const runHrSync = async ( throw error } finally { - if (pool) { - await pool.close() + try { + if (pool) await pool.close() + } finally { + hrSyncRunning = false } } } diff --git a/backend/src/services/inventoryService.ts b/backend/src/services/inventoryService.ts index 897d5bd..831f31d 100644 --- a/backend/src/services/inventoryService.ts +++ b/backend/src/services/inventoryService.ts @@ -1,5 +1,5 @@ import { randomBytes } from 'crypto' -import type { PrismaClient } from '@prisma/client' +import { Prisma, type PrismaClient } from '@prisma/client' import prisma from '../utils/prisma' import { buildBusinessDepartmentWhere } from '../utils/organization' import { @@ -701,6 +701,18 @@ const buildTaskScopeSnapshot = (scope: InventoryScopeBuildResult) => ({ const createPublicToken = () => randomBytes(24).toString('hex') +const lockOpenInventoryTask = async (tx: Prisma.TransactionClient, taskId: number) => { + const rows = await tx.$queryRaw>( + Prisma.sql`SELECT id, status FROM inventory_task WHERE id = ${taskId} FOR UPDATE` + ) + const task = rows[0] + if (!task) throw Object.assign(new Error('盘点任务不存在'), { status: 404 }) + if (task.status === INVENTORY_TASK_STATUSES.closed) { + throw Object.assign(new Error('盘点任务已关闭,不能修改盘点结果或分配责任人'), { status: 403 }) + } + return task +} + const refreshTaskCounters = async (tx: PrismaClient, taskId: number) => { const [participantsCount, itemsCount, completedCount, exceptionCount, unassignedCount] = await Promise.all([ tx.inventoryTaskParticipant.count({ @@ -1191,37 +1203,18 @@ export const getInventoryTaskDetail = async (taskId: number) => { } export const closeInventoryTask = async (taskId: number, operatorUserId: number) => { - const task = await prisma.inventoryTask.findUnique({ - where: { id: taskId }, - select: { - id: true, - status: true - } - }) - - if (!task) { - throw new Error('盘点任务不存在') - } - - if (task.status === INVENTORY_TASK_STATUSES.closed) { - return - } - await prisma.$transaction(async (tx) => { + const rows = await tx.$queryRaw>( + Prisma.sql`SELECT id, status FROM inventory_task WHERE id = ${taskId} FOR UPDATE` + ) + if (!rows[0]) throw new Error('盘点任务不存在') + if (rows[0].status === INVENTORY_TASK_STATUSES.closed) return await tx.inventoryTask.update({ where: { id: taskId }, - data: { - status: INVENTORY_TASK_STATUSES.closed, - closed_at: new Date() - } + data: { status: INVENTORY_TASK_STATUSES.closed, closed_at: new Date() } }) - await tx.inventoryTaskAction.create({ - data: { - task_id: taskId, - action_type: 'task_closed', - operator_user_id: operatorUserId - } + data: { task_id: taskId, action_type: 'task_closed', operator_user_id: operatorUserId } }) }) } @@ -1488,114 +1481,66 @@ const updateTaskItemStatus = async ( check_status: string exception_type?: string | null exception_note?: string | null + action_type: string + action_payload: Record } -) => { - const item = await prisma.inventoryTaskItem.findFirst({ - where: { - id: itemId, - task_id: taskId, - participant_id: participantId +) => prisma.$transaction(async (tx) => { + // Serialize task mutations before any aggregate snapshot is established. + // Closing uses the same row lock, so no scan can commit after task closure. + await lockOpenInventoryTask(tx, taskId) + const item = await tx.inventoryTaskItem.findFirst({ + where: { id: itemId, task_id: taskId, participant_id: participantId } + }) + if (!item) throw new Error('盘点资产不在当前人员范围内') + const updated = await tx.inventoryTaskItem.update({ + where: { id: itemId }, + data: { + check_status: payload.check_status, + checked_by_participant_id: participantId, + checked_at: new Date(), + exception_type: payload.exception_type ?? null, + exception_note: payload.exception_note ?? null } }) - - if (!item) { - throw new Error('盘点资产不在当前人员范围内') - } - - await prisma.$transaction(async (tx) => { - await tx.inventoryTaskItem.update({ - where: { id: itemId }, - data: { - check_status: payload.check_status, - checked_by_participant_id: participantId, - checked_at: new Date(), - exception_type: payload.exception_type ?? null, - exception_note: payload.exception_note ?? null - } - }) - - await tx.inventoryTaskParticipant.update({ - where: { id: participantId }, - data: { - last_action_at: new Date() - } - }) - - await refreshParticipantStatus(tx as unknown as PrismaClient, participantId) - await refreshTaskCounters(tx as unknown as PrismaClient, taskId) + await tx.inventoryTaskParticipant.update({ + where: { id: participantId }, data: { last_action_at: new Date() } }) -} + await refreshParticipantStatus(tx as unknown as PrismaClient, participantId) + await refreshTaskCounters(tx as unknown as PrismaClient, taskId) + await tx.inventoryTaskAction.create({ + data: { + task_id: taskId, participant_id: participantId, item_id: item.id, + action_type: payload.action_type, action_payload: JSON.stringify(payload.action_payload) + } + }) + return updated +}) export const submitInventoryScan = async (taskId: number, participantId: number, equipmentCode: string) => { const normalizedCode = equipmentCode.trim() - if (!normalizedCode) { - throw new Error('资产编码不能为空') - } - + if (!normalizedCode) throw new Error('资产编码不能为空') const item = await prisma.inventoryTaskItem.findFirst({ - where: { - task_id: taskId, - equipment_code: normalizedCode - } + where: { task_id: taskId, equipment_code: normalizedCode } }) - - if (!item) { - throw new Error('该资产不在本次盘点任务范围内') - } - - if (item.participant_id !== participantId) { - throw new Error('该资产不在你的盘点范围内') - } - - await updateTaskItemStatus(taskId, participantId, item.id, { + if (!item) throw new Error('该资产不在本次盘点任务范围内') + if (item.participant_id !== participantId) throw new Error('该资产不在你的盘点范围内') + return updateTaskItemStatus(taskId, participantId, item.id, { check_status: INVENTORY_ITEM_CHECK_STATUS.checked, - exception_type: null, - exception_note: null + exception_type: null, exception_note: null, + action_type: 'item_checked', action_payload: { equipment_code: normalizedCode } }) - - await prisma.inventoryTaskAction.create({ - data: { - task_id: taskId, - participant_id: participantId, - item_id: item.id, - action_type: 'item_checked', - action_payload: JSON.stringify({ - equipment_code: normalizedCode - }) - } - }) - - return item } export const submitInventoryException = async ( - taskId: number, - participantId: number, - itemId: number, - exceptionType: string, - exceptionNote?: string | null + taskId: number, participantId: number, itemId: number, + exceptionType: string, exceptionNote?: string | null ) => { - if (!['not_found', 'not_with_me'].includes(exceptionType)) { - throw new Error('异常类型无效') - } - - await updateTaskItemStatus(taskId, participantId, itemId, { + if (!['not_found', 'not_with_me'].includes(exceptionType)) throw new Error('异常类型无效') + return updateTaskItemStatus(taskId, participantId, itemId, { check_status: INVENTORY_ITEM_CHECK_STATUS.exception, - exception_type: exceptionType, - exception_note: exceptionNote?.trim() || null - }) - - await prisma.inventoryTaskAction.create({ - data: { - task_id: taskId, - participant_id: participantId, - item_id: itemId, - action_type: 'item_exception_reported', - action_payload: JSON.stringify({ - exception_type: exceptionType, - exception_note: exceptionNote?.trim() || null - }) - } + exception_type: exceptionType, exception_note: exceptionNote?.trim() || null, + action_type: 'item_exception_reported', + action_payload: { exception_type: exceptionType, exception_note: exceptionNote?.trim() || null } }) } @@ -1641,18 +1586,10 @@ const updateTaskItemStatusByAdmin = async ( action_payload?: Record | null } ) => { - const item = await prisma.inventoryTaskItem.findFirst({ - where: { - id: itemId, - task_id: taskId - } - }) - - if (!item) { - throw new Error('该资产不在本次盘点任务范围内') - } - - await prisma.$transaction(async (tx) => { + return prisma.$transaction(async (tx) => { + await lockOpenInventoryTask(tx, taskId) + const item = await tx.inventoryTaskItem.findFirst({ where: { id: itemId, task_id: taskId } }) + if (!item) throw new Error('该资产不在本次盘点任务范围内') await tx.inventoryTaskItem.update({ where: { id: item.id }, data: { @@ -1686,10 +1623,7 @@ const updateTaskItemStatusByAdmin = async ( operator_user_id: operatorUserId } }) - }) - - return prisma.inventoryTaskItem.findUnique({ - where: { id: item.id } + return tx.inventoryTaskItem.findUnique({ where: { id: item.id } }) }) } @@ -1739,49 +1673,33 @@ export const assignUnassignedTaskItem = async ( itemId: number, personId: number ) => { - const [task, item, person] = await Promise.all([ - prisma.inventoryTask.findUnique({ - where: { id: taskId }, - select: { id: true } - }), - prisma.inventoryTaskItem.findFirst({ - where: { - id: itemId, - task_id: taskId - } - }), - prisma.person.findUnique({ - where: { id: personId }, - select: { - id: true, - real_name: true, - nickname: true, - source_userid: true, - department: true, - main_department_id: true, - mainDepartment: { - select: { - id: true, - name: true - } + const person = await prisma.person.findUnique({ + where: { id: personId }, + select: { + id: true, + real_name: true, + nickname: true, + source_userid: true, + status: true, + department: true, + main_department_id: true, + mainDepartment: { + select: { + id: true, + name: true } } - }) - ]) + } + }) - if (!task) { - throw new Error('盘点任务不存在') - } - - if (!item || !item.is_unassigned) { - throw new Error('待处理资产不存在') - } - - if (!person) { - throw new Error('目标人员不存在') + if (!person || person.status !== 'active') { + throw new Error('目标人员不存在或已停用') } await prisma.$transaction(async (tx) => { + await lockOpenInventoryTask(tx, taskId) + const item = await tx.inventoryTaskItem.findFirst({ where: { id: itemId, task_id: taskId } }) + if (!item || !item.is_unassigned) throw new Error('待处理资产不存在或已被分配,请刷新后重试') let participant = await tx.inventoryTaskParticipant.findFirst({ where: { task_id: taskId, diff --git a/backend/src/services/printService.ts b/backend/src/services/printService.ts index fc07294..9609a2e 100644 --- a/backend/src/services/printService.ts +++ b/backend/src/services/printService.ts @@ -1,6 +1,7 @@ import { promises as fs } from 'fs' import path from 'path' import { createHash, randomBytes, randomUUID } from 'crypto' +import { validateLabelPng, MAX_LABELS_PER_JOB, MAX_LABEL_JOB_BYTES, MAX_LABEL_IMAGE_BYTES } from '../utils/labelPng' import type { Prisma, PrintJob, PrintTerminal } from '@prisma/client' import prisma from '../utils/prisma' @@ -90,6 +91,7 @@ export interface PairPrintTerminalInput { } export interface PrintHeartbeatInput { + protocol_version?: number name?: string | null platform?: string | null machine_name?: string | null @@ -99,7 +101,8 @@ export interface PrintHeartbeatInput { } export interface PrintJobStatusUpdateInput { - status: 'printing' | 'completed' | 'failed' + status: 'printing' | 'completed' | 'failed' | 'result_unknown' + claim_token: string error_message?: string | null retryable?: boolean } @@ -122,6 +125,7 @@ export const PRINT_JOB_STATUS = { printing: 'printing', completed: 'completed', failed: 'failed', + resultUnknown: 'result_unknown', cancelled: 'cancelled' } as const @@ -142,7 +146,7 @@ const DEFAULT_LABEL_PROFILE: NormalizedPrintLabelProfile = { const PRINT_TERMINAL_STALE_MS = 90_000 const PRINT_PAIR_CODE_TTL_MINUTES = 10 const MAX_PRINT_JOB_LIST_PAGE_SIZE = 50 -const MAX_RETRY_ATTEMPTS = 3 +export const PRINT_CLAIM_LEASE_MS = 120_000 const PAIR_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' const PNG_DATA_URL_PREFIX = 'data:image/png;base64,' const PNG_DATA_URL_PATTERN = /^data:image\/png;base64,[A-Za-z0-9+/]+={0,2}$/i @@ -476,6 +480,10 @@ const sanitizeEquipmentIds = (value: unknown) => { throw new PrintServiceError('请选择要打印的资产', 422) } + if (value.length > MAX_LABELS_PER_JOB || uniqueIds.length > MAX_LABELS_PER_JOB) { + throw new PrintServiceError(`每次最多打印 ${MAX_LABELS_PER_JOB} 张标签,请分批发送`, 422) + } + return uniqueIds } @@ -503,15 +511,16 @@ const decodePngDataUrl = (value: unknown) => { } const base64 = normalized.slice(PNG_DATA_URL_PREFIX.length) + if (base64.length > Math.ceil(MAX_LABEL_IMAGE_BYTES / 3) * 4) { + throw new PrintServiceError('单张标签图片不能超过 2 MB', 422) + } try { const buffer = Buffer.from(base64, 'base64') - if (buffer.length === 0) { - throw new Error('empty image') - } + validateLabelPng(buffer) return buffer - } catch { - throw new PrintServiceError('网页成品标签图格式无效,请刷新页面后重试', 422) + } catch (error) { + throw new PrintServiceError(error instanceof Error ? error.message : '网页成品标签图格式无效', 422) } } @@ -524,6 +533,10 @@ export const sanitizeRenderedLabelImages = ( } const imageMap = new Map() + if (value.length > MAX_LABELS_PER_JOB) { + throw new PrintServiceError(`每次最多打印 ${MAX_LABELS_PER_JOB} 张标签,请分批发送`, 422) + } + let totalBytes = 0 for (const item of value) { if (!item || typeof item !== 'object' || Array.isArray(item)) { @@ -540,7 +553,10 @@ export const sanitizeRenderedLabelImages = ( } const imageDataUrl = String((item as { image_data_url?: unknown }).image_data_url || '').trim() - decodePngDataUrl(imageDataUrl) + totalBytes += decodePngDataUrl(imageDataUrl).length + if (totalBytes > MAX_LABEL_JOB_BYTES) { + throw new PrintServiceError('标签图片总大小超限,请减少标签数量后发送', 422) + } imageMap.set(equipmentId, { equipment_id: equipmentId, @@ -809,6 +825,7 @@ const mapTerminalRecord = ( id: terminal.id, name: terminal.name, platform: terminal.platform, + protocol_version: terminal.protocol_version, machine_name: terminal.machine_name, status: parsed.effectiveStatus, is_default: normalizedDefaultFlag, @@ -872,6 +889,10 @@ const mapPrintJobRecord = async ( attempt_count: job.attempt_count, error_message: job.error_message, claimed_at: job.claimed_at, + claim_token: job.claim_token, + lease_expires_at: job.lease_expires_at, + result_confirmed_by: job.result_confirmed_by, + result_confirmed_at: job.result_confirmed_at, started_at: job.started_at, completed_at: job.completed_at, created_at: job.created_at, @@ -1315,6 +1336,7 @@ export const listPrintJobs = async ( status?: string | null ) => { const safePage = Math.max(1, Number(page) || 1) + await expireStalePrintClaims() const safePageSize = Math.min( MAX_PRINT_JOB_LIST_PAGE_SIZE, Math.max(1, Number(pageSize) || 10) @@ -1571,6 +1593,7 @@ export const heartbeatPrintTerminalAgent = async ( machine_name: sanitizeText(input.machine_name) || existing.machine_name, status: PRINT_TERMINAL_STATUS.online, last_seen_at: new Date(), + protocol_version: input.protocol_version === 2 ? 2 : 1, selected_printer_name: sanitizeText(input.selected_printer_name) || existing.selected_printer_name, printer_snapshot_json: @@ -1611,7 +1634,29 @@ const buildRequesterSelection = { name: true } as const -export const claimNextPrintJob = async (terminalId: number) => { +export const expireStalePrintClaims = async (now = new Date()) => { + // A lost acknowledgment may follow actual output. Never blindly requeue. + return prisma.printJob.updateMany({ + where: { + status: { in: [PRINT_JOB_STATUS.claimed, PRINT_JOB_STATUS.printing] }, + OR: [ + { lease_expires_at: { lte: now } }, + { lease_expires_at: null } + ] + }, + data: { + status: PRINT_JOB_STATUS.resultUnknown, + lease_expires_at: null, + error_message: '打印助手失联或旧任务缺少领取凭据,请核实是否已出纸后处理' + } + }) +} + +export const claimNextPrintJob = async (terminalId: number, protocolVersion?: number) => { + if (protocolVersion !== 2) { + throw new PrintServiceError('请先更新打印助手,再领取打印任务', 426) + } + await expireStalePrintClaims() const terminal = await getPrintTerminalById(terminalId) const terminalView = mapTerminalRecord({ ...terminal, @@ -1649,6 +1694,10 @@ export const claimNextPrintJob = async (terminalId: number) => { status: PRINT_JOB_STATUS.claimed, claimed_by_terminal_id: terminalId, claimed_at: new Date(), + claim_token: randomUUID(), + lease_expires_at: new Date(Date.now() + PRINT_CLAIM_LEASE_MS), + result_confirmed_by: null, + result_confirmed_at: null, attempt_count: { increment: 1 } @@ -1699,12 +1748,16 @@ export const claimNextPrintJob = async (terminalId: number) => { ? `任务成品标签图读取失败,请在系统里重新发送打印:${error.message}` : '任务成品标签图读取失败,请在系统里重新发送打印' - await prisma.printJob.update({ + await prisma.printJob.updateMany({ where: { - id: claimedJob.id + id: claimedJob.id, + status: PRINT_JOB_STATUS.claimed, + claimed_by_terminal_id: terminalId, + claim_token: claimedJob.claim_token }, data: { status: PRINT_JOB_STATUS.failed, + lease_expires_at: null, error_message: errorMessage } }) @@ -1722,6 +1775,12 @@ export const updateClaimedPrintJobStatus = async ( jobId: number, input: PrintJobStatusUpdateInput ) => { + if (!(['printing', 'completed', 'failed', 'result_unknown'] as string[]).includes(input.status)) { + throw new PrintServiceError('打印任务状态无效', 422) + } + const claimToken = sanitizeText(input.claim_token) + if (!claimToken) throw new PrintServiceError('缺少领取凭据,请更新打印助手', 422) + await expireStalePrintClaims() const existing = await prisma.printJob.findUnique({ where: { id: jobId @@ -1747,14 +1806,23 @@ export const updateClaimedPrintJobStatus = async ( throw new PrintServiceError('该任务不属于当前打印终端', 403) } - if (existing.claimed_by_terminal_id && existing.claimed_by_terminal_id !== terminalId) { - throw new PrintServiceError('该任务已被其他终端领取', 409) + if (existing.claimed_by_terminal_id !== terminalId || existing.claim_token !== claimToken) { + throw new PrintServiceError('领取凭据已失效,请核实任务结果', 409) } const now = new Date() const errorMessage = sanitizeText(input.error_message) - let data: Prisma.PrintJobUncheckedUpdateInput + // Repeat result submission after a network failure is safe. It never dispatches + // paper again and never lets a late printing/failed callback regress completion. + if (existing.status === input.status && input.status !== PRINT_JOB_STATUS.printing) { + return { job: await mapPrintJobRecord(existing), message: '该结果已接收' } + } + if (!(['claimed', 'printing', 'result_unknown'] as string[]).includes(existing.status)) { + throw new PrintServiceError('当前任务已结束,不能覆盖已有结果', 409) + } + + let data: Prisma.PrintJobUpdateManyMutationInput let message = '状态已更新' if (input.status === PRINT_JOB_STATUS.printing) { @@ -1767,6 +1835,7 @@ export const updateClaimedPrintJobStatus = async ( data = { status: PRINT_JOB_STATUS.printing, started_at: existing.started_at || now, + lease_expires_at: new Date(now.getTime() + PRINT_CLAIM_LEASE_MS), error_message: null } message = '任务已标记为打印中' @@ -1775,7 +1844,7 @@ export const updateClaimedPrintJobStatus = async ( !([ PRINT_JOB_STATUS.claimed, PRINT_JOB_STATUS.printing, - PRINT_JOB_STATUS.completed + PRINT_JOB_STATUS.resultUnknown ] as string[]).includes(existing.status) ) { throw new PrintServiceError('当前任务状态不能更新为已完成', 409) @@ -1785,41 +1854,42 @@ export const updateClaimedPrintJobStatus = async ( status: PRINT_JOB_STATUS.completed, started_at: existing.started_at || now, completed_at: now, + lease_expires_at: null, error_message: null } message = '任务已完成' } else { - if ( - !([PRINT_JOB_STATUS.claimed, PRINT_JOB_STATUS.printing] as string[]).includes(existing.status) - ) { - throw new PrintServiceError('当前任务状态不能更新为失败', 409) - } - - const shouldRequeue = Boolean(input.retryable) && existing.attempt_count < MAX_RETRY_ATTEMPTS - if (shouldRequeue) { - data = { - status: PRINT_JOB_STATUS.queued, - claimed_by_terminal_id: null, - claimed_at: null, - started_at: null, - error_message: errorMessage || '打印失败,已重新排队' - } - message = '打印失败,任务已重新排队' - } else { - data = { - status: PRINT_JOB_STATUS.failed, - started_at: existing.started_at || now, - error_message: errorMessage || '打印失败' - } - message = '任务已标记为失败' + const resultUnknown = input.status === PRINT_JOB_STATUS.resultUnknown || + existing.status !== PRINT_JOB_STATUS.claimed || Boolean(existing.started_at) + data = { + status: resultUnknown ? PRINT_JOB_STATUS.resultUnknown : PRINT_JOB_STATUS.failed, + lease_expires_at: null, + error_message: errorMessage || (resultUnknown ? '打印结果待核实,请确认是否已出纸' : '打印未开始,任务失败') } + message = resultUnknown ? '结果待核实,任务不会自动重印' : '任务已标记为失败' } - const updated = await prisma.printJob.update({ + const updateResult = await prisma.printJob.updateMany({ where: { - id: jobId + id: jobId, + target_terminal_id: terminalId, + claimed_by_terminal_id: terminalId, + claim_token: claimToken, + status: existing.status, + ...(input.status === PRINT_JOB_STATUS.printing ? { lease_expires_at: { gt: now } } : {}) }, - data, + data + }) + if (updateResult.count !== 1) { + // A concurrent identical completion may have won this compare-and-swap. + const current = await prisma.printJob.findUnique({ where: { id: jobId } }) + if (!(input.status === PRINT_JOB_STATUS.completed && current?.status === PRINT_JOB_STATUS.completed && + current.claim_token === claimToken && current.claimed_by_terminal_id === terminalId)) { + throw new PrintServiceError('任务状态已变化,请重新核实结果', 409) + } + } + const updated = await prisma.printJob.findUniqueOrThrow({ + where: { id: jobId }, include: { targetTerminal: { select: buildTargetTerminalSelection @@ -1833,7 +1903,7 @@ export const updateClaimedPrintJobStatus = async ( } }) - if (updated.status === PRINT_JOB_STATUS.completed || updated.status === PRINT_JOB_STATUS.failed) { + if (updateResult.count === 1 && updated.status === PRINT_JOB_STATUS.completed) { await removeStoredRenderFilesForPayload(parseAssetLabelJobPayload(updated.payload_json)) } @@ -1843,6 +1913,55 @@ export const updateClaimedPrintJobStatus = async ( } } +export const getPrintAgentJobState = async (terminalId: number, jobId: number) => { + await expireStalePrintClaims() + const job = await prisma.printJob.findUnique({ where: { id: jobId } }) + if (!job) throw new PrintServiceError('打印任务不存在', 404) + if (job.target_terminal_id !== terminalId) throw new PrintServiceError('任务不属于当前终端', 403) + return { id: job.id, status: job.status, claim_token: job.claim_token } +} + +export const resolveUnknownPrintJob = async ( + jobId: number, + actorId: number, + input: { confirmed?: boolean; outcome?: string; expected_claim_token?: string | null } +) => { + if (input.confirmed !== true || !['printed', 'not_printed', 'cancel'].includes(input.outcome || '') || + !Object.prototype.hasOwnProperty.call(input, 'expected_claim_token') || + (input.expected_claim_token !== null && typeof input.expected_claim_token !== 'string')) { + throw new PrintServiceError('请核实出纸情况后明确选择处理结果', 422) + } + await expireStalePrintClaims() + const existing = await prisma.printJob.findUnique({ where: { id: jobId } }) + if (!existing) throw new PrintServiceError('打印任务不存在', 404) + const now = new Date() + const outcome = input.outcome + const nextStatus = outcome === 'printed' ? PRINT_JOB_STATUS.completed : + outcome === 'not_printed' ? PRINT_JOB_STATUS.queued : PRINT_JOB_STATUS.cancelled + const result = await prisma.printJob.updateMany({ + where: { id: jobId, status: PRINT_JOB_STATUS.resultUnknown, claim_token: input.expected_claim_token }, + data: { + status: nextStatus, + lease_expires_at: null, + result_confirmed_by: actorId, + result_confirmed_at: now, + error_message: outcome === 'not_printed' ? '管理员已核实未出纸,重新排队' : null, + ...(outcome === 'printed' ? { completed_at: now } : { + claim_token: null, + claimed_by_terminal_id: null, + claimed_at: null, + started_at: null, + completed_at: null + }) + } + }) + if (result.count !== 1) throw new PrintServiceError('任务状态已变化,请刷新并重新核实', 409) + if (outcome !== 'not_printed') { + await removeStoredRenderFilesForPayload(parseAssetLabelJobPayload(existing.payload_json)) + } + return { id: jobId, status: nextStatus } +} + export const reportPrintAgentTestPrint = async ( terminalId: number, input: PrintAgentTestPrintInput diff --git a/backend/src/utils/__tests__/businessDate.test.ts b/backend/src/utils/__tests__/businessDate.test.ts new file mode 100644 index 0000000..0528bcf --- /dev/null +++ b/backend/src/utils/__tests__/businessDate.test.ts @@ -0,0 +1,27 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { parseBusinessDate } from '../businessDate' + +test('legacy local dates are Shanghai time regardless of process timezone, and offset ISO stays exact', () => { + const original = process.env.TZ + try { + for (const timezone of ['UTC', 'Asia/Shanghai', 'America/New_York']) { + process.env.TZ = timezone + assert.equal(parseBusinessDate('2026-10-09T18:30')!.toISOString(), '2026-10-09T10:30:00.000Z') + assert.equal(parseBusinessDate('2026-10-09')!.toISOString(), '2026-10-08T16:00:00.000Z') + assert.equal(parseBusinessDate('2026-10-09T18:30:01+08:00')!.toISOString(), '2026-10-09T10:30:01.000Z') + assert.equal(parseBusinessDate('2026-10-09T10:30:01.123Z')!.toISOString(), '2026-10-09T10:30:01.123Z') + } + } finally { + if (original === undefined) delete process.env.TZ + else process.env.TZ = original + } +}) + +test('invalid calendar dates, times, unsupported formats and values reject rather than silently erase a deadline', () => { + for (const value of ['2026-02-30', '2026-13-01', '2026-10-09T24:00', '2026-10-09T10:60', '10/09/2026', 'abc', true, 0, {}, '2026-10-09T18:30:00+25:00']) { + assert.throws(() => parseBusinessDate(value), (error: any) => error.status === 422) + } + assert.equal(parseBusinessDate(null), null) + assert.equal(parseBusinessDate(''), null) +}) diff --git a/backend/src/utils/__tests__/dashboardStatistics.test.ts b/backend/src/utils/__tests__/dashboardStatistics.test.ts index 652f176..5857feb 100644 --- a/backend/src/utils/__tests__/dashboardStatistics.test.ts +++ b/backend/src/utils/__tests__/dashboardStatistics.test.ts @@ -34,6 +34,7 @@ test('可用和已借出记录应按剩余数量拆分,保证在册总量可 maintenance: 0, pendingDisposal: 0, damaged: 0, + lost: 0, other: 0, disposed: 0 }) @@ -45,6 +46,7 @@ test('可用和已借出记录应按剩余数量拆分,保证在册总量可 { status: '待报废', total_quantity: 1, remaining_quantity: 1 }, { status: '已损坏', total_quantity: 1, remaining_quantity: 1 }, { status: '自定义状态', total_quantity: 1, remaining_quantity: 1 }, + { status: '已丢失', total_quantity: 4, remaining_quantity: 0 }, { status: '已报废', total_quantity: 5, remaining_quantity: 5 } ]) @@ -55,6 +57,7 @@ test('可用和已借出记录应按剩余数量拆分,保证在册总量可 maintenance: 2, pendingDisposal: 1, damaged: 1, + lost: 4, other: 1, disposed: 5 }) diff --git a/backend/src/utils/__tests__/hrSyncSafety.test.ts b/backend/src/utils/__tests__/hrSyncSafety.test.ts new file mode 100644 index 0000000..36212f8 --- /dev/null +++ b/backend/src/utils/__tests__/hrSyncSafety.test.ts @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { assertHrSyncSourceSafe } from '../hrSyncSafety' + +const existing = Array.from({ length: 100 }, (_, index) => `employee-${index}`) + +test('HR rejects empty or duplicate identities before a destructive sync', () => { + for (const source of [[], [''], ['employee-1', 'employee-1']]) { + assert.throws(() => assertHrSyncSourceSafe(source, existing), (error: any) => error.status === 409 && /未修改组织或人员/.test(error.message)) + } +}) + +test('HR uses missing existing identities so a same-size replacement does not conceal abnormal deactivation', () => { + assert.doesNotThrow(() => assertHrSyncSourceSafe(existing.slice(20), existing)) + assert.throws(() => assertHrSyncSourceSafe(existing.slice(21), existing), /预计停用 21\/100 人/) + const replaced = [...existing.slice(21), ...Array.from({ length: 21 }, (_, index) => `new-${index}`)] + assert.throws(() => assertHrSyncSourceSafe(replaced, existing), /预计停用 21\/100 人/) +}) + +test('HR protects absolute count and accepts configured validated bounds for legitimate changes', () => { + const large = Array.from({ length: 1000 }, (_, index) => `employee-${index}`) + assert.throws(() => assertHrSyncSourceSafe(large.slice(51), large), /预计停用 51\/1000 人/) + assert.doesNotThrow(() => assertHrSyncSourceSafe(existing.slice(30), existing, { maxRatio: '0.35', maxCount: '40' })) + assert.doesNotThrow(() => assertHrSyncSourceSafe(['first'], [])) + for (const settings of [{ maxRatio: '1' }, { maxRatio: 'bad' }, { maxRatio: '-0.1' }, { maxCount: '1.5' }, { maxCount: '-1' }]) { + assert.throws(() => assertHrSyncSourceSafe(existing, existing, settings), /阈值配置无效/) + } +}) diff --git a/backend/src/utils/__tests__/requestBody.test.ts b/backend/src/utils/__tests__/requestBody.test.ts new file mode 100644 index 0000000..50812fc --- /dev/null +++ b/backend/src/utils/__tests__/requestBody.test.ts @@ -0,0 +1,59 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import express from 'express' +import { once } from 'node:events' +import { requirePermission } from '../../middleware/auth' +import { PERMISSIONS } from '../permissions' +import { parseStandardJsonBody, parseStandardFormBody, parsePrintLabelJsonBody } from '../requestBody' + +test('HTTP请求体限制保留大标签容量,权限不足时不解析图片JSON', async (t) => { + const app = express() + app.use(parseStandardJsonBody, parseStandardFormBody) + // A local fixture supplies permissions only; no login, database or terminal. + app.use((req, res, next) => { + res.locals.user = { permissions: req.headers['x-test-print'] === 'yes' ? [PERMISSIONS.ASSETS_PRINT_LABELS] : [] } + next() + }) + const router = express.Router() + router.post('/asset-labels', requirePermission(PERMISSIONS.ASSETS_PRINT_LABELS), parsePrintLabelJsonBody, + (req, res) => res.json({ length: req.body?.image?.length || 0 })) + app.use('/api/print-jobs', router) + app.post('/api/ordinary', (req, res) => res.json({ length: req.body?.image?.length || 0 })) + app.use((error: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => { + res.status(error.status || 500).json({ error: error.type }) + }) + const server = app.listen(0, '127.0.0.1') + await once(server, 'listening') + const address = server.address() + assert.ok(address && typeof address !== 'string') + const base = `http://127.0.0.1:${address.port}` + const post = (pathname: string, body: string, allowed = true, contentType = 'application/json') => fetch(base + pathname, { + method: 'POST', headers: { 'content-type': contentType, 'x-test-print': allowed ? 'yes' : 'no' }, body + }) + const sixMb = JSON.stringify({ image: 'x'.repeat(6 * 1024 * 1024) }) + try { + await t.test('普通JSON与表单超过5MB返回413', async () => { + assert.equal((await post('/api/ordinary', sixMb)).status, 413) + assert.equal((await post('/api/ordinary', sixMb, true, 'application/x-www-form-urlencoded')).status, 413) + }) + await t.test('没有打印权限时畸形JSON直接403,不先解析成400', async () => { + for (const pathname of ['/api/print-jobs/asset-labels', '/API/PRINT-JOBS/ASSET-LABELS/?source=test', '/api/print-jobs//asset-labels']) { + assert.equal((await post(pathname, '{invalid', false)).status, 403) + } + }) + await t.test('授权标签6MB能解析,大小写、尾斜杠和query与Express路由一致', async () => { + for (const pathname of ['/api/print-jobs/asset-labels', '/API/PRINT-JOBS/ASSET-LABELS/?source=test', '/api/print-jobs//asset-labels']) { + const response = await post(pathname, sixMb) + assert.equal(response.status, 200) + assert.equal((await response.json() as { length: number }).length, 6 * 1024 * 1024) + } + }) + await t.test('近似路径不能享受大请求例外,授权标签仍有30MB上限', async () => { + assert.equal((await post('/api/print-jobs/asset-labels-extra', sixMb)).status, 413) + assert.equal((await post('/api/print-jobs/asset-labels', JSON.stringify({ image: 'x'.repeat(30 * 1024 * 1024) }))).status, 413) + }) + } finally { + server.closeAllConnections() + await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())) + } +}) diff --git a/backend/src/utils/assetValue.ts b/backend/src/utils/assetValue.ts index 649741d..b05db5d 100644 --- a/backend/src/utils/assetValue.ts +++ b/backend/src/utils/assetValue.ts @@ -2,7 +2,7 @@ import type { Prisma, PrismaClient } from '@prisma/client' type EquipmentClient = PrismaClient | Prisma.TransactionClient -const NON_AVAILABLE_STATUSES = new Set(['维护中', '待报废', '已报废', '已损坏']) +const NON_AVAILABLE_STATUSES = new Set(['维护中', '待报废', '已报废', '已损坏', '已丢失']) const toSafeNumber = (value: unknown) => { if (value === null || value === undefined || value === '') { diff --git a/backend/src/utils/businessDate.ts b/backend/src/utils/businessDate.ts new file mode 100644 index 0000000..7615801 --- /dev/null +++ b/backend/src/utils/businessDate.ts @@ -0,0 +1,18 @@ +// Explicit offsets are preferred. Legacy local inputs are always Shanghai time, +// independent of the backend process timezone. +export const parseBusinessDate = (value: unknown, field = '日期'): Date | null => { + if (value === undefined || value === null || value === '') return null + if (typeof value !== 'string') throw Object.assign(new Error(`${field}格式无效`), { status: 422 }) + const input = value.trim() + const match = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2})?)?$/.exec(input) + if (!match) throw Object.assign(new Error(`${field}格式无效`), { status: 422 }) + const [, year, month, day, hour = '00', minute = '00', second = '00', fraction = '', offset] = match + const calendar = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day))) + if (calendar.getUTCFullYear() !== Number(year) || calendar.getUTCMonth() + 1 !== Number(month) + || calendar.getUTCDate() !== Number(day) || Number(hour) > 23 || Number(minute) > 59 || Number(second) > 59) { + throw Object.assign(new Error(`${field}格式无效`), { status: 422 }) + } + const date = new Date(`${year}-${month}-${day}T${hour}:${minute}:${second}${fraction}${offset || '+08:00'}`) + if (!Number.isFinite(date.getTime())) throw Object.assign(new Error(`${field}格式无效`), { status: 422 }) + return date +} diff --git a/backend/src/utils/dashboardStatistics.ts b/backend/src/utils/dashboardStatistics.ts index 615bf8f..9ef22ad 100644 --- a/backend/src/utils/dashboardStatistics.ts +++ b/backend/src/utils/dashboardStatistics.ts @@ -10,6 +10,7 @@ export type InventoryStatusBreakdown = { maintenance: number pendingDisposal: number damaged: number + lost: number other: number disposed: number } @@ -77,6 +78,7 @@ export const getInventoryStatusBreakdown = ( maintenance: 0, pendingDisposal: 0, damaged: 0, + lost: 0, other: 0, disposed: 0 } @@ -84,6 +86,9 @@ export const getInventoryStatusBreakdown = ( if (status === '已报废') { return { ...empty, disposed: totalQuantity } } + if (status === '已丢失') { + return { ...empty, lost: totalQuantity } + } empty.current = totalQuantity @@ -119,6 +124,7 @@ export const summarizeInventoryStatusRows = ( summary.maintenance += current.maintenance summary.pendingDisposal += current.pendingDisposal summary.damaged += current.damaged + summary.lost += current.lost summary.other += current.other summary.disposed += current.disposed return summary @@ -130,6 +136,7 @@ export const summarizeInventoryStatusRows = ( maintenance: 0, pendingDisposal: 0, damaged: 0, + lost: 0, other: 0, disposed: 0 } diff --git a/backend/src/utils/hrSyncSafety.ts b/backend/src/utils/hrSyncSafety.ts new file mode 100644 index 0000000..e6aa37f --- /dev/null +++ b/backend/src/utils/hrSyncSafety.ts @@ -0,0 +1,23 @@ +export const assertHrSyncSourceSafe = ( + sourceUserIds: string[], + existingActiveSourceUserIds: string[], + settings: { maxRatio?: string; maxCount?: string } = {} +) => { + const fail = (message: string): never => { + throw Object.assign(new Error(`花名册同步已停止:${message};未修改组织或人员,请核实上游数据和筛选规则`), { status: 409 }) + } + if (!sourceUserIds.length) fail('源在职人员为空') + const incoming = new Set(sourceUserIds) + if (incoming.size !== sourceUserIds.length || sourceUserIds.some((id) => !id.trim())) { + fail('源人员标识缺失或重复') + } + const maxRatio = settings.maxRatio === undefined ? 0.2 : Number(settings.maxRatio) + const maxCount = settings.maxCount === undefined ? 50 : Number(settings.maxCount) + if (!Number.isFinite(maxRatio) || maxRatio < 0 || maxRatio >= 1 + || !Number.isSafeInteger(maxCount) || maxCount < 0) fail('减员保护阈值配置无效') + const existing = new Set(existingActiveSourceUserIds) + const missing = [...existing].filter((id) => !incoming.has(id)).length + if (missing > maxCount || (existing.size > 0 && missing / existing.size > maxRatio)) { + fail(`预计停用 ${missing}/${existing.size} 人,超过减员保护阈值(${Math.round(maxRatio * 100)}% 或 ${maxCount} 人)`) + } +} diff --git a/backend/src/utils/labelPng.ts b/backend/src/utils/labelPng.ts new file mode 100644 index 0000000..d6cb10a --- /dev/null +++ b/backend/src/utils/labelPng.ts @@ -0,0 +1,93 @@ +import { inflateSync } from 'zlib' + +export const MAX_LABELS_PER_JOB = 100 +export const MAX_LABEL_IMAGE_BYTES = 2 * 1024 * 1024 +export const MAX_LABEL_JOB_BYTES = 20 * 1024 * 1024 +const MAX_LABEL_PIXELS = 4_000_000 +const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]) + +const crc32 = (bytes: Buffer) => { + let crc = 0xffffffff + for (const byte of bytes) { + crc ^= byte + for (let bit = 0; bit < 8; bit += 1) { + crc = (crc >>> 1) ^ ((crc & 1) ? 0xedb88320 : 0) + } + } + return (crc ^ 0xffffffff) >>> 0 +} + +// Canvas labels are non-interlaced PNGs. Check the container, decoded size and +// scanline filters before persisting untrusted images or handing them to clients. +export const validateLabelPng = (buffer: Buffer) => { + if (buffer.length > MAX_LABEL_IMAGE_BYTES) { + throw new Error('单张标签图片不能超过 2 MB') + } + if (buffer.length < 57 || !buffer.subarray(0, 8).equals(PNG_SIGNATURE)) { + throw new Error('标签图片不是有效的 PNG 文件') + } + let offset = 8 + let width = 0 + let height = 0 + let rowBytes = 0 + let ended = false + let dataEnded = false + let paletteSeen = false + let colorType = -1 + const imageData: Buffer[] = [] + while (offset < buffer.length) { + if (offset + 12 > buffer.length) throw new Error('PNG 文件不完整') + const size = buffer.readUInt32BE(offset) + const end = offset + 12 + size + if (end > buffer.length) throw new Error('PNG 文件不完整') + const type = buffer.toString('ascii', offset + 4, offset + 8) + const bytes = buffer.subarray(offset + 8, end - 4) + if (crc32(buffer.subarray(offset + 4, end - 4)) !== buffer.readUInt32BE(end - 4)) { + throw new Error('PNG 文件校验失败') + } + if (offset === 8 && type !== 'IHDR') throw new Error('PNG 缺少图片尺寸信息') + if (type === 'IHDR') { + if (offset !== 8 || size !== 13) throw new Error('PNG 图片头无效') + width = bytes.readUInt32BE(0) + height = bytes.readUInt32BE(4) + const bitDepth = bytes[8] + colorType = bytes[9] + const samples = new Map([[0, 1], [2, 3], [3, 1], [4, 2], [6, 4]]).get(colorType) + const depths = colorType === 0 ? [1, 2, 4, 8, 16] : colorType === 3 ? [1, 2, 4, 8] : [8, 16] + if (!width || !height || width > 4096 || height > 4096 || width * height > MAX_LABEL_PIXELS) { + throw new Error('标签图片尺寸超限,请使用网页生成的标签图') + } + if (!samples || !depths.includes(bitDepth) || bytes[10] !== 0 || bytes[11] !== 0 || bytes[12] !== 0) { + throw new Error('PNG 图片格式不受支持,请使用网页生成的标签图') + } + rowBytes = Math.ceil(width * samples * bitDepth / 8) + } else if (type === 'PLTE') { + if (paletteSeen || imageData.length || size === 0 || size > 768 || size % 3 !== 0) { + throw new Error('PNG 调色板无效') + } + paletteSeen = true + } else if (type === 'IDAT') { + if (dataEnded || (colorType === 3 && !paletteSeen)) throw new Error('PNG 图片数据无效') + imageData.push(bytes) + } else if (type === 'IEND') { + if (size || end !== buffer.length || !imageData.length) throw new Error('PNG 文件结束标记无效') + ended = true + } else { + if (imageData.length) dataEnded = true + if (type[0] === type[0].toUpperCase()) throw new Error('PNG 包含不支持的关键数据块') + } + offset = end + } + if (!ended) throw new Error('PNG 文件不完整') + const expectedBytes = (rowBytes + 1) * height + let decoded: Buffer + try { + decoded = inflateSync(Buffer.concat(imageData), { maxOutputLength: expectedBytes }) + } catch { + throw new Error('PNG 压缩数据无效或超过图片尺寸') + } + if (decoded.length !== expectedBytes) throw new Error('PNG 像素数据与尺寸不匹配') + for (let row = 0; row < height; row += 1) { + if (decoded[row * (rowBytes + 1)] > 4) throw new Error('PNG 像素过滤方式无效') + } +} diff --git a/backend/src/utils/maintenanceSnapshot.ts b/backend/src/utils/maintenanceSnapshot.ts new file mode 100644 index 0000000..557576f --- /dev/null +++ b/backend/src/utils/maintenanceSnapshot.ts @@ -0,0 +1,60 @@ +import type { AssetTreeItem } from '../services/assetMovementService' + +type MaintenanceSnapshotItem = { + id: number + parent_id: number | null + status: string + total_quantity: number + remaining_quantity: number +} + +export class MaintenanceSnapshotConflictError extends Error { + status = 409 +} + +export const buildMaintenanceSnapshot = (tree: AssetTreeItem[]) => JSON.stringify({ + version: 1, + assets: tree.map(({ id, parent_id, status, total_quantity, remaining_quantity }) => ({ + id, parent_id, status, total_quantity, remaining_quantity + })) +}) + +export const validateMaintenanceSnapshot = ( + rawSnapshot: string | null, + tree: AssetTreeItem[], + rootId: number +): MaintenanceSnapshotItem[] => { + const invalidSnapshot = () => new MaintenanceSnapshotConflictError('维修单缺少有效的送修前快照,请人工核实原状态后处理') + let snapshot: any + try { + snapshot = JSON.parse(rawSnapshot || '') + } catch { + throw invalidSnapshot() + } + if (snapshot?.version !== 1 || !Array.isArray(snapshot.assets) || snapshot.assets.length === 0) { + throw invalidSnapshot() + } + const assets = snapshot.assets as MaintenanceSnapshotItem[] + const ids = new Set() + for (const item of assets) { + if (!item || !Number.isSafeInteger(item.id) || item.id <= 0 || ids.has(item.id) + || !(item.parent_id === null || (Number.isSafeInteger(item.parent_id) && item.parent_id > 0)) + || !['可用', '已损坏'].includes(item.status) + || !Number.isSafeInteger(item.total_quantity) || item.total_quantity < 0 + || !Number.isSafeInteger(item.remaining_quantity) || item.remaining_quantity < 0 + || item.remaining_quantity > item.total_quantity) { + throw invalidSnapshot() + } + ids.add(item.id) + } + if (!assets.some((item) => item.id === rootId && item.parent_id === null)) throw invalidSnapshot() + if (assets.length !== tree.length || tree.some((current) => { + const previous = assets.find((item) => item.id === current.id) + return !previous || previous.parent_id !== current.parent_id || current.status !== '维护中' + || previous.total_quantity !== current.total_quantity + || previous.remaining_quantity !== current.remaining_quantity + })) { + throw new MaintenanceSnapshotConflictError('资产树状态、库存或挂载关系已变化,请人工核实后处理') + } + return assets +} diff --git a/backend/src/utils/requestBody.ts b/backend/src/utils/requestBody.ts new file mode 100644 index 0000000..5683823 --- /dev/null +++ b/backend/src/utils/requestBody.ts @@ -0,0 +1,15 @@ +import express, { type RequestHandler } from 'express' + +const parseStandardJson = express.json({ limit: '5mb' }) + +// Only the label endpoint needs space for base64 PNGs. Its router parses JSON +// after authentication and the existing print permission guard. +export const parseStandardJsonBody: RequestHandler = (req, res, next) => { + if (req.method === 'POST' && /^\/api\/print-jobs\/{1,2}asset-labels\/?$/i.test(req.path)) { + return next() + } + return parseStandardJson(req, res, next) +} + +export const parseStandardFormBody = express.urlencoded({ extended: true, limit: '5mb' }) +export const parsePrintLabelJsonBody = express.json({ limit: '30mb' }) diff --git a/backend/src/utils/returnSnapshot.ts b/backend/src/utils/returnSnapshot.ts new file mode 100644 index 0000000..3780b52 --- /dev/null +++ b/backend/src/utils/returnSnapshot.ts @@ -0,0 +1,52 @@ +import { createHash } from 'crypto' +import { buildRecursiveBorrowSnapshot, type AssetTreeItem } from '../services/assetMovementService' + +type BorrowSnapshotRecord = { + id: number + equipment_id: number + children_snapshot?: string | null + updated_at?: Date +} + +export const getReturnChildrenDiff = (items: AssetTreeItem[], record: BorrowSnapshotRecord) => { + const current = buildRecursiveBorrowSnapshot(items) + let borrowed: Array<{ id: number; parent_id?: number | null; parent_equipment_id?: number | null; [key: string]: unknown }> = [] + if (record.children_snapshot) { + try { + borrowed = JSON.parse(record.children_snapshot) + if (!Array.isArray(borrowed) || borrowed.some((item) => !item || !Number.isSafeInteger(item.id) || item.id <= 0) + || new Set(borrowed.map((item) => item.id)).size !== borrowed.length) throw new Error() + } catch { + throw Object.assign(new Error('借用记录的配件快照无效,请人工核实后归还'), { status: 409 }) + } + } + const placement = (item: { parent_id?: number | null; parent_equipment_id?: number | null }) => + item.parent_id ?? item.parent_equipment_id ?? items[0].id + const previousById = new Map(borrowed.map((item) => [item.id, item])) + const currentById = new Map(current.map((item) => [item.id, item])) + const removed = borrowed.filter((item) => { + const next = currentById.get(item.id) + return !next || placement(item) !== placement(next) + }) + const added = current.filter((item) => { + const previous = previousById.get(item.id) + return !previous || placement(previous) !== placement(item) + }) + return removed.length || added.length ? { removed, added } : null +} + +export const buildReturnConfirmationToken = (items: AssetTreeItem[], record: BorrowSnapshotRecord) => + createHash('sha256').update(JSON.stringify({ + borrow_record: { + id: record.id, + equipment_id: record.equipment_id, + updated_at: record.updated_at?.getTime(), + children_snapshot: record.children_snapshot ?? null + }, + asset_tree: items.map((item) => ({ + id: item.id, parent_id: item.parent_id, department_id: item.department_id, + status: item.status, mount_status: item.mount_status, + total_quantity: item.total_quantity, remaining_quantity: item.remaining_quantity, + unit_price: item.unit_price.toString(), updated_at: item.updated_at.getTime() + })) + })).digest('hex') diff --git a/docs/2026-10-09_问题修复与验证记录.md b/docs/2026-10-09_问题修复与验证记录.md new file mode 100644 index 0000000..872ce84 --- /dev/null +++ b/docs/2026-10-09_问题修复与验证记录.md @@ -0,0 +1,75 @@ +# 2026年10月9日 固定资产系统问题修复与验证记录 + +本轮已在本地修复原审计第 2–9、13–20 项业务与使用流程问题,并处理第 26 项中可兼容升级的应用依赖。前后端、两种打印助手和隔离 MySQL 回归通过。以下为提交前的本地验收记录;记录时尚未提交或发布,该修复阶段未连接生产、修改真实资产、重发通知或操作实体打印机。 + +按用户要求,账号安全、发布与备份相关整改不做。本轮开始时记录的 12 个认证、部署及配置文件 SHA256 在结束时一致;已有工作区改动保留。原始问题及当时生产证据见[审查清单](),不能把本地修复视为生产已经生效。 + +## 已修复行为 + +| 原审计项 | 修复结果 | +| --- | --- | +| 2 详情错位 | 切换资产时重建详情页面,异步请求按当前操作范围校验;旧请求不能覆盖新资产或已关闭的归还弹窗。资料未加载完成时不能提交操作。 | +| 3 损坏、丢失归还 | 损坏归还保留实物库存,但状态为“已损坏”、可用价值为 0;丢失归还记“已丢失”和 loss 流水,剩余数量为 0。普通与批量归还一致,统计单独列出损失数量。已丢失资产不能通过调拨、报废或修改数量恢复可用状态。 | +| 4–5 维修树与并发 | 未结束维修期间禁止挂载、卸载。完成与取消维修使用维修单和整树锁,并核对关系、状态、版本;竞争操作只能有一方成功,不覆盖另一方已经改变的状态。 | +| 6 HR 同步 | 写入组织和人员前拒绝空源、缺失或重复人员标识、异常减员;同一进程不能重叠执行同步。默认停用超过现有同步人数的 20% 或超过 50 人即停止,阈值配置无效也停止。 | +| 7–9 打印一致性与恢复 | 每次领取获得独立凭据和租约,状态更新采用条件写入。过期任务进入“结果待核实”,不自动重印;完成结果可以幂等补报,旧凭据和晚到失败不能覆盖完成或新派单。Electron 和 .NET 助手在出纸前持久化阶段,重启只对账,不再次执行未核实任务。 | +| 13 会话过期循环 | 获取用户失败时同步清理 Pinia 和持久化认证状态,稳定进入登录页,不再与首页循环。后端登录、密码和会话撤销策略未改。 | +| 14 跨页选择 | 缓存全部选中资产,跨页借还不遗漏前页选择;单项操作只移除成功项,批量失败项继续保留供处理。 | +| 15 时间偏移 | 浏览器提交带时区的 ISO 时间;后端兼容历史无时区输入,并按上海时区解析,避免 UTC 服务器造成 8 小时偏移。 | +| 16 Excel 导入 | 保留工作表实际行号,包括非 A1 起始区域及空行;数量 0、非数字和非法价格逐行报错,不默认为 1 或在序列化时变成 0。 | +| 17–18 盘点状态与计数 | 修改明细、分配和更新汇总在同一事务中先锁定任务;已关闭任务拒绝再分配,并发扫描和管理员核验不会丢失汇总或审计动作。 | +| 19 配件归还确认 | 借出时为空的配件集合仍与当前树比对;归还确认绑定当前树和版本,确认后树发生变化会返回冲突,不能沿用旧确认。各普通归还页面都处理确认流程。 | +| 20 标签图片与请求大小 | 校验 PNG 签名、块顺序、CRC、尺寸、解压字节数和像素过滤格式;每任务最多 100 张,单图最多 2 MiB,总图最多 20 MiB。普通 JSON/form 最多 5 MiB;标签 JSON 在现有登录与打印权限检查后解析,上限 30 MiB,容纳 base64 开销。 | + +多数量资产的损坏、丢失归还若无法用现有单一状态准确表达,会返回 409 并保留原记录,要求单独核实,避免把部分损坏的资产误记为整批可用。丢失资产的历史购入成本继续保留,实物数量与可用价值排除损失,页面注明统计口径。 + +打印管理员处理“结果待核实”时,必须选择已出纸、未出纸或取消,并勾选已核实;没有预选结果。未出纸才允许重新派单。未知结果保留原 PNG,新派单换领取凭据;完成或取消后才清理图片。客户端校验服务器返回的任务 ID、凭据和状态,409、空响应或未知状态都不会直接丢弃恢复记录。Electron 同时只允许一个应用实例运行。 + +HR 阈值可通过 `HR_SYNC_MAX_DEACTIVATION_RATIO` 和 `HR_SYNC_MAX_DEACTIVATION_COUNT` 配置。当前重叠保护仅限同一进程;本轮没有引入多实例调度或分布式锁。 + +## 验证结果 + +| 验证 | 结果与覆盖范围 | +| --- | --- | +| 前后端 `npm run build` | 均通过;前端仍有统计相关 chunk 约 1.13 MB 的构建提示,未据此认定实际页面卡顿。 | +| 后端全套测试 | 285 项通过,2 个实库套件默认跳过;常规测试使用不可用的本机数据库地址,避免误连真实库。 | +| 本机 MySQL 打印回归 | 10/10 通过:竞争领取、重复完成、旧凭据、租约失效、部分出纸未知、人工核实竞争、PNG 保留与清理、真实 `GET /api/health`。 | +| 本机 MySQL 业务回归 | 5/5 通过:连接范围门禁、并发盘点与核验、关闭后分配拒绝、维修完成/取消竞争 4 轮、损坏与丢失归还。 | +| 前端专项回归 | 24/24 通过:路由、请求竞态、跨页选择、会话失效、时间、导入与归还确认,以及既有借还和标签展示。 | +| 请求体真实 HTTP 回归 | 5/5 通过:普通 JSON/form 大小拒绝、权限检查先于标签解析、授权大标签、大小写/尾斜杠/中间双斜杠/查询串,以及近似路径与打印上限。已计入后端全套数量。 | +| Electron 助手 | 29/29 通过,覆盖持久化恢复、回写断网、部分打印失败和无效服务器回读。 | +| .NET 助手 | 实际 Windows 项目构建 0 警告、0 错误;7 组恢复测试通过。 | +| Prisma | 客户端生成通过;新增打印迁移 SQL 在独立测试库基线执行成功,健康检查读取新增业务列成功。 | +| 浏览器 | 使用本机模拟数据操作真实前端:跨页两项均进入批量弹窗,打印核实必须主动选择并确认,390px 手机视口弹窗可用,丢失资产禁调拨,401 后稳定进入登录页。未向生产提交任何动作。 | +| 工作区检查 | `git diff --check` 通过;排除的 12 个文件与本轮开始时哈希相同。 | + +两套实库测试显式启用才运行,连接限定本机专用端口和 `equipment_audit` 数据库,拒绝其他目标和连接参数覆盖。测试样本及衍生记录已按确切 ID 清理并核查零残留;本轮创建的数据库容器、浏览器页和模拟服务已移除或停止。 + +前端回归在 `frontend` 目录执行: + +```powershell +node --test scripts/test-audit-functional-fixes.ts scripts/test-audit-routing.mjs scripts/test-borrow-scan-state.ts scripts/test-return-scan-state.ts scripts/test-notification-quick-return.ts scripts/test-phase-two-presentation.ts scripts/test-local-batch-print.ts +``` + +后端全部 `src/**/*.test.ts` 使用 `ts-node/register` 执行;实库测试分别通过 `RUN_PRINT_DB_TESTS=true`、`RUN_BUSINESS_DB_TESTS=true` 启用。Electron 使用 `npm test`,.NET 使用 `dotnet build` 和 `RecoveryTests` 控制台项目。 + +## 依赖处理与保留边界 + +前端生产依赖审计由 10 项包级告警降至 0;`xlsx` 按 [SheetJS 官方安装说明](https://docs.sheetjs.com/docs/getting-started/installation/nodejs/)切换到官方 0.20.3 分发包。后端在现有兼容范围内更新,删除无使用入口的 `multer` 及其类型包;ExcelJS 的 uuid 子依赖固定到修复版本 11.1.1,并验证 XLSX 写入、读取和条件格式往返。[uuid 安全公告](https://github.com/advisories/GHSA-w5hq-g745-h8pq) + +后端生产依赖告警由 27 项降至 11 项,不能宣称清零: + +- 8 项属于保留的 Keycloak 认证依赖链,含 6 项 high、2 项 low;本轮不做认证链跨主版本调整。 +- 3 项 moderate 来自 `sprintf-js → tedious → mssql` 同一依赖链。[上游公告](https://github.com/advisories/GHSA-hp3w-g68c-fv3c)尚无修复版本。已检查当前 SQL 驱动内使用点均为固定格式串,没有发现本项目把不可信数据用作格式串的入口;这不等于依赖告警消失。 + +第 26 项因此仅完成应用依赖中已验证的兼容修复。Node 容器运行时及容器资源、日志配置属于用户排除的发布配置,保持原状。 + +账号登录限流、密码强度、改密撤销会话、Keycloak 身份映射、旧盘点链接身份策略、打印 HTTP 长期令牌限制,以及数据库账号和端口、发布回退、备份内容/权限/保留策略均未整改。对应原清单第 1、10–12、21–25 项及 3 个条件性安全风险继续保留。 + +## 后续使用条件 + +新增打印字段位于[增量迁移]()。未来上线需要数据库迁移和支持协议 2 的新版助手配套;旧助手领取任务会得到更新提示。此次只完成源码、构建和测试,没有发布安装包或部署服务。 + +本机验证了新增增量 SQL,未证明现有历史迁移能从空库完整部署;早期迁移依赖预先存在的基础表,该发布基线缺口按用户要求保留。历史维修没有可靠快照时仍拒绝猜测还原;旧借用配件快照按既有空集合语义比较当前树,不凭空补造历史配件。 + +尚未验收实体打印出纸、真实用户登录流程和生产流量表现。当前恢复设计减少重复出纸风险,但打印机提交与服务端写入不能形成同一事务;“结果待核实”仍需要核实实物后决定,不能承诺实体打印恰好一次。 diff --git a/docs/2026-10-09_项目问题与隐患审查.md b/docs/2026-10-09_项目问题与隐患审查.md new file mode 100644 index 0000000..922602c --- /dev/null +++ b/docs/2026-10-09_项目问题与隐患审查.md @@ -0,0 +1,139 @@ +# 2026年10月9日 固定资产系统问题与隐患审查 + +本轮列出 26 项整改问题:11 项 P1、15 项 P2,另有 3 项依赖配置或历史数据的条件性风险。优先处理误操作资产、虚假可用库存、HR 空源停用、打印状态一致性,以及本地修复未上线的问题。 + +代码基准为 `main@843babd` 加当前未提交工作区,包含 10 月 7 日的本地整改。生产证据采自 10 月 9 日固定资产系统的只读核查。本轮新增审计文档,未修改业务代码、数据库数据或部署,未补发消息。 + +P1 表示可能造成错误资产操作、数据损坏、核心流程中断或明显安全风险,应优先整改;P2 表示影响一致性、安全、恢复能力和使用体验。代码缺陷与“已经在线上造成损失”分别判断,后者不能仅凭 mock 推断。 + +## 当前生产状态 + +| 项目 | 10 月 9 日核查结果 | 含义 | +| --- | --- | --- | +| 维修快照 | `maintenance_record.original_tree_snapshot` 列不存在 | 本地快照整改尚未在生产生效 | +| 健康检查 | 已部署文件中没有新 `healthController.js`;健康接口仍固定返回 `Server is running` | HTTP 200 不能证明业务表、迁移和数据库可用 | +| 数据库 | 应用使用 root;3306 绑定宿主机 IPv4 和 IPv6 全网卡 | 最小权限和端口收敛尚未完成;当前电脑直连 3306 超时,不能据此断言公网已开放或所有来源都被阻断 | +| 运行时 | Node.js `v20.20.2`,进程 UID 为 0,时区 UTC,MySQL 默认隔离级别 `REPEATABLE-READ` | 日期偏移条件已具备;并发审查需考虑旧快照;运行账号权限过大 | +| 通知 | 通知中心 70 条已读、12 条未读,没有 failed;另有 5 条旧通道停用记录 | 暂无新的通知中心失败;历史旧通道记录不应当作需要补发的新故障 | +| 证书 | 严格 TLS 校验通过,到期为北京时间 2027 年 3 月 23 日 07:59:59 | 上次证书替换有效,当前没有过期问题 | +| 打印 | 162 条完成、15 条排队,最早排队任务创建于 3 月 30 日 | 存在长期积压;排队状态本身不证明已领取任务卡死 | +| 备份权限 | 最新标准备份目录及所有祖先目录为 755,数据库和上传归档为 644 | 同服务器其他本地账号可读取备份 | + +生产当前不使用公开默认数据库密码。本轮不把上次已消除的默认密码问题再次认定为现存事实。 + +## P1 问题 + +### 1 维修和健康检查的本地整改尚未上线 + +生产没有维修快照字段,也没有新健康检查控制器;已部署维修、报废控制器与本地构建文件的 SHA256 不同。本地构建通过并不等于生产使用了这套代码,至少维修快照及真实健康检查仍未生效。 + +应把已完成的本地修复纳入可审查的发布版本,执行备份、迁移预检、增量迁移和发布验收。历史维修记录不能推测回填快照。相关文件:[维修控制器]()、[健康控制器]()、[快照迁移]()。 + +### 2 切换资产详情后显示旧资产却操作新资产 + +详情只在 `onMounted` 加载,没有监听路由 ID;同组件复用时,页面继续显示资产 A,删除和维修却取新 URL 中资产 B 的 ID。顶部全局搜索从 A 进入 B 即可触发,存在删除错误资产、错误登记维修和保存错位资料的风险。 + +Vue 内存路由复现结果:URL 为 `/equipment/edit/2`,显示 ID 为 1,删除目标为 2,加载次数仍为 1。应在 ID 变化时重载并清空旧状态,操作目标与当前展示对象保持一致。[详情加载]()、[删除目标]()、[路由容器]()。 + +### 3 损坏或丢失归还仍增加可用库存 + +归还状况只记在借用单,库存更新统一加 1 并恢复“可用”,没有让损坏或丢失进入对应资产状态。内存 mock 中,两种归还均成功,可用库存变为 1、可用价值恢复,借用单却仍记录损坏或丢失。 + +应先确定损坏、丢失的正式业务状态,再统一普通和批量归还规则;不能用备注替代库存状态。[归还状态计算]()、[普通归还]()、[批量归还]()。 + +### 4 维修中卸载原损坏配件会直接变可用 + +“维护中”父资产允许卸载,卸载后的子树只能设置为“可用”,没有核查未完成维修单及送修前状态。原损坏配件整树送修后被拆下,会绕过维修成为可借资产;之后取消维修又因树关系变化返回 409。 + +内存 mock 已复现父资产仍维护中、配件脱离后可用。应限制维修期间的资产树变更,或建立可审计的维修拆装流程并保留配件状态。[允许状态]()、[拆下后状态]()、[卸载入口]()。 + +### 5 完成维修可能覆盖已卸载并重新借出的配件 + +完成维修没有锁定整棵资产树,随后对子项仅按 ID 覆盖状态。在可重复读事务先建立快照、另一事务卸载并重新借出配件的交错下,完成事务可能从旧快照读出原配件,将其覆盖为可用,而新借用单仍未归还。 + +已用内存 mock 模拟该交错;生产默认隔离级别确为 `REPEATABLE-READ`,但尚未做隔离实库并发验证。应采用一致的整树锁顺序,并在写入时核对关系、状态和版本。[维修完成]()、[子项无条件更新]()。 + +### 6 HR 成功空源可能停用全部同步人员 + +源查询成功但人员结果为空时,流程仍用 `source_userid notIn []` 停用所有同步人员,再删除没有借用或盘点历史的人员。上游 ETL 空窗、错误筛选和异常数据都可能被误认为人员整体离职。 + +原函数内存调用已捕获空源仍执行停用和清理的条件;未据此认定线上已经发生。应在写入前校验源完整性、空集和人数骤减,并把大批停用作为需要复核的变更。[批量停用]()、[执行顺序]()、[清理删除]()。 + +### 7 打印状态并发回写可把完成任务重新排队 + +打印回写先读取并检查状态,随后只按任务 ID 更新。完成与可重试失败同时读到 `printing` 时,完成先提交,失败仍可把任务改回 `queued`;完成路径还可能已清理标签图片。 + +mock 最终状态为 `queued`,两次更新条件都只有 ID。应使用包含原状态、领取终端和版本的条件更新,保持完成状态不可倒退,并协调图片清理。[回写入口]()、[更新条件]()。 + +### 8 打印领取后缺少失联恢复 + +领取后终端断网或崩溃,任务停在 `claimed/printing`;领取入口只读取 `queued`,管理员也只能取消 `queued`,没有租约到期回收或领取任务的人工核实入口。 + +应增加领取租约和心跳、失联状态及人工恢复入口。对可能已经出纸的任务,恢复时必须区分结果未知和明确未打印,避免盲目重新排队。[领取]()、[取消限制]()、[任务模型]()。 + +### 9 .NET 打印助手把结果回写失败误报为打印失败 + +实际打印和 `completed` 请求位于同一个 try 中。打印已提交,但完成回写请求未到服务器时,catch 又上报 `failed`,使“已打印但回写未知”变成“打印失败”;管理员补发可能重复出纸。 + +应分别处理实际打印与状态回写,持久化待回写结果,重启后继续对账。[打印与完成回写]()、[失败回写]()。此项为代码控制流证据,未用实体打印机触发。 + +### 10 本地登录缺少限流且允许极弱密码 + +应用的本地登录没有账号或 IP 的失败计数、退避和锁定;后端创建及重置密码只要求非空,单字符密码可被接受。生产当前使用 local 模式,因此该登录路径启用;是否另有外围 WAF 限流没有核实。 + +应在应用层建立密码强度规则及登录限流,保留审计,同时避免把攻击者的失败尝试变成永久锁死正常账号。[登录]()、[重置校验]()。 + +### 11 发布覆盖旧源码后才做部署备份 + +发布脚本先删除并替换服务端 backend、frontend、scripts 和配置,再调用 deploy.sh 中的“发布前备份”。因此标准备份保存的是新配置,旧源码已被删除;后续构建或上线失败没有自动回到上一版本的步骤。 + +应在覆盖前保存旧版本和配置,保留可回退的镜像标识,再采用原子切换;发布失败需有明确恢复路径。[先覆盖文件]()、[后调用部署]()、[备份配置内容]()。 + +## P2 问题 + +| 编号 | 问题与影响 | 证据与整改方向 | +| --- | --- | --- | +| 12 | 重置密码不撤销旧会话;被盗会话可在改密后继续使用 | [密码更新]()仅改密码,[会话认证]()不校验认证版本。mock 确认旧会话仍通过。引入认证版本或按用户撤销会话 | +| 13 | 会话过期后,前端在登录与首页之间循环 | [路由守卫]()捕获失败但不清 Pinia 认证状态。真实守卫提取复现循环。统一 401 和会话失效的清理流程 | +| 14 | 跨页批量借出和归还静默漏掉前页所选资产 | [编码查询]()只查当前页,选中 ID 却跨页保留。源码提取复现只提交第 2 页资产。维护完整选中对象或按 ID 批量获取 | +| 15 | 预计归还时间在生产偏移 8 小时,影响逾期判断 | [前端提交]()传无时区日期,[后端解析]()按服务器时区解释。生产为 UTC;北京时间 12 点会保存为 12:00Z,回显 20 点。提交含明确时区的 ISO 时间 | +| 16 | Excel 数量 0 或非数字被改成 1,错误行被丢弃且行号错位 | [导入转换]()使用默认数量和 filter。源码提取复现 3 行仅提交 2 行。保留原始行号,逐行报告错误,禁止静默改数 | +| 17 | 已关闭盘点任务仍可分配责任人,改动已结束结果 | [分配入口]()查询任务仅选 ID,未核查状态。closed 任务 mock 分配成功。事务内校验并锁定任务状态 | +| 18 | 不同参与人并发盘点会覆盖汇总计数 | [汇总]()先聚合后覆盖,[调用]()缺少任务级串行保护。mock 明细 checked=2 而汇总为 1;尚未隔离实库验证。采用一致锁序与正确隔离读,或安全增量计数 | +| 19 | 借出时无配件的普通归还跳过后来新增配件的确认 | [差异检查]()遇 null 快照直接认为无差异。mock 更新根和新配件却不要求确认。区分可靠空集合与缺失快照,对当前资产树做完整比对 | +| 20 | 打印图片只校验 Data URL,缺少真实 PNG、尺寸和单任务数量限制 | [校验]()接受伪 PNG,后续落盘;[请求解析]()在认证前允许 50 MB。应限制解码字节、像素和标签数量,并适度限制公开端点请求体 | +| 21 | 数据库运行账号权限过大,生产端口仍绑定全网卡 | [Compose]()仍使用 root;生产实际映射 3306 与 3000。应用故障或入侵会扩大数据库影响面;公网直连未证实。分离迁移与运行账号,取消无必要端口暴露 | +| 22 | 生产数据库及上传备份可被其他本地账号读取 | 最新标准备份数据文件 644,全部父目录 755;[备份脚本]()无限制 umask。设置目录 700、文件 600,核查并修正存量归档权限 | +| 23 | 标准备份不足以独立恢复整套服务 | [备份内容]()不含环境配置、证书、对应版本源码或镜像。只持有标准备份时不能独立重建服务。分开保管加密配置和证书,记录版本及镜像,并做隔离恢复演练 | +| 24 | KEEP_BACKUPS=0 被接受并删除全部标准备份,包括刚生成的一份 | [参数校验]()接受 0,[清理]()删除全部。纯 mock 替换 rm 后已复现。要求正整数并在清理前确认至少保留一份完整备份 | +| 25 | 容器缺少内存限制和日志轮转,可能耗尽共享主机资源 | 生产 equipment 容器 Memory=0,json-file 日志配置为空;[Compose]()没有资源或日志限制。应按实际容量配置限额和 max-size/max-file,并监测磁盘;本轮未证明已经耗尽 | +| 26 | 依赖与运行时需要安全更新 | 本地生产依赖扫描后端报告 27 项包级告警、前端 10 项;告警数不等于可利用漏洞数。xlsx 0.18.5 在实际导入路径被使用,生产 Node 20 已处于官方 EOL。需逐条判断可达性、升级并回归;不要盲目采用 audit 建议的跨主版本降级 | + +第 26 项的直接证据:[xlsx 锁定版本]()、[实际读取文件]()。SheetJS 官方确认读取特制文件时的[原型污染](https://cdn.sheetjs.com/advisories/CVE-2023-30533)及[正则拒绝服务](https://cdn.sheetjs.com/advisories/CVE-2024-22363)问题覆盖该版本。Node.js 官方[版本状态表](https://nodejs.org/en/about/previous-releases)已将 Node 20 标为 EOL。 + +## 条件性风险 + +1. **Keycloak 用户名映射可能继承本地高权限身份。** [身份映射]()按用户名匹配,没有绑定 issuer 和 subject。只有启用 Keycloak 且非可信用户能注册或改成对应本地用户名时才成立。当前生产是 local 模式,不能说线上已发生该越权。 + +2. **旧 legacy 盘点共享链接可选择他人身份。** [旧身份确认]()依赖 token 和参与人 ID。当前生产唯一 legacy 任务已关闭,活跃任务为 dingtalk 模式;若将历史任务重新开放或恢复为可操作状态,需要先迁移身份验证。 + +3. **打印助手允许 HTTP 携带长期令牌。** [Electron 地址配置]()和 [.NET 地址配置]()未强制 HTTPS,客户端随后附带 Bearer。纯 mock 已证实此行为;默认 HTTPS 不触发,当前实际终端配置未核实。生产地址应拒绝 HTTP,开发例外单独控制。 + +## 修复顺序与验收 + +1. 先处理资产详情错位、损坏或丢失归还、维修拆装与完成并发、HR 空源保护。验收必须覆盖错误资产 ID、损坏库存、树关系变更、并发借出以及空源和人数骤减。 +2. 修正打印回写条件、失联恢复和 .NET 结果回写,区分已打印、未打印、结果未知;增加持久化恢复和重复上报场景。 +3. 补齐密码规则、登录限流及改密撤销会话,同时修复前端 401 循环。 +4. 收敛备份权限、修正覆盖前备份和回退流程,再把已有本地维修、权限、健康检查及部署整改发布到生产。新增数据库字段走增量迁移,不自动猜测旧记录。 +5. 处理跨页选择、日期时区、导入错误行、盘点状态和计数,以及依赖与容器资源配置。并发问题在隔离 MySQL 环境验证,不向生产注入测试数据。 + +## 已执行验证及范围 + +- 前端 `npm run build`、后端 `npm run build` 均通过。 +- 后端现有 251 项测试通过;执行时数据库地址设为不可用的本机审计地址,避免连接真实数据库。 +- Electron 打印助手现有 18 项测试通过。 +- 备份保留、报废迁移预检、数据库部署配置 3 个 shell 测试脚本分别通过。 +- 资产路由复用、跨页选择、认证循环、导入转换,以及归还、维修、HR、打印、盘点相关触发路径使用源码提取或内存 mock 验证。并发 mock 不能替代隔离实库验证,具体限制已随条目标明。 +- 生产只读核查限定固定资产目录、equipment 容器及 equipment_management 数据库,未操作同机其他系统。 + +本轮未完成实体打印验收、带真实登录态的浏览器流程验收和隔离实库并发压测。现有测试通过说明已覆盖场景正常,不能排除清单中的未覆盖分支。前端构建中统计相关共享 chunk 约 1.12 MB,移动端加载性能也值得进一步测量;构建体积本身不证明页面已经卡顿。 diff --git a/downloads/print-agent/latest/manifest.json b/downloads/print-agent/latest/manifest.json index c556ecf..ff9e696 100644 --- a/downloads/print-agent/latest/manifest.json +++ b/downloads/print-agent/latest/manifest.json @@ -1,9 +1,11 @@ { - "version": "0.2.6", - "published_at": "2026-03-28T23:59:00+08:00", + "version": "0.2.7", + "published_at": "2026-10-09T13:58:38+08:00", "filename": "print-agent-setup.exe", - "notes": "打印助手最新稳定版,已修复远程批量打印时网页成品标签图没有被打印助手实际使用的问题;现在优先直接打印网页最终 PNG,批量标签的版式与网页预览保持一致,并同步放宽大批量任务的请求容量限制。", + "notes": "打印助手 0.2.7,支持断网后补报结果、重启恢复及打印结果核实。配合本次系统更新使用,请升级后再接单;安装器内置 .NET 8 运行时。", "source_type": "server_release", "relative_path": "downloads/print-agent/latest/print-agent-setup.exe", - "relativePath": "downloads/print-agent/latest/print-agent-setup.exe" + "relativePath": "downloads/print-agent/latest/print-agent-setup.exe", + "sha256": "b6707f6ecffdcaaf5fa9d841bb342d9945d30728e1f9cd18133b4026c178e030", + "protocol_version": 2 } diff --git a/downloads/print-agent/releases/0.2.7/manifest.json b/downloads/print-agent/releases/0.2.7/manifest.json new file mode 100644 index 0000000..1b4d5ff --- /dev/null +++ b/downloads/print-agent/releases/0.2.7/manifest.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.7", + "published_at": "2026-10-09T13:58:38+08:00", + "filename": "print-agent-setup-0.2.7.exe", + "notes": "打印助手 0.2.7,支持断网后补报结果、重启恢复及打印结果核实。配合本次系统更新使用,请升级后再接单;安装器内置 .NET 8 运行时。", + "source_type": "server_release", + "relative_path": "downloads/print-agent/releases/0.2.7/print-agent-setup-0.2.7.exe", + "relativePath": "downloads/print-agent/releases/0.2.7/print-agent-setup-0.2.7.exe", + "sha256": "b6707f6ecffdcaaf5fa9d841bb342d9945d30728e1f9cd18133b4026c178e030", + "protocol_version": 2 +} diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 8fc7fbf..79e9587 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -85,8 +85,8 @@ server { root /usr/share/nginx/html; index index.html; - ssl_certificate /etc/letsencrypt/live/asset.jiansun.vip/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/asset.jiansun.vip/privkey.pem; + ssl_certificate /etc/letsencrypt/equipment-management/asset.jiansun.vip/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/equipment-management/asset.jiansun.vip/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_protocols TLSv1.2 TLSv1.3; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a5d5471..7378b5e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,7 +20,7 @@ "qrcode": "^1.5.4", "vue": "^3.5.24", "vue-router": "^4.6.3", - "xlsx": "^0.18.5" + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" }, "devDependencies": { "@tailwindcss/forms": "^0.5.10", @@ -37,9 +37,9 @@ } }, "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", + "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", "dev": true, "license": "MIT", "engines": { @@ -50,30 +50,30 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -83,22 +83,22 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -130,9 +130,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -419,9 +419,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -436,9 +436,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -453,9 +453,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -470,9 +470,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -487,9 +487,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -504,9 +504,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -521,9 +521,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -546,27 +546,9 @@ "vue": ">= 3" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", - "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", @@ -577,7 +559,7 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", @@ -586,14 +568,14 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", - "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", @@ -602,6 +584,26 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -640,28 +642,17 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.29", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", - "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", - "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.3.tgz", + "integrity": "sha512-a31GFbL70GlGUGhn8goVjRLtdDG1urLCJFWLZ7JcxdHK9pPvCohLFtYLgJDdNbtrNZSrJl1u9bK05ISsSC2EpA==", "cpu": [ "arm" ], @@ -673,9 +664,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", - "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.3.tgz", + "integrity": "sha512-IRv757RLiPGlNxAX5JXU0eiUlUPvKiwFRbkqLd+gywptLK4uSEg1qkl6en6r0cXMazSnWUWOBABv+QfuLV3n5Q==", "cpu": [ "arm64" ], @@ -687,9 +678,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", - "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.3.tgz", + "integrity": "sha512-UkzI35Fnz6+MJNATdnjnZ3TpdG/rXab8h7oDOD9R0zDVIFmuuYrvgUj8V8mh+A//+LR4TlEQo4ZW4RhyRtWv3A==", "cpu": [ "arm64" ], @@ -701,9 +692,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", - "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.3.tgz", + "integrity": "sha512-C3AyMtAnKQ/jSkZ0chIoWbPXRbFz8JnPLhl1ULecEhcaOGkT19xt7KmO8BulQxt9S/XideHRXvsVQUbGj4JCOg==", "cpu": [ "x64" ], @@ -715,9 +706,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", - "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.3.tgz", + "integrity": "sha512-M8yAdBMyHh14JLeXDNGvEkljzuHdcoLBQ399O4Fp4nvX51lueuEY/5U2G9Ci0D6/bQlBp/3JLiBQH55STwwbwg==", "cpu": [ "arm64" ], @@ -729,9 +720,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", - "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.3.tgz", + "integrity": "sha512-WQjuOKGh2FN98ZK6i0uM6M6YqnPEds85p3oRkrxiEeTQo/p7RpuU8c9MwQ88LfgwVAzohumlzbQQSikz4Zw9rw==", "cpu": [ "x64" ], @@ -743,13 +734,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", - "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.3.tgz", + "integrity": "sha512-5tDLZG5s9WyVKoEHgwos6FpKrgpotl61EYeHSLSVkQzMzTsaClwEr8mok9rm3y734zjiGInG4dfw/56ebWVvEQ==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -757,13 +751,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", - "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.3.tgz", + "integrity": "sha512-Mh5KbLeplM2HX6lQI18jsxSsqvPW8d+Hyk8HtY6Zl93woMitRSCZ92rBEzq6v8bfZGlpyMgj4P/sKW4KCjhH3A==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -771,13 +768,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", - "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.3.tgz", + "integrity": "sha512-QZrrwHnznTv6y4iiHtCqDN+ZsF4qNFQObMOLgPrbxhh9O5o4rgoUe5OnkqG/ceCObMrAjhQt+eQ6HSDzBFgVMw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -785,13 +785,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", - "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.3.tgz", + "integrity": "sha512-4vLehpXSwlkltnvk5ETB+Hz+d3xayZS3NamZ5Vq4bSo+IUoIALovEJF1YgWPMjHwN7R5o/R4BpP9h3q8EK1xAA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -799,13 +802,33 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", - "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.3.tgz", + "integrity": "sha512-SEN7TTTOdXJKZmsWKOnQeVfgy3Dje/08ISXWwFhTggQ3SxjuIaChvmOB5ZDZqJNlBjaXrV6nAHraqEAvdLGqcw==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.3.tgz", + "integrity": "sha512-1LDsQMRMCEdGF9Dih5qyh6fF1LK3NnA+pAtE2+9AgZwUdXyd19TOBVC9crSB63iZBHR4vOG7h8gN/LcUH3J7xw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -813,13 +836,33 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", - "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.3.tgz", + "integrity": "sha512-v3D2tAfRriqGOi+5Ac5CiYbBo2NXtLVxJlludTvUZRj502lVMFeVIuvvWbz4qCrotn3e3Uvpvh7FcMpG2QxxXQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.3.tgz", + "integrity": "sha512-db5q6dP189V2+JkMpsXBl8ym6bC0x8ydZ47fUJZ9YdM6T2OF5uqNzX9HaAZHRP/lqjA9bJeNwi0qw1/2/37dUA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -827,13 +870,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", - "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.3.tgz", + "integrity": "sha512-nArAVDmrL5ix9UoipcKEsYJw4kMUQCIPYRWuYF2POOC7jd9rmN95d/48ewsYFC/OdtwEx6VYYWYxrp8FbMOU2Q==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -841,13 +887,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", - "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.3.tgz", + "integrity": "sha512-2dc7LElu7EVSvpQT8epNnBLa02wlqilNYqHN0FA/RaKqCr9U9ZFYQ0Eh4sfgSzLneLDuJsJRjcezR/l2UyYGXA==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -855,13 +904,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", - "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.3.tgz", + "integrity": "sha512-9PYq2IEzSwXcvQLERcT9LKXRzw8J05GFTbJHA2anJR2WmlRjA1esgXVYhanp+bmjKsDVoZeU2L7Z2pRClLcZTw==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -869,13 +921,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", - "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.3.tgz", + "integrity": "sha512-vMh+mt0cw1BHldd8cqaU6A5s1W4EUKl6+8MRsv86mj954L4xyruKD7qPXMUI2icImadAZARK10h0onaGTLmCVg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -883,9 +938,26 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", - "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.3.tgz", + "integrity": "sha512-PpiKU+sWbqDGc0U0su8oXiyeyqm0UQjmaaPKHktX+DaLe6YKJwW2YVD0f2AXHdVvblZbRqpwRVClWP2F5HCqrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.3.tgz", + "integrity": "sha512-/3enY/AmaIt8IS0fWjX4ztYVPBqkj49cQTy/hCryhBFaEzDfJwwEolq9sxvyWAM2qmsmVuPdeRg5JXSrmeqvJg==", "cpu": [ "x64" ], @@ -893,13 +965,13 @@ "license": "MIT", "optional": true, "os": [ - "linux" + "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", - "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.3.tgz", + "integrity": "sha512-zsEP/QDuet52hhwvi5T1VPg0/Tkms0m3zkVv7CPsznvhdO4pL4oJTMz26EJsiCe18OeLKPgpTu3XVO3C1eq3ew==", "cpu": [ "arm64" ], @@ -911,9 +983,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", - "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.3.tgz", + "integrity": "sha512-4ESknW4Db+kqvazVOfsReYJaHxcIsOqcgwjKV2J1tG5Bn4RVf5ocTQKSGuq2HWBJcOLuyim8IWX8DuH6nKmELA==", "cpu": [ "arm64" ], @@ -925,9 +997,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", - "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.3.tgz", + "integrity": "sha512-3B8VCQyIyv7FQxYYhb3HpR3bIpronR20txjMSffSoz/sm/DDs3/49CFpEueOsvq9qW9sWoI/6pi6mtciD9NXFg==", "cpu": [ "ia32" ], @@ -939,9 +1011,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", - "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.3.tgz", + "integrity": "sha512-YOoDkKh2oQxOxRB5D9AR8sj3qpMShpmyTkjojiBjMGigz3caV7UvCnXWWUQC0TBmrZ5zuHe2pWl8S+cMfvXzRg==", "cpu": [ "x64" ], @@ -953,9 +1025,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", - "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.3.tgz", + "integrity": "sha512-qTv4bG+eKJKifzxNJevMUh8L6CjJrz9/Kn1IbP2F5bo0tBzNIR7HMg4vni6XmDzcKUl00lHHY+J5ZgjbGiujOQ==", "cpu": [ "x64" ], @@ -967,9 +1039,9 @@ ] }, "node_modules/@tailwindcss/forms": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", - "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.11.tgz", + "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==", "dev": true, "license": "MIT", "dependencies": { @@ -980,30 +1052,30 @@ } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "version": "24.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.1.tgz", + "integrity": "sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==", "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/offscreencanvas": { "version": "2019.7.3", - "resolved": "https://registry.npmmirror.com/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", "license": "MIT" }, "node_modules/@types/qrcode": { "version": "1.5.6", - "resolved": "https://registry.npmmirror.com/@types/qrcode/-/qrcode-1.5.6.tgz", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", "license": "MIT", "dependencies": { @@ -1012,123 +1084,123 @@ }, "node_modules/@types/xlsx": { "version": "0.0.35", - "resolved": "https://registry.npmmirror.com/@types/xlsx/-/xlsx-0.0.35.tgz", + "resolved": "https://registry.npmjs.org/@types/xlsx/-/xlsx-0.0.35.tgz", "integrity": "sha512-s0x3DYHZzOkxtjqOk/Nv1ezGzpbN7I8WX+lzlV/nFfTDOv7x4d8ZwGHcnaiB8UCx89omPsftQhS5II3jeWePxQ==", "dev": true, "license": "MIT" }, "node_modules/@vitejs/plugin-vue": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", - "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.9.tgz", + "integrity": "sha512-rD/MORlhaZMlXWW0rEn4FB4wMinWC0z/D7Ye160S5+Rs1mC8ZDAdcDp4SjKfUhG5t8q1ktcPVw4xuTkPRlzrSA==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.29" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, "node_modules/@volar/language-core": { - "version": "2.4.23", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", - "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", "dev": true, "license": "MIT", "dependencies": { - "@volar/source-map": "2.4.23" + "@volar/source-map": "2.4.28" } }, "node_modules/@volar/source-map": { - "version": "2.4.23", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", - "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", "dev": true, "license": "MIT" }, "node_modules/@volar/typescript": { - "version": "2.4.23", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", - "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.23", + "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "node_modules/@vue/compiler-core": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", - "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", + "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.24", - "entities": "^4.5.0", + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.43", + "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", - "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", + "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/compiler-core": "3.5.43", + "@vue/shared": "3.5.43" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", - "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", + "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.24", - "@vue/compiler-dom": "3.5.24", - "@vue/compiler-ssr": "3.5.24", - "@vue/shared": "3.5.24", + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.43", + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-ssr": "3.5.43", + "@vue/shared": "3.5.43", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", - "postcss": "^8.5.6", + "postcss": "^8.5.28", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", - "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", + "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/compiler-dom": "3.5.43", + "@vue/shared": "3.5.43" } }, "node_modules/@vue/devtools-api": { - "version": "7.7.8", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.8.tgz", - "integrity": "sha512-BtFcAmDbtXGwurWUFf8ogIbgZyR+rcVES1TSNEI8Em80fD8Anu+qTRN1Fc3J6vdRHlVM3fzPV1qIo+B4AiqGzw==", + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.10.tgz", + "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==", "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^7.7.8" + "@vue/devtools-kit": "^7.7.10" } }, "node_modules/@vue/devtools-kit": { - "version": "7.7.8", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.8.tgz", - "integrity": "sha512-4Y8op+AoxOJhB9fpcEF6d5vcJXWKgHxC3B0ytUB8zz15KbP9g9WgVzral05xluxi2fOeAy6t140rdQ943GcLRQ==", + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz", + "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==", "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^7.7.8", + "@vue/devtools-shared": "^7.7.10", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", @@ -1138,86 +1210,89 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.7.8", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.8.tgz", - "integrity": "sha512-XHpO3jC5nOgYr40M9p8Z4mmKfTvUxKyRcUnpBAYg11pE78eaRFBKb0kG5yKLroMuJeeNH9LWmKp2zMU5LUc7CA==", + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", "license": "MIT", "dependencies": { "rfdc": "^1.4.1" } }, "node_modules/@vue/language-core": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.3.tgz", - "integrity": "sha512-KpR1F/eGAG9D1RZ0/T6zWJs6dh/pRLfY5WupecyYKJ1fjVmDMgTPw9wXmKv2rBjo4zCJiOSiyB8BDP1OUwpMEA==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.12.tgz", + "integrity": "sha512-k7uIKR0n4CBidk233y7cac9osnDQ5WRidTl6nsrNvpZ3nf8cx9iJxRVsRzVsoPRHbowT/iKgGMTS9RGov3jeIA==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.23", + "@volar/language-core": "2.4.28", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", - "alien-signals": "^3.0.0", + "alien-signals": "^3.2.1", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", - "picomatch": "^4.0.2" + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/language-core/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/@vue/reactivity": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", - "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", + "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.24" + "@vue/shared": "3.5.43" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", - "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", + "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/reactivity": "3.5.43", + "@vue/shared": "3.5.43" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", - "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", + "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.24", - "@vue/runtime-core": "3.5.24", - "@vue/shared": "3.5.24", - "csstype": "^3.1.3" + "@vue/reactivity": "3.5.43", + "@vue/runtime-core": "3.5.43", + "@vue/shared": "3.5.43", + "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", - "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", + "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.24", - "@vue/shared": "3.5.24" - }, - "peerDependencies": { - "vue": "3.5.24" + "@vue/compiler-ssr": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/shared": "3.5.43" } }, "node_modules/@vue/shared": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", - "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", + "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", "license": "MIT" }, "node_modules/@vue/tsconfig": { @@ -1239,43 +1314,44 @@ } } }, - "node_modules/adler-32": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz", - "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", - "license": "Apache-2.0", + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, "engines": { - "node": ">=0.8" + "node": ">= 6.0.0" } }, "node_modules/alien-signals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.0.tgz", - "integrity": "sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", "dev": true, "license": "MIT" }, "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -1302,19 +1378,6 @@ "node": ">= 8" } }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -1329,9 +1392,9 @@ "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz", - "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.6.1.tgz", + "integrity": "sha512-cL1Qz6ADZhcEbny/8HPfe99J6HhNoYtpX2LFLIbhgGE7Q1hlQVkYFdetDN7Id3KiQxhDrHwzlHr/YQCnZ8+xSA==", "dev": true, "funding": [ { @@ -1349,10 +1412,9 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.27.0", - "caniuse-lite": "^1.0.30001754", + "browserslist": "^4.28.9", + "caniuse-lite": "^1.0.30001810", "fraction.js": "^5.3.4", - "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, @@ -1367,23 +1429,17 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -1395,7 +1451,7 @@ }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { @@ -1414,13 +1470,16 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.26", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.26.tgz", - "integrity": "sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q==", + "version": "2.11.28", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.28.tgz", + "integrity": "sha512-br0LOmjJqr6Rt4AEkadbcNf67Zpo7EPwKsWbjnRrJIa1QYFHQBqM4C7F2WTbV8ahTEcg1Eo+Y0aWjjORInYFyA==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/binary-extensions": { @@ -1437,24 +1496,14 @@ } }, "node_modules/birpc": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.8.0.tgz", - "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -1469,9 +1518,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", - "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.3.tgz", + "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", "dev": true, "funding": [ { @@ -1488,13 +1537,12 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.8.25", - "caniuse-lite": "^1.0.30001754", - "electron-to-chromium": "^1.5.249", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.1.4" + "baseline-browser-mapping": "^2.11.26", + "caniuse-lite": "^1.0.30001813", + "electron-to-chromium": "^1.5.439", + "node-releases": "^2.0.57", + "update-browserslist-db": "^1.3.3" }, "bin": { "browserslist": "cli.js" @@ -1518,7 +1566,7 @@ }, "node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "license": "MIT", "engines": { @@ -1536,9 +1584,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001754", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", - "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", + "version": "1.0.30001815", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001815.tgz", + "integrity": "sha512-uDybUEyfgbyJJ93J1vikTylBX31AkdVYeVPeffMQpf8XN7hSfr0oZOn4VFOIMNvtU5+rHvC5Udg2TgtJKHm23Q==", "dev": true, "funding": [ { @@ -1556,19 +1604,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/cfb": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz", - "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", - "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "crc-32": "~1.2.0" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1609,7 +1644,7 @@ }, "node_modules/cliui": { "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "license": "ISC", "dependencies": { @@ -1618,85 +1653,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/codepage": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz", - "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1738,13 +1694,10 @@ } }, "node_modules/copy-anything": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", - "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.1.5.tgz", + "integrity": "sha512-vePst2VLTSqPglW7Q98VwFpGiGpOHTyXIo81MO5VyLecA5zMbZw/P/V1vmwD1jksa2iJpn/YkDrJ3XhTfzUyhQ==", "license": "MIT", - "dependencies": { - "is-what": "^5.2.0" - }, "engines": { "node": ">=18" }, @@ -1752,33 +1705,6 @@ "url": "https://github.com/sponsors/mesqueeb" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/css-line-break": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", @@ -1802,14 +1728,31 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "license": "MIT", "engines": { @@ -1834,7 +1777,7 @@ }, "node_modules/dijkstrajs": { "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "license": "MIT" }, @@ -1859,41 +1802,33 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/echarts": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", - "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.1.0.tgz", + "integrity": "sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==", "license": "Apache-2.0", "dependencies": { "tslib": "2.3.0", - "zrender": "6.0.0" + "zrender": "6.1.0" } }, "node_modules/electron-to-chromium": { - "version": "1.5.250", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz", - "integrity": "sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==", + "version": "1.5.452", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.452.tgz", + "integrity": "sha512-cVvk1YBSj6571VmV6hdJTqj94Y79xSu8cj6Y9t6ae4Y4431z8/7EJFvu3rVp90xhfuv9BpPKUmaLfTo4+FOGBg==", "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -1921,9 +1856,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -1948,9 +1883,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1961,32 +1896,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escalade": { @@ -2036,33 +1971,15 @@ } }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2078,7 +1995,7 @@ }, "node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "license": "MIT", "dependencies": { @@ -2090,9 +2007,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz", + "integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==", "funding": [ { "type": "individual", @@ -2109,48 +2026,22 @@ } } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "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" } }, - "node_modules/frac": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz", - "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/fraction.js": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", @@ -2191,7 +2082,7 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", "engines": { @@ -2235,27 +2126,6 @@ "node": ">= 0.4" } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2309,9 +2179,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2339,6 +2209,19 @@ "node": ">=8.0.0" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2353,13 +2236,13 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.4" }, "engines": { "node": ">= 0.4" @@ -2410,56 +2293,31 @@ "node": ">=0.12.0" } }, - "node_modules/is-what": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", - "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "bin": { + "jiti": "bin/jiti.js" } }, "node_modules/js-sha256": { "version": "0.10.1", - "resolved": "https://registry.npmmirror.com/js-sha256/-/js-sha256-0.10.1.tgz", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz", "integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==", "license": "MIT" }, "node_modules/jsbarcode": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.12.1.tgz", - "integrity": "sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==", + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.12.3.tgz", + "integrity": "sha512-CuHU9hC6dPsHF5oVFMo8NW76uQVjH4L22CsP4hW+dNnGywJHC/B0ThA1CTDVLnxKLrrpYdicBLnd2xsgTfRnvg==", "license": "MIT" }, "node_modules/jwt-decode": { "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/jwt-decode/-/jwt-decode-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", "license": "MIT", "engines": { @@ -2468,7 +2326,7 @@ }, "node_modules/keycloak-js": { "version": "23.0.7", - "resolved": "https://registry.npmmirror.com/keycloak-js/-/keycloak-js-23.0.7.tgz", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-23.0.7.tgz", "integrity": "sha512-OmszsKzBhhm5yP4W1q/tMd+nNnKpOAdeVYcoGhphlv8Fj1bNk4wRTYzp7pn5BkvueLz7fhvKHz7uOc33524YrA==", "license": "Apache-2.0", "dependencies": { @@ -2499,7 +2357,7 @@ }, "node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "license": "MIT", "dependencies": { @@ -2509,13 +2367,6 @@ "node": ">=8" } }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -2558,19 +2409,6 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -2602,38 +2440,18 @@ "mini-svg-data-uri": "cli.js" } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/muggle-string": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", @@ -2654,9 +2472,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "funding": [ { "type": "github", @@ -2672,11 +2490,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.58.tgz", + "integrity": "sha512-BdiKAgbfRlvfMq/XzO/AF9NPmaA8a+YpLAyAFTVFBj3gARrAIfWvNq7Q7NE4se3KgFu0KkrvQMxTS0XVnsGzHg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -2688,16 +2509,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2720,7 +2531,7 @@ }, "node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "license": "MIT", "dependencies": { @@ -2735,7 +2546,7 @@ }, "node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "license": "MIT", "dependencies": { @@ -2747,20 +2558,13 @@ }, "node_modules/p-try": { "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -2770,23 +2574,13 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2794,23 +2588,6 @@ "dev": true, "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -2824,29 +2601,18 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pinia": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz", @@ -2880,7 +2646,7 @@ }, "node_modules/pngjs": { "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", "license": "MIT", "engines": { @@ -2888,9 +2654,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", "funding": [ { "type": "opencollective", @@ -2906,11 +2672,10 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.19", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3030,9 +2795,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3051,14 +2816,17 @@ "license": "MIT" }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/qr-scanner": { "version": "1.4.2", - "resolved": "https://registry.npmmirror.com/qr-scanner/-/qr-scanner-1.4.2.tgz", + "resolved": "https://registry.npmjs.org/qr-scanner/-/qr-scanner-1.4.2.tgz", "integrity": "sha512-kV1yQUe2FENvn59tMZW6mOVfpq9mGxGf8l6+EGaXUOd4RBOLg7tRC83OrirM5AtDvZRpdjdlXURsHreAOSPOUw==", "license": "MIT", "dependencies": { @@ -3067,7 +2835,7 @@ }, "node_modules/qrcode": { "version": "1.5.4", - "resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", "license": "MIT", "dependencies": { @@ -3104,14 +2872,11 @@ "license": "MIT" }, "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.2.tgz", + "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } + "license": "MIT" }, "node_modules/readdirp": { "version": "3.6.0", @@ -3126,22 +2891,9 @@ "node": ">=8.10.0" } }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", "engines": { @@ -3150,18 +2902,19 @@ }, "node_modules/require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "license": "ISC" }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.13", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.13.tgz", + "integrity": "sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.1", + "es-errors": "^1.3.0", + "is-core-module": "^2.17.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -3193,13 +2946,13 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", - "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", + "version": "4.64.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.3.tgz", + "integrity": "sha512-iIMWbptLYsZdGTJMH8ngApXdsCatE6bCMv2eqGIfGE1++kwh7TCWBdWKK0FKC0yx++196vGK5VdagzRXSLMa8A==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -3209,28 +2962,32 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.2", - "@rollup/rollup-android-arm64": "4.53.2", - "@rollup/rollup-darwin-arm64": "4.53.2", - "@rollup/rollup-darwin-x64": "4.53.2", - "@rollup/rollup-freebsd-arm64": "4.53.2", - "@rollup/rollup-freebsd-x64": "4.53.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", - "@rollup/rollup-linux-arm-musleabihf": "4.53.2", - "@rollup/rollup-linux-arm64-gnu": "4.53.2", - "@rollup/rollup-linux-arm64-musl": "4.53.2", - "@rollup/rollup-linux-loong64-gnu": "4.53.2", - "@rollup/rollup-linux-ppc64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-musl": "4.53.2", - "@rollup/rollup-linux-s390x-gnu": "4.53.2", - "@rollup/rollup-linux-x64-gnu": "4.53.2", - "@rollup/rollup-linux-x64-musl": "4.53.2", - "@rollup/rollup-openharmony-arm64": "4.53.2", - "@rollup/rollup-win32-arm64-msvc": "4.53.2", - "@rollup/rollup-win32-ia32-msvc": "4.53.2", - "@rollup/rollup-win32-x64-gnu": "4.53.2", - "@rollup/rollup-win32-x64-msvc": "4.53.2", + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.64.3", + "@rollup/rollup-android-arm64": "4.64.3", + "@rollup/rollup-darwin-arm64": "4.64.3", + "@rollup/rollup-darwin-x64": "4.64.3", + "@rollup/rollup-freebsd-arm64": "4.64.3", + "@rollup/rollup-freebsd-x64": "4.64.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.64.3", + "@rollup/rollup-linux-arm-musleabihf": "4.64.3", + "@rollup/rollup-linux-arm64-gnu": "4.64.3", + "@rollup/rollup-linux-arm64-musl": "4.64.3", + "@rollup/rollup-linux-loong64-gnu": "4.64.3", + "@rollup/rollup-linux-loong64-musl": "4.64.3", + "@rollup/rollup-linux-ppc64-gnu": "4.64.3", + "@rollup/rollup-linux-ppc64-musl": "4.64.3", + "@rollup/rollup-linux-riscv64-gnu": "4.64.3", + "@rollup/rollup-linux-riscv64-musl": "4.64.3", + "@rollup/rollup-linux-s390x-gnu": "4.64.3", + "@rollup/rollup-linux-x64-gnu": "4.64.3", + "@rollup/rollup-linux-x64-musl": "4.64.3", + "@rollup/rollup-openbsd-x64": "4.64.3", + "@rollup/rollup-openharmony-arm64": "4.64.3", + "@rollup/rollup-win32-arm64-msvc": "4.64.3", + "@rollup/rollup-win32-ia32-msvc": "4.64.3", + "@rollup/rollup-win32-x64-gnu": "4.64.3", + "@rollup/rollup-win32-x64-msvc": "4.64.3", "fsevents": "~2.3.2" } }, @@ -3260,50 +3017,14 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "license": "ISC" }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -3318,42 +3039,10 @@ "node": ">=0.10.0" } }, - "node_modules/ssf": { - "version": "0.11.2", - "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz", - "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", - "license": "Apache-2.0", - "dependencies": { - "frac": "~1.1.2" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -3364,58 +3053,10 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -3424,29 +3065,19 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { @@ -3458,9 +3089,9 @@ } }, "node_modules/superjson": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.5.tgz", - "integrity": "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", "license": "MIT", "dependencies": { "copy-anything": "^4" @@ -3483,12 +3114,11 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", - "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -3521,16 +3151,6 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss/node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -3564,14 +3184,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -3580,6 +3200,37 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3612,7 +3263,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3622,15 +3272,15 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", "dev": true, "funding": [ { @@ -3675,14 +3325,13 @@ } }, "node_modules/vite": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", - "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", + "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "esbuild": "^0.25.0", + "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -3750,25 +3399,55 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", "dev": true, "license": "MIT" }, "node_modules/vue": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", - "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", + "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", "license": "MIT", - "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.24", - "@vue/compiler-sfc": "3.5.24", - "@vue/runtime-dom": "3.5.24", - "@vue/server-renderer": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-sfc": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/server-renderer": "3.5.43", + "@vue/shared": "3.5.43" }, "peerDependencies": { "typescript": "*" @@ -3780,9 +3459,9 @@ } }, "node_modules/vue-router": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", - "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", "license": "MIT", "dependencies": { "@vue/devtools-api": "^6.6.4" @@ -3801,14 +3480,14 @@ "license": "MIT" }, "node_modules/vue-tsc": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.1.3.tgz", - "integrity": "sha512-StMNfZHwPIXQgY3KxPKM0Jsoc8b46mDV3Fn2UlHCBIwRJApjqrSwqeMYgWf0zpN+g857y74pv7GWuBm+UqQe1w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.12.tgz", + "integrity": "sha512-kbpaz18O9jPVkXVHD1BEjpUXM6WhXSu+62L1sGGr6Wbzd8GbvkBQWjoj3KZ/C0aq2MZllce7UdtWzwCIYsBMGA==", "dev": true, "license": "MIT", "dependencies": { - "@volar/typescript": "2.4.23", - "@vue/language-core": "3.1.3" + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.12" }, "bin": { "vue-tsc": "bin/vue-tsc.js" @@ -3817,158 +3496,31 @@ "typescript": ">=5.0.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/which-module": { "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "license": "ISC" }, - "node_modules/wmf": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz", - "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/word": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz", - "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } }, "node_modules/xlsx": { - "version": "0.18.5", - "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz", - "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "version": "0.20.3", + "resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", + "integrity": "sha512-oLDq3jw7AcLqKWH2AhCpVTZl8mf6X2YReP+Neh0SJUzV/BdZYjth94tG5toiMB1PPrYtxOCfaoUCkvtuH+3AJA==", "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "cfb": "~1.2.1", - "codepage": "~1.15.0", - "crc-32": "~1.2.1", - "ssf": "~0.11.2", - "wmf": "~1.0.1", - "word": "~0.3.0" - }, "bin": { "xlsx": "bin/xlsx.njs" }, @@ -3978,13 +3530,13 @@ }, "node_modules/y18n": { "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "license": "ISC" }, "node_modules/yargs": { "version": "15.4.1", - "resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "license": "MIT", "dependencies": { @@ -4006,7 +3558,7 @@ }, "node_modules/yargs-parser": { "version": "18.1.3", - "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "license": "ISC", "dependencies": { @@ -4017,51 +3569,10 @@ "node": ">=6" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/zrender": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", - "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.1.0.tgz", + "integrity": "sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==", "license": "BSD-3-Clause", "dependencies": { "tslib": "2.3.0" diff --git a/frontend/package.json b/frontend/package.json index ea56edf..ef3d5a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,7 @@ "qrcode": "^1.5.4", "vue": "^3.5.24", "vue-router": "^4.6.3", - "xlsx": "^0.18.5" + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" }, "devDependencies": { "@tailwindcss/forms": "^0.5.10", diff --git a/frontend/scripts/test-audit-functional-fixes.ts b/frontend/scripts/test-audit-functional-fixes.ts new file mode 100644 index 0000000..39f2665 --- /dev/null +++ b/frontend/scripts/test-audit-functional-fixes.ts @@ -0,0 +1,123 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { toApiDateTime } from '../src/utils/dateTime.ts' +import { clearSessionState } from '../src/auth/sessionState.ts' +import { createRequestScope } from '../src/utils/requestScope.ts' +import { indexEquipmentSelection, resolveSelectedEquipmentCodes } from '../src/utils/equipmentSelection.ts' +import { parseEquipmentImportRows } from '../src/utils/equipmentImport.ts' +import { getReturnCompletionText, submitReturnWithConfirmation } from '../src/utils/returnConfirmation.ts' + +test('local browser datetime becomes an unambiguous instant and survives server timezone changes', () => { + const previousTimezone = process.env.TZ + try { + process.env.TZ = 'Asia/Shanghai' + const payload = toApiDateTime('2026-10-09T12:00') + assert.equal(payload, '2026-10-09T04:00:00.000Z') + process.env.TZ = 'UTC' + assert.equal(new Date(payload!).toISOString(), payload) + assert.equal(toApiDateTime(''), undefined) + assert.throws(() => toApiDateTime('not-a-date'), /无效/) + } finally { + if (previousTimezone === undefined) delete process.env.TZ + else process.env.TZ = previousTimezone + } +}) + +test('failed user refresh clears both live authentication and the persisted token', () => { + const removed: string[] = [] + const state = { user: { id: 4 }, token: 'session' as string | null, isAuthenticated: true } + clearSessionState(state, { removeItem: (key) => { removed.push(key) } }) + assert.deepEqual(state, { user: null, token: null, isAuthenticated: false }) + assert.deepEqual(removed, ['token']) +}) + +test('late requests cannot replace a newer detail or mutate a disposed view', async () => { + const scope = createRequestScope() + let displayed = '' + let finishFirst: () => void = () => {} + const first = scope.begin() + const delayed = new Promise((resolve) => { finishFirst = resolve }).then(() => { + if (scope.isCurrent(first)) displayed = 'asset A' + }) + const second = scope.begin() + if (scope.isCurrent(second)) displayed = 'asset B' + finishFirst() + await delayed + assert.equal(displayed, 'asset B') + scope.dispose() + assert.equal(scope.isCurrent(second), false) +}) + +test('all selected asset codes survive page changes, including nested accessories', () => { + const pageOne = indexEquipmentSelection([{ id: 1, equipment_code: 'A', equipment_name: 'A', children: [ + { id: 2, equipment_code: 'A-CHILD', equipment_name: 'child', parent_id: 1 } + ] }]) + const pageTwo = indexEquipmentSelection([{ id: 21, equipment_code: 'B', equipment_name: 'B' }]) + const selected = new Map([...pageOne, ...pageTwo]) + assert.deepEqual(resolveSelectedEquipmentCodes([1, 2, 21], selected), ['A', 'A-CHILD', 'B']) + assert.throws(() => resolveSelectedEquipmentCodes([1, 999, 21], selected), /#999/) +}) + +test('Excel data validation preserves invalid rows and original row numbers through empty rows', () => { + const rows = parseEquipmentImportRows([ + ['name', 'type'], + ['zero', 'computer', '', '', '', 100, 0], + [], + ['missing type', '', '', '', '', 50, 2], + ['bad quantity', 'computer', '', '', '', 20, 'oops'], + ['negative', 'computer', '', '', '', 10, -2], + ['fraction', 'computer', '', '', '', 10, 1.5] + ]) + assert.equal(rows.length, 5) + assert.deepEqual(rows.map((row) => row.source_row), [2, 4, 5, 6, 7]) + assert.deepEqual(rows.map((row) => row.total_quantity), [0, 2, 0, -2, 1.5]) + assert.equal(rows[1]?.equipment_type, '') + const shiftedHeader = parseEquipmentImportRows([['name', 'type'], ['asset', 'computer', '', '', '', 10, 1]], 2) + assert.equal(shiftedHeader[0]?.source_row, 4) +}) + +test('invalid nonempty Excel prices survive the real JSON transport for server rejection', () => { + const payload = JSON.parse(JSON.stringify(parseEquipmentImportRows([ + ['name', 'type'], + ['invalid price', 'computer', '', '', '', ' bad-price ', 1], + ['infinite price', 'computer', '', '', '', 'Infinity', 1], + ['zero price', 'computer', '', '', '', 0, 1], + ['valid price', 'computer', '', '', '', '12.50', 1], + ['empty price', 'computer', '', '', '', '', 1] + ]))) + assert.deepEqual(payload.map((row: { unit_price?: number | string }) => row.unit_price), ['bad-price', 'Infinity', 0, 12.5, undefined]) + assert.equal(payload[0].source_row, 2) + assert.equal(Number.isFinite(Number(payload[0].unit_price)), false) + assert.equal(Number.isFinite(Number(payload[1].unit_price)), false) +}) + +test('return confirmation acknowledges each current tree and does not report success for another need_confirm', async () => { + const tokens: Array = [] + let prompts = 0 + const result = await submitReturnWithConfirmation({ equipment_code: 'A' }, async (payload) => { + tokens.push(payload.confirmation_token) + if (tokens.length === 1) return { data: { need_confirm: true, confirmation_token: 'tree-v1' } } + if (tokens.length === 2) return { data: { need_confirm: true, confirmation_token: 'tree-v2' } } + return { data: { need_confirm: false } } + }, () => { prompts += 1; return true }) + assert.deepEqual(tokens, [undefined, 'tree-v1', 'tree-v2']) + assert.equal(prompts, 2) + assert.equal(result?.need_confirm, false) +}) + +test('declining differences leaves the return unsubmitted and missing confirmation tokens fail closed', async () => { + let submissions = 0 + const cancelled = await submitReturnWithConfirmation({}, async () => { + submissions += 1 + return { data: { need_confirm: true, confirmation_token: 'tree-v1' } } + }, () => false) + assert.equal(cancelled, null) + assert.equal(submissions, 1) + await assert.rejects(() => submitReturnWithConfirmation({}, async () => ({ data: { need_confirm: true } }), () => true), /确认信息不完整/) +}) + +test('lost and damaged completion messages cannot claim available stock was restored', () => { + assert.equal(getReturnCompletionText('丢失'), '已登记丢失(未入仓)') + assert.equal(getReturnCompletionText('损坏'), '已归还,状态为已损坏') + assert.equal(getReturnCompletionText('正常'), '已完成归还') +}) diff --git a/frontend/scripts/test-audit-routing.mjs b/frontend/scripts/test-audit-routing.mjs new file mode 100644 index 0000000..7b5f2e0 --- /dev/null +++ b/frontend/scripts/test-audit-routing.mjs @@ -0,0 +1,190 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import fs from 'node:fs' +import ts from 'typescript' +import { createRenderer, h, onMounted, ref, nextTick } from 'vue' +import { createRouter, createMemoryHistory, RouterView, useRoute } from 'vue-router' +import { clearSessionState } from '../src/auth/sessionState.ts' +import { createRequestScope } from '../src/utils/requestScope.ts' +import { parseEquipmentImportRows } from '../src/utils/equipmentImport.ts' +import * as XLSX from 'xlsx' + +test('the real route guard reaches login once after an expired session, without dashboard bounce', async () => { + const source = fs.readFileSync(new URL('../src/router/index.ts', import.meta.url), 'utf8') + const guardSource = source.slice(source.indexOf('router.beforeEach(async ') + 18, source.lastIndexOf('\n})') + 2) + const executable = ts.transpileModule(`const guard = ${guardSource}`.replace('import.meta.env.VITE_AUTH_MODE', "'local'"), { compilerOptions: { target: ts.ScriptTarget.ES2020 } }).outputText + const state = { + user: { id: 4 }, token: 'session', isAuthenticated: true, + refreshUser: async () => { throw new Error('401 session expired') }, + clearSession: () => clearSessionState(state, { removeItem: () => {} }) + } + const guard = new Function('useAuthStore', 'resolveAuthMode', 'keycloak', `${executable}; return guard;`)(() => state, () => 'local', {}) + let nextTarget + await guard({ path: '/equipment', meta: { requiresAuth: true } }, {}, (target) => { nextTarget = target }) + assert.equal(nextTarget, '/login') + nextTarget = 'not-called' + await guard({ path: '/login', meta: { requiresAuth: false } }, {}, (target) => { nextTarget = target }) + assert.equal(nextTarget, undefined) + assert.equal(state.isAuthenticated, false) +}) + +test('changing detail IDs remounts the detail view with the key used by MainLayout', async () => { + const layout = fs.readFileSync(new URL('../src/layouts/MainLayout.vue', import.meta.url), 'utf8') + assert.match(layout, / ({ type, children: [] }), createText: (text) => ({ text }), createComment: (text) => ({ text }), + setText: (node, text) => { node.text = text }, setElementText: (node, text) => { node.text = text }, + parentNode: (node) => node.parent || null, nextSibling: () => null, patchProp: () => {}, + insert: (node, parent) => { parent.children.push(node); node.parent = parent }, + remove: (node) => { if (node.parent) node.parent.children = node.parent.children.filter((item) => item !== node) } + }) + let currentDetail + let mountedCount = 0 + const Detail = { setup() { + const route = useRoute() + const displayedId = ref(null) + onMounted(() => { mountedCount += 1; displayedId.value = Number(route.params.id) }) + currentDetail = { displayedId, targetId: () => Number(route.params.id) } + return () => h('div', String(displayedId.value)) + } } + const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/equipment/edit/:id', component: Detail }] }) + await router.push('/equipment/edit/1') + const app = renderer.createApp({ setup() { const route = useRoute(); return () => h(RouterView, { key: route.path }) } }) + app.use(router) + app.mount({ children: [] }) + await nextTick() + await router.push('/equipment/edit/2') + await nextTick() + assert.equal(currentDetail.displayedId.value, 2) + assert.equal(currentDetail.targetId(), 2) + assert.equal(mountedCount, 2) + app.unmount() +}) + +test('print resolution requires a checked outcome and submits the originally inspected claim token', async () => { + const source = fs.readFileSync(new URL('../src/views/settings/components/PrintJobManagement.vue', import.meta.url), 'utf8') + const start = source.indexOf('const submitResolution = async () =>') + const end = source.indexOf('\nonMounted(', start) + const compiled = ts.transpileModule(source.slice(start, end), { compilerOptions: { target: ts.ScriptTarget.ES2020 } }).outputText + const canResolve = { value: true } + const resolutionJob = { value: { id: 83, claim_token: 'inspected-claim' } } + const resolutionOutcome = { value: 'not_printed' } + const resolutionVerified = { value: false } + const busyJobId = { value: null } + const submissions = [] + let refreshes = 0 + const submit = new Function('canResolve', 'resolutionJob', 'resolutionOutcome', 'resolutionVerified', 'busyJobId', 'resolvePrintJob', 'fetchJobs', 'alert', `${compiled}; return submitResolution;`)( + canResolve, resolutionJob, resolutionOutcome, resolutionVerified, busyJobId, + async (id, payload) => { submissions.push({ id, payload }) }, async () => { refreshes += 1 }, () => {} + ) + await submit() + assert.equal(submissions.length, 0) + resolutionVerified.value = true + canResolve.value = false + await submit() + assert.equal(submissions.length, 0) + canResolve.value = true + await submit() + assert.deepEqual(submissions, [{ id: 83, payload: { confirmed: true, outcome: 'not_printed', expected_claim_token: 'inspected-claim' } }]) + assert.equal(resolutionJob.value, null) + assert.equal(busyJobId.value, null) + assert.equal(refreshes, 1) +}) + +const extractListFunction = (startMarker, endMarker, bindings) => { + const source = fs.readFileSync(new URL('../src/views/equipment/EquipmentList.vue', import.meta.url), 'utf8') + const start = source.indexOf(`const ${startMarker}`) + const end = source.indexOf(`const ${endMarker}`, start) + const compiled = ts.transpileModule(source.slice(start, end), { compilerOptions: { target: ts.ScriptTarget.ES2020 } }).outputText + return new Function(...Object.keys(bindings), `${compiled}; return ${startMarker};`)(...Object.values(bindings)) +} + +for (const delayedStage of ['detail', 'records', 'fallback']) { + test(`opening asset B supersedes a slow asset A return lookup at ${delayedStage}`, async () => { + const returnDialogScope = createRequestScope() + const selectedIds = { value: [1] } + const returnAssetInfo = { value: null } + const returnMode = { value: 'batch' } + const returnAnomalyInfo = { value: null } + const returnSelectedRecordId = { value: null } + const returnRecordKeyword = { value: '' } + const showBatchReturnModal = { value: false } + let finishA + let notifyReached + const reached = new Promise((resolve) => { notifyReached = resolve }) + const delayed = () => { notifyReached(); return new Promise((resolve) => { finishA = resolve }) } + const detail = (id) => ({ data: { id, equipment_code: id === 1 ? 'A' : 'B', equipment_name: id === 1 ? 'Asset A' : 'Asset B' } }) + const records = (code) => ({ data: { can_return: true, equipment_code: code, equipment_name: code, borrow_records: [{ id: code === 'A' ? 101 : 202 }] } }) + const bindings = { + returnDialogScope, selectedIds, returnAssetInfo, returnMode, returnAnomalyInfo, + returnSelectedRecordId, returnRecordKeyword, showBatchReturnModal, + returnSubmitting: { value: false }, borrowSubmitting: { value: false }, + clearSingleActionContext: () => {}, + resetReturnForm: () => { returnDialogScope.begin(); returnAssetInfo.value = null; returnSelectedRecordId.value = null }, + canQuickReturnInline: () => false, + buildManualReturnRecordDisplay: (record) => String(record.id), + alert: (message) => { throw new Error(`unexpected alert: ${message}`) }, + getEquipmentDetail: async (id) => id === 1 && delayedStage === 'detail' ? delayed() : detail(id), + getBorrowRecordByCode: async (code) => { + if (code === 'A' && delayedStage === 'records') return delayed() + if (code === 'A' && delayedStage === 'fallback') return { data: { ...records('A').data, can_return: false } } + return records(code) + }, + searchReturnAssets: async () => delayed() + } + const open = extractListFunction('openBatchReturnModalInternal', 'openBatchReturnModal =', bindings) + const pendingA = open() + await reached + selectedIds.value = [2] + await open() + assert.equal(returnAssetInfo.value.equipment_code, 'B') + assert.equal(returnSelectedRecordId.value, 202) + finishA(delayedStage === 'detail' ? detail(1) : delayedStage === 'records' ? records('A') : { data: [{ equipment_id: 1, can_return: false }] }) + await pendingA + assert.equal(returnAssetInfo.value.equipment_code, 'B') + assert.equal(returnSelectedRecordId.value, 202) + assert.equal(returnMode.value, 'record-select') + assert.equal(returnAnomalyInfo.value, null) + }) +} + +test('successful single action preserves prior cross-page selections and removes only its successful asset', () => { + const selectedIds = { value: [30] } + const singleActionContext = { value: { previousSelectedIds: [1, 21, 30] } } + const selectionEquipment = { value: new Map([[1, { equipment_code: 'A' }], [21, { equipment_code: 'B' }], [30, { equipment_code: 'Z' }]]) } + const finish = extractListFunction('finishSuccessfulAssetAction', 'restoreSelectionAfterSingleActionCancel', { + selectedIds, singleActionContext, selectionEquipment, + clearSingleActionContext: () => { singleActionContext.value = null } + }) + finish(new Set(['Z'])) + assert.deepEqual(selectedIds.value, [1, 21]) + assert.equal(singleActionContext.value, null) +}) + +test('a workbook starting at A3 reports its data row as actual Excel row 4', () => { + const sheet = { + A3: { t: 's', v: 'name' }, B3: { t: 's', v: 'type' }, + A4: { t: 's', v: 'asset' }, B4: { t: 's', v: 'computer' }, G4: { t: 'n', v: 1 }, + '!ref': 'A3:G4' + } + const workbook = XLSX.utils.book_new() + XLSX.utils.book_append_sheet(workbook, sheet, 'assets') + const reread = XLSX.read(XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }), { type: 'buffer' }) + const actual = reread.Sheets.assets + const rows = XLSX.utils.sheet_to_json(actual, { header: 1, blankrows: true }) + assert.equal(parseEquipmentImportRows(rows, XLSX.utils.decode_range(actual['!ref']).s.r)[0].source_row, 4) +}) + +test('sending more than 100 remote labels refuses before generating or creating any real task', async () => { + const source = fs.readFileSync(new URL('../src/components/BarcodePrint.vue', import.meta.url), 'utf8') + const start = source.indexOf('const handleRemotePrint = async () =>') + const end = source.indexOf('const handleCancelRemoteJob', start) + const compiled = ts.transpileModule(source.slice(start, end), { compilerOptions: { target: ts.ScriptTarget.ES2020 } }).outputText + const notices = [] + const remotePrint = new Function('props', 'equipmentList', 'alert', `${compiled}; return handleRemotePrint;`)( + { equipmentIds: Array.from({ length: 101 }, (_, index) => index + 1) }, { value: [] }, (message) => { notices.push(message) } + ) + await remotePrint() + assert.equal(notices.length, 1) + assert.match(notices[0], /100/) +}) diff --git a/frontend/src/api/batch.ts b/frontend/src/api/batch.ts index 8842f2e..21c0eda 100644 --- a/frontend/src/api/batch.ts +++ b/frontend/src/api/batch.ts @@ -1,4 +1,5 @@ import request from './index' +import { toApiDateTime } from '@/utils/dateTime' import type { ApiResponse } from '@/types' export interface BatchResult { @@ -20,7 +21,7 @@ export const batchBorrow = (data: { is_long_term?: boolean notes?: string }) => { - return request.post>('/batch/borrow', data).then((response) => response.data) + return request.post>('/batch/borrow', { ...data, expected_return_time: data.is_long_term ? undefined : toApiDateTime(data.expected_return_time) }).then((response) => response.data) } // 批量归还 diff --git a/frontend/src/api/batchImport.ts b/frontend/src/api/batchImport.ts index 6d0944d..48d86af 100644 --- a/frontend/src/api/batchImport.ts +++ b/frontend/src/api/batchImport.ts @@ -13,12 +13,13 @@ interface PersonImportData { } interface EquipmentImportData { + source_row?: number equipment_name: string equipment_type: string equipment_model?: string department?: string storage_location?: string - unit_price?: number + unit_price?: number | string total_quantity: number supplier?: string purchase_date?: string diff --git a/frontend/src/api/borrow.ts b/frontend/src/api/borrow.ts index 3af2493..d410fde 100644 --- a/frontend/src/api/borrow.ts +++ b/frontend/src/api/borrow.ts @@ -1,4 +1,5 @@ import api from './index' +import { toApiDateTime } from '@/utils/dateTime' import type { BorrowRecord, PaginatedResponse, @@ -46,7 +47,7 @@ export const borrowEquipment = async (data: { notes?: string is_long_term?: boolean }) => { - const response = await api.post>('/borrow', data) + const response = await api.post>('/borrow', { ...data, expected_return_time: data.is_long_term ? undefined : toApiDateTime(data.expected_return_time) }) return response.data } @@ -58,7 +59,7 @@ export const scanBorrow = async (data: { notes?: string is_long_term?: boolean }) => { - const response = await api.post>('/borrow/scan', data) + const response = await api.post>('/borrow/scan', { ...data, expected_return_time: data.is_long_term ? undefined : toApiDateTime(data.expected_return_time) }) return response.data } @@ -78,7 +79,7 @@ export const scanBorrowBatch = async (data: { status: string message: string }> - }>>('/borrow/scan-batch', data) + }>>('/borrow/scan-batch', { ...data, expected_return_time: data.is_long_term ? undefined : toApiDateTime(data.expected_return_time) }) return response.data } @@ -91,6 +92,7 @@ export const returnEquipment = async (data: { return_condition?: string notes?: string confirm_children_diff?: boolean + confirmation_token?: string }) => { const response = await api.post>('/return', data) return response.data @@ -104,6 +106,7 @@ export const scanReturn = async (data: { person_id?: number borrow_record_id?: number confirm_children_diff?: boolean + confirmation_token?: string }) => { const response = await api.post< ApiResponse<{ @@ -114,6 +117,8 @@ export const scanReturn = async (data: { status: string person_id?: number need_confirm?: boolean + message?: string + confirmation_token?: string children_diff?: { removed: Array> added: Array> diff --git a/frontend/src/api/print.ts b/frontend/src/api/print.ts index 26a9a88..efc6f53 100644 --- a/frontend/src/api/print.ts +++ b/frontend/src/api/print.ts @@ -155,6 +155,7 @@ const normalizePrintTerminal = (value: unknown): PrintTerminal => { name: toNullableString(record.name) || toNullableString(record.machine_name) || `终端 ${toNumber(record.id)}`, platform: toNullableString(record.platform), machine_name: toNullableString(record.machine_name), + protocol_version: record.protocol_version == null ? null : toNumber(record.protocol_version), status: toNullableString(record.status) || 'offline', is_default: toBoolean(record.is_default), last_seen_at: toNullableString(record.last_seen_at), @@ -221,6 +222,8 @@ const normalizePrintJob = (value: unknown): PrintJob => { error_message: toNullableString(record.error_message), attempt_count: toNumber(record.attempt_count, 0), claimed_at: toNullableString(record.claimed_at), + claim_token: toNullableString(record.claim_token), + lease_expires_at: toNullableString(record.lease_expires_at), started_at: toNullableString(record.started_at), completed_at: toNullableString(record.completed_at), created_at: toNullableString(record.created_at), @@ -374,6 +377,15 @@ export const cancelQueuedPrintJob = async (id: number) => { return normalizePrintJob(response.data.data) } +export const resolvePrintJob = async (id: number, payload: { + confirmed: true + outcome: 'printed' | 'not_printed' | 'cancel' + expected_claim_token: string | null +}) => { + const response = await api.post>(`/print-jobs/${id}/resolve`, payload) + return normalizePrintJob(response.data.data) +} + export const setDefaultPrintTerminal = async (id: number) => { const response = await api.patch>(`/print-terminals/${id}/default`) return normalizePrintTerminal(response.data.data) diff --git a/frontend/src/auth/sessionState.ts b/frontend/src/auth/sessionState.ts new file mode 100644 index 0000000..9095f09 --- /dev/null +++ b/frontend/src/auth/sessionState.ts @@ -0,0 +1,12 @@ +export interface SessionState { + user: unknown + token: string | null + isAuthenticated: boolean +} + +export const clearSessionState = (state: SessionState, storage: Pick) => { + state.user = null + state.token = null + state.isAuthenticated = false + storage.removeItem('token') +} diff --git a/frontend/src/components/BarcodePrint.vue b/frontend/src/components/BarcodePrint.vue index db8c7eb..23cfa47 100644 --- a/frontend/src/components/BarcodePrint.vue +++ b/frontend/src/components/BarcodePrint.vue @@ -254,6 +254,10 @@ const remoteStatusDescription = computed(() => { return '当前还没有设置默认打印电脑,请联系管理员完成终端配置。' } + if (defaultTerminal.value.status !== 'disabled' && (defaultTerminal.value.protocol_version || 0) < 2) { + return '打印助手需要升级。发送的任务会排队等待兼容的助手接单,请联系管理员更新办公室电脑。' + } + if (defaultTerminal.value.status === 'online' && defaultTerminal.value.readiness?.is_ready) { return `默认打印电脑 ${defaultTerminal.value.name} 在线,发送后会直接交给 ${remotePrinterName.value}。` } @@ -282,13 +286,14 @@ const getJobStatusText = (status?: string | null) => { if (status === 'completed') return '已完成' if (status === 'failed') return '失败' if (status === 'cancelled') return '已取消' + if (status === 'result_unknown') return '结果待核实' return status || '未知' } const getJobStatusBadgeClass = (status?: string | null) => { if (status === 'completed') return 'badge-success' if (status === 'failed') return 'badge-error' - if (status === 'queued' || status === 'claimed' || status === 'printing') return 'badge-warning' + if (status === 'queued' || status === 'claimed' || status === 'printing' || status === 'result_unknown') return 'badge-warning' return 'badge-neutral' } @@ -729,6 +734,10 @@ const fetchEquipment = async () => { } const handleRemotePrint = async () => { + if (props.equipmentIds.length > 100 || equipmentList.value.length > 100) { + alert('每次远程打印最多支持 100 张标签,请减少本次选择后再发送。') + return + } if (equipmentList.value.length === 0) { alert('没有可发送的标签') return @@ -756,6 +765,11 @@ const handleRemotePrint = async () => { throw new Error('标签图片生成不完整,请稍后重试') } + const imageBytes = images.map((image) => Math.ceil((image.split(',')[1]?.length || 0) * 3 / 4)) + if (imageBytes.some((size) => size > 2 * 1024 * 1024) || imageBytes.reduce((sum, size) => sum + size, 0) > 20 * 1024 * 1024) { + throw new Error('标签图片超过远程打印大小限制,请减少本次选择后重试。') + } + const renderedLabels = equipmentList.value.map((equipment, index) => ({ equipment_id: equipment.id, image_data_url: images[index] diff --git a/frontend/src/components/GlobalSearch.vue b/frontend/src/components/GlobalSearch.vue index 794e296..de8ccd1 100644 --- a/frontend/src/components/GlobalSearch.vue +++ b/frontend/src/components/GlobalSearch.vue @@ -118,7 +118,9 @@ const getStatusClass = (status: string) => { '已借出': 'badge-warning', '维护中': 'badge-neutral', '待报废': 'badge-warning', - '已报废': 'badge-error' + '已报废': 'badge-error', + '已损坏': 'badge-error', + '已丢失': 'badge-error' } return map[status] || 'badge-neutral' } diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue index 36048c2..55d0616 100644 --- a/frontend/src/layouts/MainLayout.vue +++ b/frontend/src/layouts/MainLayout.vue @@ -121,7 +121,7 @@
- +
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 159167c..360df1e 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -184,7 +184,9 @@ router.beforeEach(async (to, _from, next) => { try { await authStore.refreshUser() } catch { - if (authMode === 'local' || authStore.token === 'session') { + const usedSession = authStore.token === 'session' + authStore.clearSession() + if (authMode === 'local' || usedSession) { next('/login') } else { await keycloak.login() diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts index ec424ae..2a24ff0 100644 --- a/frontend/src/stores/auth.ts +++ b/frontend/src/stores/auth.ts @@ -4,6 +4,7 @@ import keycloak from '@/auth/keycloak' import api from '@/api' import { resolveAuthMode } from '@/utils/authMode' import { normalizeUser } from '@/auth/user' +import { clearSessionState } from '@/auth/sessionState' interface AuthState { user: User | null @@ -19,6 +20,9 @@ export const useAuthStore = defineStore('auth', { }), actions: { + clearSession() { + clearSessionState(this, localStorage) + }, setUser(user: User) { this.user = normalizeUser(user) this.isAuthenticated = true @@ -49,10 +53,7 @@ export const useAuthStore = defineStore('auth', { async logout() { const usedSession = this.token === 'session' const authMode = resolveAuthMode(import.meta.env.VITE_AUTH_MODE) - this.user = null - this.token = null - this.isAuthenticated = false - localStorage.removeItem('token') + this.clearSession() try { await api.post('/auth/logout') diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 8f87b58..d79cb5c 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -2,7 +2,7 @@ export type EquipmentType = string // 资产状态 -export type EquipmentStatus = '可用' | '已借出' | '维护中' | '已损坏' | '待报废' | '已报废' +export type EquipmentStatus = '可用' | '已借出' | '维护中' | '已损坏' | '已丢失' | '待报废' | '已报废' export interface BorrowAnomalyInfo { state: 'none' | 'returnable' | 'borrow_record_missing' @@ -600,6 +600,7 @@ export interface DashboardStats { maintenance_equipment: number pending_disposal_equipment: number damaged_equipment: number + lost_equipment?: number other_equipment: number disposed_equipment: number borrowed_without_active_record: number @@ -615,7 +616,8 @@ export interface DashboardStats { today_return_value: number generated_at: string business_timezone: 'Asia/Shanghai' - inventory_scope: 'current_excluding_disposed' + inventory_scope: 'current_excluding_disposed' | 'current_excluding_disposed_and_lost' + value_scope?: 'book_value_excluding_disposed_including_lost' inventory_by_type: Array<{ equipment_type: string total_quantity: number @@ -624,6 +626,7 @@ export interface DashboardStats { maintenance_quantity: number pending_disposal_quantity: number damaged_quantity: number + lost_quantity?: number other_quantity: number mountable_available_quantity: number is_child_type: boolean @@ -632,6 +635,7 @@ export interface DashboardStats { // 资产价值统计接口 export interface ValueStatistics { + value_scope?: 'book_value_excluding_disposed_including_lost' total_value: number borrowed_value: number available_value: number @@ -833,6 +837,7 @@ export type PrintJobStatus = | 'completed' | 'failed' | 'cancelled' + | 'result_unknown' export interface PrintTerminalPrinter { name: string @@ -873,6 +878,7 @@ export interface PrintTerminal { name: string platform?: string | null machine_name?: string | null + protocol_version?: number | null status: PrintTerminalStatus | string is_default: boolean last_seen_at?: string | null @@ -917,6 +923,8 @@ export interface PrintJob { error_message?: string | null attempt_count?: number claimed_at?: string | null + claim_token?: string | null + lease_expires_at?: string | null started_at?: string | null completed_at?: string | null created_at?: string | null diff --git a/frontend/src/utils/dateTime.ts b/frontend/src/utils/dateTime.ts new file mode 100644 index 0000000..899b5c5 --- /dev/null +++ b/frontend/src/utils/dateTime.ts @@ -0,0 +1,7 @@ +// datetime-local represents the user's browser timezone. Convert it before crossing the API boundary. +export const toApiDateTime = (value?: string): string | undefined => { + if (!value?.trim()) return undefined + const date = new Date(value) + if (!Number.isFinite(date.getTime())) throw new Error('日期时间无效,请重新选择') + return date.toISOString() +} diff --git a/frontend/src/utils/equipmentImport.ts b/frontend/src/utils/equipmentImport.ts new file mode 100644 index 0000000..099b6e2 --- /dev/null +++ b/frontend/src/utils/equipmentImport.ts @@ -0,0 +1,42 @@ +export interface EquipmentImportRow { + source_row: number + equipment_name: string + equipment_type: string + equipment_model?: string + department?: string + storage_location?: string + unit_price?: number | string + total_quantity: number + supplier?: string + purchase_date?: string + description?: string +} + +const optionalText = (value: unknown) => value === undefined || value === null || String(value).trim() === '' + ? undefined + : String(value).trim() + +// Keep invalid data rows for the server's validation; skip only entirely empty rows. +export const parseEquipmentImportRows = (rows: unknown[][], headerRowOffset = 0): EquipmentImportRow[] => rows + .slice(1) + .map((cols, index) => ({ cols, source_row: headerRowOffset + index + 2 })) + .filter(({ cols }) => cols.some((value) => optionalText(value) !== undefined)) + .map(({ cols, source_row }) => { + const quantity = Number(cols[6]) + const priceText = optionalText(cols[5]) + const price = priceText === undefined ? undefined : Number(priceText) + return { + source_row, + equipment_name: String(cols[0] ?? '').trim(), + equipment_type: String(cols[1] ?? '').trim(), + equipment_model: optionalText(cols[2]), + department: optionalText(cols[3]), + storage_location: optionalText(cols[4]), + // Invalid nonempty prices must survive JSON instead of NaN becoming null/default zero. + unit_price: price === undefined ? undefined : Number.isFinite(price) ? price : priceText, + total_quantity: Number.isFinite(quantity) ? quantity : 0, + supplier: optionalText(cols[7]), + purchase_date: optionalText(cols[8]), + description: optionalText(cols[9]) + } + }) diff --git a/frontend/src/utils/equipmentSelection.ts b/frontend/src/utils/equipmentSelection.ts new file mode 100644 index 0000000..982679e --- /dev/null +++ b/frontend/src/utils/equipmentSelection.ts @@ -0,0 +1,27 @@ +export interface SelectableEquipment { + id: number + equipment_code: string + equipment_name: string + status?: string + parent_id?: number | null + children?: SelectableEquipment[] +} + +export const indexEquipmentSelection = (items: SelectableEquipment[]) => { + const indexed = new Map() + const visit = (item: SelectableEquipment, parentId?: number) => { + indexed.set(item.id, parentId === undefined ? item : { ...item, parent_id: item.parent_id ?? parentId }) + item.children?.forEach((child) => visit(child, item.id)) + } + items.forEach((item) => visit(item)) + return indexed +} + +export const resolveSelectedEquipmentCodes = ( + selectedIds: number[], + indexed: Map +) => selectedIds.map((id) => { + const code = indexed.get(id)?.equipment_code + if (!code) throw new Error(`资产 #${id} 的资料未加载,请重新选择后再操作`) + return code +}) diff --git a/frontend/src/utils/requestScope.ts b/frontend/src/utils/requestScope.ts new file mode 100644 index 0000000..4094681 --- /dev/null +++ b/frontend/src/utils/requestScope.ts @@ -0,0 +1,9 @@ +export const createRequestScope = () => { + let sequence = 0 + let disposed = false + return { + begin: () => ++sequence, + isCurrent: (version: number) => !disposed && version === sequence, + dispose: () => { disposed = true; sequence += 1 } + } +} diff --git a/frontend/src/utils/returnConfirmation.ts b/frontend/src/utils/returnConfirmation.ts new file mode 100644 index 0000000..7d1b52e --- /dev/null +++ b/frontend/src/utils/returnConfirmation.ts @@ -0,0 +1,28 @@ +interface ConfirmationResult { + need_confirm?: boolean + confirmation_token?: string + message?: string +} + +export const getReturnCompletionText = (condition: string) => condition === '丢失' + ? '已登记丢失(未入仓)' + : condition === '损坏' ? '已归还,状态为已损坏' : '已完成归还' + +// Each acknowledgement is tied to the asset tree the server just returned. +export const submitReturnWithConfirmation = async ( + payload: P, + submit: (payload: P & { confirmation_token?: string }) => Promise<{ data?: R }>, + confirm: (message: string) => boolean +): Promise => { + let confirmationToken: string | undefined + for (let attempt = 0; attempt < 3; attempt += 1) { + const response = await submit({ ...payload, confirmation_token: confirmationToken }) + const result = response.data + if (!result) throw new Error('归还接口未返回结果,请刷新核实状态') + if (!result.need_confirm) return result + if (!result.confirmation_token) throw new Error('配件差异确认信息不完整,请刷新后重试') + if (!confirm(result.message || '配件与借出记录不一致或原始快照缺失,请核对实物后确认是否继续入库。')) return null + confirmationToken = result.confirmation_token + } + throw new Error('配件状态持续变化,请刷新资产后重新确认') +} diff --git a/frontend/src/views/borrow/BorrowScan.vue b/frontend/src/views/borrow/BorrowScan.vue index bde0485..3cc898f 100644 --- a/frontend/src/views/borrow/BorrowScan.vue +++ b/frontend/src/views/borrow/BorrowScan.vue @@ -367,7 +367,7 @@ const personSearchLoading = ref(false) const canSubmit = computed(() => { return canSubmitBorrowScan({ - hasEquipment: Boolean(equipmentInfo.value), + hasEquipment: Boolean(equipmentInfo.value && equipmentInfo.value.status !== '已丢失'), selectedPersonId: selectedPersonId.value, loading: loading.value }) @@ -397,6 +397,7 @@ const getStatusBadgeClass = (status: string) => { case '维护中': return 'badge-error' case '已报废': + case '已丢失': return 'badge-neutral' default: return 'badge-neutral' @@ -554,6 +555,7 @@ const handleManualSubmit = async () => { } const handleSubmit = async () => { + if (!canSubmit.value) return if (!equipmentInfo.value || selectedPersonId.value === null || Number.isNaN(selectedPersonId.value)) { return } diff --git a/frontend/src/views/borrow/ReturnScan.vue b/frontend/src/views/borrow/ReturnScan.vue index 5e38c86..8454e32 100644 --- a/frontend/src/views/borrow/ReturnScan.vue +++ b/frontend/src/views/borrow/ReturnScan.vue @@ -341,6 +341,7 @@ diff --git a/frontend/src/views/equipment/EquipmentList.vue b/frontend/src/views/equipment/EquipmentList.vue index 0709a04..195a401 100644 --- a/frontend/src/views/equipment/EquipmentList.vue +++ b/frontend/src/views/equipment/EquipmentList.vue @@ -56,6 +56,8 @@ + + @@ -735,7 +737,7 @@

选中项中有 {{ selectedBorrowedCount }} 项仍在借用,请先完成归还。

- 选中项中有 {{ selectedTransferBlockedCount }} 项处于维修、待报废或已报废状态,不能调拨。 + 选中项中有 {{ selectedTransferBlockedCount }} 项资料不完整或处于维修、待报废、报废、丢失状态,不能调拨。

@@ -760,13 +762,17 @@ diff --git a/frontend/src/views/notifications/NotificationCenter.vue b/frontend/src/views/notifications/NotificationCenter.vue index 714c986..b452078 100644 --- a/frontend/src/views/notifications/NotificationCenter.vue +++ b/frontend/src/views/notifications/NotificationCenter.vue @@ -465,6 +465,7 @@