From bdd40abeb21b806736bdd860f955691b41a143a2 Mon Sep 17 00:00:00 2001 From: Himanshu Rai Date: Wed, 22 Apr 2026 21:37:27 +0530 Subject: [PATCH] Upgrade dependencies, update tools with new API features 1. Upgrade all dependencies to latest versions 2. Use fetch instead of axios 3. Use registerTool with input/output zod schemas 4. Update with new API features --- biome.json | 11 +- package-lock.json | 3628 ++++++++++------------------- package.json | 43 +- src/LoggingTransport.ts | 6 +- src/api.ts | 78 + src/index.ts | 2 +- src/schemas.ts | 800 ++++++- src/tools/customFields.ts | 57 +- src/tools/folders.ts | 210 +- src/tools/index.ts | 20 +- src/tools/projects.ts | 101 +- src/tools/requirements.ts | 105 +- src/tools/shared-preconditions.ts | 175 +- src/tools/shared-steps.ts | 172 +- src/tools/tags.ts | 66 +- src/tools/tcases.ts | 615 +---- src/types.ts | 302 --- src/utils.test.ts | 143 -- src/utils.ts | 60 - tsconfig.json | 9 +- 20 files changed, 2537 insertions(+), 4066 deletions(-) create mode 100644 src/api.ts delete mode 100644 src/types.ts delete mode 100644 src/utils.test.ts delete mode 100644 src/utils.ts diff --git a/biome.json b/biome.json index 294aeb7..eedb33b 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.12/schema.json", "vcs": { "enabled": false, "clientKind": "git", @@ -7,7 +7,7 @@ }, "files": { "ignoreUnknown": true, - "ignore": [] + "includes": ["**"] }, "formatter": { "enabled": true, @@ -15,9 +15,7 @@ "indentWidth": 2, "lineWidth": 100 }, - "organizeImports": { - "enabled": true - }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { "enabled": true, "rules": { @@ -37,9 +35,6 @@ "a11y": { "useKeyWithClickEvents": "error", "useMediaCaption": "error" - }, - "nursery": { - "useImportRestrictions": "off" } } }, diff --git a/package-lock.json b/package-lock.json index 634e8d3..be4ae95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,38 +1,36 @@ { "name": "qasphere-mcp", - "version": "0.2.1", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "qasphere-mcp", - "version": "0.2.1", + "version": "0.3.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", - "axios": "^1.6.7", - "dotenv": "^16.4.5", - "zod": "^3.22.4" + "@modelcontextprotocol/sdk": "^1.29.0", + "dotenv": "^17.4.2", + "zod": "^4.3.6" }, "bin": { "qasphere-mcp": "dist/index.js" }, "devDependencies": { - "@biomejs/biome": "1.9.4", - "@types/node": "^22.13.16", + "@biomejs/biome": "^2.4.12", + "@types/node": "^25.6.0", "husky": "^9.1.7", - "lint-staged": "^15.5.1", - "tsx": "^4.19.3", - "typescript": "^5.8.2", - "vitest": "^3.1.1" + "lint-staged": "^16.4.0", + "tsx": "^4.21.0", + "typescript": "^6.0.3" } }, "node_modules/@biomejs/biome": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", - "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.12.tgz", + "integrity": "sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA==", "dev": true, - "hasInstallScript": true, + "license": "MIT OR Apache-2.0", "bin": { "biome": "bin/biome" }, @@ -44,24 +42,25 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.4", - "@biomejs/cli-darwin-x64": "1.9.4", - "@biomejs/cli-linux-arm64": "1.9.4", - "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" + "@biomejs/cli-darwin-arm64": "2.4.12", + "@biomejs/cli-darwin-x64": "2.4.12", + "@biomejs/cli-linux-arm64": "2.4.12", + "@biomejs/cli-linux-arm64-musl": "2.4.12", + "@biomejs/cli-linux-x64": "2.4.12", + "@biomejs/cli-linux-x64-musl": "2.4.12", + "@biomejs/cli-win32-arm64": "2.4.12", + "@biomejs/cli-win32-x64": "2.4.12" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", - "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.12.tgz", + "integrity": "sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" @@ -71,13 +70,14 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", - "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.12.tgz", + "integrity": "sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A==", "cpu": [ "x64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" @@ -87,13 +87,17 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", - "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.12.tgz", + "integrity": "sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -103,13 +107,17 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", - "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.12.tgz", + "integrity": "sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -119,13 +127,17 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", - "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.12.tgz", + "integrity": "sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -135,13 +147,17 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", - "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.12.tgz", + "integrity": "sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -151,13 +167,14 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", - "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.12.tgz", + "integrity": "sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" @@ -167,13 +184,14 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", - "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.12.tgz", + "integrity": "sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" @@ -182,1063 +200,262 @@ "node": ">=14.21.3" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", "cpu": [ - "ppc64" + "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "aix" + "openharmony" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", - "cpu": [ - "x64" - ], + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, "engines": { - "node": ">=18" + "node": ">= 0.6" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", - "cpu": [ - "x64" - ], + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", - "cpu": [ - "arm" - ], + "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, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", - "cpu": [ - "arm64" - ], + "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, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.8" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", - "cpu": [ - "riscv64" - ], + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", - "cpu": [ - "s390x" - ], + "node_modules/cli-truncate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.8.0.tgz", - "integrity": "sha512-e06W7SwrontJDHwCawNO5SGxG+nU9AAx+jpHHZqGl/WrDBdWOpvirC+s58VpJTB5QemI4jTRcjWT4Pt3Q1NPQQ==", - "dependencies": { - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.3", - "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^4.1.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.38.0.tgz", - "integrity": "sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.38.0.tgz", - "integrity": "sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.38.0.tgz", - "integrity": "sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.38.0.tgz", - "integrity": "sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.38.0.tgz", - "integrity": "sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.38.0.tgz", - "integrity": "sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.38.0.tgz", - "integrity": "sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.38.0.tgz", - "integrity": "sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.38.0.tgz", - "integrity": "sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.38.0.tgz", - "integrity": "sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.38.0.tgz", - "integrity": "sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.38.0.tgz", - "integrity": "sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.38.0.tgz", - "integrity": "sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.38.0.tgz", - "integrity": "sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.38.0.tgz", - "integrity": "sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.38.0.tgz", - "integrity": "sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.38.0.tgz", - "integrity": "sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.38.0.tgz", - "integrity": "sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.38.0.tgz", - "integrity": "sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.38.0.tgz", - "integrity": "sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "22.13.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.16.tgz", - "integrity": "sha512-15tM+qA4Ypml/N7kyRdvfRjBQT2RL461uF1Bldn06K0Nzn1lY3nAPgHlsVrJxdZ9WhZiW0Fmc1lOYMtDsAuB3w==", - "dev": true, - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", - "dev": true, - "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", - "dev": true, - "dependencies": { - "@vitest/spy": "3.1.1", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", - "dev": true, - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", - "dev": true, - "dependencies": { - "@vitest/utils": "3.1.1", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", - "dev": true, - "dependencies": { - "@vitest/pretty-format": "3.1.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", - "dev": true, - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", - "dev": true, - "dependencies": { - "@vitest/pretty-format": "3.1.1", - "loupe": "^3.1.3", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "dev": true, - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/body-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz", - "integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.5.2", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/body-parser/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==" - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "engines": { - "node": ">= 16" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1248,52 +465,46 @@ "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } + "dev": true, + "license": "MIT" }, "node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "dependencies": { - "safe-buffer": "5.2.1" - }, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/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" } @@ -1302,6 +513,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", "engines": { "node": ">=6.6.0" } @@ -1332,11 +544,12 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "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.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1347,45 +560,20 @@ } } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.8" } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -1409,18 +597,21 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1430,6 +621,7 @@ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1453,12 +645,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -1470,88 +656,27 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" - } + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" }, "node_modules/eventsource": { "version": "3.0.5", @@ -1572,84 +697,57 @@ "node": ">=18.0.0" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz", - "integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.0.1", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", - "content-type": "~1.0.4", - "cookie": "0.7.1", + "content-type": "^1.0.5", + "cookie": "^0.7.1", "cookie-signature": "^1.2.1", - "debug": "4.3.6", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "^2.0.0", - "fresh": "2.0.0", - "http-errors": "2.0.0", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", - "methods": "~1.1.2", "mime-types": "^3.0.0", - "on-finished": "2.4.1", - "once": "1.4.0", - "parseurl": "~1.3.3", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "router": "^2.0.0", - "safe-buffer": "5.2.1", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", "send": "^1.1.0", - "serve-static": "^2.1.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "^2.0.0", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-rate-limit": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", - "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, "engines": { "node": ">= 16" }, @@ -1657,25 +755,36 @@ "url": "https://github.com/sponsors/express-rate-limit" }, "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" + "express": ">= 4.11" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -1685,90 +794,18 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/finalhandler/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==" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "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==", - "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" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" + "node": ">= 18.0.0" }, - "engines": { - "node": ">= 0.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1777,6 +814,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1804,10 +842,11 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1850,18 +889,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-tsconfig": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", @@ -1896,21 +923,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1922,28 +934,33 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.12.14", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", + "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/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" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/husky": { @@ -1962,141 +979,135 @@ } }, "node_modules/iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "engines": { - "node": ">=14" - }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/antonk52" + "url": "https://github.com/sponsors/panva" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/lint-staged": { - "version": "15.5.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.1.tgz", - "integrity": "sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", + "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^5.4.1", - "commander": "^13.1.0", - "debug": "^4.4.0", - "execa": "^8.0.1", - "lilconfig": "^3.1.3", - "listr2": "^8.2.5", - "micromatch": "^4.0.8", - "pidtree": "^0.6.0", + "commander": "^14.0.3", + "listr2": "^9.0.5", + "picomatch": "^4.0.3", "string-argv": "^0.3.2", - "yaml": "^2.7.0" + "tinyexec": "^1.0.4", + "yaml": "^2.8.2" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": ">=18.12.0" + "node": ">=20.17" }, "funding": { "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/lint-staged/node_modules/tinyexec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, + "license": "MIT", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18" } }, - "node_modules/lint-staged/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 - }, "node_modules/listr2": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.2.tgz", - "integrity": "sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, + "license": "MIT", "dependencies": { - "cli-truncate": "^4.0.0", + "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", @@ -2104,7 +1115,7 @@ "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/log-update": { @@ -2112,6 +1123,7 @@ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, + "license": "MIT", "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", @@ -2126,26 +1138,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dev": true, - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" @@ -2154,22 +1152,7 @@ "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, "node_modules/math-intrinsics": { @@ -2184,6 +1167,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2192,6 +1176,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -2199,62 +1184,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz", - "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", "dependencies": { - "mime-db": "^1.53.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-function": { @@ -2262,6 +1214,7 @@ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2270,63 +1223,20 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } + "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/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2339,6 +1249,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2350,6 +1261,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -2361,20 +1273,22 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, + "license": "MIT", "dependencies": { - "mimic-fn": "^4.0.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2384,6 +1298,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2397,686 +1312,877 @@ } }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, + "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==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, "engines": { - "node": ">= 14.16" + "node": ">= 0.10" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, "engines": { - "node": ">=8.6" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" + "license": "MIT" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" }, "engines": { - "node": ">=0.10" + "node": ">= 18" } }, - "node_modules/pkce-challenge": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz", - "integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, "engines": { - "node": ">=16.20.0" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "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==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/side-channel-list": { + "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": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "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": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==" - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { - "side-channel": "^1.0.6" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { - "node": ">=0.6" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "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": ">= 0.6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", + "dev": true, + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "engines": { + "node": ">=0.6.19" } }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "node_modules/string-width": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "dev": true, + "license": "MIT", "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, + "license": "MIT", "dependencies": { - "mimic-function": "^5.0.0" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">=18" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } }, - "node_modules/rollup": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz", - "integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==", + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" }, "bin": { - "rollup": "dist/bin/rollup" + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">=18.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.38.0", - "@rollup/rollup-android-arm64": "4.38.0", - "@rollup/rollup-darwin-arm64": "4.38.0", - "@rollup/rollup-darwin-x64": "4.38.0", - "@rollup/rollup-freebsd-arm64": "4.38.0", - "@rollup/rollup-freebsd-x64": "4.38.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.38.0", - "@rollup/rollup-linux-arm-musleabihf": "4.38.0", - "@rollup/rollup-linux-arm64-gnu": "4.38.0", - "@rollup/rollup-linux-arm64-musl": "4.38.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.38.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.38.0", - "@rollup/rollup-linux-riscv64-gnu": "4.38.0", - "@rollup/rollup-linux-riscv64-musl": "4.38.0", - "@rollup/rollup-linux-s390x-gnu": "4.38.0", - "@rollup/rollup-linux-x64-gnu": "4.38.0", - "@rollup/rollup-linux-x64-musl": "4.38.0", - "@rollup/rollup-win32-arm64-msvc": "4.38.0", - "@rollup/rollup-win32-ia32-msvc": "4.38.0", - "@rollup/rollup-win32-x64-msvc": "4.38.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/router": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz", - "integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==", - "dependencies": { - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz", - "integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==", - "dependencies": { - "debug": "^4.3.5", - "destroy": "^1.2.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^0.5.2", - "http-errors": "^2.0.0", - "mime-types": "^2.1.35", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/send/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" + "fsevents": "~2.3.3" } }, - "node_modules/send/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/send/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "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==" - }, - "node_modules/serve-static": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz", - "integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.0.0" - }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 18" + "node": ">=18" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "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==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "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==", + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "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==", + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "node": ">=18" } }, - "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==", + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/std-env": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.1.tgz", - "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==", - "dev": true - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.6.19" + "node": ">=18" } }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=18" } }, - "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==", + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=18" } }, - "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": ">=18" } }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8.0" + "node": ">=18" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.6" + "node": ">=18" } }, - "node_modules/tsx": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz", - "integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==", + "node_modules/tsx/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "dev": true, - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, + "hasInstallScript": true, + "license": "MIT", "bin": { - "tsx": "dist/cli.mjs" + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" }, "optionalDependencies": { - "fsevents": "~2.3.3" + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" } }, "node_modules/type-is": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz", - "integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -3087,10 +2193,11 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3100,27 +2207,21 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "dev": true, + "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3129,249 +2230,6 @@ "node": ">= 0.8" } }, - "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", - "dev": true, - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-node/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/vite-node/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 - }, - "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.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", - "dev": true, - "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", - "chai": "^5.2.0", - "debug": "^4.4.0", - "expect-type": "^1.2.0", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "std-env": "^3.8.1", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinypool": "^1.0.2", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/vitest/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 - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3386,70 +2244,80 @@ "node": ">= 8" } }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, + "license": "MIT", "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, + "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25.28 || ^4" } } } diff --git a/package.json b/package.json index 654067b..fc2245c 100644 --- a/package.json +++ b/package.json @@ -1,46 +1,53 @@ { "name": "qasphere-mcp", - "version": "0.3.0", + "version": "0.4.0", "description": "MCP server for QA Sphere integration", "type": "module", "main": "dist/index.js", "bin": { "qasphere-mcp": "./dist/index.js" }, - "files": ["dist", "README.md", "LICENSE"], + "files": [ + "dist", + "README.md", + "LICENSE" + ], "repository": { "type": "git", "url": "git+https://github.com/Hypersequent/qasphere-mcp.git" }, - "keywords": ["mcp", "qasphere", "tms"], + "keywords": [ + "mcp", + "qasphere", + "tms" + ], "author": "Hypersequent", "license": "MIT", + "engines": { + "node": ">=20" + }, "scripts": { "build": "tsc && chmod +x dist/index.js", "dev": "tsx src/index.ts", - "lint": "biome lint --write .", - "format": "biome format --write .", + "check": "biome check src", + "check:fix": "biome check --write src", "inspector": "npx @modelcontextprotocol/inspector tsx src/index.ts", - "test": "vitest run", "prepare": "husky" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", - "axios": "^1.6.7", - "dotenv": "^16.4.5", - "zod": "^3.22.4" + "@modelcontextprotocol/sdk": "^1.29.0", + "dotenv": "^17.4.2", + "zod": "^4.3.6" }, "devDependencies": { - "@biomejs/biome": "1.9.4", - "@types/node": "^22.13.16", + "@biomejs/biome": "^2.4.12", + "@types/node": "^25.6.0", "husky": "^9.1.7", - "lint-staged": "^15.5.1", - "tsx": "^4.19.3", - "typescript": "^5.8.2", - "vitest": "^3.1.1" + "lint-staged": "^16.4.0", + "tsx": "^4.21.0", + "typescript": "^6.0.3" }, "lint-staged": { - "*.{ts,js}": ["biome lint --write", "biome format --write"], - "*.json": "biome format --write --no-errors-on-unmatched" + "*.{ts,js,json}": "biome check --write --no-errors-on-unmatched" } } diff --git a/src/LoggingTransport.ts b/src/LoggingTransport.ts index 18ab27f..f29098f 100644 --- a/src/LoggingTransport.ts +++ b/src/LoggingTransport.ts @@ -1,8 +1,8 @@ -import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' -import type { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' -import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js' import * as fs from 'node:fs' import * as path from 'node:path' +import type { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' +import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' +import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js' /** * A wrapper transport that logs all MCP communication to a file diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..0f92636 --- /dev/null +++ b/src/api.ts @@ -0,0 +1,78 @@ +import type { z } from 'zod' +import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from './config.js' + +export class ApiError extends Error { + status: number + body: unknown + + constructor(message: string, status: number, body?: unknown) { + super(message) + this.name = 'ApiError' + this.status = status + this.body = body + } +} + +type QueryValue = string | number | boolean | string[] | number[] | undefined + +export interface ApiQueryOptions { + schema: T + method?: 'GET' | 'POST' | 'PATCH' + query?: Record + body?: unknown +} + +const buildQueryString = (query?: Record): string => { + if (!query) return '' + const params = new URLSearchParams() + for (const [key, value] of Object.entries(query)) { + if (value === undefined) continue + if (Array.isArray(value)) { + for (const item of value) params.append(key, String(item)) + } else { + params.append(key, String(value)) + } + } + const str = params.toString() + return str ? `?${str}` : '' +} + +export async function apiQuery( + path: string, + opts: ApiQueryOptions +): Promise> { + const url = `${QASPHERE_TENANT_URL}${path}${buildQueryString(opts.query)}` + const method = opts.method ?? 'GET' + const headers: Record = { + Authorization: `ApiKey ${QASPHERE_API_KEY}`, + 'Content-Type': 'application/json', + } + + const res = await fetch(url, { + method, + headers, + body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined, + }) + + if (!res.ok) { + let body: unknown + let message = res.statusText + try { + body = await res.json() + if (body && typeof body === 'object' && 'message' in body) { + const m = (body as { message?: unknown }).message + if (typeof m === 'string' && m.length > 0) message = m + } + } catch { + // ignore JSON parse failures; fall back to statusText + } + throw new ApiError(message, res.status, body) + } + + const raw = await res.json() + const parsed = opts.schema.safeParse(raw) + if (!parsed.success) { + throw new ApiError(`Response validation failed: ${parsed.error.message}`, res.status, raw) + } + return parsed.data +} diff --git a/src/index.ts b/src/index.ts index 86f3347..e11725e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ #!/usr/bin/env node +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js' -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { LoggingTransport } from './LoggingTransport.js' import { registerTools } from './tools/index.js' diff --git a/src/schemas.ts b/src/schemas.ts index eed05cc..a057aea 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -1,6 +1,800 @@ -import { z } from 'zod' +/** biome-ignore-all lint/suspicious/noTemplateCurlyInString: docstrings describe QASphere's `${var}` placeholder syntax, not JS template literals */ -export const projectCodeSchema = z +import z from 'zod' + +// ---------- Reusable output sub-schemas ---------- + +const projectCodeSchema = z .string() - .regex(/^[A-Z0-9]{2,5}$/, 'Marker must be 2 to 5 characters in format PROJECT_CODE (e.g., BDI)') + .regex( + /^[A-Z0-9]{2,5}$/, + 'Project code must be 2 to 5 uppercase alphanumeric characters (e.g., BDI)' + ) .describe('Project code identifier (e.g., BDI)') + +const testCaseMarkerSchema = z + .string() + .regex( + /^[A-Z0-9]{2,5}-\d+$/, + 'Marker must be in format PROJECT_CODE-TEST_CASE_SEQUENCE (e.g., BDI-123). Project code must be 2 to 5 characters in format PROJECT_CODE (e.g., BDI). Test case sequence must be a number.' + ) + .describe('Test case marker in format PROJECT_CODE-TEST_CASE_SEQUENCE (e.g., BDI-123)') + +const testCaseStepInputSchema = z.object({ + sharedStepId: z + .number() + .int() + .positive('Shared step ID must be a positive integer') + .optional() + .describe('Use a shared step by specifying its unique id. Skip for standalone step'), + description: z + .string() + .optional() + .describe( + 'Action to be performed in the step (HTML format). Required for standalone steps; skip for shared steps. In template test cases, may contain `${var}` placeholders that are substituted via `parameterValues` to produce filled test cases.' + ), + expected: z + .string() + .optional() + .describe( + 'Expected result from the step (HTML format). Required for standalone steps; skip for shared steps. In template test cases, may contain `${var}` placeholders that are substituted via `parameterValues` to produce filled test cases.' + ), +}) + +const testCaseRequirementInputSchema = z.object({ + text: z + .string() + .min(1, 'Requirement text must be at least 1 character') + .max(255, 'Requirement text must be at most 255 characters') + .describe('Title of the requirement'), + url: z + .url('Requirement URL must be a valid URL') + .max(255, 'Requirement URL must be at most 255 characters') + .or(z.literal('')) + .describe('URL of the requirement'), +}) + +const testCaseLinkInputSchema = z.object({ + text: z + .string() + .min(1, 'Link text must be at least 1 character') + .max(255, 'Link text must be at most 255 characters') + .describe('Title of the link'), + url: z + .url('Link URL must be a valid URL') + .min(1, 'Link URL must be at least 1 character') + .max(255, 'Link URL must be at most 255 characters') + .describe('URL of the link'), +}) + +const testCasePreconditionInputSchema = z.object({ + sharedPreconditionId: z + .number() + .int() + .positive('Shared precondition ID must be a positive integer') + .optional() + .describe( + 'Use a shared precondition by specifying its unique id. Skip for standalone precondition' + ), + text: z + .string() + .optional() + .describe( + 'Standalone precondition text (HTML format). Skip for shared precondition. In template test cases, may contain `${var}` placeholders that are substituted via `parameterValues` to produce filled test cases.' + ), +}) + +const testCaseCustomFieldInputSchema = z.object({ + isDefault: z + .boolean() + .describe('Whether to use the default value (only if the field defines one)'), + value: z.string().optional().describe('Custom field value (omit when setting the default)'), +}) + +type TestStep = { + id: number + version: number + isLatest: boolean + type: string + title?: string + description?: string + expected?: string + subSteps?: TestStep[] + deletedAt?: string +} + +export const testStepSchemaShape = { + id: z.number().describe('Unique identifier of the step'), + version: z.number().describe('Version of the step'), + isLatest: z.boolean().describe('Whether this is the latest version of the step'), + type: z + .string() + .describe('Type of the step. Known values: "standalone" | "shared" | "shared_sub_step"'), + title: z.string().optional().describe('Title of the step (only for shared steps)'), + description: z + .string() + .optional() + .describe('Details of the step (HTML; only for standalone and shared_sub_step)'), + expected: z + .string() + .optional() + .describe('Expected result from the step (HTML; only for standalone and shared_sub_step)'), + get subSteps() { + return z + .array(testStepSchema) + .optional() + .describe( + "Sub-steps of a shared step, each with type 'shared_sub_step' (present only on shared steps)" + ) + }, + deletedAt: z.string().optional().describe('Date the step was deleted on (ISO 8601)'), +} +const testStepSchema: z.ZodType = z.object(testStepSchemaShape) + +const testTagSchema = z.object({ + id: z.number().describe('Tag identifier'), + title: z.string().describe('Tag title'), +}) + +const testFileSchema = z.object({ + id: z.string().describe('File identifier'), + fileName: z.string().describe('Original file name'), + mimeType: z.string().describe('MIME type of the file'), + size: z.number().describe('File size in bytes'), + url: z.url().optional().describe('URL of the file (missing for files uploaded via older routes)'), +}) + +const testRequirementSchema = z.object({ + id: z.string().describe('Requirement identifier'), + text: z.string().describe('Title of the requirement'), + url: z.url().or(z.literal('')).describe('URL of the requirement'), +}) + +const requirementIntegrationLinkSchema = z.object({ + type: z.string().optional().describe('Integration type (e.g., "jira")'), + integrationId: z.string().optional().describe('ID of the integration'), + issueId: z.string().describe('Remote issue ID (e.g., "PROJ-123")'), + issueTitle: z.string().describe('Title of the remote issue'), + issueUrl: z.string().describe('Full URL of the remote issue'), + remoteLinkId: z.string().describe('Remote link ID assigned by the integration'), +}) + +const requirementListItemSchema = testRequirementSchema.extend({ + integrationLink: requirementIntegrationLinkSchema + .nullable() + .describe('Integration link details (present only if the requirement is linked to Jira)'), + tcaseCount: z + .number() + .optional() + .describe('Number of test cases linked to this requirement (only when `tcaseCount` included)'), +}) + +const linkSchema = z.object({ + text: z.string().describe('Title of the link'), + url: z.url().describe('URL of the link'), +}) + +const testFolderSchema = z.object({ + projectId: z.string().describe('ID of the project the folder belongs to'), + id: z.number().describe('Unique identifier for the folder'), + parentId: z.number().describe('ID of the parent folder (0 for root folders)'), + pos: z.number().describe('Position of the folder among its siblings'), + title: z.string().describe('Name of the folder'), + comment: z.string().describe('Additional notes or description (HTML)'), +}) + +const testPreconditionSchema = z.object({ + projectId: z.string().describe('Project id the precondition belongs to'), + id: z.number().describe('ID of the precondition'), + version: z.number().describe('Version of the precondition'), + isLatest: z.boolean().describe("Whether the precondition's version is the latest"), + type: z.string().describe('Type of the precondition. Known values: "standalone" | "shared"'), + title: z + .string() + .optional() + .describe('Title of the precondition (only for shared preconditions)'), + text: z.string().describe('Precondition text (HTML format)'), + createdAt: z.string().describe('Precondition creation time (ISO 8601)'), + updatedAt: z.string().describe('Precondition last-update time (ISO 8601)'), + deletedAt: z.string().optional().describe('Precondition deletion time (ISO 8601)'), +}) + +const testCustomFieldValueSchema = z.object({ + value: z.string().describe('Current custom field value'), + isDefault: z.boolean().describe('Whether the value is the default set by the system'), +}) + +const customFieldOptionSchema = z.object({ + id: z.string().describe('Option identifier'), + value: z.string().describe('Option display value'), +}) + +const customFieldSchema = z.object({ + id: z.string().describe('Unique custom field identifier'), + type: z + .string() + .describe('Field type. Known values: "text" | "dropdown" | "checkbox" | "richtext"'), + systemName: z.string().describe('System identifier for the field (used in API requests)'), + name: z.string().describe('Display name of the field'), + required: z.boolean().describe('Whether the field is required for test cases'), + enabled: z.boolean().describe('Whether the field is currently enabled'), + options: z + .array(customFieldOptionSchema) + .nullable() + .describe('Available options (only for dropdown fields)'), + defaultValue: z.string().optional().describe('Default value for the field'), + pos: z.number().describe('Display position/order'), + allowAllProjects: z.boolean().describe('Whether the field is available to all projects'), + allowedProjectIds: z + .array(z.string()) + .nullable() + .describe('List of project IDs if not available to all projects'), + createdAt: z.string().describe('ISO 8601 timestamp when the field was created'), + updatedAt: z.string().describe('ISO 8601 timestamp when the field was last updated'), +}) + +const testParameterValuesSchema = z.object({ + tcaseId: z.string().describe('ID of the filled test case'), + tcaseVersion: z.number().describe('Version of the filled test case'), + values: z + .record(z.string(), z.string()) + .describe('Parameter values substituted for this filled test case'), +}) + +// ---------- Tool input/output schemas ---------- + +export const getProjectInputSchema = z.object({ + projectCode: projectCodeSchema, +}) +export type GetProjectInput = z.infer + +export const getProjectOutputSchema = z.object({ + id: z.string().describe('Unique identifier of the project'), + code: z.string().describe('Project code (e.g., BDI)'), + title: z.string().describe('Project title'), + overviewTitle: z.string().describe('Project overview title'), + overviewDescription: z.string().describe('Project overview description (HTML)'), + links: z.array(linkSchema).nullable().describe('Project links'), + createdAt: z.string().describe('Project creation time (ISO 8601)'), + updatedAt: z.string().describe('Project last-update time (ISO 8601)'), + archivedAt: z + .string() + .nullable() + .describe('Project archival time (ISO 8601); null if the project is not archived'), +}) +export type GetProjectOutput = z.infer + +export const listProjectsInputSchema = z.object({}) +export type ListProjectsInput = z.infer + +export const listProjectsOutputSchema = z.object({ + projects: z.array(getProjectOutputSchema).nullable().describe('List of projects'), +}) +export type ListProjectsOutput = z.infer + +export const getTestCaseInputSchema = z.object({ + marker: testCaseMarkerSchema, +}) +export type GetTestCaseInput = z.infer + +export const getTestCaseOutputSchema = z.object({ + id: z.string().describe('Unique identifier of the test case'), + legacyId: z.string().describe('Legacy identifier of the test case (empty string if none is set)'), + version: z + .number() + .describe('Version of the test case. Updates (except folder/pos) create a new version'), + type: z + .string() + .describe('Type of the test case. Known values: "standalone" | "template" | "filled"'), + title: z.string().describe('Title of the test case'), + seq: z + .number() + .describe('Sequence number of the test case within its project (assigned incrementally)'), + folderId: z.number().describe('Identifier of the folder where the test case is placed'), + pos: z.number().describe('Ordered position (0-based) of the test case in its folder'), + priority: z + .string() + .describe('Priority of the test case. Known values: "high" | "medium" | "low"'), + comment: z + .string() + .describe('Test case precondition text (HTML). DEPRECATED — prefer the `precondition` object'), + precondition: testPreconditionSchema + .optional() + .describe('Test case precondition object (may be a shared or standalone precondition)'), + files: z.array(testFileSchema).nullable().describe('List of files attached to the test case'), + links: z.array(linkSchema).nullable().describe('Additional links relevant to the test case'), + authorId: z.number().describe('Unique identifier of the user who added the test case'), + isDraft: z.boolean().describe('Whether the test case is still in draft state'), + isLatestVersion: z.boolean().describe('Whether this is the latest version of the test case'), + isEmpty: z.boolean().describe('Whether the test case is empty (has no precondition and steps)'), + steps: z.array(testStepSchema).optional().describe('List of test case steps'), + tags: z.array(testTagSchema).optional().describe('List of test case tags'), + requirements: z.array(testRequirementSchema).optional().describe('Test case requirements'), + customFields: z + .record(z.string(), testCustomFieldValueSchema) + .optional() + .describe('Custom field values'), + templateTCaseId: z + .string() + .optional() + .describe('Corresponding template test case ID (only for filled test cases)'), + numFilledTCases: z + .number() + .optional() + .describe('Number of corresponding filled test cases (only for template test cases)'), + parameterValues: z + .array(testParameterValuesSchema) + .optional() + .describe('Parameter substitutions for filled test cases (only for template test cases)'), + filledTCaseTitleSuffixParams: z + .array(z.string()) + .optional() + .describe( + 'Parameter names whose substituted values are appended to each filled test case title for disambiguation (only for template test cases).' + ), + createdAt: z.string().describe('Test case creation time (ISO 8601)'), + updatedAt: z.string().describe('Test case last-update time (ISO 8601)'), +}) +export type GetTestCaseOutput = z.infer + +export const listTestCasesInputSchema = z.object({ + projectCode: projectCodeSchema, + page: z.number().optional().describe('Page number for pagination'), + limit: z.number().optional().default(20).describe('Number of items per page'), + sortField: z + .enum([ + 'id', + 'seq', + 'folder_id', + 'author_id', + 'pos', + 'title', + 'priority', + 'created_at', + 'updated_at', + 'legacy_id', + ]) + .optional() + .describe('Field to sort results by'), + sortOrder: z + .enum(['asc', 'desc']) + .optional() + .describe('Sort direction (ascending or descending). Requires sortField.'), + search: z.string().optional().describe('Search term to filter test cases'), + include: z + .array( + z.enum([ + 'precondition', + 'steps', + 'tags', + 'project', + 'folder', + 'path', + 'requirements', + 'customFields', + 'parameterValues', + ]) + ) + .optional() + .describe('Related data to include in the response (not present by default)'), + folders: z.array(z.number()).optional().describe('Filter by folder IDs'), + tags: z.array(z.number()).optional().describe('Filter by tag IDs'), + types: z + .array(z.enum(['standalone', 'template', 'filled'])) + .optional() + .describe('Filter by test case type'), + priorities: z + .array(z.enum(['high', 'medium', 'low'])) + .optional() + .describe('Filter by priority levels'), + templateTCaseIds: z + .array(z.string()) + .optional() + .describe('Filter filled test cases by their parent template test case IDs'), + requirementIds: z.array(z.string()).optional().describe('Filter by requirement IDs'), + customFields: z + .record(z.string(), z.array(z.string())) + .optional() + .describe( + 'Filter by custom field values. Keys are custom field system names (without the `cf_` prefix); values are arrays of allowed values.' + ), + draft: z.boolean().optional().describe('Filter draft vs published test cases'), +}) +export type ListTestCasesInput = z.infer + +export const listTestCasesOutputSchema = z.object({ + total: z.number().describe('Total number of filtered test cases'), + page: z.number().describe('Current page number'), + limit: z.number().describe('Number of test cases per page'), + data: z + .array( + getTestCaseOutputSchema.extend({ + folder: testFolderSchema.optional().describe('Folder where the test case is placed'), + path: z.array(testFolderSchema).optional().describe('Folder path to the test case'), + project: getProjectOutputSchema.optional().describe('Project the test case belongs to'), + }) + ) + .nullable() + .describe('List of test cases'), +}) +export type ListTestCasesOutput = z.infer + +export const createTestCaseInputSchema = z.object({ + projectCode: projectCodeSchema, + title: z + .string() + .min(1, 'Title must be at least 1 character') + .max(511, 'Title must be at most 511 characters') + .describe( + 'Test case title. In template test cases, may contain `${var}` placeholders that are substituted via `parameterValues` to produce filled test case titles.' + ), + type: z + .enum(['standalone', 'template']) + .describe( + "Type of test case. A 'template' test case uses `${var}` placeholders in its title, precondition, and step fields; each entry in `parameterValues` substitutes those placeholders to generate one filled test case." + ), + folderId: z + .number() + .int() + .positive('Folder ID must be a positive integer') + .describe( + 'ID of the folder where the test case will be placed. Use upsert_folders tool to create new folders or get existing folders.' + ), + priority: z.enum(['high', 'medium', 'low']).describe('Test case priority'), + pos: z + .number() + .int() + .min(0, 'Position must be non-negative') + .optional() + .describe('Position within the folder (0-based index)'), + comment: z + .string() + .optional() + .describe( + 'Test case precondition (HTML format). DEPRECATED — prefer the `precondition` object' + ), + precondition: testCasePreconditionInputSchema + .optional() + .describe( + 'Test case precondition: either `{sharedPreconditionId}` to reference a shared one, or `{text}` for a standalone precondition' + ), + steps: z.array(testCaseStepInputSchema).optional().describe('List of test case steps'), + tags: z + .array(z.string().max(255, 'Tag title must be at most 255 characters')) + .optional() + .describe('List of tag titles'), + requirements: z + .array(testCaseRequirementInputSchema) + .optional() + .describe('Test case requirements'), + links: z + .array(testCaseLinkInputSchema) + .optional() + .describe('Additional links relevant to the test case'), + customFields: z + .record(z.string(), testCaseCustomFieldInputSchema) + .optional() + .describe( + 'Custom field values. Keys are custom field system names. Custom fields must exist in the project (created via the web UI).' + ), + parameterValues: z + .array( + z.object({ + values: z + .record(z.string(), z.string()) + .describe( + 'Map of parameter name to substitution value. Keys are the names inside `${...}` placeholders (without the `${}` wrapper); values must be simple strings. Each entry in the outer array produces one filled test case.' + ), + }) + ) + .optional() + .describe( + 'One set of parameter substitutions per filled test case to generate from the template. Applies only when `type` is `template`; ignored for standalone test cases. A placeholder with no matching key is left untouched (e.g., `${action}` with no `action` value stays literal).' + ), + filledTCaseTitleSuffixParams: z + .array(z.string()) + .optional() + .describe( + 'Parameter names whose substituted values are appended to each filled test case title for disambiguation (e.g., pass `["env"]` to suffix titles with the `env` value).' + ), + isDraft: z.boolean().optional().default(false).describe('Whether to create as draft'), +}) +export type CreateTestCaseInput = z.infer + +export const createTestCaseOutputSchema = z.object({ + id: z.string().describe('Unique identifier of the created test case'), + seq: z.number().describe('Sequence number of the test case in the project'), +}) +export type CreateTestCaseOutput = z.infer + +export const updateTestCaseInputSchema = z.object({ + projectCode: projectCodeSchema, + tcaseOrLegacyId: z + .string() + .describe( + 'Test case identifier (can be one of test case UUID, sequence or legacy ID). Note: when the target is a `filled` test case, only `priority` can be updated directly; to change other fields, update the parent template or use `parameterValues`.' + ), + title: z + .string() + .min(1, 'Title must be at least 1 character') + .max(511, 'Title must be at most 511 characters') + .optional() + .describe( + 'Test case title. In template test cases, may contain `${var}` placeholders that are substituted via `parameterValues` to produce filled test case titles.' + ), + priority: z.enum(['high', 'medium', 'low']).optional().describe('Test case priority'), + comment: z + .string() + .optional() + .describe( + 'Test case precondition (HTML format). DEPRECATED — prefer the `precondition` object' + ), + precondition: testCasePreconditionInputSchema + .optional() + .describe( + 'Test case precondition: either `{sharedPreconditionId}` to reference a shared one, or `{text}` for a standalone precondition' + ), + isDraft: z + .boolean() + .optional() + .describe('To publish a draft test case. A published test case cannot be converted to draft'), + steps: z.array(testCaseStepInputSchema).optional().describe('List of test case steps'), + tags: z + .array(z.string().max(255, 'Tag title must be at most 255 characters')) + .optional() + .describe('List of tag titles'), + requirements: z + .array(testCaseRequirementInputSchema) + .optional() + .describe('Test case requirements'), + links: z + .array(testCaseLinkInputSchema) + .optional() + .describe('Additional links relevant to the test case'), + customFields: z + .record(z.string(), testCaseCustomFieldInputSchema) + .optional() + .describe( + 'Custom field values to update. Only the specified keys are modified; others remain unchanged.' + ), + parameterValues: z + .array( + z.object({ + tcaseId: z + .string() + .optional() + .describe( + 'ID of an existing filled test case to update. Omit to generate a new filled test case from the template.' + ), + values: z + .record(z.string(), z.string()) + .describe( + 'Map of parameter name to substitution value. Keys are the names inside `${...}` placeholders (without the `${}` wrapper); values must be simple strings.' + ), + }) + ) + .optional() + .describe( + 'Full replacement of the template test case parameter values. Applies only when the target is a template test case: entries with `tcaseId` update the corresponding filled test case; entries without `tcaseId` generate a new one; any existing filled test case whose `tcaseId` is not present in this array will be deleted. To preserve existing filled test cases while appending, include their current `{tcaseId, values}` pairs.' + ), + filledTCaseTitleSuffixParams: z + .array(z.string()) + .optional() + .describe( + 'Parameter names whose substituted values are appended to each filled test case title for disambiguation (e.g., pass `["env"]` to suffix titles with the `env` value). Applies only to template test cases.' + ), +}) +export type UpdateTestCaseInput = z.infer + +export const updateTestCaseOutputSchema = z.object({ + message: z.string().describe('Success message'), +}) +export type UpdateTestCaseOutput = z.infer + +export const listFoldersInputSchema = z.object({ + projectCode: projectCodeSchema, + page: z.number().optional().describe('Page number for pagination'), + limit: z.number().optional().default(100).describe('Number of items per page'), + sortField: z + .enum(['id', 'project_id', 'title', 'pos', 'parent_id', 'created_at', 'updated_at']) + .optional() + .describe('Field to sort results by'), + sortOrder: z + .enum(['asc', 'desc']) + .optional() + .describe('Sort direction (ascending or descending)'), +}) +export type ListFoldersInput = z.infer + +export const listFoldersOutputSchema = z.object({ + total: z.number().describe('Total number of items available'), + page: z.number().describe('Current page number'), + limit: z.number().describe('Number of items per page'), + data: z.array(testFolderSchema).nullable().describe('List of folders'), +}) +export type ListFoldersOutput = z.infer + +export const upsertFoldersInputSchema = z.object({ + projectCode: projectCodeSchema, + folders: z + .array( + z.object({ + path: z + .array(z.string().min(1).max(255)) + .min(1) + .describe('Array of folder names representing the hierarchy'), + comment: z + .string() + .nullable() + .describe( + 'Additional notes or description for the leaf folder (HTML format). Set null to keep existing comment of an existing folder.' + ), + }) + ) + .min(1) + .describe('Array of folder requests to create or update'), +}) +export type UpsertFoldersInput = z.infer + +export const upsertFoldersOutputSchema = z.object({ + ids: z + .array(z.array(z.number())) + .nullable() + .describe('One folder-ID array per input folder, representing the full path hierarchy'), +}) +export type UpsertFoldersOutput = z.infer + +export const listTestCasesTagsInputSchema = z.object({ + projectCode: projectCodeSchema, + sortField: z.enum(['created_at', 'title']).optional().describe('Field to sort results by'), + sortOrder: z + .enum(['asc', 'desc']) + .optional() + .describe('Sort direction (ascending or descending). Requires sortField.'), + include: z + .enum(['tcaseCount']) + .optional() + .describe('Include optional fields like the number of test cases using each tag'), +}) +export type ListTestCasesTagsInput = z.infer + +export const listTestCasesTagsOutputSchema = z.object({ + tags: z + .array( + testTagSchema.extend({ + tcaseCount: z + .number() + .optional() + .describe('Number of test cases using this tag (only when `tcaseCount` is included)'), + }) + ) + .nullable() + .describe('List of tags defined in the project'), +}) +export type ListTestCasesTagsOutput = z.infer + +export const listCustomFieldsInputSchema = z.object({ + projectCode: projectCodeSchema, +}) +export type ListCustomFieldsInput = z.infer + +export const listCustomFieldsOutputSchema = z.object({ + customFields: z + .array(customFieldSchema) + .nullable() + .describe('List of custom fields defined in the project'), +}) +export type ListCustomFieldsOutput = z.infer + +export const listRequirementsInputSchema = z.object({ + projectCode: projectCodeSchema, + sortField: z.enum(['created_at', 'text']).optional().describe('Field to sort results by'), + sortOrder: z + .enum(['asc', 'desc']) + .optional() + .describe('Sort direction (ascending or descending). Requires sortField.'), + include: z + .enum(['tcaseCount']) + .optional() + .describe('Include optional fields like the number of test cases linked to each requirement'), +}) +export type ListRequirementsInput = z.infer + +export const listRequirementsOutputSchema = z.object({ + requirements: z + .array(requirementListItemSchema) + .nullable() + .describe('List of requirements defined in the project'), +}) +export type ListRequirementsOutput = z.infer + +export const getSharedPreconditionInputSchema = z.object({ + projectCode: projectCodeSchema, + sharedPreconditionId: z + .number() + .int() + .positive('Shared precondition ID must be a positive integer') + .describe('Identifier of the shared precondition'), +}) +export type GetSharedPreconditionInput = z.infer + +export const getSharedPreconditionOutputSchema = testPreconditionSchema +export type GetSharedPreconditionOutput = z.infer + +export const listSharedPreconditionsInputSchema = z.object({ + projectCode: projectCodeSchema, + sortField: z.enum(['created_at', 'title']).optional().describe('Field to sort results by'), + sortOrder: z + .enum(['asc', 'desc']) + .optional() + .describe('Sort direction (ascending or descending). Requires sortField.'), + include: z + .enum(['tcaseCount']) + .optional() + .describe( + 'Include optional fields like the number of test cases referencing each precondition' + ), +}) +export type ListSharedPreconditionsInput = z.infer + +export const listSharedPreconditionsOutputSchema = z.object({ + sharedPreconditions: z + .array( + getSharedPreconditionOutputSchema.extend({ + tcaseCount: z + .number() + .optional() + .describe( + 'Number of test cases using this shared precondition (only when `tcaseCount` is included)' + ), + }) + ) + .nullable() + .describe('List of shared preconditions defined in the project'), +}) +export type ListSharedPreconditionsOutput = z.infer + +export const getSharedStepInputSchema = z.object({ + projectCode: projectCodeSchema, + sharedStepId: z + .number() + .int() + .positive('Shared step ID must be a positive integer') + .describe('Identifier of the shared step'), +}) +export type GetSharedStepInput = z.infer + +export const getSharedStepOutputSchema = testStepSchema +export type GetSharedStepOutput = z.infer + +export const listSharedStepsInputSchema = z.object({ + projectCode: projectCodeSchema, + sortField: z.enum(['created_at', 'title']).optional().describe('Field to sort results by'), + sortOrder: z + .enum(['asc', 'desc']) + .optional() + .describe('Sort direction (ascending or descending). Requires sortField.'), + include: z + .enum(['tcaseCount']) + .optional() + .describe('Include optional fields like the number of test cases referencing each shared step'), +}) +export type ListSharedStepsInput = z.infer + +export const listSharedStepsOutputSchema = z.object({ + sharedSteps: z + .array( + z.object({ + ...testStepSchemaShape, + tcaseCount: z + .number() + .optional() + .describe( + 'Number of test cases using this shared step (only when `tcaseCount` is included)' + ), + }) + ) + .nullable() + .describe('List of shared steps defined in the project'), +}) +export type ListSharedStepsOutput = z.infer diff --git a/src/tools/customFields.ts b/src/tools/customFields.ts index 8b6148d..e588aba 100644 --- a/src/tools/customFields.ts +++ b/src/tools/customFields.ts @@ -1,50 +1,33 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import type { CustomFieldsResponse } from '../types.js' -import { projectCodeSchema } from '../schemas.js' +import { ApiError, apiQuery } from '../api.js' +import { listCustomFieldsInputSchema, listCustomFieldsOutputSchema } from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'list_custom_fields', - "List all custom fields available for a project. This endpoint is useful when creating or updating test cases that include custom field values. Custom fields allow you to extend test cases with additional metadata specific to your organization's needs.", { - projectCode: projectCodeSchema, + title: 'List Custom Fields', + description: + "List all custom fields available for a project. Useful when creating or updating test cases with custom field values — use the `systemName` property as the key on the `customFields` parameter of create_test_case / update_test_case. Only `enabled` fields should be used. Custom fields allow you to extend test cases with additional metadata specific to your organization's needs.", + inputSchema: listCustomFieldsInputSchema.shape, + outputSchema: listCustomFieldsOutputSchema.shape, }, - async ({ projectCode }: { projectCode: string }) => { + async ({ projectCode }) => { try { - const response = await axios.get( - `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/custom-field`, - { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - } - ) - + const result = await apiQuery(`/api/public/v0/project/${projectCode}/custom-field`, { + schema: listCustomFieldsOutputSchema, + }) return { - content: [ - { - type: 'text', - text: JSON.stringify(response.data.customFields), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - switch (error.response?.status) { - case 404: - throw new Error(`Project with code '${projectCode}' not found.`) - case 401: - throw new Error('Invalid or missing API key') - case 403: - throw new Error('Insufficient permissions or suspended tenant') - default: - throw new Error( - `Failed to fetch custom fields: ${error.response?.data?.message || error.message}` - ) - } + if (error instanceof ApiError) { + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found.`) + if (error.status === 500) throw new Error('Internal server error') + throw new Error(`Failed to fetch custom fields: ${error.message}`) } throw error } diff --git a/src/tools/folders.ts b/src/tools/folders.ts index 4b6a0d5..203e934 100644 --- a/src/tools/folders.ts +++ b/src/tools/folders.ts @@ -1,205 +1,99 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import { z } from 'zod' -import type { - TestFolderListResponse, - BulkUpsertFoldersRequest, - BulkUpsertFoldersResponse, -} from '../types.js' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import { projectCodeSchema } from '../schemas.js' +import { ApiError, apiQuery } from '../api.js' +import { + listFoldersInputSchema, + listFoldersOutputSchema, + type UpsertFoldersInput, + upsertFoldersInputSchema, + upsertFoldersOutputSchema, +} from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'list_folders', - 'List folders for test cases within a specific QA Sphere project. Allows pagination and sorting.', { - projectCode: projectCodeSchema, - page: z.number().optional().describe('Page number for pagination'), - limit: z.number().optional().default(100).describe('Number of items per page'), - sortField: z - .enum(['id', 'project_id', 'title', 'pos', 'parent_id', 'created_at', 'updated_at']) - .optional() - .describe('Field to sort results by'), - sortOrder: z - .enum(['asc', 'desc']) - .optional() - .describe('Sort direction (ascending or descending)'), + title: 'List Folders', + description: + 'List folders for test cases within a specific QA Sphere project. Allows pagination and sorting.', + inputSchema: listFoldersInputSchema.shape, + outputSchema: listFoldersOutputSchema.shape, }, - async ({ projectCode, page, limit = 100, sortField, sortOrder }) => { + async ({ projectCode, page, limit, sortField, sortOrder }) => { try { - // Build query parameters - const params = new URLSearchParams() - - if (page !== undefined) params.append('page', page.toString()) - if (limit !== undefined) params.append('limit', limit.toString()) - if (sortField) params.append('sortField', sortField) - if (sortOrder) params.append('sortOrder', sortOrder) - - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/tcase/folders` - - const response = await axios.get(url, { - params, - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, + const result = await apiQuery(`/api/public/v0/project/${projectCode}/tcase/folders`, { + schema: listFoldersOutputSchema, + query: { page, limit, sortField, sortOrder }, }) - - const folderList = response.data - - // Basic validation of response - if (!folderList || !Array.isArray(folderList.data)) { - throw new Error('Invalid response: expected a list of folders') - } - - // check for other fields from TestFolderListResponse - if ( - folderList.total === undefined || - folderList.page === undefined || - folderList.limit === undefined - ) { - throw new Error('Invalid response: missing required fields (total, page, or limit)') - } - - // if array is non-empty check if object has id and title fields - if (folderList.data.length > 0) { - const firstFolder = folderList.data[0] - if (firstFolder.id === undefined || !firstFolder.title) { - throw new Error('Invalid folder data: missing required fields (id or title)') - } - } - return { - content: [ - { - type: 'text', - text: JSON.stringify(folderList), // Use standard stringify, no special mapping needed for folders - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - if (error.response?.status === 404) { + if (error instanceof ApiError) { + if (error.status === 404) { throw new Error(`Project with code '${projectCode}' not found.`) } - throw new Error( - `Failed to fetch test case folders: ${error.response?.data?.message || error.message}` - ) + throw new Error(`Failed to fetch test case folders: ${error.message}`) } throw error } } ) - server.tool( + server.registerTool( 'upsert_folders', - "Creates or updates multiple folders in a single request using folder path hierarchies. Automatically creates nested folder structures and updates existing folders' comments. Returns an array of folder ID arrays, each representing the full folder path hierarchy as an array of folder IDs.", { - projectCode: projectCodeSchema, - folders: z - .array( - z.object({ - path: z - .array(z.string().min(1).max(255)) - .min(1) - .describe('Array of folder names representing the hierarchy'), - comment: z - .string() - .optional() - .describe( - 'Additional notes or description for the leaf folder (HTML format). Set null or omit to keep existing comment of an existing folder.' - ), - }) - ) - .min(1) - .describe('Array of folder requests to create or update'), + title: 'Upsert Folders', + description: + "Creates or updates multiple folders in a single request using folder path hierarchies. Automatically creates nested folder structures and updates existing folders' comments. Returns an array of folder ID arrays, each representing the full folder path hierarchy as an array of folder IDs.", + inputSchema: upsertFoldersInputSchema.shape, + outputSchema: upsertFoldersOutputSchema.shape, }, async ({ projectCode, folders }) => { try { - // Validate folder paths - for (const folder of folders) { - for (const folderName of folder.path) { - if (folderName.includes('/')) { - throw new Error('Folder names cannot contain forward slash (/) characters') - } - if (folderName.trim() === '') { - throw new Error('Folder names cannot be empty strings') - } - } - } - - const requestBody: BulkUpsertFoldersRequest = { - folders: folders.map((folder) => ({ - path: folder.path, - comment: folder.comment, - })), - } - - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/tcase/folder/bulk` - - const response = await axios.post(url, requestBody, { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, + const body: Omit = { folders } + const result = await apiQuery(`/api/public/v0/project/${projectCode}/tcase/folder/bulk`, { + schema: upsertFoldersOutputSchema, + method: 'POST', + body, }) - const result = response.data - - // Basic validation of response - if (!result || !Array.isArray(result.ids)) { - throw new Error('Invalid response: expected an array of folder ID arrays') - } - // Validate that the number of returned ID arrays matches the number of input folders - if (result.ids.length !== folders.length) { + const resultIds = result.ids ?? [] + if (resultIds.length !== folders.length) { throw new Error( - `Invalid response: expected ${folders.length} folder ID arrays, got ${result.ids.length}` + `Invalid response: expected ${folders.length} folder ID arrays, got ${resultIds.length}` ) } // Validate that each ID array has the correct length - for (let i = 0; i < result.ids.length; i++) { - const idArray = result.ids[i] + for (let i = 0; i < resultIds.length; i++) { + const idArray = resultIds[i] const expectedLength = folders[i].path.length - if (!Array.isArray(idArray) || idArray.length !== expectedLength) { + if (idArray.length !== expectedLength) { throw new Error( - `Invalid response: folder ${i} expected ${expectedLength} IDs, got ${idArray?.length || 0}` + `Invalid response: folder ${i} expected ${expectedLength} IDs, got ${idArray.length}` ) } } return { - content: [ - { - type: 'text', - text: JSON.stringify(result, null, 2), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - if (error.response?.status === 400) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) { throw new Error( - `Invalid request: ${error.response?.data?.message || 'Invalid request body or folder path format'}` + `Invalid request: ${message || 'Invalid request body or folder path format'}` ) } - if (error.response?.status === 401) { - throw new Error('Invalid or missing API key') - } - if (error.response?.status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (error.response?.status === 404) { - throw new Error(`Project with code '${projectCode}' not found`) - } - if (error.response?.status === 500) { - throw new Error('Internal server error') - } - throw new Error( - `Failed to bulk upsert folders: ${error.response?.data?.message || error.message}` - ) + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found`) + if (error.status === 500) throw new Error('Internal server error') + throw new Error(`Failed to bulk upsert folders: ${message}`) } throw error } diff --git a/src/tools/index.ts b/src/tools/index.ts index 6472474..32f7cfc 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,20 +1,20 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' +import { registerTools as registerCustomFieldsTools } from './customFields.js' +import { registerTools as registerFoldersTools } from './folders.js' import { registerTools as registerProjectsTools } from './projects.js' -import { registerTools as registerTestCasesTools } from './tcases.js' -import { registerTools as registerTestFoldersTools } from './folders.js' -import { registerTools as registerTestTagsTools } from './tags.js' +import { registerTools as registerRequirementsTools } from './requirements.js' import { registerTools as registerSharedPreconditionsTools } from './shared-preconditions.js' import { registerTools as registerSharedStepsTools } from './shared-steps.js' -import { registerTools as registerCustomFieldsTools } from './customFields.js' -import { registerTools as registerRequirementsTools } from './requirements.js' +import { registerTools as registerTagsTools } from './tags.js' +import { registerTools as registerTCasesTools } from './tcases.js' export const registerTools = (server: McpServer) => { + registerCustomFieldsTools(server) + registerFoldersTools(server) registerProjectsTools(server) - registerTestCasesTools(server) - registerTestFoldersTools(server) - registerTestTagsTools(server) + registerRequirementsTools(server) registerSharedPreconditionsTools(server) registerSharedStepsTools(server) - registerCustomFieldsTools(server) - registerRequirementsTools(server) + registerTagsTools(server) + registerTCasesTools(server) } diff --git a/src/tools/projects.ts b/src/tools/projects.ts index 552be2d..ad6c4c8 100644 --- a/src/tools/projects.ts +++ b/src/tools/projects.ts @@ -1,84 +1,69 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import type { Project } from '../types.js' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import { projectCodeSchema } from '../schemas.js' +import { ApiError, apiQuery } from '../api.js' +import { QASPHERE_TENANT_URL } from '../config.js' +import { + getProjectInputSchema, + getProjectOutputSchema, + listProjectsInputSchema, + listProjectsOutputSchema, +} from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'get_project', - `Get a project information from QA Sphere using a project code (e.g., BDI). You can extract PROJECT_CODE from URLs ${QASPHERE_TENANT_URL}/project/%PROJECT_CODE%/...`, { - projectCode: projectCodeSchema, + title: 'Get Project', + description: `Get a project information from QA Sphere using a project code (e.g., BDI). You can extract PROJECT_CODE from URLs ${QASPHERE_TENANT_URL}/project/%PROJECT_CODE%/...`, + inputSchema: getProjectInputSchema.shape, + outputSchema: getProjectOutputSchema.shape, }, - async ({ projectCode }: { projectCode: string }) => { + async ({ projectCode }) => { try { - const response = await axios.get( - `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}`, - { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - } - ) - - const projectData = response.data - if (!projectData.id || !projectData.title) { - throw new Error('Invalid project data: missing required fields (id or title)') - } - + const project = await apiQuery(`/api/public/v0/project/${projectCode}`, { + schema: getProjectOutputSchema, + }) return { - content: [{ type: 'text', text: JSON.stringify(projectData) }], + content: [{ type: 'text', text: JSON.stringify(project) }], + structuredContent: project, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - if (error.response?.status === 404) { - throw new Error(`Project with code '${projectCode}' not found.`) - } - throw new Error( - `Failed to fetch project: ${error.response?.data?.message || error.message}` - ) + if (error instanceof ApiError) { + const message = error.message + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found.`) + if (error.status === 500) throw new Error('Internal server error while fetching project') + throw new Error(`Failed to fetch project: ${message}`) } throw error } } ) - server.tool( + server.registerTool( 'list_projects', - 'Get a list of all projects from current QA Sphere TMS account (qasphere.com)', - {}, + { + title: 'List Projects', + description: 'Get a list of all projects from current QA Sphere TMS account (qasphere.com)', + inputSchema: listProjectsInputSchema.shape, + outputSchema: listProjectsOutputSchema.shape, + }, async () => { try { - const response = await axios.get(`${QASPHERE_TENANT_URL}/api/public/v0/project`, { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, + const projects = await apiQuery('/api/public/v0/project', { + schema: listProjectsOutputSchema, }) - - const projectsData = response.data - if (!Array.isArray(projectsData.projects)) { - throw new Error('Invalid response: expected an array of projects') - } - - // if array is non-empty check if object has id and title fields - if (projectsData.projects.length > 0) { - const firstProject = projectsData.projects[0] - if (!firstProject.id || !firstProject.title) { - throw new Error('Invalid project data: missing required fields (id or title)') - } - } - return { - content: [{ type: 'text', text: JSON.stringify(projectsData) }], + content: [{ type: 'text', text: JSON.stringify(projects) }], + structuredContent: projects, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - throw new Error( - `Failed to fetch projects: ${error.response?.data?.message || error.message}` - ) + if (error instanceof ApiError) { + const message = error.message + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 500) throw new Error('Internal server error while fetching projects') + throw new Error(`Failed to fetch projects: ${message}`) } throw error } diff --git a/src/tools/requirements.ts b/src/tools/requirements.ts index c5a235e..8746919 100644 --- a/src/tools/requirements.ts +++ b/src/tools/requirements.ts @@ -1,101 +1,36 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import { z, ZodError } from 'zod' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import { projectCodeSchema } from '../schemas.js' - -const requirementIntegrationLinkSchema = z.object({ - issueId: z.string(), - issueTitle: z.string(), - issueUrl: z.string(), - remoteLinkId: z.number(), -}) - -const requirementSchema = z.object({ - id: z.string(), - text: z.string(), - url: z.string(), - integrationLink: requirementIntegrationLinkSchema.optional(), - tcaseCount: z.number().optional(), -}) - -const requirementsListResponseSchema = z.object({ - requirements: z.array(requirementSchema), -}) +import { ApiError, apiQuery } from '../api.js' +import { listRequirementsInputSchema, listRequirementsOutputSchema } from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'list_requirements', - 'List requirements linked to test cases in a project. Requirements are references to external documentation or specifications (like Jira issues) that test cases verify. Use this tool to find requirement IDs when you need to filter test cases by requirement using list_test_cases with requirementIds parameter.', { - projectCode: projectCodeSchema, - sortField: z.enum(['created_at', 'text']).optional().describe('Field to sort results by'), - sortOrder: z - .enum(['asc', 'desc']) - .optional() - .describe('Sort direction (ascending or descending). Requires sortField.'), - include: z - .enum(['tcaseCount']) - .optional() - .describe( - 'Include optional fields like the number of test cases linked to each requirement' - ), + title: 'List Requirements', + description: + 'List requirements linked to test cases in a project. Requirements are references to external documentation or specifications (like Jira issues) that test cases verify. Use this tool to find requirement IDs when you need to filter test cases by requirement using list_test_cases with the `requirementIds` parameter.', + inputSchema: listRequirementsInputSchema.shape, + outputSchema: listRequirementsOutputSchema.shape, }, async ({ projectCode, sortField, sortOrder, include }) => { try { - if (sortOrder && !sortField) { - throw new Error('sortOrder can only be specified when sortField is provided.') - } - - const params = new URLSearchParams() - if (sortField) params.append('sortField', sortField) - if (sortOrder) params.append('sortOrder', sortOrder) - if (include) params.append('include', include) - - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/requirement` - - const response = await axios.get(url, { - params, - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, + const result = await apiQuery(`/api/public/v0/project/${projectCode}/requirement`, { + schema: listRequirementsOutputSchema, + query: { sortField, sortOrder, include }, }) - - const requirementsResponse = requirementsListResponseSchema.parse(response.data) - return { - content: [ - { - type: 'text', - text: JSON.stringify(requirementsResponse), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (error instanceof ZodError) { - throw new Error(`Invalid response data: ${error.errors.map((e) => e.message).join(', ')}`) - } - if (axios.isAxiosError(error)) { - const status = error.response?.status - const message = error.response?.data?.message || error.message - - if (status === 400) { - throw new Error(`Invalid request data: ${message}`) - } - if (status === 401) { - throw new Error('Invalid or missing API key') - } - if (status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (status === 404) { - throw new Error(`Project with code '${projectCode}' not found.`) - } - if (status === 500) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) throw new Error(`Invalid request data: ${message}`) + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found.`) + if (error.status === 500) throw new Error('Internal server error while fetching requirements') - } - throw new Error(`Failed to fetch requirements: ${message}`) } throw error diff --git a/src/tools/shared-preconditions.ts b/src/tools/shared-preconditions.ts index eaaed7b..e3112f4 100644 --- a/src/tools/shared-preconditions.ts +++ b/src/tools/shared-preconditions.ts @@ -1,99 +1,45 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import { z } from 'zod' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import { projectCodeSchema } from '../schemas.js' -import type { SharedPrecondition, SharedPreconditionListResponse } from '../types.js' - -const sharedPreconditionIdSchema = z - .number() - .int() - .positive('Shared precondition ID must be a positive integer') - .describe('Identifier of the shared precondition') +import { ApiError, apiQuery } from '../api.js' +import { + getSharedPreconditionInputSchema, + getSharedPreconditionOutputSchema, + listSharedPreconditionsInputSchema, + listSharedPreconditionsOutputSchema, +} from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'list_shared_preconditions', - 'List reusable shared preconditions for a project. Supports sorting by title or creation date and can include test case usage counts.', { - projectCode: projectCodeSchema, - sortField: z.enum(['created_at', 'title']).optional().describe('Field to sort results by'), - sortOrder: z - .enum(['asc', 'desc']) - .optional() - .describe('Sort direction (ascending or descending). Requires sortField.'), - include: z - .enum(['tcaseCount']) - .optional() - .describe( - 'Include optional fields like the number of test cases referencing each precondition' - ), + title: 'List Shared Preconditions', + description: + 'List reusable shared preconditions for a project. Supports sorting by title or creation date and can include test case usage counts. Use the returned `id` as `sharedPreconditionId` when referencing a shared precondition from create_test_case / update_test_case.', + inputSchema: listSharedPreconditionsInputSchema.shape, + outputSchema: listSharedPreconditionsOutputSchema.shape, }, async ({ projectCode, sortField, sortOrder, include }) => { try { - if (sortOrder && !sortField) { - throw new Error('sortOrder can only be specified when sortField is provided.') - } - - const params = new URLSearchParams() - if (sortField) params.append('sortField', sortField) - if (sortOrder) params.append('sortOrder', sortOrder) - if (include) params.append('include', include) - - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/shared-precondition` - - const response = await axios.get(url, { - params, - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - }) - - const sharedPreconditions = response.data - - if (!Array.isArray(sharedPreconditions)) { - throw new Error('Invalid response: expected an array of shared preconditions') - } - - if (sharedPreconditions.length > 0) { - const first = sharedPreconditions[0] - if (first.id === undefined || !first.title || !first.text) { - throw new Error( - 'Invalid shared precondition data: missing required fields (id, title, or text)' - ) + const sharedPreconditions = await apiQuery( + `/api/public/v0/project/${projectCode}/shared-precondition`, + { + schema: listSharedPreconditionsOutputSchema.shape.sharedPreconditions, + query: { sortField, sortOrder, include }, } - } - + ) + const result = { sharedPreconditions } return { - content: [ - { - type: 'text', - text: JSON.stringify(sharedPreconditions), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const status = error.response?.status - const message = error.response?.data?.message || error.message - - if (status === 400) { - throw new Error(`Invalid request data: ${message}`) - } - if (status === 401) { - throw new Error('Invalid or missing API key') - } - if (status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (status === 404) { - throw new Error(`Project with code '${projectCode}' not found.`) - } - if (status === 500) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) throw new Error(`Invalid request data: ${message}`) + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found.`) + if (error.status === 500) throw new Error('Internal server error while fetching shared preconditions') - } - throw new Error(`Failed to fetch shared preconditions: ${message}`) } throw error @@ -101,63 +47,36 @@ export const registerTools = (server: McpServer) => { } ) - server.tool( + server.registerTool( 'get_shared_precondition', - 'Fetch details for a single shared precondition by ID.', { - projectCode: projectCodeSchema, - sharedPreconditionId: sharedPreconditionIdSchema, + title: 'Get Shared Precondition', + description: 'Fetch details for a single shared precondition by ID.', + inputSchema: getSharedPreconditionInputSchema.shape, + outputSchema: getSharedPreconditionOutputSchema.shape, }, async ({ projectCode, sharedPreconditionId }) => { try { - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/shared-precondition/${sharedPreconditionId}` - - const response = await axios.get(url, { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - }) - - const sharedPrecondition = response.data - - if (!sharedPrecondition.id || !sharedPrecondition.title || !sharedPrecondition.text) { - throw new Error( - 'Invalid shared precondition data: missing required fields (id, title, or text)' - ) - } - + const result = await apiQuery( + `/api/public/v0/project/${projectCode}/shared-precondition/${sharedPreconditionId}`, + { schema: getSharedPreconditionOutputSchema } + ) return { - content: [ - { - type: 'text', - text: JSON.stringify(sharedPrecondition), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const status = error.response?.status - const message = error.response?.data?.message || error.message - - if (status === 400) { - throw new Error(`Invalid request data: ${message}`) - } - if (status === 401) { - throw new Error('Invalid or missing API key') - } - if (status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (status === 404) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) throw new Error(`Invalid request data: ${message}`) + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error( `Project or shared precondition not found: ${message || `Shared precondition ${sharedPreconditionId}`}` ) - } - if (status === 500) { + if (error.status === 500) throw new Error('Internal server error while fetching shared precondition') - } - throw new Error(`Failed to fetch shared precondition: ${message}`) } throw error diff --git a/src/tools/shared-steps.ts b/src/tools/shared-steps.ts index c5d38dd..52edc1d 100644 --- a/src/tools/shared-steps.ts +++ b/src/tools/shared-steps.ts @@ -1,99 +1,42 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import { z } from 'zod' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import { projectCodeSchema } from '../schemas.js' -import type { SharedStep, SharedStepListResponse } from '../types.js' - -const sharedStepIdSchema = z - .number() - .int() - .positive('Shared step ID must be a positive integer') - .describe('Identifier of the shared step') +import { ApiError, apiQuery } from '../api.js' +import { + getSharedStepInputSchema, + getSharedStepOutputSchema, + listSharedStepsInputSchema, + listSharedStepsOutputSchema, + testStepSchemaShape, +} from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'list_shared_steps', - 'List reusable shared steps for a project. Supports sorting by title or creation date and can include test case usage counts.', { - projectCode: projectCodeSchema, - sortField: z.enum(['created_at', 'title']).optional().describe('Field to sort results by'), - sortOrder: z - .enum(['asc', 'desc']) - .optional() - .describe('Sort direction (ascending or descending). Requires sortField.'), - include: z - .enum(['tcaseCount']) - .optional() - .describe( - 'Include optional fields like the number of test cases referencing each shared step' - ), + title: 'List Shared Steps', + description: + 'List reusable shared steps for a project. Supports sorting by title or creation date and can include test case usage counts. Use the returned `id` as `sharedStepId` when referencing a shared step from create_test_case / update_test_case.', + inputSchema: listSharedStepsInputSchema.shape, + outputSchema: listSharedStepsOutputSchema.shape, }, async ({ projectCode, sortField, sortOrder, include }) => { try { - if (sortOrder && !sortField) { - throw new Error('sortOrder can only be specified when sortField is provided.') - } - - const params = new URLSearchParams() - if (sortField) params.append('sortField', sortField) - if (sortOrder) params.append('sortOrder', sortOrder) - if (include) params.append('include', include) - - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/shared-step` - - const response = await axios.get(url, { - params, - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, + const result = await apiQuery(`/api/public/v0/project/${projectCode}/shared-step`, { + schema: listSharedStepsOutputSchema, + query: { sortField, sortOrder, include }, }) - - const sharedSteps = response.data - - if (!sharedSteps || !Array.isArray(sharedSteps.sharedSteps)) { - throw new Error('Invalid response: expected an object with a "sharedSteps" array') - } - - if (sharedSteps.sharedSteps.length > 0) { - const first = sharedSteps.sharedSteps[0] - if (!first.id || !first.title || !Array.isArray(first.subSteps)) { - throw new Error( - 'Invalid shared step data: missing required fields (id, title, or subSteps)' - ) - } - } - return { - content: [ - { - type: 'text', - text: JSON.stringify(sharedSteps), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const status = error.response?.status - const message = error.response?.data?.message || error.message - - if (status === 400) { - throw new Error(`Invalid request data: ${message}`) - } - if (status === 401) { - throw new Error('Invalid or missing API key') - } - if (status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (status === 404) { - throw new Error(`Project with code '${projectCode}' not found.`) - } - if (status === 500) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) throw new Error(`Invalid request data: ${message}`) + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found.`) + if (error.status === 500) throw new Error('Internal server error while fetching shared steps') - } - throw new Error(`Failed to fetch shared steps: ${message}`) } throw error @@ -101,63 +44,36 @@ export const registerTools = (server: McpServer) => { } ) - server.tool( + server.registerTool( 'get_shared_step', - 'Fetch details for a single shared step by ID.', { - projectCode: projectCodeSchema, - sharedStepId: sharedStepIdSchema, + title: 'Get Shared Step', + description: 'Fetch details for a single shared step by ID.', + inputSchema: getSharedStepInputSchema.shape, + outputSchema: testStepSchemaShape, }, async ({ projectCode, sharedStepId }) => { try { - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/shared-step/${sharedStepId}` - - const response = await axios.get(url, { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - }) - - const sharedStep = response.data - - if (!sharedStep.id || !sharedStep.title || !Array.isArray(sharedStep.subSteps)) { - throw new Error( - 'Invalid shared step data: missing required fields (id, title, or subSteps)' - ) - } - + const result = await apiQuery( + `/api/public/v0/project/${projectCode}/shared-step/${sharedStepId}`, + { schema: getSharedStepOutputSchema } + ) return { - content: [ - { - type: 'text', - text: JSON.stringify(sharedStep), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const status = error.response?.status - const message = error.response?.data?.message || error.message - - if (status === 400) { - throw new Error(`Invalid request data: ${message}`) - } - if (status === 401) { - throw new Error('Invalid or missing API key') - } - if (status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (status === 404) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) throw new Error(`Invalid request data: ${message}`) + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error( `Project or shared step not found: ${message || `Shared step ${sharedStepId}`}` ) - } - if (status === 500) { + if (error.status === 500) throw new Error('Internal server error while fetching shared step') - } - throw new Error(`Failed to fetch shared step: ${message}`) } throw error diff --git a/src/tools/tags.ts b/src/tools/tags.ts index 4e32af9..505f61e 100644 --- a/src/tools/tags.ts +++ b/src/tools/tags.ts @@ -1,59 +1,35 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import { projectCodeSchema } from '../schemas.js' +import { ApiError, apiQuery } from '../api.js' +import { listTestCasesTagsInputSchema, listTestCasesTagsOutputSchema } from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'list_test_cases_tags', - 'List all tags defined within a specific QA Sphere project.', { - projectCode: projectCodeSchema, + title: 'List Test Case Tags', + description: 'List all tags defined within a specific QA Sphere project.', + inputSchema: listTestCasesTagsInputSchema.shape, + outputSchema: listTestCasesTagsOutputSchema.shape, }, - async ({ projectCode }: { projectCode: string }) => { + async ({ projectCode, sortField, sortOrder, include }) => { try { - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/tag` - - const response = await axios.get<{ - tags: Array<{ id: number; title: string }> - }>(url, { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, + const result = await apiQuery(`/api/public/v0/project/${projectCode}/tag`, { + schema: listTestCasesTagsOutputSchema, + query: { sortField, sortOrder, include }, }) - - const tagsData = response.data - - // Basic validation of response - if (!tagsData || !Array.isArray(tagsData.tags)) { - throw new Error('Invalid response: expected an object with a "tags" array') - } - - // if array is non-empty check if object has id and title fields - if (tagsData.tags.length > 0) { - const firstTag = tagsData.tags[0] - if (firstTag.id === undefined || !firstTag.title) { - throw new Error('Invalid tag data: missing required fields (id or title)') - } - } - return { - content: [ - { - type: 'text', - text: JSON.stringify(tagsData), // Use standard stringify - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - if (error.response?.status === 404) { - throw new Error(`Project with identifier '${projectCode}' not found.`) - } - throw new Error( - `Failed to fetch project tags: ${error.response?.data?.message || error.message}` - ) + if (error instanceof ApiError) { + const message = error.message + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found.`) + if (error.status === 500) + throw new Error('Internal server error while fetching project tags') + throw new Error(`Failed to fetch project tags: ${message}`) } throw error } diff --git a/src/tools/tcases.ts b/src/tools/tcases.ts index 9b89d72..3de6de4 100644 --- a/src/tools/tcases.ts +++ b/src/tools/tcases.ts @@ -1,372 +1,155 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp' -import axios from 'axios' -import { z } from 'zod' -import type { - TestCase, - TestCasesListResponse, - CreateTestCaseRequest, - CreateTestCaseResponse, - UpdateTestCaseRequest, - MessageResponse, -} from '../types.js' -import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from '../config.js' -import { JSONStringify } from '../utils.js' -import { projectCodeSchema } from '../schemas.js' +import { ApiError, apiQuery } from '../api.js' +import { QASPHERE_TENANT_URL } from '../config.js' +import { + type CreateTestCaseInput, + createTestCaseInputSchema, + createTestCaseOutputSchema, + getTestCaseInputSchema, + getTestCaseOutputSchema, + listTestCasesInputSchema, + listTestCasesOutputSchema, + type UpdateTestCaseInput, + updateTestCaseInputSchema, + updateTestCaseOutputSchema, +} from '../schemas.js' export const registerTools = (server: McpServer) => { - server.tool( + server.registerTool( 'get_test_case', - `Get a test case from QA Sphere using a marker in the format PROJECT_CODE-SEQUENCE (e.g., BDI-123). You can use URLs like: ${QASPHERE_TENANT_URL}/project/%PROJECT_CODE%/tcase/%SEQUENCE%?any Extract %PROJECT_CODE% and %SEQUENCE% from the URL and use them as the marker.`, { - marker: testCaseMarkerSchema, + title: 'Get Test Case', + description: `Get a test case from QA Sphere using a marker in the format PROJECT_CODE-SEQUENCE (e.g., BDI-123). You can use URLs like: ${QASPHERE_TENANT_URL}/project/%PROJECT_CODE%/tcase/%SEQUENCE%?any Extract %PROJECT_CODE% and %SEQUENCE% from the URL and use them as the marker.`, + inputSchema: getTestCaseInputSchema.shape, + outputSchema: getTestCaseOutputSchema.shape, }, - async ({ marker }: { marker: string }) => { + async ({ marker }) => { try { - const [projectId, sequence] = marker.split('-') - const response = await axios.get( - `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectId}/tcase/${sequence}`, - { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - } - ) - - const testCase = response.data - - // Sanity check for required fields - if (!testCase.id || !testCase.title || !testCase.version) { - throw new Error('Invalid test case data: missing required fields (id, title, or version)') - } - + const [projectCode, sequence] = marker.split('-') + const testCase = await apiQuery(`/api/public/v0/project/${projectCode}/tcase/${sequence}`, { + schema: getTestCaseOutputSchema, + }) return { - content: [ - { - type: 'text', - text: JSONStringify(testCase, { - steps: { description: 'action', expected: 'expected_result' }, - }), - }, - ], + content: [{ type: 'text', text: JSON.stringify(testCase) }], + structuredContent: testCase, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - throw new Error( - `Failed to fetch test case: ${error.response?.data?.message || error.message}` - ) + if (error instanceof ApiError) { + const message = error.message + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Test case '${marker}' not found.`) + if (error.status === 500) + throw new Error('Internal server error while fetching test case') + throw new Error(`Failed to fetch test case: ${message}`) } throw error } } ) - server.tool( + server.registerTool( 'list_test_cases', - 'List test cases from a project in QA Sphere. Supports pagination and various filtering options. Usually it makes sense to call get_project tool first to get the project context.', { - projectCode: projectCodeSchema, - page: z.number().optional().describe('Page number for pagination'), - limit: z.number().optional().default(20).describe('Number of items per page'), - sortField: z - .enum([ - 'id', - 'seq', - 'folder_id', - 'author_id', - 'pos', - 'title', - 'priority', - 'created_at', - 'updated_at', - 'legacy_id', - ]) - .optional() - .describe('Field to sort results by'), - sortOrder: z - .enum(['asc', 'desc']) - .optional() - .describe('Sort direction (ascending or descending)'), - search: z.string().optional().describe('Search term to filter test cases'), - include: z - .array(z.enum(['steps', 'tags', 'project', 'folder', 'path', 'requirements'])) - .optional() - .describe('Related data to include in the response'), - folders: z.array(z.number()).optional().describe('Filter by folder IDs'), - tags: z.array(z.number()).optional().describe('Filter by tag IDs'), - priorities: z - .array(z.enum(['high', 'medium', 'low'])) - .optional() - .describe('Filter by priority levels'), - draft: z.boolean().optional().describe('Filter draft vs published test cases'), - requirementIds: z - .array(z.string()) - .optional() - .describe( - 'Filter by requirement IDs (OR logic - returns test cases linked to any of the specified requirements). Use list_requirements tool to find requirement IDs if you only have the requirement title or URL.' - ), + title: 'List Test Cases', + description: + 'List test cases from a project in QA Sphere. Supports pagination and various filtering options. Usually it makes sense to call get_project tool first to get the project context.', + inputSchema: listTestCasesInputSchema.shape, + outputSchema: listTestCasesOutputSchema.shape, }, async ({ projectCode, page, - limit = 20, + limit, sortField, sortOrder, search, include, folders, tags, + types, priorities, - draft, + templateTCaseIds, requirementIds, + customFields, + draft, }) => { try { - // Build query parameters - const params = new URLSearchParams() - - if (page !== undefined) params.append('page', page.toString()) - if (limit !== undefined) params.append('limit', limit.toString()) - if (sortField) params.append('sortField', sortField) - if (sortOrder) params.append('sortOrder', sortOrder) - if (search) params.append('search', search) - - // Add array parameters - if (include) include.forEach((item) => params.append('include', item)) - if (folders) folders.forEach((item) => params.append('folders', item.toString())) - if (tags) tags.forEach((item) => params.append('tags', item.toString())) - if (priorities) priorities.forEach((item) => params.append('priorities', item)) - if (requirementIds) requirementIds.forEach((item) => params.append('requirementIds', item)) - - if (draft !== undefined) params.append('draft', draft.toString()) - - const url = `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectCode}/tcase` - - const response = await axios.get(url, { - params, - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - }) - - const testCasesList = response.data - - // Basic validation of response - if (!testCasesList || !Array.isArray(testCasesList.data)) { - throw new Error('Invalid response: expected a list of test cases') - } - - // check for other fields from TestCasesListResponse - if ( - testCasesList.total === undefined || - testCasesList.page === undefined || - testCasesList.limit === undefined - ) { - throw new Error('Invalid response: missing required fields (total, page, or limit)') + const query: Record = { + page, + limit, + sortField, + sortOrder, + search, + include, + folders, + tags, + types, + priorities, + templateTCaseIds, + requirementIds, + draft, } - - // if array is non-empty check if object has id and title fields - if (testCasesList.data.length > 0) { - const firstTestCase = testCasesList.data[0] - if (!firstTestCase.id || !firstTestCase.title) { - throw new Error('Invalid test case data: missing required fields (id or title)') + if (customFields) { + for (const [field, values] of Object.entries(customFields)) { + query[`cf_${field}`] = values } } - + const result = await apiQuery(`/api/public/v0/project/${projectCode}/tcase`, { + schema: listTestCasesOutputSchema, + query, + }) return { - content: [ - { - type: 'text', - text: JSONStringify(testCasesList, { - data: { - steps: { - description: 'action', - expected: 'expected_result', - }, - }, - }), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - if (error.response?.status === 404) { - throw new Error(`Project with code '${projectCode}' not found.`) - } - throw new Error( - `Failed to fetch test cases: ${error.response?.data?.message || error.message}` - ) + if (error instanceof ApiError) { + const message = error.message + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project with code '${projectCode}' not found.`) + if (error.status === 500) + throw new Error('Internal server error while fetching test cases') + throw new Error(`Failed to fetch test cases: ${message}`) } throw error } } ) - server.tool( + server.registerTool( 'create_test_case', - 'Create a new test case in QA Sphere. Supports both standalone and template test cases with various options like steps, tags, requirements, and parameter values for templates.', { - projectId: projectCodeSchema, - title: z - .string() - .min(1, 'Title must be at least 1 character') - .max(511, 'Title must be at most 511 characters') - .describe('Test case title'), - type: z - .enum(['standalone', 'template']) - .describe('Type of test case (standalone or template)'), - folderId: z - .number() - .int() - .positive('Folder ID must be a positive integer') - .describe( - 'ID of the folder where the test case will be placed. Use bulk_upsert_folders tool to create new folders or get existing folders.' - ), - priority: z.enum(['high', 'medium', 'low']).describe('Test case priority'), - pos: z - .number() - .int() - .min(0, 'Position must be non-negative') - .optional() - .describe('Position within the folder (0-based index)'), - precondition: z - .union([ - z.object({ - sharedPreconditionId: z - .number() - .int() - .min(1) - .describe('Reference to a shared precondition by ID'), - }), - z.object({ - text: z.string().describe('Standalone precondition text (HTML format)'), - }), - ]) - .optional() - .describe('Test case precondition (reference by id or provide text in HTML format)'), - steps: z - .array( - z.object({ - sharedStepId: z - .number() - .int() - .positive() - .optional() - .describe('Unique identifier of the shared step'), - description: z.string().optional().describe('Details of steps (HTML format)'), - expected: z.string().optional().describe('Expected result from the step (HTML format)'), - }) - ) - .optional() - .describe('List of test case steps'), - tags: z - .array(z.string().max(255, 'Tag title must be at most 255 characters')) - .optional() - .describe('List of tag titles'), - requirements: z - .array( - z.object({ - text: z - .string() - .min(1, 'Requirement text must be at least 1 character') - .max(255, 'Requirement text must be at most 255 characters') - .describe('Title of the requirement'), - url: z - .string() - .min(1, 'Requirement URL must be at least 1 character') - .max(255, 'Requirement URL must be at most 255 characters') - .url('Requirement URL must be a valid URL') - .describe('URL of the requirement'), - }) - ) - .optional() - .describe('Test case requirements'), - links: z - .array( - z.object({ - text: z - .string() - .min(1, 'Link text must be at least 1 character') - .max(255, 'Link text must be at most 255 characters') - .describe('Title of the link'), - url: z - .string() - .min(1, 'Link URL must be at least 1 character') - .max(255, 'Link URL must be at most 255 characters') - .url('Link URL must be a valid URL') - .describe('URL of the link'), - }) - ) - .optional() - .describe('Additional links relevant to the test case'), - customFields: tcaseCustomFieldParamSchema, - parameterValues: z - .array( - z.object({ - values: z - .record(z.string()) - .describe('Values for the parameters in the template test case'), - }) - ) - .optional() - .describe('Values to substitute for parameters in template test cases'), - filledTCaseTitleSuffixParams: z - .array(z.string()) - .optional() - .describe('Parameters to append to filled test case titles'), - isDraft: z.boolean().optional().default(false).describe('Whether to create as draft'), + title: 'Create Test Case', + description: + 'Create a new test case in QA Sphere. Supports both standalone and template test cases with various options like steps, tags, requirements, and parameter values for templates.', + inputSchema: createTestCaseInputSchema.shape, + outputSchema: createTestCaseOutputSchema.shape, }, - async ({ projectId, ...tcaseParams }) => { + async ({ projectCode, ...rest }) => { try { - const requestData: CreateTestCaseRequest = { - ...tcaseParams, - } - - const response = await axios.post( - `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectId}/tcase`, - requestData, - { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, - } - ) - - const result = response.data - + const body: Omit = rest + const result = await apiQuery(`/api/public/v0/project/${projectCode}/tcase`, { + schema: createTestCaseOutputSchema, + method: 'POST', + body, + }) return { - content: [ - { - type: 'text', - text: JSON.stringify(result), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const status = error.response?.status - const message = error.response?.data?.message || error.message - - if (status === 400) { - throw new Error(`Invalid request data: ${message}`) - } - if (status === 401) { - throw new Error('Invalid or missing API key') - } - if (status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (status === 404) { - throw new Error(`Project or folder not found: ${message}`) - } - if (status === 409) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) throw new Error(`Invalid request data: ${message}`) + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project or folder not found: ${message}`) + if (error.status === 409) throw new Error(`Position conflict or duplicate requirement: ${message}`) - } - if (status === 500) { + if (error.status === 500) throw new Error('Internal server error while creating test case') - } - throw new Error(`Failed to create test case: ${message}`) } throw error @@ -374,163 +157,42 @@ export const registerTools = (server: McpServer) => { } ) - server.tool( + server.registerTool( 'update_test_case', - 'Update an existing test case in QA Sphere. Only users with role User or higher are allowed to update test cases. Optional fields can be omitted to keep the current value.', { - projectId: projectCodeSchema, - tcaseOrLegacyId: z - .string() - .describe('Test case identifier (can be one of test case UUID, sequence or legacy ID)'), - title: z - .string() - .min(1, 'Title must be at least 1 character') - .max(511, 'Title must be at most 511 characters') - .optional() - .describe('Test case title'), - priority: z.enum(['high', 'medium', 'low']).optional().describe('Test case priority'), - precondition: z - .union([ - z.object({ - sharedPreconditionId: z - .number() - .int() - .min(1) - .describe('Reference to a shared precondition by ID'), - }), - z.object({ - text: z.string().describe('Standalone precondition text (HTML format)'), - }), - ]) - .optional() - .describe('Test case precondition (reference by id or provide text in HTML format)'), - isDraft: z - .boolean() - .optional() - .describe( - 'To publish a draft test case. A published test case cannot be converted to draft' - ), - steps: z - .array( - z.object({ - sharedStepId: z - .number() - .int() - .positive() - .optional() - .describe('Unique identifier of the shared step'), - description: z.string().optional().describe('Details of steps (HTML format)'), - expected: z.string().optional().describe('Expected result from the step (HTML format)'), - }) - ) - .optional() - .describe('List of test case steps'), - tags: z - .array(z.string().max(255, 'Tag title must be at most 255 characters')) - .optional() - .describe('List of tag titles'), - requirements: z - .array( - z.object({ - text: z - .string() - .min(1, 'Requirement text must be at least 1 character') - .max(255, 'Requirement text must be at most 255 characters') - .describe('Title of the requirement'), - url: z - .string() - .min(1, 'Requirement URL must be at least 1 character') - .max(255, 'Requirement URL must be at most 255 characters') - .url('Requirement URL must be a valid URL') - .describe('URL of the requirement'), - }) - ) - .optional() - .describe('Test case requirements'), - links: z - .array( - z.object({ - text: z - .string() - .min(1, 'Link text must be at least 1 character') - .max(255, 'Link text must be at most 255 characters') - .describe('Title of the link'), - url: z - .string() - .min(1, 'Link URL must be at least 1 character') - .max(255, 'Link URL must be at most 255 characters') - .url('Link URL must be a valid URL') - .describe('URL of the link'), - }) - ) - .optional() - .describe('Additional links relevant to the test case'), - customFields: tcaseCustomFieldParamSchema, - parameterValues: z - .array( - z.object({ - tcaseId: z - .string() - .optional() - .describe('Should be specified to update existing filled test case'), - values: z - .record(z.string()) - .describe('Values for the parameters in the template test case'), - }) - ) - .optional() - .describe('Values to substitute for parameters in template test cases'), + title: 'Update Test Case', + description: + 'Update an existing test case in QA Sphere. Only users with role User or higher are allowed to update test cases. Optional fields can be omitted to keep the current value.', + inputSchema: updateTestCaseInputSchema.shape, + outputSchema: updateTestCaseOutputSchema.shape, }, - async ({ projectId, tcaseOrLegacyId, ...updateParams }) => { + async ({ projectCode, tcaseOrLegacyId, ...rest }) => { try { - const requestData: UpdateTestCaseRequest = { - ...updateParams, - } - - const response = await axios.patch( - `${QASPHERE_TENANT_URL}/api/public/v0/project/${projectId}/tcase/${tcaseOrLegacyId}`, - requestData, + const body: Omit = rest + const result = await apiQuery( + `/api/public/v0/project/${projectCode}/tcase/${tcaseOrLegacyId}`, { - headers: { - Authorization: `ApiKey ${QASPHERE_API_KEY}`, - 'Content-Type': 'application/json', - }, + schema: updateTestCaseOutputSchema, + method: 'PATCH', + body, } ) - - const result = response.data - return { - content: [ - { - type: 'text', - text: JSON.stringify(result), - }, - ], + content: [{ type: 'text', text: JSON.stringify(result) }], + structuredContent: result, } } catch (error: unknown) { - if (axios.isAxiosError(error)) { - const status = error.response?.status - const message = error.response?.data?.message || error.message - - if (status === 400) { + if (error instanceof ApiError) { + const message = error.message + if (error.status === 400) throw new Error( `Invalid request data or converting a published test case to draft: ${message}` ) - } - if (status === 401) { - throw new Error('Invalid or missing API key') - } - if (status === 403) { - throw new Error('Insufficient permissions or suspended tenant') - } - if (status === 404) { - throw new Error(`Project or test case not found: ${message}`) - } - if (status === 500) { + if (error.status === 401) throw new Error('Invalid or missing API key') + if (error.status === 403) throw new Error('Insufficient permissions or suspended tenant') + if (error.status === 404) throw new Error(`Project or test case not found: ${message}`) + if (error.status === 500) throw new Error('Internal server error while updating test case') - } - throw new Error(`Failed to update test case: ${message}`) } throw error @@ -538,38 +200,3 @@ export const registerTools = (server: McpServer) => { } ) } - -const tcaseCustomFieldParamSchema = z - .record( - z.string(), - z - .object({ - value: z - .string() - .optional() - .describe( - "The actual value for the field. For text fields: any string value. For dropdown fields: must match one of the option value strings from the field's options array. Omit if 'isDefault' is true." - ), - isDefault: z - .boolean() - .optional() - .describe( - "Boolean indicating whether to use the field's default value (if true, the value field should be omitted)" - ), - }) - .refine((data) => data.value !== undefined || data.isDefault !== undefined, { - message: "For each custom field provided, either 'value' or 'isDefault' must be specified.", - }) - ) - .optional() - .describe( - 'Custom field values. Use the systemName property from custom fields as the key. Only enabled fields should be used. Use list_custom_fields tool to get the custom fields.' - ) - -const testCaseMarkerSchema = z - .string() - .regex( - /^[A-Z0-9]{2,5}-\d+$/, - 'Marker must be in format PROJECT_CODE-SEQUENCE (e.g., BDI-123). Project code must be 2 to 5 characters in format PROJECT_CODE (e.g., BDI). Sequence must be a number.' - ) - .describe('Test case marker in format PROJECT_CODE-SEQUENCE (e.g., BDI-123)') diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index e4f4cc4..0000000 --- a/src/types.ts +++ /dev/null @@ -1,302 +0,0 @@ -export interface TestStep { - description: string - expected: string -} - -export interface TestPrecondition { - id: number // Unique identifier of the precondition - version: number // Version of the precondition - title: string // Title of the precondition - text: string // Precondition text content - type: 'shared' | 'standalone' // Type of precondition (shared across tests or standalone) - isLatest: boolean // Whether this is the latest version of the precondition - createdAt: string // Precondition creation time (ISO 8601 format) - updatedAt: string // Precondition update time (ISO 8601 format) -} - -export interface SharedPrecondition { - projectId: string // Unique identifier of the project - id: number // Unique identifier of the shared precondition - version: number // Version of the shared precondition - title: string // Title of the shared precondition - type: 'shared' // Type of the precondition (always "shared" for shared preconditions) - text: string // Text content of the precondition (HTML format) - isLatest: boolean // Whether this is the latest version of the precondition - createdAt: string // Precondition creation time (ISO 8601 format) - updatedAt: string // Precondition update time (ISO 8601 format) - deletedAt?: string | null // Date the precondition was deleted on (ISO 8601 format) - tcaseCount?: number // Number of test cases using this shared precondition (included only when requested) -} - -export type SharedPreconditionListResponse = SharedPrecondition[] // List of shared preconditions - -export interface SharedSubStep { - id: number // Unique identifier of the sub-step - type: 'shared_sub_step' // Type of the sub-step (always shared_sub_step) - version: number // Version of the shared step (same as parent) - isLatest: boolean // Whether this is the latest version - description: string // Description of the action (HTML) - expected: string // Expected result (HTML) - deletedAt?: string | null // Date the sub-step was deleted on (ISO 8601 format) -} - -export interface SharedStep { - id: number // Unique identifier of the shared step - version: number // Version of the shared step - type: 'shared' // Type of the step (always shared) - title: string // Title of the shared step - isLatest: boolean // Whether this is the latest version - subSteps: SharedSubStep[] // List of sub-steps - deletedAt?: string | null // Date the shared step was deleted on (ISO 8601 format) - tcaseCount?: number // Number of test cases using this shared step (included only when requested) -} - -export interface SharedStepListResponse { - sharedSteps: SharedStep[] // List of shared steps -} - -export interface TestTag { - id: number - title: string -} - -export interface TestFile { - id: string - fileName: string - mimeType: string - size: number - url: string -} - -export interface TestRequirement { - id: string - text: string - url: string -} - -export interface TestLink { - text: string - url: string -} - -export interface TestCase { - id: string // Unique identifier of the test case - legacyId: string // Legacy identifier of the test case. Empty string if the test case has no legacy ID - version: number // Version of the test case. Updates to test (except folder/pos) creates a new version - title: string // Title of the test case - seq: number // Sequence number of the test case. Test cases in a project are assigned incremental sequence numbers - folderId: number // Identifier of the folder where the test case is placed - pos: number // Ordered position (0 based) of the test case in its folder - priority: 'high' | 'medium' | 'low' // Priority of the test case - /** - * @deprecated Use `precondition` instead. This field is kept for backward compatibility. - */ - comment: string // Test description/precondition (deprecated - use precondition instead) - precondition?: TestPrecondition // Test precondition and setup requirements - steps: TestStep[] // List of test case steps - tags: TestTag[] // List of test case tags - files: TestFile[] // List of files attached to the test case - requirements: TestRequirement[] // Test case requirement (currently only single requirement is supported on UI) - links: TestLink[] // Additional links relevant to the test case - authorId: number // Unique identifier of the user who added the test case - isDraft: boolean // Whether the test case is still in draft state - isLatestVersion: boolean // Whether this is the latest version of the test case - createdAt: string // Test case creation time (ISO 8601 format) - updatedAt: string // Test case updation time (ISO 8601 format) -} - -export interface TestCasesListResponse { - total: number // Total number of filtered test cases - page: number // Current page number - limit: number // Number of test cases per page - data: TestCase[] // List of test case objects -} - -export interface ProjectLink { - url: string - text: string -} - -export interface Project { - id: string - code: string - title: string - description: string - overviewTitle: string - overviewDescription: string - links: ProjectLink[] - createdAt: string - updatedAt: string - archivedAt: string | null -} - -export interface TestFolder { - id: number // Unique identifier for the folder - title: string // Name of the folder - comment: string // Additional notes or description - pos: number // Position of the folder among its siblings - parentId: number // ID of the parent folder (0 for root folders) - projectId: string // ID of the project the folder belongs to -} - -export interface TestFolderListResponse { - total: number // Total number of items available - page: number // Current page number - limit: number // Number of items per page - data: TestFolder[] // Array of folder objects -} - -export interface BulkUpsertFolderRequest { - path: string[] // Array of folder names representing the hierarchy - comment?: string // Additional notes or description for the leaf folder (HTML format) -} - -export interface BulkUpsertFoldersRequest { - folders: BulkUpsertFolderRequest[] // Array of folder requests -} - -export interface BulkUpsertFoldersResponse { - ids: number[][] // Each array represents the full folder path hierarchy as an array of folder IDs -} - -// Request type for test case precondition - either reference by ID or provide text -export type TestPreconditionRequest = - | { sharedPreconditionId: number } // Reference an existing shared precondition by ID - | { text: string } // Provide standalone precondition text - -// Create Test Case API Types -export interface CreateTestCaseStep { - sharedStepId?: number // For shared steps - description?: string // For standalone steps - expected?: string // For standalone steps -} - -export interface CreateTestCaseRequirement { - text: string // Title of the requirement (1-255 characters) - url: string // URL of the requirement (1-255 characters) -} - -export interface CreateTestCaseLink { - text: string // Title of the link (1-255 characters) - url: string // URL of the link (1-255 characters) -} - -export interface TestCaseCustomFieldValue { - isDefault?: boolean // Whether to set the default value (if true, the value field should be omitted) - value?: string // Custom field value to be set. For text fields: any string value. For dropdown fields: must match one of the option value strings. Omit if 'isDefault' is true. -} - -export interface CreateTestCaseParameterValue { - values: { [key: string]: string } // Values for the parameters in the template test case -} - -export interface CreateTestCaseRequest { - title: string // Required: Test case title (1-511 characters) - type: 'standalone' | 'template' // Required: Type of test case - folderId: number // Required: ID of the folder where the test case will be placed - priority: 'high' | 'medium' | 'low' // Required: Test case priority - pos?: number // Optional: Position within the folder (0-based index) - precondition?: TestPreconditionRequest // Optional: Test case precondition - steps?: CreateTestCaseStep[] // Optional: List of test case steps - tags?: string[] // Optional: List of tag titles (max 255 characters each) - requirements?: CreateTestCaseRequirement[] // Optional: Test case requirements - links?: CreateTestCaseLink[] // Optional: Additional links relevant to the test case - customFields?: { [key: string]: TestCaseCustomFieldValue } // Optional: Custom field values - parameterValues?: CreateTestCaseParameterValue[] // Optional: Values to substitute for parameters in template test cases - filledTCaseTitleSuffixParams?: string[] // Optional: Parameters to append to filled test case titles - isDraft?: boolean // Whether to create as draft, default false -} - -export interface CreateTestCaseResponse { - id: string // Unique identifier of the created test case - seq: number // Sequence number of the test case in the project -} - -// Update Test Case API Types -export interface UpdateTestCaseStep { - sharedStepId?: number // For shared steps - description?: string // For standalone steps - expected?: string // For standalone steps -} - -export interface UpdateTestCaseRequirement { - text: string // Title of the requirement (1-255 characters) - url: string // URL of the requirement (1-255 characters) -} - -export interface UpdateTestCaseLink { - text: string // Title of the link (1-255 characters) - url: string // URL of the link (1-255 characters) -} - -export interface UpdateTestCaseParameterValue { - tcaseId?: string // Should be specified to update existing filled test case - values: { [key: string]: string } // Values for the parameters in the template test case -} - -export interface UpdateTestCaseRequest { - title?: string // Optional: Test case title (1-511 characters) - priority?: 'high' | 'medium' | 'low' // Optional: Test case priority - /** - * @deprecated Use `precondition` instead. This field is kept for backward compatibility. - */ - comment?: string // Optional: Test case precondition (HTML) - precondition?: TestPreconditionRequest // Optional: Test case precondition - isDraft?: boolean // Optional: To publish a draft test case - steps?: UpdateTestCaseStep[] // Optional: List of test case steps - tags?: string[] // Optional: List of tag titles (max 255 characters each) - requirements?: UpdateTestCaseRequirement[] // Optional: Test case requirements - links?: UpdateTestCaseLink[] // Optional: Additional links relevant to the test case - customFields?: { [key: string]: TestCaseCustomFieldValue } // Optional: Custom field values - parameterValues?: UpdateTestCaseParameterValue[] // Optional: Values to substitute for parameters in template test cases -} - -export interface MessageResponse { - message: string // Success message -} - -// Custom Fields API Types -export interface CustomFieldOption { - id: string // Option identifier - value: string // Option display value -} - -export interface CustomField { - id: string // Unique custom field identifier - type: 'text' | 'dropdown' // Field type - systemName: string // System identifier for the field (used in API requests) - name: string // Display name of the field - required: boolean // Whether the field is required for test cases - enabled: boolean // Whether the field is currently enabled - options?: CustomFieldOption[] // Available options (only for dropdown fields) - defaultValue?: string // Default value for the field - pos: number // Display position/order - allowAllProjects: boolean // Whether the field is available to all projects - allowedProjectIds?: string[] // List of project IDs if not available to all projects - createdAt: string // ISO 8601 timestamp when the field was created - updatedAt: string // ISO 8601 timestamp when the field was last updated -} - -export interface CustomFieldsResponse { - customFields: CustomField[] // Array of custom fields -} - -// Requirements API Types -export interface RequirementIntegrationLink { - issueId: string // Jira issue ID (e.g., "PROJ-123") - issueTitle: string // Title of the Jira issue - issueUrl: string // Full URL to the Jira issue - remoteLinkId: number // Jira remote link ID -} - -export interface Requirement { - id: string // Unique identifier of the requirement (HQID7 format) - text: string // Descriptive label for the requirement - url: string // URL to the external requirement document (empty string if not set) - integrationLink?: RequirementIntegrationLink // Jira integration link (only present if linked to Jira) - tcaseCount?: number // Number of test cases linked to this requirement (only included if requested) -} - -export interface RequirementsListResponse { - requirements: Requirement[] // List of requirements -} diff --git a/src/utils.test.ts b/src/utils.test.ts deleted file mode 100644 index eb4398e..0000000 --- a/src/utils.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { describe, it, expect } from 'vitest' -import { JSONStringify, type RenameMap } from './utils' - -describe('JSONStringify', () => { - const baseObject = { - a: 1, - b: 2, - c: { a: 11, b: 22, d: { a: 111 } }, - d: [ - { a: 50, x: 100 }, - { b: 60, a: 70 }, - ], - e: null, - f: [1, 2, 3], - g: { a: 'keep_g' }, - } - - it('should return JSON string of object without changes when renameKeys is empty', () => { - const result = JSONStringify(baseObject, {}) - expect(JSON.parse(result)).toEqual(baseObject) - }) - - it('should handle the first example: {a:z}', () => { - const o = { a: 1, b: 2, c: { a: 11, b: 22 } } - const renameMap: RenameMap = { a: 'z' } - const expected = { z: 1, b: 2, c: { a: 11, b: 22 } } - const result = JSONStringify(o, renameMap) - expect(JSON.parse(result)).toEqual(expected) - }) - - it('should handle the second example: {c:{a:z}}', () => { - const o = { a: 1, b: 2, c: { a: 11, b: 22 } } - const renameMap: RenameMap = { c: { a: 'z' } } - const expected = { a: 1, b: 2, c: { z: 11, b: 22 } } - const result = JSONStringify(o, renameMap) - expect(JSON.parse(result)).toEqual(expected) - }) - - it('should rename only specified top-level keys when using string values', () => { - const renameMap: RenameMap = { a: 'z', b: 'y' } - const expected = { - z: 1, // renamed - y: 2, // renamed - c: { a: 11, b: 22, d: { a: 111 } }, // nested a/b untouched - d: [ - { a: 50, x: 100 }, - { b: 60, a: 70 }, - ], // nested a/b untouched - e: null, - f: [1, 2, 3], - g: { a: 'keep_g' }, // nested a untouched - } - const result = JSONStringify(baseObject, renameMap) - expect(JSON.parse(result)).toEqual(expected) - }) - - it('should rename nested keys using nested rename map objects', () => { - const renameMap: RenameMap = { - c: { a: 'z', d: { a: 'k' } }, - g: { a: 'g_new' }, - } - const expected = { - a: 1, - b: 2, - c: { z: 11, b: 22, d: { k: 111 } }, // c.a renamed to z, c.d.a renamed to k - d: [ - { a: 50, x: 100 }, - { b: 60, a: 70 }, - ], // d array untouched - e: null, - f: [1, 2, 3], - g: { g_new: 'keep_g' }, // g.a renamed - } - const result = JSONStringify(baseObject, renameMap) - expect(JSON.parse(result)).toEqual(expected) - }) - - it('should rename keys deeply within arrays if a matching nested rule exists', () => { - // To rename 'a' inside the objects within the array 'd', - // the rename map needs to target 'a' at the level where it occurs. - const renameMap: RenameMap = { d: { a: 'z' } } // This applies universally - const expected = { - a: 1, - b: 2, - c: { a: 11, b: 22, d: { a: 111 } }, - d: [ - { z: 50, x: 100 }, - { b: 60, z: 70 }, - ], - e: null, - f: [1, 2, 3], - g: { a: 'keep_g' }, - } - const result = JSONStringify(baseObject, renameMap) - expect(JSON.parse(result)).toEqual(expected) - }) - - it('should handle mixed simple and nested renaming rules', () => { - const renameMap: RenameMap = { a: 'z', c: { b: 'y' } } - const expected = { - z: 1, // Renamed by top-level rule {a: 'z'} - b: 2, - c: { a: 11, y: 22, d: { a: 111 } }, // c.b renamed to y by nested rule, c.a and c.d.a untouched by *this* rule - d: [ - { a: 50, x: 100 }, - { b: 60, a: 70 }, - ], - e: null, - f: [1, 2, 3], - g: { a: 'keep_g' }, - } - const result = JSONStringify(baseObject, renameMap) - expect(JSON.parse(result)).toEqual(expected) - }) - - it('should handle empty objects', () => { - const renameMap: RenameMap = { a: 'z' } - expect(JSON.parse(JSONStringify({}, renameMap))).toEqual({}) - }) - - it('should handle objects with null values', () => { - const obj = { a: 1, b: null } - const renameMap: RenameMap = { a: 'z' } - const expected = { z: 1, b: null } - expect(JSON.parse(JSONStringify(obj, renameMap))).toEqual(expected) - }) - - it('should handle arrays directly if passed', () => { - const arr = [{ a: 1 }, { a: 2 }] - const renameMap: RenameMap = { a: 'z' } - const expected = [{ z: 1 }, { z: 2 }] - expect(JSON.parse(JSONStringify(arr, renameMap))).toEqual(expected) - }) - - it('should handle non-string/object values in renameKeys gracefully (treat as no-op for that key)', () => { - // The type system prevents this, but testing javascript flexibility - const renameMap: any = { a: true, b: 'y' } - const obj = { a: 1, b: 2, c: 3 } - const expected = { a: 1, y: 2, c: 3 } // 'a' is not renamed (invalid rule type), 'b' is - const result = JSONStringify(obj, renameMap) - expect(JSON.parse(result)).toEqual(expected) - }) -}) diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index 973ce93..0000000 --- a/src/utils.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Type definition for the renameKeys map -export type RenameMap = { - [key: string]: string | RenameMap -} - -// Helper function to recursively rename keys and process nested structures -const renameKeyInObject = (obj: any, renameKeys: RenameMap): any => { - // Base case: return non-objects/arrays as is - if (typeof obj !== 'object' || obj === null) { - return obj - } - - // Handle arrays: recursively process each element with the *original* full rename map - if (Array.isArray(obj)) { - // Important: Pass the original renameKeys map down, not a potentially nested part of it. - return obj.map((item) => renameKeyInObject(item, renameKeys)) - } - - // Handle objects - const newObj: Record = {} - for (const key in obj) { - // Ensure it's an own property - if (Object.prototype.hasOwnProperty.call(obj, key)) { - const currentValue = obj[key] - const renameTarget = renameKeys[key] - - if (typeof renameTarget === 'string') { - // Simple rename: Use the new key, recursively process the value with the *original* full rename map - newObj[renameTarget] = currentValue - } else if ( - typeof renameTarget === 'object' && - renameTarget !== null && - !Array.isArray(renameTarget) - ) { - // Nested rename rule provided: Keep the original key, recursively process the value with the specific *nested* rules - newObj[key] = renameKeyInObject(currentValue, renameTarget as RenameMap) - } else { - newObj[key] = currentValue - } - } - } - return newObj -} - -/** - * Creates a JSON string from an object after renaming keys according to a map. - * Handles nested objects and arrays, applying rules deeply. - * - * @param obj The input object or array. - * @param renameKeys A map defining key renames. String values rename the key directly. - * Object values indicate nested rules for the value of that key. - * Example: { oldKey: 'newKey', nestedKey: { oldInnerKey: 'newInnerKey' } } - * @returns A JSON string representation of the transformed object. - */ -export function JSONStringify(obj: any, renameKeys: RenameMap = {}): string { - const transformedObj = renameKeyInObject(obj, renameKeys) - // Use JSON.stringify with indentation for better readability if needed - // return JSON.stringify(transformedObj, null, 2); - return JSON.stringify(transformedObj) -} diff --git a/tsconfig.json b/tsconfig.json index 2511a3a..7e40f9b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,13 @@ { "compilerOptions": { - "target": "ES2020", - "module": "ES2020", - "moduleResolution": "node", + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true + "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules"]