diff --git a/changelog/24.improvement.md b/changelog/24.improvement.md new file mode 100644 index 0000000..5e8ca81 --- /dev/null +++ b/changelog/24.improvement.md @@ -0,0 +1 @@ +Upgraded `@hey-api/openapi-ts` to v0.95.0 and removed the standalone `@hey-api/client-fetch` dependency, which is now bundled with the code generator. diff --git a/frontend/openapi-ts.config.ts b/frontend/openapi-ts.config.ts index 979b4d9..fd4b49b 100644 --- a/frontend/openapi-ts.config.ts +++ b/frontend/openapi-ts.config.ts @@ -1,10 +1,11 @@ -import { defaultPlugins, defineConfig } from "@hey-api/openapi-ts"; +import { defineConfig } from "@hey-api/openapi-ts"; export default defineConfig({ input: "./openapi.json", output: "src/client", plugins: [ - ...defaultPlugins, + "@hey-api/typescript", + "@hey-api/sdk", "@hey-api/client-fetch", "@hey-api/schemas", "@tanstack/react-query", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 011ec49..4df5eb4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,7 +9,6 @@ "version": "0.1.0", "dependencies": { "@floating-ui/react": "^0.27.16", - "@hey-api/client-fetch": "^0.10.0", "@mdx-js/react": "^3.1.0", "@mdx-js/rollup": "^3.1.0", "@plausible-analytics/tracker": "^0.4.3", @@ -53,7 +52,7 @@ }, "devDependencies": { "@biomejs/biome": "^2.2.4", - "@hey-api/openapi-ts": "^0.67.1", + "@hey-api/openapi-ts": "^0.95.0", "@sentry/vite-plugin": "^4.3.0", "@tailwindcss/typography": "^0.5.18", "@tanstack/react-router-devtools": "^1.115.3", @@ -1240,61 +1239,142 @@ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "license": "MIT" }, - "node_modules/@hey-api/client-fetch": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.10.2.tgz", - "integrity": "sha512-AGiFYDx+y8VT1wlQ3EbzzZtfU8EfV+hLLRTtr8Y/tjYZaxIECwJagVZf24YzNbtEBXONFV50bwcU1wLVGXe1ow==", - "deprecated": "Starting with v0.73.0, this package is bundled directly inside @hey-api/openapi-ts.", + "node_modules/@hey-api/codegen-core": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@hey-api/codegen-core/-/codegen-core-0.7.4.tgz", + "integrity": "sha512-DGd9yeSQzflOWO3Y5mt1GRXkXH9O/yIMgbxPjwLI3jwu/3nAjoXXD26lEeFb6tclYlg0JAqTIs5d930G/qxHeA==", + "dev": true, "license": "MIT", + "dependencies": { + "@hey-api/types": "0.1.4", + "ansi-colors": "4.1.3", + "c12": "3.3.3", + "color-support": "1.1.3" + }, + "engines": { + "node": ">=20.19.0" + }, "funding": { "url": "https://github.com/sponsors/hey-api" - }, - "peerDependencies": { - "@hey-api/openapi-ts": "< 2" } }, "node_modules/@hey-api/json-schema-ref-parser": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.6.tgz", - "integrity": "sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.3.1.tgz", + "integrity": "sha512-7atnpUkT8TyUPHYPLk91j/GyaqMuwTEHanLOe50Dlx0EEvNuQqFD52Yjg8x4KU0UFL1mWlyhE+sUE/wAtQ1N2A==", + "dev": true, "license": "MIT", "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.15", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21" + "@jsdevtools/ono": "7.1.3", + "@types/json-schema": "7.0.15", + "js-yaml": "4.1.1" }, "engines": { - "node": ">= 16" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" } }, "node_modules/@hey-api/openapi-ts": { - "version": "0.67.6", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.67.6.tgz", - "integrity": "sha512-ywZggKKYieVjM6O6T60/Bl+QBRvhcKAov8dAIQor7reyKpFbEn3Ws+9WKoXR8QUuXN8AR8nMFjOuYPer5db/dg==", + "version": "0.95.0", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.95.0.tgz", + "integrity": "sha512-lk5C+WKl5yqEmliQihEyhX/jNcWlAykTSEqkDeKa9xSq5YDAzOFvx7oos8YTqiIzdc4TemtlEaB8Rns7+8A0qg==", + "dev": true, "license": "MIT", "dependencies": { - "@hey-api/json-schema-ref-parser": "1.0.6", - "c12": "2.0.1", - "commander": "13.0.0", - "handlebars": "4.7.8" + "@hey-api/codegen-core": "0.7.4", + "@hey-api/json-schema-ref-parser": "1.3.1", + "@hey-api/shared": "0.3.0", + "@hey-api/spec-types": "0.1.0", + "@hey-api/types": "0.1.4", + "ansi-colors": "4.1.3", + "color-support": "1.1.3", + "commander": "14.0.3", + "get-tsconfig": "4.13.6" }, "bin": { - "openapi-ts": "bin/index.cjs" + "openapi-ts": "bin/run.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=22.10.0" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" }, "peerDependencies": { - "typescript": "^5.5.3" + "typescript": ">=5.5.3 || >=6.0.0 || 6.0.1-rc" } }, + "node_modules/@hey-api/openapi-ts/node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/@hey-api/shared": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@hey-api/shared/-/shared-0.3.0.tgz", + "integrity": "sha512-G+4GPojdLEh9bUwRG88teMPM1HdqMm/IsJ38cbnNxhyDu1FkFGwilkA1EqnULCzfTam/ZoZkaLdmAd8xEh4Xsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hey-api/codegen-core": "0.7.4", + "@hey-api/json-schema-ref-parser": "1.3.1", + "@hey-api/spec-types": "0.1.0", + "@hey-api/types": "0.1.4", + "ansi-colors": "4.1.3", + "cross-spawn": "7.0.6", + "open": "11.0.0", + "semver": "7.7.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/shared/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@hey-api/spec-types": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@hey-api/spec-types/-/spec-types-0.1.0.tgz", + "integrity": "sha512-StS4RrAO5pyJCBwe6uF9MAuPflkztriW+FPnVb7oEjzDYv1sxPwP+f7fL6u6D+UVrKpZ/9bPNx/xXVdkeWPU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hey-api/types": "0.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/types": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@hey-api/types/-/types-0.1.4.tgz", + "integrity": "sha512-thWfawrDIP7wSI9ioT13I5soaaqB5vAPIiZmgD8PbeEVKNrkonc0N/Sjj97ezl7oQgusZmaNphGdMKipPO6IBg==", + "dev": true, + "license": "MIT" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1362,6 +1442,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true, "license": "MIT" }, "node_modules/@mdx-js/mdx": { @@ -4656,6 +4737,7 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, "license": "MIT" }, "node_modules/@types/mdast": { @@ -4914,6 +4996,16 @@ "node": ">= 6.0.0" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4979,6 +5071,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/aria-hidden": { @@ -5158,27 +5251,44 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/c12": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/c12/-/c12-2.0.1.tgz", - "integrity": "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.3.tgz", + "integrity": "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q==", + "dev": true, "license": "MIT", "dependencies": { - "chokidar": "^4.0.1", - "confbox": "^0.1.7", + "chokidar": "^5.0.0", + "confbox": "^0.2.2", "defu": "^6.1.4", - "dotenv": "^16.4.5", - "giget": "^1.2.3", - "jiti": "^2.3.0", - "mlly": "^1.7.1", - "ohash": "^1.1.4", - "pathe": "^1.1.2", - "perfect-debounce": "^1.0.0", - "pkg-types": "^1.2.0", + "dotenv": "^17.2.3", + "exsolve": "^1.0.8", + "giget": "^2.0.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.0.0", + "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { - "magicast": "^0.3.5" + "magicast": "*" }, "peerDependenciesMeta": { "magicast": { @@ -5187,33 +5297,42 @@ } }, "node_modules/c12/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "readdirp": "^5.0.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, - "node_modules/c12/node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "license": "MIT" + "node_modules/c12/node_modules/dotenv": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } }, "node_modules/c12/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 14.18.0" + "node": ">= 20.19.0" }, "funding": { "type": "individual", @@ -5326,19 +5445,11 @@ "fsevents": "~2.3.2" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -5411,6 +5522,16 @@ "dev": true, "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -5422,24 +5543,27 @@ } }, "node_modules/commander": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz", - "integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==", + "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/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -6016,10 +6140,54 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defu": { "version": "6.1.6", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.6.tgz", "integrity": "sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==", + "dev": true, "license": "MIT" }, "node_modules/delaunator": { @@ -6044,6 +6212,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -6106,6 +6275,7 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -6372,6 +6542,13 @@ "node": ">=12.0.0" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -6451,36 +6628,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -6528,18 +6675,18 @@ } }, "node_modules/giget": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.5.tgz", - "integrity": "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", - "nypm": "^0.5.4", - "pathe": "^2.0.3", - "tar": "^6.2.1" + "nypm": "^0.6.0", + "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" @@ -6609,36 +6756,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/hast-util-to-estree": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", @@ -6842,6 +6959,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -6885,6 +7018,38 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "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", @@ -6914,6 +7079,22 @@ "dev": true, "license": "MIT" }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isbot": { "version": "5.1.37", "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.37.tgz", @@ -6965,6 +7146,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -8236,15 +8418,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -8255,61 +8428,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", - "license": "MIT", - "dependencies": { - "acorn": "^8.16.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.3" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -8334,12 +8452,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -8365,6 +8477,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "dev": true, "license": "MIT" }, "node_modules/node-fetch/node_modules/tr46": { @@ -8410,25 +8523,30 @@ } }, "node_modules/nypm": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.5.4.tgz", - "integrity": "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "dev": true, "license": "MIT", "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", + "citty": "^0.2.0", "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "tinyexec": "^0.3.2", - "ufo": "^1.5.4" + "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" }, "engines": { - "node": "^14.16.0 || >=16.10.0" + "node": ">=18" } }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -8450,11 +8568,33 @@ "license": "MIT" }, "node_modules/ohash": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.6.tgz", - "integrity": "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, "license": "MIT" }, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8580,12 +8720,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, "license": "MIT" }, "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "dev": true, "license": "MIT" }, "node_modules/picocolors": { @@ -8607,14 +8749,15 @@ } }, "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" } }, "node_modules/postcss": { @@ -8659,6 +8802,19 @@ "node": ">=4" } }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prettier": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", @@ -8749,6 +8905,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -9188,6 +9345,19 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", @@ -9528,39 +9698,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -9575,10 +9712,14 @@ "license": "MIT" }, "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==", - "license": "MIT" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { "version": "0.2.15", @@ -9715,6 +9856,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -9724,25 +9866,6 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", - "license": "MIT" - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/undici": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", @@ -10179,16 +10302,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/vitest/node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -10287,12 +10400,6 @@ "node": ">=8" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT" - }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -10394,6 +10501,23 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 14a8bb7..2622d7e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,6 @@ }, "dependencies": { "@floating-ui/react": "^0.27.16", - "@hey-api/client-fetch": "^0.10.0", "@mdx-js/react": "^3.1.0", "@mdx-js/rollup": "^3.1.0", "@plausible-analytics/tracker": "^0.4.3", @@ -57,7 +56,7 @@ }, "devDependencies": { "@biomejs/biome": "^2.2.4", - "@hey-api/openapi-ts": "^0.67.1", + "@hey-api/openapi-ts": "^0.95.0", "@sentry/vite-plugin": "^4.3.0", "@tailwindcss/typography": "^0.5.18", "@tanstack/react-router-devtools": "^1.115.3", diff --git a/frontend/src/client/@tanstack/react-query.gen.ts b/frontend/src/client/@tanstack/react-query.gen.ts index 348143b..29fe2ff 100644 --- a/frontend/src/client/@tanstack/react-query.gen.ts +++ b/frontend/src/client/@tanstack/react-query.gen.ts @@ -1,24 +1,29 @@ // This file is auto-generated by @hey-api/openapi-ts -import { type Options, cmip7AssessmentFastTrackAftListAftDiagnostics, cmip7AssessmentFastTrackAftGetAftDiagnostic, datasetsList, datasetsGet, datasetsExecutions, diagnosticsList, diagnosticsFacets, diagnosticsGet, diagnosticsListExecutionGroups, diagnosticsListExecutions, diagnosticsListMetricValues, executionsGetExecutionStatistics, executionsListRecentExecutionGroups, executionsGet, executionsExecution, executionsExecutionDatasets, executionsExecutionLogs, executionsMetricBundle, executionsListMetricValues, executionsExecutionArchive, explorerListCollections, explorerGetCollection, explorerListThemes, explorerGetTheme, resultsGetResult, utilsHealthCheck } from '../sdk.gen'; -import { queryOptions, infiniteQueryOptions, type InfiniteData } from '@tanstack/react-query'; -import type { Cmip7AssessmentFastTrackAftListAftDiagnosticsData, Cmip7AssessmentFastTrackAftGetAftDiagnosticData, DatasetsListData, DatasetsListError, DatasetsListResponse, DatasetsGetData, DatasetsExecutionsData, DatasetsExecutionsError, DatasetsExecutionsResponse, DiagnosticsListData, DiagnosticsFacetsData, DiagnosticsGetData, DiagnosticsListExecutionGroupsData, DiagnosticsListExecutionsData, DiagnosticsListMetricValuesData, DiagnosticsListMetricValuesError, DiagnosticsListMetricValuesResponse, ExecutionsGetExecutionStatisticsData, ExecutionsListRecentExecutionGroupsData, ExecutionsListRecentExecutionGroupsError, ExecutionsListRecentExecutionGroupsResponse, ExecutionsGetData, ExecutionsExecutionData, ExecutionsExecutionDatasetsData, ExecutionsExecutionLogsData, ExecutionsMetricBundleData, ExecutionsListMetricValuesData, ExecutionsListMetricValuesError, ExecutionsListMetricValuesResponse, ExecutionsExecutionArchiveData, ExplorerListCollectionsData, ExplorerGetCollectionData, ExplorerListThemesData, ExplorerGetThemeData, ResultsGetResultData, UtilsHealthCheckData } from '../types.gen'; -import { client as _heyApiClient } from '../client.gen'; +import { type DefaultError, type InfiniteData, infiniteQueryOptions, queryOptions } from '@tanstack/react-query'; + +import { client } from '../client.gen'; +import { cmip7AssessmentFastTrackAftGetAftDiagnostic, cmip7AssessmentFastTrackAftListAftDiagnostics, datasetsExecutions, datasetsGet, datasetsList, diagnosticsFacets, diagnosticsGet, diagnosticsList, diagnosticsListExecutionGroups, diagnosticsListExecutions, diagnosticsListMetricValues, executionsExecution, executionsExecutionArchive, executionsExecutionDatasets, executionsExecutionLogs, executionsGet, executionsGetExecutionStatistics, executionsListMetricValues, executionsListRecentExecutionGroups, executionsMetricBundle, explorerGetCollection, explorerGetTheme, explorerListCollections, explorerListThemes, type Options, resultsGetResult, utilsHealthCheck } from '../sdk.gen'; +import type { Cmip7AssessmentFastTrackAftGetAftDiagnosticData, Cmip7AssessmentFastTrackAftGetAftDiagnosticError, Cmip7AssessmentFastTrackAftGetAftDiagnosticResponse, Cmip7AssessmentFastTrackAftListAftDiagnosticsData, Cmip7AssessmentFastTrackAftListAftDiagnosticsResponse, DatasetsExecutionsData, DatasetsExecutionsError, DatasetsExecutionsResponse, DatasetsGetData, DatasetsGetError, DatasetsGetResponse, DatasetsListData, DatasetsListError, DatasetsListResponse, DiagnosticsFacetsData, DiagnosticsFacetsResponse, DiagnosticsGetData, DiagnosticsGetError, DiagnosticsGetResponse, DiagnosticsListData, DiagnosticsListExecutionGroupsData, DiagnosticsListExecutionGroupsError, DiagnosticsListExecutionGroupsResponse, DiagnosticsListExecutionsData, DiagnosticsListExecutionsError, DiagnosticsListExecutionsResponse, DiagnosticsListMetricValuesData, DiagnosticsListMetricValuesError, DiagnosticsListMetricValuesResponse, DiagnosticsListResponse, ExecutionsExecutionArchiveData, ExecutionsExecutionArchiveError, ExecutionsExecutionData, ExecutionsExecutionDatasetsData, ExecutionsExecutionDatasetsError, ExecutionsExecutionDatasetsResponse, ExecutionsExecutionError, ExecutionsExecutionLogsData, ExecutionsExecutionLogsError, ExecutionsExecutionResponse, ExecutionsGetData, ExecutionsGetError, ExecutionsGetExecutionStatisticsData, ExecutionsGetExecutionStatisticsResponse, ExecutionsGetResponse, ExecutionsListMetricValuesData, ExecutionsListMetricValuesError, ExecutionsListMetricValuesResponse, ExecutionsListRecentExecutionGroupsData, ExecutionsListRecentExecutionGroupsError, ExecutionsListRecentExecutionGroupsResponse, ExecutionsMetricBundleData, ExecutionsMetricBundleError, ExecutionsMetricBundleResponse, ExplorerGetCollectionData, ExplorerGetCollectionError, ExplorerGetCollectionResponse, ExplorerGetThemeData, ExplorerGetThemeError, ExplorerGetThemeResponse, ExplorerListCollectionsData, ExplorerListCollectionsResponse, ExplorerListThemesData, ExplorerListThemesResponse, ResultsGetResultData, ResultsGetResultError, UtilsHealthCheckData, UtilsHealthCheckResponse } from '../types.gen'; export type QueryKey = [ Pick & { _id: string; _infinite?: boolean; + tags?: ReadonlyArray; } ]; -const createQueryKey = (id: string, options?: TOptions, infinite?: boolean): [ +const createQueryKey = (id: string, options?: TOptions, infinite?: boolean, tags?: ReadonlyArray): [ QueryKey[0] ] => { - const params: QueryKey[0] = { _id: id, baseUrl: (options?.client ?? _heyApiClient).getConfig().baseUrl } as QueryKey[0]; + const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; if (infinite) { params._infinite = infinite; } + if (tags) { + params.tags = tags; + } if (options?.body) { params.body = options.body; } @@ -31,76 +36,71 @@ const createQueryKey = (id: string, options?: TOptions if (options?.query) { params.query = options.query; } - return [ - params - ]; + return [params]; }; export const cmip7AssessmentFastTrackAftListAftDiagnosticsQueryKey = (options?: Options) => createQueryKey('cmip7AssessmentFastTrackAftListAftDiagnostics', options); /** * List Aft Diagnostics + * * Get all AFT diagnostics. */ -export const cmip7AssessmentFastTrackAftListAftDiagnosticsOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await cmip7AssessmentFastTrackAftListAftDiagnostics({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: cmip7AssessmentFastTrackAftListAftDiagnosticsQueryKey(options) - }); -}; +export const cmip7AssessmentFastTrackAftListAftDiagnosticsOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await cmip7AssessmentFastTrackAftListAftDiagnostics({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: cmip7AssessmentFastTrackAftListAftDiagnosticsQueryKey(options) +}); export const cmip7AssessmentFastTrackAftGetAftDiagnosticQueryKey = (options: Options) => createQueryKey('cmip7AssessmentFastTrackAftGetAftDiagnostic', options); /** * Get Aft Diagnostic + * * Get detailed AFT diagnostic by ID. */ -export const cmip7AssessmentFastTrackAftGetAftDiagnosticOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await cmip7AssessmentFastTrackAftGetAftDiagnostic({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: cmip7AssessmentFastTrackAftGetAftDiagnosticQueryKey(options) - }); -}; +export const cmip7AssessmentFastTrackAftGetAftDiagnosticOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await cmip7AssessmentFastTrackAftGetAftDiagnostic({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: cmip7AssessmentFastTrackAftGetAftDiagnosticQueryKey(options) +}); export const datasetsListQueryKey = (options?: Options) => createQueryKey('datasetsList', options); /** * List + * * Paginated list of currently ingested datasets */ -export const datasetsListOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await datasetsList({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: datasetsListQueryKey(options) - }); -}; +export const datasetsListOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await datasetsList({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: datasetsListQueryKey(options) +}); const createInfiniteParams = [0], 'body' | 'headers' | 'path' | 'query'>>(queryKey: QueryKey, page: K) => { - const params = queryKey[0]; + const params = { ...queryKey[0] }; if (page.body) { params.body = { ...queryKey[0].body as any, @@ -132,215 +132,207 @@ export const datasetsListInfiniteQueryKey = (options?: Options /** * List + * * Paginated list of currently ingested datasets */ -export const datasetsListInfiniteOptions = (options?: Options) => { - return infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( - // @ts-ignore - { - queryFn: async ({ pageParam, queryKey, signal }) => { - // @ts-ignore - const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { - query: { - offset: pageParam - } - }; - const params = createInfiniteParams(queryKey, page); - const { data } = await datasetsList({ - ...options, - ...params, - signal, - throwOnError: true - }); - return data; - }, - queryKey: datasetsListInfiniteQueryKey(options) - }); -}; +export const datasetsListInfiniteOptions = (options?: Options) => infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( +// @ts-ignore +{ + queryFn: async ({ pageParam, queryKey, signal }) => { + // @ts-ignore + const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { + query: { + offset: pageParam + } + }; + const params = createInfiniteParams(queryKey, page); + const { data } = await datasetsList({ + ...options, + ...params, + signal, + throwOnError: true + }); + return data; + }, + queryKey: datasetsListInfiniteQueryKey(options) +}); export const datasetsGetQueryKey = (options: Options) => createQueryKey('datasetsGet', options); /** * Get + * * Get a single dataset by slug */ -export const datasetsGetOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await datasetsGet({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: datasetsGetQueryKey(options) - }); -}; +export const datasetsGetOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await datasetsGet({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: datasetsGetQueryKey(options) +}); export const datasetsExecutionsQueryKey = (options: Options) => createQueryKey('datasetsExecutions', options); /** * Executions + * * List the currently registered diagnostics */ -export const datasetsExecutionsOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await datasetsExecutions({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: datasetsExecutionsQueryKey(options) - }); -}; +export const datasetsExecutionsOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await datasetsExecutions({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: datasetsExecutionsQueryKey(options) +}); export const datasetsExecutionsInfiniteQueryKey = (options: Options): QueryKey> => createQueryKey('datasetsExecutions', options, true); /** * Executions + * * List the currently registered diagnostics */ -export const datasetsExecutionsInfiniteOptions = (options: Options) => { - return infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( - // @ts-ignore - { - queryFn: async ({ pageParam, queryKey, signal }) => { - // @ts-ignore - const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { - query: { - offset: pageParam - } - }; - const params = createInfiniteParams(queryKey, page); - const { data } = await datasetsExecutions({ - ...options, - ...params, - signal, - throwOnError: true - }); - return data; - }, - queryKey: datasetsExecutionsInfiniteQueryKey(options) - }); -}; +export const datasetsExecutionsInfiniteOptions = (options: Options) => infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( +// @ts-ignore +{ + queryFn: async ({ pageParam, queryKey, signal }) => { + // @ts-ignore + const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { + query: { + offset: pageParam + } + }; + const params = createInfiniteParams(queryKey, page); + const { data } = await datasetsExecutions({ + ...options, + ...params, + signal, + throwOnError: true + }); + return data; + }, + queryKey: datasetsExecutionsInfiniteQueryKey(options) +}); export const diagnosticsListQueryKey = (options?: Options) => createQueryKey('diagnosticsList', options); /** * List + * * List the currently registered diagnostics */ -export const diagnosticsListOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await diagnosticsList({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: diagnosticsListQueryKey(options) - }); -}; +export const diagnosticsListOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await diagnosticsList({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: diagnosticsListQueryKey(options) +}); export const diagnosticsFacetsQueryKey = (options?: Options) => createQueryKey('diagnosticsFacets', options); /** * Facets + * * Query the unique dimensions and metrics for all diagnostics (both scalar and series) */ -export const diagnosticsFacetsOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await diagnosticsFacets({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: diagnosticsFacetsQueryKey(options) - }); -}; +export const diagnosticsFacetsOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await diagnosticsFacets({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: diagnosticsFacetsQueryKey(options) +}); export const diagnosticsGetQueryKey = (options: Options) => createQueryKey('diagnosticsGet', options); /** * Get + * * Fetch a result using the slug */ -export const diagnosticsGetOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await diagnosticsGet({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: diagnosticsGetQueryKey(options) - }); -}; +export const diagnosticsGetOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await diagnosticsGet({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: diagnosticsGetQueryKey(options) +}); export const diagnosticsListExecutionGroupsQueryKey = (options: Options) => createQueryKey('diagnosticsListExecutionGroups', options); /** * List Execution Groups + * * Fetch execution groups for a diagnostic. */ -export const diagnosticsListExecutionGroupsOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await diagnosticsListExecutionGroups({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: diagnosticsListExecutionGroupsQueryKey(options) - }); -}; +export const diagnosticsListExecutionGroupsOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await diagnosticsListExecutionGroups({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: diagnosticsListExecutionGroupsQueryKey(options) +}); export const diagnosticsListExecutionsQueryKey = (options: Options) => createQueryKey('diagnosticsListExecutions', options); /** * List Executions + * * Fetch executions for a specific diagnostic, with arbitrary filters on the dataset. * * e.g. `?source_id=MIROC6&experiment_id=ssp585` */ -export const diagnosticsListExecutionsOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await diagnosticsListExecutions({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: diagnosticsListExecutionsQueryKey(options) - }); -}; +export const diagnosticsListExecutionsOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await diagnosticsListExecutions({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: diagnosticsListExecutionsQueryKey(options) +}); export const diagnosticsListMetricValuesQueryKey = (options: Options) => createQueryKey('diagnosticsListMetricValues', options); /** * List Metric Values + * * Get all the diagnostic values for a given diagnostic (both scalar and series) * * - `value_type`: Type of metric values - 'scalar', 'series', or 'all' (required) @@ -348,25 +340,24 @@ export const diagnosticsListMetricValuesQueryKey = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await diagnosticsListMetricValues({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: diagnosticsListMetricValuesQueryKey(options) - }); -}; +export const diagnosticsListMetricValuesOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await diagnosticsListMetricValues({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: diagnosticsListMetricValuesQueryKey(options) +}); export const diagnosticsListMetricValuesInfiniteQueryKey = (options: Options): QueryKey> => createQueryKey('diagnosticsListMetricValues', options, true); /** * List Metric Values + * * Get all the diagnostic values for a given diagnostic (both scalar and series) * * - `value_type`: Type of metric values - 'scalar', 'series', or 'all' (required) @@ -374,58 +365,56 @@ export const diagnosticsListMetricValuesInfiniteQueryKey = (options: Options) => { - return infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( - // @ts-ignore - { - queryFn: async ({ pageParam, queryKey, signal }) => { - // @ts-ignore - const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { - query: { - offset: pageParam - } - }; - const params = createInfiniteParams(queryKey, page); - const { data } = await diagnosticsListMetricValues({ - ...options, - ...params, - signal, - throwOnError: true - }); - return data; - }, - queryKey: diagnosticsListMetricValuesInfiniteQueryKey(options) - }); -}; +export const diagnosticsListMetricValuesInfiniteOptions = (options: Options) => infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( +// @ts-ignore +{ + queryFn: async ({ pageParam, queryKey, signal }) => { + // @ts-ignore + const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { + query: { + offset: pageParam + } + }; + const params = createInfiniteParams(queryKey, page); + const { data } = await diagnosticsListMetricValues({ + ...options, + ...params, + signal, + throwOnError: true + }); + return data; + }, + queryKey: diagnosticsListMetricValuesInfiniteQueryKey(options) +}); export const executionsGetExecutionStatisticsQueryKey = (options?: Options) => createQueryKey('executionsGetExecutionStatistics', options); /** * Get Execution Statistics + * * Get execution statistics for the dashboard. * * Returns counts of total, successful, and failed execution groups, * plus recent activity count. */ -export const executionsGetExecutionStatisticsOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsGetExecutionStatistics({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsGetExecutionStatisticsQueryKey(options) - }); -}; +export const executionsGetExecutionStatisticsOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsGetExecutionStatistics({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsGetExecutionStatisticsQueryKey(options) +}); export const executionsListRecentExecutionGroupsQueryKey = (options?: Options) => createQueryKey('executionsListRecentExecutionGroups', options); /** * List Recent Execution Groups + * * List the most recent execution groups * * Supports filtering by: @@ -436,25 +425,24 @@ export const executionsListRecentExecutionGroupsQueryKey = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsListRecentExecutionGroups({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsListRecentExecutionGroupsQueryKey(options) - }); -}; +export const executionsListRecentExecutionGroupsOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsListRecentExecutionGroups({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsListRecentExecutionGroupsQueryKey(options) +}); export const executionsListRecentExecutionGroupsInfiniteQueryKey = (options?: Options): QueryKey> => createQueryKey('executionsListRecentExecutionGroups', options, true); /** * List Recent Execution Groups + * * List the most recent execution groups * * Supports filtering by: @@ -465,141 +453,135 @@ export const executionsListRecentExecutionGroupsInfiniteQueryKey = (options?: Op * - source_id (filters groups that include an execution whose datasets * include a CMIP6 dataset with this source_id) */ -export const executionsListRecentExecutionGroupsInfiniteOptions = (options?: Options) => { - return infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( - // @ts-ignore - { - queryFn: async ({ pageParam, queryKey, signal }) => { - // @ts-ignore - const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { - query: { - offset: pageParam - } - }; - const params = createInfiniteParams(queryKey, page); - const { data } = await executionsListRecentExecutionGroups({ - ...options, - ...params, - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsListRecentExecutionGroupsInfiniteQueryKey(options) - }); -}; +export const executionsListRecentExecutionGroupsInfiniteOptions = (options?: Options) => infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( +// @ts-ignore +{ + queryFn: async ({ pageParam, queryKey, signal }) => { + // @ts-ignore + const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { + query: { + offset: pageParam + } + }; + const params = createInfiniteParams(queryKey, page); + const { data } = await executionsListRecentExecutionGroups({ + ...options, + ...params, + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsListRecentExecutionGroupsInfiniteQueryKey(options) +}); export const executionsGetQueryKey = (options: Options) => createQueryKey('executionsGet', options); /** * Get + * * Inspect a specific execution */ -export const executionsGetOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsGet({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsGetQueryKey(options) - }); -}; +export const executionsGetOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsGet({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsGetQueryKey(options) +}); export const executionsExecutionQueryKey = (options: Options) => createQueryKey('executionsExecution', options); /** * Execution + * * Inspect a specific execution * * Gets the latest result if no execution_id is provided */ -export const executionsExecutionOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsExecution({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsExecutionQueryKey(options) - }); -}; +export const executionsExecutionOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsExecution({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsExecutionQueryKey(options) +}); export const executionsExecutionDatasetsQueryKey = (options: Options) => createQueryKey('executionsExecutionDatasets', options); /** * Execution Datasets + * * Query the datasets that were used for a specific execution */ -export const executionsExecutionDatasetsOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsExecutionDatasets({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsExecutionDatasetsQueryKey(options) - }); -}; +export const executionsExecutionDatasetsOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsExecutionDatasets({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsExecutionDatasetsQueryKey(options) +}); export const executionsExecutionLogsQueryKey = (options: Options) => createQueryKey('executionsExecutionLogs', options); /** * Execution Logs + * * Fetch the logs for an execution result */ -export const executionsExecutionLogsOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsExecutionLogs({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsExecutionLogsQueryKey(options) - }); -}; +export const executionsExecutionLogsOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsExecutionLogs({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsExecutionLogsQueryKey(options) +}); export const executionsMetricBundleQueryKey = (options: Options) => createQueryKey('executionsMetricBundle', options); /** * Metric Bundle + * * Fetch a result using the slug */ -export const executionsMetricBundleOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsMetricBundle({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsMetricBundleQueryKey(options) - }); -}; +export const executionsMetricBundleOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsMetricBundle({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsMetricBundleQueryKey(options) +}); export const executionsListMetricValuesQueryKey = (options: Options) => createQueryKey('executionsListMetricValues', options); /** * List Metric Values + * * Fetch metric values for a specific execution (both scalar and series) * * - `value_type`: Type of metric values - 'scalar', 'series', or 'all' (required) @@ -607,25 +589,24 @@ export const executionsListMetricValuesQueryKey = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsListMetricValues({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsListMetricValuesQueryKey(options) - }); -}; +export const executionsListMetricValuesOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsListMetricValues({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsListMetricValuesQueryKey(options) +}); export const executionsListMetricValuesInfiniteQueryKey = (options: Options): QueryKey> => createQueryKey('executionsListMetricValues', options, true); /** * List Metric Values + * * Fetch metric values for a specific execution (both scalar and series) * * - `value_type`: Type of metric values - 'scalar', 'series', or 'all' (required) @@ -633,170 +614,156 @@ export const executionsListMetricValuesInfiniteQueryKey = (options: Options) => { - return infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( - // @ts-ignore - { - queryFn: async ({ pageParam, queryKey, signal }) => { - // @ts-ignore - const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { - query: { - offset: pageParam - } - }; - const params = createInfiniteParams(queryKey, page); - const { data } = await executionsListMetricValues({ - ...options, - ...params, - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsListMetricValuesInfiniteQueryKey(options) - }); -}; +export const executionsListMetricValuesInfiniteOptions = (options: Options) => infiniteQueryOptions, QueryKey>, number | Pick>[0], 'body' | 'headers' | 'path' | 'query'>>( +// @ts-ignore +{ + queryFn: async ({ pageParam, queryKey, signal }) => { + // @ts-ignore + const page: Pick>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : { + query: { + offset: pageParam + } + }; + const params = createInfiniteParams(queryKey, page); + const { data } = await executionsListMetricValues({ + ...options, + ...params, + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsListMetricValuesInfiniteQueryKey(options) +}); export const executionsExecutionArchiveQueryKey = (options: Options) => createQueryKey('executionsExecutionArchive', options); /** * Execution Archive + * * Stream a tar.gz archive of the execution results * * The archive is created on-the-fly and streamed directly to the client. */ -export const executionsExecutionArchiveOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await executionsExecutionArchive({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: executionsExecutionArchiveQueryKey(options) - }); -}; +export const executionsExecutionArchiveOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await executionsExecutionArchive({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: executionsExecutionArchiveQueryKey(options) +}); export const explorerListCollectionsQueryKey = (options?: Options) => createQueryKey('explorerListCollections', options); /** * List Collections */ -export const explorerListCollectionsOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await explorerListCollections({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: explorerListCollectionsQueryKey(options) - }); -}; +export const explorerListCollectionsOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await explorerListCollections({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: explorerListCollectionsQueryKey(options) +}); export const explorerGetCollectionQueryKey = (options: Options) => createQueryKey('explorerGetCollection', options); /** * Get Collection */ -export const explorerGetCollectionOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await explorerGetCollection({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: explorerGetCollectionQueryKey(options) - }); -}; +export const explorerGetCollectionOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await explorerGetCollection({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: explorerGetCollectionQueryKey(options) +}); export const explorerListThemesQueryKey = (options?: Options) => createQueryKey('explorerListThemes', options); /** * List Themes */ -export const explorerListThemesOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await explorerListThemes({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: explorerListThemesQueryKey(options) - }); -}; +export const explorerListThemesOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await explorerListThemes({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: explorerListThemesQueryKey(options) +}); export const explorerGetThemeQueryKey = (options: Options) => createQueryKey('explorerGetTheme', options); /** * Get Theme */ -export const explorerGetThemeOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await explorerGetTheme({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: explorerGetThemeQueryKey(options) - }); -}; +export const explorerGetThemeOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await explorerGetTheme({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: explorerGetThemeQueryKey(options) +}); export const resultsGetResultQueryKey = (options: Options) => createQueryKey('resultsGetResult', options); /** * Get Result + * * Fetch a result */ -export const resultsGetResultOptions = (options: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await resultsGetResult({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: resultsGetResultQueryKey(options) - }); -}; +export const resultsGetResultOptions = (options: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await resultsGetResult({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: resultsGetResultQueryKey(options) +}); export const utilsHealthCheckQueryKey = (options?: Options) => createQueryKey('utilsHealthCheck', options); /** * Health Check */ -export const utilsHealthCheckOptions = (options?: Options) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await utilsHealthCheck({ - ...options, - ...queryKey[0], - signal, - throwOnError: true - }); - return data; - }, - queryKey: utilsHealthCheckQueryKey(options) - }); -}; \ No newline at end of file +export const utilsHealthCheckOptions = (options?: Options) => queryOptions>({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await utilsHealthCheck({ + ...options, + ...queryKey[0], + signal, + throwOnError: true + }); + return data; + }, + queryKey: utilsHealthCheckQueryKey(options) +}); diff --git a/frontend/src/client/client.gen.ts b/frontend/src/client/client.gen.ts index 6759c1f..cab3c70 100644 --- a/frontend/src/client/client.gen.ts +++ b/frontend/src/client/client.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { ClientOptions } from './types.gen'; -import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-fetch'; +import { type ClientOptions, type Config, createClient, createConfig } from './client'; +import type { ClientOptions as ClientOptions2 } from './types.gen'; /** * The `createClientConfig()` function will be called on client initialization @@ -11,6 +11,6 @@ import { type Config, type ClientOptions as DefaultClientOptions, createClient, * `setConfig()`. This is useful for example if you're using Next.js * to ensure your client always has the correct values. */ -export type CreateClientConfig = (override?: Config) => Config & T>; +export type CreateClientConfig = (override?: Config) => Config & T>; -export const client = createClient(createConfig()); \ No newline at end of file +export const client = createClient(createConfig()); diff --git a/frontend/src/client/client/client.gen.ts b/frontend/src/client/client/client.gen.ts new file mode 100644 index 0000000..9ec9ad8 --- /dev/null +++ b/frontend/src/client/client/client.gen.ts @@ -0,0 +1,298 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { createSseClient } from '../core/serverSentEvents.gen'; +import type { HttpMethod } from '../core/types.gen'; +import { getValidRequestBody } from '../core/utils.gen'; +import type { Client, Config, RequestOptions, ResolvedRequestOptions } from './types.gen'; +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; + +type ReqInit = Omit & { + body?: any; + headers: ReturnType; +}; + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); + + const getConfig = (): Config => ({ ..._config }); + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; + + const interceptors = createInterceptors(); + + const beforeRequest = async < + TData = unknown, + TResponseStyle extends 'data' | 'fields' = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, + >( + options: RequestOptions, + ) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined as string | undefined, + }; + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } + + if (opts.requestValidator) { + await opts.requestValidator(opts); + } + + if (opts.body !== undefined && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body) as string | undefined; + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } + + const resolvedOpts = opts as typeof opts & + ResolvedRequestOptions; + const url = buildUrl(resolvedOpts); + + return { opts: resolvedOpts, url }; + }; + + const request: Client['request'] = async (options) => { + const { opts, url } = await beforeRequest(options); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: getValidRequestBody(opts), + }; + + let request = new Request(url, requestInit); + + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response: Response; + + try { + response = await _fetch(request); + } catch (error) { + // Handle fetch exceptions (AbortError, network errors, etc.) + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, undefined as any, request, opts)) as unknown; + } + } + + finalError = finalError || ({} as unknown); + + if (opts.throwOnError) { + throw finalError; + } + + // Return error response + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + request, + response: undefined as any, + }; + } + + for (const fn of interceptors.response.fns) { + if (fn) { + response = await fn(response, request, opts); + } + } + + const result = { + request, + response, + }; + + if (response.ok) { + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; + + if (response.status === 204 || response.headers.get('Content-Length') === '0') { + let emptyData: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'text': + emptyData = await response[parseAs](); + break; + case 'formData': + emptyData = new FormData(); + break; + case 'stream': + emptyData = response.body; + break; + case 'json': + default: + emptyData = {}; + break; + } + return opts.responseStyle === 'data' + ? emptyData + : { + data: emptyData, + ...result, + }; + } + + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'text': + data = await response[parseAs](); + break; + case 'json': { + // Some servers return 200 with no Content-Length and empty body. + // response.json() would throw; read as text and parse if non-empty. + const text = await response.text(); + data = text ? JSON.parse(text) : {}; + break; + } + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } + + const textError = await response.text(); + let jsonError: unknown; + + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } + + const error = jsonError ?? textError; + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } + + finalError = finalError || ({} as string); + + if (opts.throwOnError) { + throw finalError; + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; + + const makeMethodFn = (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); + + const makeSseFn = (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + onRequest: async (url, init) => { + let request = new Request(url, init); + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + return request; + }, + serializedBody: getValidRequestBody(opts) as BodyInit | null | undefined, + url, + }); + }; + + const _buildUrl: Client['buildUrl'] = (options) => buildUrl({ ..._config, ...options }); + + return { + buildUrl: _buildUrl, + connect: makeMethodFn('CONNECT'), + delete: makeMethodFn('DELETE'), + get: makeMethodFn('GET'), + getConfig, + head: makeMethodFn('HEAD'), + interceptors, + options: makeMethodFn('OPTIONS'), + patch: makeMethodFn('PATCH'), + post: makeMethodFn('POST'), + put: makeMethodFn('PUT'), + request, + setConfig, + sse: { + connect: makeSseFn('CONNECT'), + delete: makeSseFn('DELETE'), + get: makeSseFn('GET'), + head: makeSseFn('HEAD'), + options: makeSseFn('OPTIONS'), + patch: makeSseFn('PATCH'), + post: makeSseFn('POST'), + put: makeSseFn('PUT'), + trace: makeSseFn('TRACE'), + }, + trace: makeMethodFn('TRACE'), + } as Client; +}; diff --git a/frontend/src/client/client/index.ts b/frontend/src/client/client/index.ts new file mode 100644 index 0000000..b295ede --- /dev/null +++ b/frontend/src/client/client/index.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; +export { createClient } from './client.gen'; +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/frontend/src/client/client/types.gen.ts b/frontend/src/client/client/types.gen.ts new file mode 100644 index 0000000..9813eea --- /dev/null +++ b/frontend/src/client/client/types.gen.ts @@ -0,0 +1,214 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth } from '../core/auth.gen'; +import type { + ServerSentEventsOptions, + ServerSentEventsResult, +} from '../core/serverSentEvents.gen'; +import type { Client as CoreClient, Config as CoreConfig } from '../core/types.gen'; +import type { Middleware } from './utils.gen'; + +export type ResponseStyle = 'data' | 'fields'; + +export interface Config + extends Omit, CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: 'arrayBuffer' | 'auto' | 'blob' | 'formData' | 'json' | 'stream' | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +export interface RequestOptions< + TData = unknown, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> + extends + Config<{ + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }>, + Pick< + ServerSentEventsOptions, + | 'onRequest' + | 'onSseError' + | 'onSseEvent' + | 'sseDefaultRetryDelay' + | 'sseMaxRetryAttempts' + | 'sseMaxRetryDelay' + > { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} + +export interface ResolvedRequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends RequestOptions { + serializedBody?: string; +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record ? TData[keyof TData] : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? (TData extends Record ? TData[keyof TData] : TData) | undefined + : ( + | { + data: TData extends Record ? TData[keyof TData] : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record ? TError[keyof TError] : TError; + } + ) & { + request: Request; + response: Response; + } + >; + +export interface ClientOptions { + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => RequestResult; + +type SseFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => Promise>; + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick>, 'method'>, +) => RequestResult; + +type BuildUrlFn = < + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, +>( + options: TData & Options, +) => string; + +export type Client = CoreClient & { + interceptors: Middleware; +}; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; + +export interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponse = unknown, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + ([TData] extends [never] ? unknown : Omit); diff --git a/frontend/src/client/client/utils.gen.ts b/frontend/src/client/client/utils.gen.ts new file mode 100644 index 0000000..5162192 --- /dev/null +++ b/frontend/src/client/client/utils.gen.ts @@ -0,0 +1,316 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { getAuthToken } from '../core/auth.gen'; +import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import { getUrl } from '../core/utils.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; + +export const createQuerySerializer = ({ + parameters = {}, + ...args +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; + + if (value === undefined || value === null) { + continue; + } + + const options = parameters[name] || args; + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'form', + value, + ...options.array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...options.object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved: options.allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; +}; + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = (contentType: string | null): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } + + const cleanContent = contentType.split(';')[0]?.trim(); + + if (!cleanContent) { + return; + } + + if (cleanContent.startsWith('application/json') || cleanContent.endsWith('+json')) { + return 'json'; + } + + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => cleanContent.startsWith(type)) + ) { + return 'blob'; + } + + if (cleanContent.startsWith('text/')) { + return 'text'; + } + + return; +}; + +const checkForExistence = ( + options: Pick & { + headers: Headers; + }, + name?: string, +): boolean => { + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; +}; + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } + + const token = await getAuthToken(auth, options.auth); + + if (!token) { + continue; + } + + const name = auth.name ?? 'Authorization'; + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } +}; + +export const buildUrl: Client['buildUrl'] = (options) => + getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + +const headersEntries = (headers: Headers): Array<[string, string]> => { + const entries: Array<[string, string]> = []; + headers.forEach((value, key) => { + entries.push([key, value]); + }); + return entries; +}; + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header) { + continue; + } + + const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header); + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e., their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; +}; + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise; + +type ReqInterceptor = (request: Req, options: Options) => Req | Promise; + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; + +class Interceptors { + fns: Array = []; + + clear(): void { + this.fns = []; + } + + eject(id: number | Interceptor): void { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = null; + } + } + + exists(id: number | Interceptor): boolean { + const index = this.getInterceptorIndex(id); + return Boolean(this.fns[index]); + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this.fns[id] ? id : -1; + } + return this.fns.indexOf(id); + } + + update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = fn; + return id; + } + return false; + } + + use(fn: Interceptor): number { + this.fns.push(fn); + return this.fns.length - 1; + } +} + +export interface Middleware { + error: Interceptors>; + request: Interceptors>; + response: Interceptors>; +} + +export const createInterceptors = (): Middleware< + Req, + Res, + Err, + Options +> => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}); + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}); + +const defaultHeaders = { + 'Content-Type': 'application/json', +}; + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}); diff --git a/frontend/src/client/core/auth.gen.ts b/frontend/src/client/core/auth.gen.ts new file mode 100644 index 0000000..3ebf994 --- /dev/null +++ b/frontend/src/client/core/auth.gen.ts @@ -0,0 +1,41 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AuthToken = string | undefined; + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/frontend/src/client/core/bodySerializer.gen.ts b/frontend/src/client/core/bodySerializer.gen.ts new file mode 100644 index 0000000..67daca6 --- /dev/null +++ b/frontend/src/client/core/bodySerializer.gen.ts @@ -0,0 +1,82 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { ArrayStyle, ObjectStyle, SerializerOptions } from './pathSerializer.gen'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: unknown) => unknown; + +type QuerySerializerOptionsObject = { + allowReserved?: boolean; + array?: Partial>; + object?: Partial>; +}; + +export type QuerySerializerOptions = QuerySerializerOptionsObject & { + /** + * Per-parameter serialization overrides. When provided, these settings + * override the global array/object settings for specific parameter names. + */ + parameters?: Record; +}; + +const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: (body: unknown): FormData => { + const data = new FormData(); + + Object.entries(body as Record).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: unknown): string => + JSON.stringify(body, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: (body: unknown): string => { + const data = new URLSearchParams(); + + Object.entries(body as Record).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data.toString(); + }, +}; diff --git a/frontend/src/client/core/params.gen.ts b/frontend/src/client/core/params.gen.ts new file mode 100644 index 0000000..7955601 --- /dev/null +++ b/frontend/src/client/core/params.gen.ts @@ -0,0 +1,169 @@ +// This file is auto-generated by @hey-api/openapi-ts + +type Slot = 'body' | 'headers' | 'path' | 'query'; + +export type Field = + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + } + | { + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If `in` is omitted, `map` aliases `key` to the transport layer. + */ + map: Slot; + }; + +export interface Fields { + allowExtra?: Partial>; + args?: ReadonlyArray; +} + +export type FieldsConfig = ReadonlyArray; + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +}; +const extraPrefixes = Object.entries(extraPrefixesMap); + +type KeyMap = Map< + string, + | { + in: Slot; + map?: string; + } + | { + in?: never; + map: Slot; + } +>; + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map(); + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if ('key' in config) { + map.set(config.key, { + map: config.map, + }); + } else if (config.args) { + buildKeyMap(config.args, map); + } + } + + return map; +}; + +interface Params { + body: unknown; + headers: Record; + path: Record; + query: Record; +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Array.isArray(value) && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } +}; + +export const buildClientParams = (args: ReadonlyArray, fields: FieldsConfig) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; + + const map = buildKeyMap(fields); + + let config: FieldsConfig[number] | undefined; + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } + + if (!config) { + continue; + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + if (field.in) { + (params[field.in] as Record)[name] = arg; + } + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); + + if (field) { + if (field.in) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + params[field.map] = value; + } + } else { + const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)); + + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[key.slice(prefix.length)] = value; + } else if ('allowExtra' in config && config.allowExtra) { + for (const [slot, allowed] of Object.entries(config.allowExtra)) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } + + stripEmptySlots(params); + + return params; +}; diff --git a/frontend/src/client/core/pathSerializer.gen.ts b/frontend/src/client/core/pathSerializer.gen.ts new file mode 100644 index 0000000..994b284 --- /dev/null +++ b/frontend/src/client/core/pathSerializer.gen.ts @@ -0,0 +1,171 @@ +// This file is auto-generated by @hey-api/openapi-ts + +interface SerializeOptions extends SerializePrimitiveOptions, SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date; + valueOnly?: boolean; +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues; +}; diff --git a/frontend/src/client/core/queryKeySerializer.gen.ts b/frontend/src/client/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000..5000df6 --- /dev/null +++ b/frontend/src/client/core/queryKeySerializer.gen.ts @@ -0,0 +1,117 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b)); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => { + if (value === null) { + return null; + } + + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return value; + } + + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if (typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/frontend/src/client/core/serverSentEvents.gen.ts b/frontend/src/client/core/serverSentEvents.gen.ts new file mode 100644 index 0000000..6aa6cf0 --- /dev/null +++ b/frontend/src/client/core/serverSentEvents.gen.ts @@ -0,0 +1,243 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Config } from './types.gen'; + +export type ServerSentEventsOptions = Omit & + Pick & { + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Implementing clients can call request interceptors inside this hook. + */ + onRequest?: (url: string, init: RequestInit) => Promise; + /** + * Callback invoked when a network or parsing error occurs during streaming. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param error The error that occurred. + */ + onSseError?: (error: unknown) => void; + /** + * Callback invoked when an event is streamed from the server. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param event Event streamed from the server. + * @returns Nothing (void). + */ + onSseEvent?: (event: StreamEvent) => void; + serializedBody?: RequestInit['body']; + /** + * Default retry delay in milliseconds. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 3000 + */ + sseDefaultRetryDelay?: number; + /** + * Maximum number of retry attempts before giving up. + */ + sseMaxRetryAttempts?: number; + /** + * Maximum retry delay in milliseconds. + * + * Applies only when exponential backoff is used. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 30000 + */ + sseMaxRetryDelay?: number; + /** + * Optional sleep function for retry backoff. + * + * Defaults to using `setTimeout`. + */ + sseSleepFn?: (ms: number) => Promise; + url: string; + }; + +export interface StreamEvent { + data: TData; + event?: string; + id?: string; + retry?: number; +} + +export type ServerSentEventsResult = { + stream: AsyncGenerator< + TData extends Record ? TData[keyof TData] : TData, + TReturn, + TNext + >; +}; + +export const createSseClient = ({ + onRequest, + onSseError, + onSseEvent, + responseTransformer, + responseValidator, + sseDefaultRetryDelay, + sseMaxRetryAttempts, + sseMaxRetryDelay, + sseSleepFn, + url, + ...options +}: ServerSentEventsOptions): ServerSentEventsResult => { + let lastEventId: string | undefined; + + const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + + const createStream = async function* () { + let retryDelay: number = sseDefaultRetryDelay ?? 3000; + let attempt = 0; + const signal = options.signal ?? new AbortController().signal; + + while (true) { + if (signal.aborted) break; + + attempt++; + + const headers = + options.headers instanceof Headers + ? options.headers + : new Headers(options.headers as Record | undefined); + + if (lastEventId !== undefined) { + headers.set('Last-Event-ID', lastEventId); + } + + try { + const requestInit: RequestInit = { + redirect: 'follow', + ...options, + body: options.serializedBody, + headers, + signal, + }; + let request = new Request(url, requestInit); + if (onRequest) { + request = await onRequest(url, requestInit); + } + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = options.fetch ?? globalThis.fetch; + const response = await _fetch(request); + + if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`); + + if (!response.body) throw new Error('No body in SSE response'); + + const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); + + let buffer = ''; + + const abortHandler = () => { + try { + reader.cancel(); + } catch { + // noop + } + }; + + signal.addEventListener('abort', abortHandler); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += value; + // Normalize line endings: CRLF -> LF, then CR -> LF + buffer = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() ?? ''; + + for (const chunk of chunks) { + const lines = chunk.split('\n'); + const dataLines: Array = []; + let eventName: string | undefined; + + for (const line of lines) { + if (line.startsWith('data:')) { + dataLines.push(line.replace(/^data:\s*/, '')); + } else if (line.startsWith('event:')) { + eventName = line.replace(/^event:\s*/, ''); + } else if (line.startsWith('id:')) { + lastEventId = line.replace(/^id:\s*/, ''); + } else if (line.startsWith('retry:')) { + const parsed = Number.parseInt(line.replace(/^retry:\s*/, ''), 10); + if (!Number.isNaN(parsed)) { + retryDelay = parsed; + } + } + } + + let data: unknown; + let parsedJson = false; + + if (dataLines.length) { + const rawData = dataLines.join('\n'); + try { + data = JSON.parse(rawData); + parsedJson = true; + } catch { + data = rawData; + } + } + + if (parsedJson) { + if (responseValidator) { + await responseValidator(data); + } + + if (responseTransformer) { + data = await responseTransformer(data); + } + } + + onSseEvent?.({ + data, + event: eventName, + id: lastEventId, + retry: retryDelay, + }); + + if (dataLines.length) { + yield data as any; + } + } + } + } finally { + signal.removeEventListener('abort', abortHandler); + reader.releaseLock(); + } + + break; // exit loop on normal completion + } catch (error) { + // connection failed or aborted; retry after delay + onSseError?.(error); + + if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) { + break; // stop after firing error + } + + // exponential backoff: double retry each attempt, cap at 30s + const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000); + await sleep(backoff); + } + } + }; + + const stream = createStream(); + + return { stream }; +}; diff --git a/frontend/src/client/core/types.gen.ts b/frontend/src/client/core/types.gen.ts new file mode 100644 index 0000000..9efe71d --- /dev/null +++ b/frontend/src/client/core/types.gen.ts @@ -0,0 +1,104 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth, AuthToken } from './auth.gen'; +import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from './bodySerializer.gen'; + +export type HttpMethod = + | 'connect' + | 'delete' + | 'get' + | 'head' + | 'options' + | 'patch' + | 'post' + | 'put' + | 'trace'; + +export type Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, + SseFn = never, +> = { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + getConfig: () => Config; + request: RequestFn; + setConfig: (config: Config) => Config; +} & { + [K in HttpMethod]: MethodFn; +} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } }); + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + string | number | boolean | (string | number | boolean)[] | null | undefined | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: Uppercase; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g., converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true ? never : K]: T[K]; +}; diff --git a/frontend/src/client/core/utils.gen.ts b/frontend/src/client/core/utils.gen.ts new file mode 100644 index 0000000..9a4fec7 --- /dev/null +++ b/frontend/src/client/core/utils.gen.ts @@ -0,0 +1,140 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen'; +import { + type ArraySeparatorStyle, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer.gen'; + +export interface PathSerializer { + path: Record; + url: string; +} + +export const PATH_PARAM_RE = /\{[^{}]+\}/g; + +export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; + + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } + + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } + + const value = path[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + url = url.replace(match, serializeArrayParam({ explode, name, style, value })); + continue; + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; +}; + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; +}; + +export function getValidRequestBody(options: { + body?: unknown; + bodySerializer?: BodySerializer | null; + serializedBody?: unknown; +}) { + const hasBody = options.body !== undefined; + const isSerializedBody = hasBody && options.bodySerializer; + + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== ''; + + return hasSerializedBody ? options.serializedBody : null; + } + + // not all clients implement a serializedBody property (i.e., client-axios) + return options.body !== '' ? options.body : null; + } + + // plain/text body + if (hasBody) { + return options.body; + } + + // no body was provided + return undefined; +} diff --git a/frontend/src/client/index.ts b/frontend/src/client/index.ts index e64537d..360ca49 100644 --- a/frontend/src/client/index.ts +++ b/frontend/src/client/index.ts @@ -1,3 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file + +export { cmip7AssessmentFastTrackAftGetAftDiagnostic, cmip7AssessmentFastTrackAftListAftDiagnostics, datasetsExecutions, datasetsGet, datasetsList, diagnosticsFacets, diagnosticsGet, diagnosticsList, diagnosticsListExecutionGroups, diagnosticsListExecutions, diagnosticsListMetricValues, executionsExecution, executionsExecutionArchive, executionsExecutionDatasets, executionsExecutionLogs, executionsGet, executionsGetExecutionStatistics, executionsListMetricValues, executionsListRecentExecutionGroups, executionsMetricBundle, explorerGetCollection, explorerGetTheme, explorerListCollections, explorerListThemes, type Options, resultsGetResult, utilsHealthCheck } from './sdk.gen'; +export type { AftCollectionCard, AftCollectionCardContent, AftCollectionContent, AftCollectionDetail, AftCollectionDiagnosticLink, AftCollectionFilterControl, AftCollectionGroupingConfig, AftCollectionPlainLanguage, AftCollectionSummary, AftDiagnosticDetail, AftDiagnosticSummary, ClientOptions, CmecMetric, Cmip6DatasetMetadata, Cmip7AssessmentFastTrackAftGetAftDiagnosticData, Cmip7AssessmentFastTrackAftGetAftDiagnosticError, Cmip7AssessmentFastTrackAftGetAftDiagnosticErrors, Cmip7AssessmentFastTrackAftGetAftDiagnosticResponse, Cmip7AssessmentFastTrackAftGetAftDiagnosticResponses, Cmip7AssessmentFastTrackAftListAftDiagnosticsData, Cmip7AssessmentFastTrackAftListAftDiagnosticsResponse, Cmip7AssessmentFastTrackAftListAftDiagnosticsResponses, CollectionDataset, CollectionDatasetWritable, CollectionDiagnosticSummary, CollectionDiagnosticSummaryWritable, CollectionExecution, CollectionExecutionGroup, CollectionExecutionGroupWritable, CollectionExecutionWritable, Dataset, DatasetsExecutionsData, DatasetsExecutionsError, DatasetsExecutionsErrors, DatasetsExecutionsResponse, DatasetsExecutionsResponses, DatasetsGetData, DatasetsGetError, DatasetsGetErrors, DatasetsGetResponse, DatasetsGetResponses, DatasetsListData, DatasetsListError, DatasetsListErrors, DatasetsListResponse, DatasetsListResponses, DatasetWritable, DiagnosticsFacetsData, DiagnosticsFacetsResponse, DiagnosticsFacetsResponses, DiagnosticsGetData, DiagnosticsGetError, DiagnosticsGetErrors, DiagnosticsGetResponse, DiagnosticsGetResponses, DiagnosticsListData, DiagnosticsListExecutionGroupsData, DiagnosticsListExecutionGroupsError, DiagnosticsListExecutionGroupsErrors, DiagnosticsListExecutionGroupsResponse, DiagnosticsListExecutionGroupsResponses, DiagnosticsListExecutionsData, DiagnosticsListExecutionsError, DiagnosticsListExecutionsErrors, DiagnosticsListExecutionsResponse, DiagnosticsListExecutionsResponses, DiagnosticsListMetricValuesData, DiagnosticsListMetricValuesError, DiagnosticsListMetricValuesErrors, DiagnosticsListMetricValuesResponse, DiagnosticsListMetricValuesResponses, DiagnosticsListResponse, DiagnosticsListResponses, DiagnosticSummary, Execution, ExecutionGroup, ExecutionOutput, ExecutionsExecutionArchiveData, ExecutionsExecutionArchiveError, ExecutionsExecutionArchiveErrors, ExecutionsExecutionArchiveResponses, ExecutionsExecutionData, ExecutionsExecutionDatasetsData, ExecutionsExecutionDatasetsError, ExecutionsExecutionDatasetsErrors, ExecutionsExecutionDatasetsResponse, ExecutionsExecutionDatasetsResponses, ExecutionsExecutionError, ExecutionsExecutionErrors, ExecutionsExecutionLogsData, ExecutionsExecutionLogsError, ExecutionsExecutionLogsErrors, ExecutionsExecutionLogsResponses, ExecutionsExecutionResponse, ExecutionsExecutionResponses, ExecutionsGetData, ExecutionsGetError, ExecutionsGetErrors, ExecutionsGetExecutionStatisticsData, ExecutionsGetExecutionStatisticsResponse, ExecutionsGetExecutionStatisticsResponses, ExecutionsGetResponse, ExecutionsGetResponses, ExecutionsListMetricValuesData, ExecutionsListMetricValuesError, ExecutionsListMetricValuesErrors, ExecutionsListMetricValuesResponse, ExecutionsListMetricValuesResponses, ExecutionsListRecentExecutionGroupsData, ExecutionsListRecentExecutionGroupsError, ExecutionsListRecentExecutionGroupsErrors, ExecutionsListRecentExecutionGroupsResponse, ExecutionsListRecentExecutionGroupsResponses, ExecutionsMetricBundleData, ExecutionsMetricBundleError, ExecutionsMetricBundleErrors, ExecutionsMetricBundleResponse, ExecutionsMetricBundleResponses, ExecutionStats, ExecutionStatsWritable, ExplorerGetCollectionData, ExplorerGetCollectionError, ExplorerGetCollectionErrors, ExplorerGetCollectionResponse, ExplorerGetCollectionResponses, ExplorerGetThemeData, ExplorerGetThemeError, ExplorerGetThemeErrors, ExplorerGetThemeResponse, ExplorerGetThemeResponses, ExplorerListCollectionsData, ExplorerListCollectionsResponse, ExplorerListCollectionsResponses, ExplorerListThemesData, ExplorerListThemesResponse, ExplorerListThemesResponses, Facet, GroupBy, HttpValidationError, MetricDimensions, MetricValueCollection, MetricValueFacetSummary, MetricValueType, ProviderSummary, RefDiagnosticLink, ReferenceDatasetLink, ResultOutputType, ResultsGetResultData, ResultsGetResultError, ResultsGetResultErrors, ResultsGetResultResponses, ScalarValue, SeriesValue, ThemeDetail, ThemeSummary, UtilsHealthCheckData, UtilsHealthCheckResponse, UtilsHealthCheckResponses, ValidationError } from './types.gen'; diff --git a/frontend/src/client/schemas.gen.ts b/frontend/src/client/schemas.gen.ts index c3e471c..b8b73ec 100644 --- a/frontend/src/client/schemas.gen.ts +++ b/frontend/src/client/schemas.gen.ts @@ -30,14 +30,17 @@ export const AFTCollectionCardSchema = { }, content: { items: { - '$ref': '#/components/schemas/AFTCollectionCardContent' + $ref: '#/components/schemas/AFTCollectionCardContent' }, type: 'array', title: 'Content' } }, type: 'object', - required: ['title', 'content'], + required: [ + 'title', + 'content' + ], title: 'AFTCollectionCard' } as const; @@ -45,7 +48,12 @@ export const AFTCollectionCardContentSchema = { properties: { type: { type: 'string', - enum: ['box-whisker-chart', 'figure-gallery', 'series-chart', 'taylor-diagram'], + enum: [ + 'box-whisker-chart', + 'figure-gallery', + 'series-chart', + 'taylor-diagram' + ], title: 'Type' }, provider: { @@ -86,7 +94,10 @@ export const AFTCollectionCardContentSchema = { anyOf: [ { type: 'integer', - enum: [1, 2] + enum: [ + 1, + 2 + ] }, { type: 'null' @@ -232,7 +243,7 @@ export const AFTCollectionCardContentSchema = { grouping_config: { anyOf: [ { - '$ref': '#/components/schemas/AFTCollectionGroupingConfig' + $ref: '#/components/schemas/AFTCollectionGroupingConfig' }, { type: 'null' @@ -243,7 +254,7 @@ export const AFTCollectionCardContentSchema = { anyOf: [ { items: { - '$ref': '#/components/schemas/AFTCollectionFilterControl' + $ref: '#/components/schemas/AFTCollectionFilterControl' }, type: 'array' }, @@ -257,7 +268,7 @@ export const AFTCollectionCardContentSchema = { anyOf: [ { items: { - '$ref': '#/components/schemas/ReferenceDatasetLink' + $ref: '#/components/schemas/ReferenceDatasetLink' }, type: 'array' }, @@ -269,7 +280,12 @@ export const AFTCollectionCardContentSchema = { } }, type: 'object', - required: ['type', 'provider', 'diagnostic', 'title'], + required: [ + 'type', + 'provider', + 'diagnostic', + 'title' + ], title: 'AFTCollectionCardContent' } as const; @@ -322,7 +338,7 @@ export const AFTCollectionContentSchema = { plain_language: { anyOf: [ { - '$ref': '#/components/schemas/AFTCollectionPlainLanguage' + $ref: '#/components/schemas/AFTCollectionPlainLanguage' }, { type: 'null' @@ -405,7 +421,7 @@ export const AFTCollectionDetailSchema = { content: { anyOf: [ { - '$ref': '#/components/schemas/AFTCollectionContent' + $ref: '#/components/schemas/AFTCollectionContent' }, { type: 'null' @@ -414,21 +430,26 @@ export const AFTCollectionDetailSchema = { }, diagnostics: { items: { - '$ref': '#/components/schemas/AFTCollectionDiagnosticLink' + $ref: '#/components/schemas/AFTCollectionDiagnosticLink' }, type: 'array', title: 'Diagnostics' }, explorer_cards: { items: { - '$ref': '#/components/schemas/AFTCollectionCard' + $ref: '#/components/schemas/AFTCollectionCard' }, type: 'array', title: 'Explorer Cards' } }, type: 'object', - required: ['id', 'name', 'diagnostics', 'explorer_cards'], + required: [ + 'id', + 'name', + 'diagnostics', + 'explorer_cards' + ], title: 'AFTCollectionDetail' } as const; @@ -458,7 +479,10 @@ export const AFTCollectionDiagnosticLinkSchema = { } }, type: 'object', - required: ['provider_slug', 'diagnostic_slug'], + required: [ + 'provider_slug', + 'diagnostic_slug' + ], title: 'AFTCollectionDiagnosticLink' } as const; @@ -506,7 +530,9 @@ export const AFTCollectionFilterControlSchema = { } }, type: 'object', - required: ['filter_key'], + required: [ + 'filter_key' + ], title: 'AFTCollectionFilterControl', description: 'A user-facing filter control (e.g. a dropdown) that overrides an other_filters key.' } as const; @@ -534,7 +560,10 @@ export const AFTCollectionGroupingConfigSchema = { } }, type: 'object', - required: ['group_by', 'hue'], + required: [ + 'group_by', + 'hue' + ], title: 'AFTCollectionGroupingConfig' } as const; @@ -616,7 +645,11 @@ export const AFTCollectionSummarySchema = { } }, type: 'object', - required: ['id', 'name', 'card_count'], + required: [ + 'id', + 'name', + 'card_count' + ], title: 'AFTCollectionSummary' } as const; @@ -712,14 +745,25 @@ export const AFTDiagnosticDetailSchema = { }, diagnostics: { items: { - '$ref': '#/components/schemas/RefDiagnosticLink' + $ref: '#/components/schemas/RefDiagnosticLink' }, type: 'array', title: 'Diagnostics' } }, type: 'object', - required: ['id', 'name', 'theme', 'version_control', 'reference_dataset', 'endorser', 'provider_link', 'description', 'short_description', 'diagnostics'], + required: [ + 'id', + 'name', + 'theme', + 'version_control', + 'reference_dataset', + 'endorser', + 'provider_link', + 'description', + 'short_description', + 'diagnostics' + ], title: 'AFTDiagnosticDetail' } as const; @@ -815,14 +859,24 @@ export const AFTDiagnosticSummarySchema = { } }, type: 'object', - required: ['id', 'name', 'theme', 'version_control', 'reference_dataset', 'endorser', 'provider_link', 'description', 'short_description'], + required: [ + 'id', + 'name', + 'theme', + 'version_control', + 'reference_dataset', + 'endorser', + 'provider_link', + 'description', + 'short_description' + ], title: 'AFTDiagnosticSummary' } as const; export const CMECMetricSchema = { properties: { DIMENSIONS: { - '$ref': '#/components/schemas/MetricDimensions' + $ref: '#/components/schemas/MetricDimensions' }, RESULTS: { additionalProperties: true, @@ -868,11 +922,12 @@ export const CMECMetricSchema = { }, additionalProperties: true, type: 'object', - required: ['DIMENSIONS', 'RESULTS'], + required: [ + 'DIMENSIONS', + 'RESULTS' + ], title: 'CMECMetric', - description: `CMEC diagnostic bundle object - -Contains the diagnostics calculated during a diagnostic execution, in a standardised format.` + description: 'CMEC diagnostic bundle object\n\nContains the diagnostics calculated during a diagnostic execution, in a standardised format.' } as const; export const CMIP6DatasetMetadataSchema = { @@ -895,7 +950,12 @@ export const CMIP6DatasetMetadataSchema = { } }, type: 'object', - required: ['variable_id', 'source_id', 'experiment_id', 'variant_label'], + required: [ + 'variable_id', + 'source_id', + 'experiment_id', + 'variant_label' + ], title: 'CMIP6DatasetMetadata' } as const; @@ -903,7 +963,7 @@ export const Collection_Dataset_Schema = { properties: { data: { items: { - '$ref': '#/components/schemas/Dataset' + $ref: '#/components/schemas/Dataset' }, type: 'array', title: 'Data' @@ -927,7 +987,10 @@ export const Collection_Dataset_Schema = { } }, type: 'object', - required: ['data', 'count'], + required: [ + 'data', + 'count' + ], title: 'Collection[Dataset]' } as const; @@ -935,7 +998,7 @@ export const Collection_DiagnosticSummary_Schema = { properties: { data: { items: { - '$ref': '#/components/schemas/DiagnosticSummary' + $ref: '#/components/schemas/DiagnosticSummary' }, type: 'array', title: 'Data' @@ -959,7 +1022,10 @@ export const Collection_DiagnosticSummary_Schema = { } }, type: 'object', - required: ['data', 'count'], + required: [ + 'data', + 'count' + ], title: 'Collection[DiagnosticSummary]' } as const; @@ -967,7 +1033,7 @@ export const Collection_ExecutionGroup_Schema = { properties: { data: { items: { - '$ref': '#/components/schemas/ExecutionGroup' + $ref: '#/components/schemas/ExecutionGroup' }, type: 'array', title: 'Data' @@ -991,7 +1057,10 @@ export const Collection_ExecutionGroup_Schema = { } }, type: 'object', - required: ['data', 'count'], + required: [ + 'data', + 'count' + ], title: 'Collection[ExecutionGroup]' } as const; @@ -999,7 +1068,7 @@ export const Collection_Execution_Schema = { properties: { data: { items: { - '$ref': '#/components/schemas/Execution' + $ref: '#/components/schemas/Execution' }, type: 'array', title: 'Data' @@ -1023,7 +1092,10 @@ export const Collection_Execution_Schema = { } }, type: 'object', - required: ['data', 'count'], + required: [ + 'data', + 'count' + ], title: 'Collection[Execution]' } as const; @@ -1044,7 +1116,7 @@ export const DatasetSchema = { metadata: { anyOf: [ { - '$ref': '#/components/schemas/CMIP6DatasetMetadata' + $ref: '#/components/schemas/CMIP6DatasetMetadata' }, { type: 'null' @@ -1065,7 +1137,13 @@ export const DatasetSchema = { } }, type: 'object', - required: ['id', 'slug', 'dataset_type', 'metadata', 'more_info_url'], + required: [ + 'id', + 'slug', + 'dataset_type', + 'metadata', + 'more_info_url' + ], title: 'Dataset' } as const; @@ -1076,7 +1154,7 @@ export const DiagnosticSummarySchema = { title: 'Id' }, provider: { - '$ref': '#/components/schemas/ProviderSummary' + $ref: '#/components/schemas/ProviderSummary' }, slug: { type: 'string', @@ -1127,7 +1205,7 @@ export const DiagnosticSummarySchema = { }, group_by: { items: { - '$ref': '#/components/schemas/GroupBy' + $ref: '#/components/schemas/GroupBy' }, type: 'array', title: 'Group By' @@ -1135,7 +1213,7 @@ export const DiagnosticSummarySchema = { aft_link: { anyOf: [ { - '$ref': '#/components/schemas/AFTDiagnosticDetail' + $ref: '#/components/schemas/AFTDiagnosticDetail' }, { type: 'null' @@ -1146,7 +1224,7 @@ export const DiagnosticSummarySchema = { anyOf: [ { items: { - '$ref': '#/components/schemas/ReferenceDatasetLink' + $ref: '#/components/schemas/ReferenceDatasetLink' }, type: 'array' }, @@ -1172,12 +1250,25 @@ export const DiagnosticSummarySchema = { } }, type: 'object', - required: ['id', 'provider', 'slug', 'name', 'description', 'execution_groups', 'has_metric_values', 'has_scalar_values', 'has_series_values', 'execution_count', 'successful_execution_count', 'execution_group_count', 'successful_execution_group_count', 'group_by', 'aft_link'], + required: [ + 'id', + 'provider', + 'slug', + 'name', + 'description', + 'execution_groups', + 'has_metric_values', + 'has_scalar_values', + 'has_series_values', + 'execution_count', + 'successful_execution_count', + 'execution_group_count', + 'successful_execution_group_count', + 'group_by', + 'aft_link' + ], title: 'DiagnosticSummary', - description: `Summary information about a diagnostic. - -A diagnostic is a specific metric or set of metrics calculated by a provider. -Each diagnostic is associated may be associated with one CMIP Assessment Fast Track (AFT) diagnostics.` + description: 'Summary information about a diagnostic.\n\nA diagnostic is a specific metric or set of metrics calculated by a provider.\nEach diagnostic is associated may be associated with one CMIP Assessment Fast Track (AFT) diagnostics.' } as const; export const ExecutionSchema = { @@ -1214,14 +1305,23 @@ export const ExecutionSchema = { }, outputs: { items: { - '$ref': '#/components/schemas/ExecutionOutput' + $ref: '#/components/schemas/ExecutionOutput' }, type: 'array', title: 'Outputs' } }, type: 'object', - required: ['id', 'dataset_hash', 'dataset_count', 'successful', 'retracted', 'created_at', 'updated_at', 'outputs'], + required: [ + 'id', + 'dataset_hash', + 'dataset_count', + 'successful', + 'retracted', + 'created_at', + 'updated_at', + 'outputs' + ], title: 'Execution' } as const; @@ -1241,7 +1341,7 @@ export const ExecutionGroupSchema = { }, executions: { items: { - '$ref': '#/components/schemas/Execution' + $ref: '#/components/schemas/Execution' }, type: 'array', title: 'Executions' @@ -1249,7 +1349,7 @@ export const ExecutionGroupSchema = { latest_execution: { anyOf: [ { - '$ref': '#/components/schemas/Execution' + $ref: '#/components/schemas/Execution' }, { type: 'null' @@ -1277,7 +1377,7 @@ export const ExecutionGroupSchema = { title: 'Selectors' }, diagnostic: { - '$ref': '#/components/schemas/DiagnosticSummary' + $ref: '#/components/schemas/DiagnosticSummary' }, created_at: { type: 'string', @@ -1291,7 +1391,17 @@ export const ExecutionGroupSchema = { } }, type: 'object', - required: ['id', 'key', 'dirty', 'executions', 'latest_execution', 'selectors', 'diagnostic', 'created_at', 'updated_at'], + required: [ + 'id', + 'key', + 'dirty', + 'executions', + 'latest_execution', + 'selectors', + 'diagnostic', + 'created_at', + 'updated_at' + ], title: 'ExecutionGroup' } as const; @@ -1306,7 +1416,7 @@ export const ExecutionOutputSchema = { title: 'Execution Id' }, output_type: { - '$ref': '#/components/schemas/ResultOutputType' + $ref: '#/components/schemas/ResultOutputType' }, filename: { type: 'string', @@ -1340,7 +1450,18 @@ export const ExecutionOutputSchema = { } }, type: 'object', - required: ['id', 'execution_id', 'output_type', 'filename', 'short_name', 'long_name', 'description', 'created_at', 'updated_at', 'url'], + required: [ + 'id', + 'execution_id', + 'output_type', + 'filename', + 'short_name', + 'long_name', + 'description', + 'created_at', + 'updated_at', + 'url' + ], title: 'ExecutionOutput' } as const; @@ -1382,7 +1503,16 @@ export const ExecutionStatsSchema = { } }, type: 'object', - required: ['total_execution_groups', 'successful_execution_groups', 'failed_execution_groups', 'scalar_value_count', 'series_value_count', 'total_datasets', 'total_files', 'success_rate_percentage'], + required: [ + 'total_execution_groups', + 'successful_execution_groups', + 'failed_execution_groups', + 'scalar_value_count', + 'series_value_count', + 'total_datasets', + 'total_files', + 'success_rate_percentage' + ], title: 'ExecutionStats', description: 'Statistics for execution groups and their success rates.' } as const; @@ -1402,7 +1532,10 @@ export const FacetSchema = { } }, type: 'object', - required: ['key', 'values'], + required: [ + 'key', + 'values' + ], title: 'Facet' } as const; @@ -1428,7 +1561,10 @@ export const GroupBySchema = { } }, type: 'object', - required: ['source_type', 'group_by'], + required: [ + 'source_type', + 'group_by' + ], title: 'GroupBy' } as const; @@ -1436,7 +1572,7 @@ export const HTTPValidationErrorSchema = { properties: { detail: { items: { - '$ref': '#/components/schemas/ValidationError' + $ref: '#/components/schemas/ValidationError' }, type: 'array', title: 'Detail' @@ -1450,10 +1586,7 @@ export const MetricDimensionsSchema = { additionalProperties: true, type: 'object', title: 'MetricDimensions', - description: `CMEC diagnostic bundle DIMENSIONS object - -This describes the order of the dimensions and their possible values. -The order of the dimensions matter as that determines how the executions are nested.`, + description: 'CMEC diagnostic bundle DIMENSIONS object\n\nThis describes the order of the dimensions and their possible values.\nThe order of the dimensions matter as that determines how the executions are nested.', default: { json_structure: [] } @@ -1465,10 +1598,10 @@ export const MetricValueCollectionSchema = { items: { anyOf: [ { - '$ref': '#/components/schemas/ScalarValue' + $ref: '#/components/schemas/ScalarValue' }, { - '$ref': '#/components/schemas/SeriesValue' + $ref: '#/components/schemas/SeriesValue' } ] }, @@ -1485,7 +1618,7 @@ export const MetricValueCollectionSchema = { }, facets: { items: { - '$ref': '#/components/schemas/Facet' + $ref: '#/components/schemas/Facet' }, type: 'array', title: 'Facets' @@ -1521,7 +1654,13 @@ export const MetricValueCollectionSchema = { } }, type: 'object', - required: ['data', 'count', 'total_count', 'facets', 'types'], + required: [ + 'data', + 'count', + 'total_count', + 'facets', + 'types' + ], title: 'MetricValueCollection' } as const; @@ -1543,14 +1682,20 @@ export const MetricValueFacetSummarySchema = { } }, type: 'object', - required: ['dimensions', 'count'], + required: [ + 'dimensions', + 'count' + ], title: 'MetricValueFacetSummary', description: 'Summary of the dimensions used in a metric value collection.' } as const; export const MetricValueTypeSchema = { type: 'string', - enum: ['scalar', 'series'], + enum: [ + 'scalar', + 'series' + ], title: 'MetricValueType', description: 'Type of metric values to query.' } as const; @@ -1567,11 +1712,12 @@ export const ProviderSummarySchema = { } }, type: 'object', - required: ['slug', 'name'], + required: [ + 'slug', + 'name' + ], title: 'ProviderSummary', - description: `Summary information about a Metric Provider. - -The diagnostic provider is the framework that was used to generate a set of metrics.` + description: 'Summary information about a Metric Provider.\n\nThe diagnostic provider is the framework that was used to generate a set of metrics.' } as const; export const RefDiagnosticLinkSchema = { @@ -1586,7 +1732,10 @@ export const RefDiagnosticLinkSchema = { } }, type: 'object', - required: ['provider_slug', 'diagnostic_slug'], + required: [ + 'provider_slug', + 'diagnostic_slug' + ], title: 'RefDiagnosticLink', description: 'Link to a specific diagnostic calculated by a provider.' } as const; @@ -1596,7 +1745,7 @@ export const ReferenceDatasetLinkSchema = { slug: { type: 'string', title: 'Slug', - description: "Unique identifier for the dataset(e.g., 'obs4mips.CERES-EBAF.v4.2')" + description: 'Unique identifier for the dataset(e.g., \'obs4mips.CERES-EBAF.v4.2\')' }, description: { anyOf: [ @@ -1612,30 +1761,33 @@ export const ReferenceDatasetLinkSchema = { }, type: { type: 'string', - enum: ['primary', 'secondary', 'comparison'], + enum: [ + 'primary', + 'secondary', + 'comparison' + ], title: 'Type', - description: `Role of this reference dataset: -- 'primary': Main reference dataset for the diagnostic -- 'secondary': Additional reference for comparison or validation -- 'comparison': Used for comparative analysis` + description: 'Role of this reference dataset:\n- \'primary\': Main reference dataset for the diagnostic\n- \'secondary\': Additional reference for comparison or validation\n- \'comparison\': Used for comparative analysis' } }, type: 'object', - required: ['slug', 'type'], + required: [ + 'slug', + 'type' + ], title: 'ReferenceDatasetLink', - description: `Link to a reference dataset used by a diagnostic. - -Reference datasets are observational or reanalysis datasets that diagnostics -compare model outputs against. They can be classified by their role in the analysis.` + description: 'Link to a reference dataset used by a diagnostic.\n\nReference datasets are observational or reanalysis datasets that diagnostics\ncompare model outputs against. They can be classified by their role in the analysis.' } as const; export const ResultOutputTypeSchema = { type: 'string', - enum: ['plot', 'data', 'html'], + enum: [ + 'plot', + 'data', + 'html' + ], title: 'ResultOutputType', - description: `Types of supported outputs - -These map to the categories of output in the CMEC output bundle` + description: 'Types of supported outputs\n\nThese map to the categories of output in the CMEC output bundle' } as const; export const ScalarValueSchema = { @@ -1709,7 +1861,10 @@ export const ScalarValueSchema = { anyOf: [ { type: 'string', - enum: ['verified', 'unverified'] + enum: [ + 'verified', + 'unverified' + ] }, { type: 'null' @@ -1719,11 +1874,15 @@ export const ScalarValueSchema = { } }, type: 'object', - required: ['dimensions', 'value', 'id', 'execution_group_id', 'execution_id'], + required: [ + 'dimensions', + 'value', + 'id', + 'execution_group_id', + 'execution_id' + ], title: 'ScalarValue', - description: `A flattened representation of a scalar diagnostic value - -This includes the dimensions and the value of the diagnostic` + description: 'A flattened representation of a scalar diagnostic value\n\nThis includes the dimensions and the value of the diagnostic' } as const; export const SeriesValueSchema = { @@ -1816,11 +1975,15 @@ export const SeriesValueSchema = { } }, type: 'object', - required: ['id', 'dimensions', 'values', 'execution_group_id', 'execution_id'], + required: [ + 'id', + 'dimensions', + 'values', + 'execution_group_id', + 'execution_id' + ], title: 'SeriesValue', - description: `A flattened representation of a series diagnostic value - -This includes the dimensions, values array, index array, and index name` + description: 'A flattened representation of a series diagnostic value\n\nThis includes the dimensions, values array, index array, and index name' } as const; export const ThemeDetailSchema = { @@ -1846,21 +2009,26 @@ export const ThemeDetailSchema = { }, collections: { items: { - '$ref': '#/components/schemas/AFTCollectionDetail' + $ref: '#/components/schemas/AFTCollectionDetail' }, type: 'array', title: 'Collections' }, explorer_cards: { items: { - '$ref': '#/components/schemas/AFTCollectionCard' + $ref: '#/components/schemas/AFTCollectionCard' }, type: 'array', title: 'Explorer Cards' } }, type: 'object', - required: ['slug', 'title', 'collections', 'explorer_cards'], + required: [ + 'slug', + 'title', + 'collections', + 'explorer_cards' + ], title: 'ThemeDetail' } as const; @@ -1895,7 +2063,12 @@ export const ThemeSummarySchema = { } }, type: 'object', - required: ['slug', 'title', 'collection_count', 'card_count'], + required: [ + 'slug', + 'title', + 'collection_count', + 'card_count' + ], title: 'ThemeSummary' } as const; @@ -1932,6 +2105,202 @@ export const ValidationErrorSchema = { } }, type: 'object', - required: ['loc', 'msg', 'type'], + required: [ + 'loc', + 'msg', + 'type' + ], title: 'ValidationError' -} as const; \ No newline at end of file +} as const; + +export const Collection_Dataset_WritableSchema = { + properties: { + data: { + items: { + $ref: '#/components/schemas/DatasetWritable' + }, + type: 'array', + title: 'Data' + }, + total_count: { + anyOf: [ + { + type: 'integer' + }, + { + type: 'null' + } + ], + title: 'Total Count' + } + }, + type: 'object', + required: [ + 'data' + ], + title: 'Collection[Dataset]' +} as const; + +export const Collection_DiagnosticSummary_WritableSchema = { + properties: { + data: { + items: { + $ref: '#/components/schemas/DiagnosticSummary' + }, + type: 'array', + title: 'Data' + }, + total_count: { + anyOf: [ + { + type: 'integer' + }, + { + type: 'null' + } + ], + title: 'Total Count' + } + }, + type: 'object', + required: [ + 'data' + ], + title: 'Collection[DiagnosticSummary]' +} as const; + +export const Collection_ExecutionGroup_WritableSchema = { + properties: { + data: { + items: { + $ref: '#/components/schemas/ExecutionGroup' + }, + type: 'array', + title: 'Data' + }, + total_count: { + anyOf: [ + { + type: 'integer' + }, + { + type: 'null' + } + ], + title: 'Total Count' + } + }, + type: 'object', + required: [ + 'data' + ], + title: 'Collection[ExecutionGroup]' +} as const; + +export const Collection_Execution_WritableSchema = { + properties: { + data: { + items: { + $ref: '#/components/schemas/Execution' + }, + type: 'array', + title: 'Data' + }, + total_count: { + anyOf: [ + { + type: 'integer' + }, + { + type: 'null' + } + ], + title: 'Total Count' + } + }, + type: 'object', + required: [ + 'data' + ], + title: 'Collection[Execution]' +} as const; + +export const DatasetWritableSchema = { + properties: { + id: { + type: 'integer', + title: 'Id' + }, + slug: { + type: 'string', + title: 'Slug' + }, + dataset_type: { + type: 'string', + title: 'Dataset Type' + }, + metadata: { + anyOf: [ + { + $ref: '#/components/schemas/CMIP6DatasetMetadata' + }, + { + type: 'null' + } + ] + } + }, + type: 'object', + required: [ + 'id', + 'slug', + 'dataset_type', + 'metadata' + ], + title: 'Dataset' +} as const; + +export const ExecutionStatsWritableSchema = { + properties: { + total_execution_groups: { + type: 'integer', + title: 'Total Execution Groups' + }, + successful_execution_groups: { + type: 'integer', + title: 'Successful Execution Groups' + }, + failed_execution_groups: { + type: 'integer', + title: 'Failed Execution Groups' + }, + scalar_value_count: { + type: 'integer', + title: 'Scalar Value Count' + }, + series_value_count: { + type: 'integer', + title: 'Series Value Count' + }, + total_datasets: { + type: 'integer', + title: 'Total Datasets' + }, + total_files: { + type: 'integer', + title: 'Total Files' + } + }, + type: 'object', + required: [ + 'total_execution_groups', + 'successful_execution_groups', + 'failed_execution_groups', + 'scalar_value_count', + 'series_value_count', + 'total_datasets', + 'total_files' + ], + title: 'ExecutionStats', + description: 'Statistics for execution groups and their success rates.' +} as const; diff --git a/frontend/src/client/sdk.gen.ts b/frontend/src/client/sdk.gen.ts index c4f1419..d7e126b 100644 --- a/frontend/src/client/sdk.gen.ts +++ b/frontend/src/client/sdk.gen.ts @@ -1,10 +1,10 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { Cmip7AssessmentFastTrackAftListAftDiagnosticsData, Cmip7AssessmentFastTrackAftListAftDiagnosticsResponse, Cmip7AssessmentFastTrackAftGetAftDiagnosticData, Cmip7AssessmentFastTrackAftGetAftDiagnosticResponse, Cmip7AssessmentFastTrackAftGetAftDiagnosticError, DatasetsListData, DatasetsListResponse, DatasetsListError, DatasetsGetData, DatasetsGetResponse, DatasetsGetError, DatasetsExecutionsData, DatasetsExecutionsResponse, DatasetsExecutionsError, DiagnosticsListData, DiagnosticsListResponse, DiagnosticsFacetsData, DiagnosticsFacetsResponse, DiagnosticsGetData, DiagnosticsGetResponse, DiagnosticsGetError, DiagnosticsListExecutionGroupsData, DiagnosticsListExecutionGroupsResponse, DiagnosticsListExecutionGroupsError, DiagnosticsListExecutionsData, DiagnosticsListExecutionsResponse, DiagnosticsListExecutionsError, DiagnosticsListMetricValuesData, DiagnosticsListMetricValuesResponse, DiagnosticsListMetricValuesError, ExecutionsGetExecutionStatisticsData, ExecutionsGetExecutionStatisticsResponse, ExecutionsListRecentExecutionGroupsData, ExecutionsListRecentExecutionGroupsResponse, ExecutionsListRecentExecutionGroupsError, ExecutionsGetData, ExecutionsGetResponse, ExecutionsGetError, ExecutionsExecutionData, ExecutionsExecutionResponse, ExecutionsExecutionError, ExecutionsExecutionDatasetsData, ExecutionsExecutionDatasetsResponse, ExecutionsExecutionDatasetsError, ExecutionsExecutionLogsData, ExecutionsExecutionLogsError, ExecutionsMetricBundleData, ExecutionsMetricBundleResponse, ExecutionsMetricBundleError, ExecutionsListMetricValuesData, ExecutionsListMetricValuesResponse, ExecutionsListMetricValuesError, ExecutionsExecutionArchiveData, ExecutionsExecutionArchiveError, ExplorerListCollectionsData, ExplorerListCollectionsResponse, ExplorerGetCollectionData, ExplorerGetCollectionResponse, ExplorerGetCollectionError, ExplorerListThemesData, ExplorerListThemesResponse, ExplorerGetThemeData, ExplorerGetThemeResponse, ExplorerGetThemeError, ResultsGetResultData, ResultsGetResultError, UtilsHealthCheckData, UtilsHealthCheckResponse } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; +import type { Client, Options as Options2, TDataShape } from './client'; +import { client } from './client.gen'; +import type { Cmip7AssessmentFastTrackAftGetAftDiagnosticData, Cmip7AssessmentFastTrackAftGetAftDiagnosticErrors, Cmip7AssessmentFastTrackAftGetAftDiagnosticResponses, Cmip7AssessmentFastTrackAftListAftDiagnosticsData, Cmip7AssessmentFastTrackAftListAftDiagnosticsResponses, DatasetsExecutionsData, DatasetsExecutionsErrors, DatasetsExecutionsResponses, DatasetsGetData, DatasetsGetErrors, DatasetsGetResponses, DatasetsListData, DatasetsListErrors, DatasetsListResponses, DiagnosticsFacetsData, DiagnosticsFacetsResponses, DiagnosticsGetData, DiagnosticsGetErrors, DiagnosticsGetResponses, DiagnosticsListData, DiagnosticsListExecutionGroupsData, DiagnosticsListExecutionGroupsErrors, DiagnosticsListExecutionGroupsResponses, DiagnosticsListExecutionsData, DiagnosticsListExecutionsErrors, DiagnosticsListExecutionsResponses, DiagnosticsListMetricValuesData, DiagnosticsListMetricValuesErrors, DiagnosticsListMetricValuesResponses, DiagnosticsListResponses, ExecutionsExecutionArchiveData, ExecutionsExecutionArchiveErrors, ExecutionsExecutionArchiveResponses, ExecutionsExecutionData, ExecutionsExecutionDatasetsData, ExecutionsExecutionDatasetsErrors, ExecutionsExecutionDatasetsResponses, ExecutionsExecutionErrors, ExecutionsExecutionLogsData, ExecutionsExecutionLogsErrors, ExecutionsExecutionLogsResponses, ExecutionsExecutionResponses, ExecutionsGetData, ExecutionsGetErrors, ExecutionsGetExecutionStatisticsData, ExecutionsGetExecutionStatisticsResponses, ExecutionsGetResponses, ExecutionsListMetricValuesData, ExecutionsListMetricValuesErrors, ExecutionsListMetricValuesResponses, ExecutionsListRecentExecutionGroupsData, ExecutionsListRecentExecutionGroupsErrors, ExecutionsListRecentExecutionGroupsResponses, ExecutionsMetricBundleData, ExecutionsMetricBundleErrors, ExecutionsMetricBundleResponses, ExplorerGetCollectionData, ExplorerGetCollectionErrors, ExplorerGetCollectionResponses, ExplorerGetThemeData, ExplorerGetThemeErrors, ExplorerGetThemeResponses, ExplorerListCollectionsData, ExplorerListCollectionsResponses, ExplorerListThemesData, ExplorerListThemesResponses, ResultsGetResultData, ResultsGetResultErrors, ResultsGetResultResponses, UtilsHealthCheckData, UtilsHealthCheckResponses } from './types.gen'; -export type Options = ClientOptions & { +export type Options = Options2 & { /** * You can provide a client instance returned by `createClient()` instead of * individual options. This might be also useful if you want to implement a @@ -20,118 +20,79 @@ export type Options(options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/cmip7-aft-diagnostics/', - ...options - }); -}; +export const cmip7AssessmentFastTrackAftListAftDiagnostics = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/cmip7-aft-diagnostics/', ...options }); /** * Get Aft Diagnostic + * * Get detailed AFT diagnostic by ID. */ -export const cmip7AssessmentFastTrackAftGetAftDiagnostic = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/cmip7-aft-diagnostics/{aft_id}', - ...options - }); -}; +export const cmip7AssessmentFastTrackAftGetAftDiagnostic = (options: Options) => (options.client ?? client).get({ url: '/api/v1/cmip7-aft-diagnostics/{aft_id}', ...options }); /** * List + * * Paginated list of currently ingested datasets */ -export const datasetsList = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/datasets/', - ...options - }); -}; +export const datasetsList = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/datasets/', ...options }); /** * Get + * * Get a single dataset by slug */ -export const datasetsGet = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/datasets/{slug}', - ...options - }); -}; +export const datasetsGet = (options: Options) => (options.client ?? client).get({ url: '/api/v1/datasets/{slug}', ...options }); /** * Executions + * * List the currently registered diagnostics */ -export const datasetsExecutions = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/datasets/{dataset_id}/executions', - ...options - }); -}; +export const datasetsExecutions = (options: Options) => (options.client ?? client).get({ url: '/api/v1/datasets/{dataset_id}/executions', ...options }); /** * List + * * List the currently registered diagnostics */ -export const diagnosticsList = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/diagnostics/', - ...options - }); -}; +export const diagnosticsList = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/diagnostics/', ...options }); /** * Facets + * * Query the unique dimensions and metrics for all diagnostics (both scalar and series) */ -export const diagnosticsFacets = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/diagnostics/facets', - ...options - }); -}; +export const diagnosticsFacets = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/diagnostics/facets', ...options }); /** * Get + * * Fetch a result using the slug */ -export const diagnosticsGet = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}', - ...options - }); -}; +export const diagnosticsGet = (options: Options) => (options.client ?? client).get({ url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}', ...options }); /** * List Execution Groups + * * Fetch execution groups for a diagnostic. */ -export const diagnosticsListExecutionGroups = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}/execution_groups', - ...options - }); -}; +export const diagnosticsListExecutionGroups = (options: Options) => (options.client ?? client).get({ url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}/execution_groups', ...options }); /** * List Executions + * * Fetch executions for a specific diagnostic, with arbitrary filters on the dataset. * * e.g. `?source_id=MIROC6&experiment_id=ssp585` */ -export const diagnosticsListExecutions = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}/executions', - ...options - }); -}; +export const diagnosticsListExecutions = (options: Options) => (options.client ?? client).get({ url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}/executions', ...options }); /** * List Metric Values + * * Get all the diagnostic values for a given diagnostic (both scalar and series) * * - `value_type`: Type of metric values - 'scalar', 'series', or 'all' (required) @@ -139,29 +100,21 @@ export const diagnosticsListExecutions = ( * - `offset`: Number of items to skip (default 0) * - `limit`: Maximum number of items to return (default 50, max 500) */ -export const diagnosticsListMetricValues = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}/values', - ...options - }); -}; +export const diagnosticsListMetricValues = (options: Options) => (options.client ?? client).get({ url: '/api/v1/diagnostics/{provider_slug}/{diagnostic_slug}/values', ...options }); /** * Get Execution Statistics + * * Get execution statistics for the dashboard. * * Returns counts of total, successful, and failed execution groups, * plus recent activity count. */ -export const executionsGetExecutionStatistics = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/executions/statistics', - ...options - }); -}; +export const executionsGetExecutionStatistics = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/executions/statistics', ...options }); /** * List Recent Execution Groups + * * List the most recent execution groups * * Supports filtering by: @@ -172,72 +125,48 @@ export const executionsGetExecutionStatistics = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/executions/', - ...options - }); -}; +export const executionsListRecentExecutionGroups = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/executions/', ...options }); /** * Get + * * Inspect a specific execution */ -export const executionsGet = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/executions/{group_id}', - ...options - }); -}; +export const executionsGet = (options: Options) => (options.client ?? client).get({ url: '/api/v1/executions/{group_id}', ...options }); /** * Execution + * * Inspect a specific execution * * Gets the latest result if no execution_id is provided */ -export const executionsExecution = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/executions/{group_id}/execution', - ...options - }); -}; +export const executionsExecution = (options: Options) => (options.client ?? client).get({ url: '/api/v1/executions/{group_id}/execution', ...options }); /** * Execution Datasets + * * Query the datasets that were used for a specific execution */ -export const executionsExecutionDatasets = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/executions/{group_id}/datasets', - ...options - }); -}; +export const executionsExecutionDatasets = (options: Options) => (options.client ?? client).get({ url: '/api/v1/executions/{group_id}/datasets', ...options }); /** * Execution Logs + * * Fetch the logs for an execution result */ -export const executionsExecutionLogs = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/executions/{group_id}/logs', - ...options - }); -}; +export const executionsExecutionLogs = (options: Options) => (options.client ?? client).get({ url: '/api/v1/executions/{group_id}/logs', ...options }); /** * Metric Bundle + * * Fetch a result using the slug */ -export const executionsMetricBundle = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/executions/{group_id}/metric_bundle', - ...options - }); -}; +export const executionsMetricBundle = (options: Options) => (options.client ?? client).get({ url: '/api/v1/executions/{group_id}/metric_bundle', ...options }); /** * List Metric Values + * * Fetch metric values for a specific execution (both scalar and series) * * - `value_type`: Type of metric values - 'scalar', 'series', or 'all' (required) @@ -245,83 +174,45 @@ export const executionsMetricBundle = (opt * - `offset`: Number of items to skip (default 0) * - `limit`: Maximum number of items to return (default 50, max 500) */ -export const executionsListMetricValues = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/executions/{group_id}/values', - ...options - }); -}; +export const executionsListMetricValues = (options: Options) => (options.client ?? client).get({ url: '/api/v1/executions/{group_id}/values', ...options }); /** * Execution Archive + * * Stream a tar.gz archive of the execution results * * The archive is created on-the-fly and streamed directly to the client. */ -export const executionsExecutionArchive = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/executions/{group_id}/archive', - ...options - }); -}; +export const executionsExecutionArchive = (options: Options) => (options.client ?? client).get({ url: '/api/v1/executions/{group_id}/archive', ...options }); /** * List Collections */ -export const explorerListCollections = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/explorer/collections/', - ...options - }); -}; +export const explorerListCollections = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/explorer/collections/', ...options }); /** * Get Collection */ -export const explorerGetCollection = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/explorer/collections/{collection_id}', - ...options - }); -}; +export const explorerGetCollection = (options: Options) => (options.client ?? client).get({ url: '/api/v1/explorer/collections/{collection_id}', ...options }); /** * List Themes */ -export const explorerListThemes = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/explorer/themes/', - ...options - }); -}; +export const explorerListThemes = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/explorer/themes/', ...options }); /** * Get Theme */ -export const explorerGetTheme = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/explorer/themes/{theme_slug}', - ...options - }); -}; +export const explorerGetTheme = (options: Options) => (options.client ?? client).get({ url: '/api/v1/explorer/themes/{theme_slug}', ...options }); /** * Get Result + * * Fetch a result */ -export const resultsGetResult = (options: Options) => { - return (options.client ?? _heyApiClient).get({ - url: '/api/v1/results/{result_id}', - ...options - }); -}; +export const resultsGetResult = (options: Options) => (options.client ?? client).get({ url: '/api/v1/results/{result_id}', ...options }); /** * Health Check */ -export const utilsHealthCheck = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/api/v1/utils/health-check/', - ...options - }); -}; \ No newline at end of file +export const utilsHealthCheck = (options?: Options) => (options?.client ?? client).get({ url: '/api/v1/utils/health-check/', ...options }); diff --git a/frontend/src/client/types.gen.ts b/frontend/src/client/types.gen.ts index dbf4b57..f456d0b 100644 --- a/frontend/src/client/types.gen.ts +++ b/frontend/src/client/types.gen.ts @@ -1,265 +1,678 @@ // This file is auto-generated by @hey-api/openapi-ts +export type ClientOptions = { + baseUrl: `${string}://${string}` | (string & {}); +}; + +/** + * AFTCollectionCard + */ export type AftCollectionCard = { + /** + * Title + */ title: string; + /** + * Description + */ description?: string | null; + /** + * Placeholder + */ placeholder?: boolean | null; + /** + * Content + */ content: Array; }; +/** + * AFTCollectionCardContent + */ export type AftCollectionCardContent = { + /** + * Type + */ type: 'box-whisker-chart' | 'figure-gallery' | 'series-chart' | 'taylor-diagram'; + /** + * Provider + */ provider: string; + /** + * Diagnostic + */ diagnostic: string; + /** + * Title + */ title: string; + /** + * Description + */ description?: string | null; + /** + * Interpretation + */ interpretation?: string | null; - span?: (1 | 2) | null; + /** + * Span + */ + span?: 1 | 2 | null; + /** + * Placeholder + */ placeholder?: boolean | null; + /** + * Metric Units + */ metric_units?: string | null; + /** + * Clip Min + */ clip_min?: number | null; + /** + * Clip Max + */ clip_max?: number | null; + /** + * Y Min + */ y_min?: number | null; + /** + * Y Max + */ y_max?: number | null; + /** + * Show Zero Line + */ show_zero_line?: boolean | null; + /** + * Symmetrical Axes + */ symmetrical_axes?: boolean | null; + /** + * Reference Stddev + */ reference_stddev?: number | null; + /** + * Label Template + */ label_template?: string | null; + /** + * Filename Filter + */ filename_filter?: string | null; + /** + * Other Filters + */ other_filters?: { [key: string]: string; } | null; grouping_config?: AftCollectionGroupingConfig | null; + /** + * Filter Controls + */ filter_controls?: Array | null; + /** + * Reference Datasets + */ reference_datasets?: Array | null; }; +/** + * AFTCollectionContent + */ export type AftCollectionContent = { + /** + * Description + */ description?: string | null; + /** + * Short Description + */ short_description?: string | null; + /** + * Why It Matters + */ why_it_matters?: string | null; + /** + * Takeaway + */ takeaway?: string | null; plain_language?: AftCollectionPlainLanguage | null; }; +/** + * AFTCollectionDetail + */ export type AftCollectionDetail = { + /** + * Id + */ id: string; + /** + * Name + */ name: string; + /** + * Theme + */ theme?: string | null; + /** + * Endorser + */ endorser?: string | null; + /** + * Version Control + */ version_control?: string | null; + /** + * Reference Dataset + */ reference_dataset?: string | null; + /** + * Provider Link + */ provider_link?: string | null; content?: AftCollectionContent | null; + /** + * Diagnostics + */ diagnostics: Array; + /** + * Explorer Cards + */ explorer_cards: Array; }; +/** + * AFTCollectionDiagnosticLink + */ export type AftCollectionDiagnosticLink = { + /** + * Provider Slug + */ provider_slug: string; + /** + * Diagnostic Slug + */ diagnostic_slug: string; + /** + * Provider Link + */ provider_link?: string | null; }; /** + * AFTCollectionFilterControl + * * A user-facing filter control (e.g. a dropdown) that overrides an other_filters key. */ export type AftCollectionFilterControl = { + /** + * Filter Key + */ filter_key: string; + /** + * Label + */ label?: string | null; + /** + * Default Value + */ default_value?: string | null; + /** + * Exclude Values + */ exclude_values?: Array | null; }; +/** + * AFTCollectionGroupingConfig + */ export type AftCollectionGroupingConfig = { + /** + * Group By + */ group_by: string; + /** + * Hue + */ hue: string; + /** + * Style + */ style?: string | null; }; +/** + * AFTCollectionPlainLanguage + */ export type AftCollectionPlainLanguage = { + /** + * Description + */ description?: string | null; + /** + * Why It Matters + */ why_it_matters?: string | null; + /** + * Takeaway + */ takeaway?: string | null; }; +/** + * AFTCollectionSummary + */ export type AftCollectionSummary = { + /** + * Id + */ id: string; + /** + * Name + */ name: string; + /** + * Theme + */ theme?: string | null; + /** + * Endorser + */ endorser?: string | null; + /** + * Card Count + */ card_count: number; }; +/** + * AFTDiagnosticDetail + */ export type AftDiagnosticDetail = { + /** + * Id + */ id: string; + /** + * Name + */ name: string; + /** + * Theme + */ theme: string | null; + /** + * Version Control + */ version_control: string | null; + /** + * Reference Dataset + */ reference_dataset: string | null; + /** + * Endorser + */ endorser: string | null; + /** + * Provider Link + */ provider_link: string | null; + /** + * Description + */ description: string | null; + /** + * Short Description + */ short_description: string | null; + /** + * Diagnostics + */ diagnostics: Array; }; +/** + * AFTDiagnosticSummary + */ export type AftDiagnosticSummary = { + /** + * Id + */ id: string; + /** + * Name + */ name: string; + /** + * Theme + */ theme: string | null; + /** + * Version Control + */ version_control: string | null; + /** + * Reference Dataset + */ reference_dataset: string | null; + /** + * Endorser + */ endorser: string | null; + /** + * Provider Link + */ provider_link: string | null; + /** + * Description + */ description: string | null; + /** + * Short Description + */ short_description: string | null; }; /** + * CMECMetric + * * CMEC diagnostic bundle object * * Contains the diagnostics calculated during a diagnostic execution, in a standardised format. */ export type CmecMetric = { DIMENSIONS: MetricDimensions; + /** + * Results + */ RESULTS: { [key: string]: unknown; }; + /** + * Provenance + */ PROVENANCE?: { [key: string]: unknown; } | null; + /** + * Disclaimer + */ DISCLAIMER?: { [key: string]: unknown; } | null; + /** + * Notes + */ NOTES?: { [key: string]: unknown; } | null; - [key: string]: unknown | MetricDimensions | { - [key: string]: unknown; - } | ({ - [key: string]: unknown; - } | null) | ({ - [key: string]: unknown; - } | null) | ({ - [key: string]: unknown; - } | null) | undefined; + [key: string]: unknown; }; +/** + * CMIP6DatasetMetadata + */ export type Cmip6DatasetMetadata = { + /** + * Variable Id + */ variable_id: string; + /** + * Source Id + */ source_id: string; + /** + * Experiment Id + */ experiment_id: string; + /** + * Variant Label + */ variant_label: string; }; -export type CollectionDatasetReadable = { +/** + * Collection[Dataset] + */ +export type CollectionDataset = { + /** + * Data + */ data: Array; + /** + * Total Count + */ total_count?: number | null; /** + * Count + * * Number of data items present */ readonly count: number; }; -export type CollectionDatasetWritable = { - data: Array; - total_count?: number | null; -}; - -export type CollectionDiagnosticSummaryReadable = { +/** + * Collection[DiagnosticSummary] + */ +export type CollectionDiagnosticSummary = { + /** + * Data + */ data: Array; + /** + * Total Count + */ total_count?: number | null; /** + * Count + * * Number of data items present */ readonly count: number; }; -export type CollectionDiagnosticSummaryWritable = { - data: Array; - total_count?: number | null; -}; - -export type CollectionExecutionGroupReadable = { +/** + * Collection[ExecutionGroup] + */ +export type CollectionExecutionGroup = { + /** + * Data + */ data: Array; + /** + * Total Count + */ total_count?: number | null; /** + * Count + * * Number of data items present */ readonly count: number; }; -export type CollectionExecutionGroupWritable = { - data: Array; - total_count?: number | null; -}; - -export type CollectionExecutionReadable = { +/** + * Collection[Execution] + */ +export type CollectionExecution = { + /** + * Data + */ data: Array; + /** + * Total Count + */ total_count?: number | null; /** + * Count + * * Number of data items present */ readonly count: number; }; -export type CollectionExecutionWritable = { - data: Array; - total_count?: number | null; -}; - +/** + * Dataset + */ export type Dataset = { + /** + * Id + */ id: number; + /** + * Slug + */ slug: string; + /** + * Dataset Type + */ dataset_type: string; metadata: Cmip6DatasetMetadata | null; - more_info_url: string | null; + /** + * More Info Url + */ + readonly more_info_url: string | null; }; /** + * DiagnosticSummary + * * Summary information about a diagnostic. * * A diagnostic is a specific metric or set of metrics calculated by a provider. * Each diagnostic is associated may be associated with one CMIP Assessment Fast Track (AFT) diagnostics. */ export type DiagnosticSummary = { + /** + * Id + */ id: number; provider: ProviderSummary; + /** + * Slug + */ slug: string; + /** + * Name + */ name: string; + /** + * Description + */ description: string; + /** + * Execution Groups + */ execution_groups: Array; + /** + * Has Metric Values + */ has_metric_values: boolean; + /** + * Has Scalar Values + */ has_scalar_values: boolean; + /** + * Has Series Values + */ has_series_values: boolean; + /** + * Execution Count + */ execution_count: number; + /** + * Successful Execution Count + */ successful_execution_count: number; + /** + * Execution Group Count + */ execution_group_count: number; + /** + * Successful Execution Group Count + */ successful_execution_group_count: number; + /** + * Group By + */ group_by: Array; aft_link: AftDiagnosticDetail | null; + /** + * Reference Datasets + */ reference_datasets?: Array | null; + /** + * Tags + */ tags?: Array | null; }; +/** + * Execution + */ export type Execution = { + /** + * Id + */ id: number; + /** + * Dataset Hash + */ dataset_hash: string; + /** + * Dataset Count + */ dataset_count: number; + /** + * Successful + */ successful: boolean; + /** + * Retracted + */ retracted: boolean; + /** + * Created At + */ created_at: string; + /** + * Updated At + */ updated_at: string; + /** + * Outputs + */ outputs: Array; }; +/** + * ExecutionGroup + */ export type ExecutionGroup = { + /** + * Id + */ id: number; + /** + * Key + */ key: string; + /** + * Dirty + */ dirty: boolean; + /** + * Executions + */ executions: Array; latest_execution: Execution | null; + /** + * Selectors + */ selectors: { [key: string]: Array<[ string, @@ -267,68 +680,142 @@ export type ExecutionGroup = { ]>; }; diagnostic: DiagnosticSummary; + /** + * Created At + */ created_at: string; + /** + * Updated At + */ updated_at: string; }; +/** + * ExecutionOutput + */ export type ExecutionOutput = { + /** + * Id + */ id: number; + /** + * Execution Id + */ execution_id: number; output_type: ResultOutputType; + /** + * Filename + */ filename: string; + /** + * Short Name + */ short_name: string; + /** + * Long Name + */ long_name: string; + /** + * Description + */ description: string; + /** + * Created At + */ created_at: string; + /** + * Updated At + */ updated_at: string; + /** + * Url + */ url: string; }; /** + * ExecutionStats + * * Statistics for execution groups and their success rates. */ -export type ExecutionStatsReadable = { +export type ExecutionStats = { + /** + * Total Execution Groups + */ total_execution_groups: number; + /** + * Successful Execution Groups + */ successful_execution_groups: number; + /** + * Failed Execution Groups + */ failed_execution_groups: number; + /** + * Scalar Value Count + */ scalar_value_count: number; + /** + * Series Value Count + */ series_value_count: number; + /** + * Total Datasets + */ total_datasets: number; + /** + * Total Files + */ total_files: number; /** + * Success Rate Percentage + * * Success rate as a percentage (0-100). */ readonly success_rate_percentage: number; }; /** - * Statistics for execution groups and their success rates. + * Facet */ -export type ExecutionStatsWritable = { - total_execution_groups: number; - successful_execution_groups: number; - failed_execution_groups: number; - scalar_value_count: number; - series_value_count: number; - total_datasets: number; - total_files: number; -}; - export type Facet = { + /** + * Key + */ key: string; + /** + * Values + */ values: Array; }; +/** + * GroupBy + */ export type GroupBy = { + /** + * Source Type + */ source_type: string; + /** + * Group By + */ group_by: Array | null; }; +/** + * HTTPValidationError + */ export type HttpValidationError = { + /** + * Detail + */ detail?: Array; }; /** + * MetricDimensions + * * CMEC diagnostic bundle DIMENSIONS object * * This describes the order of the dimensions and their possible values. @@ -338,50 +825,102 @@ export type MetricDimensions = { [key: string]: unknown; }; +/** + * MetricValueCollection + */ export type MetricValueCollection = { + /** + * Data + */ data: Array; + /** + * Count + */ count: number; + /** + * Total Count + */ total_count: number; + /** + * Facets + */ facets: Array; + /** + * Types + */ types: Array; + /** + * Had Outliers + */ had_outliers?: boolean | null; + /** + * Outlier Count + */ outlier_count?: number | null; }; /** + * MetricValueFacetSummary + * * Summary of the dimensions used in a metric value collection. */ export type MetricValueFacetSummary = { + /** + * Dimensions + */ dimensions: { [key: string]: Array; }; + /** + * Count + */ count: number; }; /** + * MetricValueType + * * Type of metric values to query. */ export type MetricValueType = 'scalar' | 'series'; /** + * ProviderSummary + * * Summary information about a Metric Provider. * * The diagnostic provider is the framework that was used to generate a set of metrics. */ export type ProviderSummary = { + /** + * Slug + */ slug: string; + /** + * Name + */ name: string; }; /** + * RefDiagnosticLink + * * Link to a specific diagnostic calculated by a provider. */ export type RefDiagnosticLink = { + /** + * Provider Slug + */ provider_slug: string; + /** + * Diagnostic Slug + */ diagnostic_slug: string; }; /** + * ReferenceDatasetLink + * * Link to a reference dataset used by a diagnostic. * * Reference datasets are observational or reanalysis datasets that diagnostics @@ -389,14 +928,20 @@ export type RefDiagnosticLink = { */ export type ReferenceDatasetLink = { /** + * Slug + * * Unique identifier for the dataset(e.g., 'obs4mips.CERES-EBAF.v4.2') */ slug: string; /** + * Description + * * Description of how this dataset is used in the diagnostic */ description?: string | null; /** + * Type + * * Role of this reference dataset: * - 'primary': Main reference dataset for the diagnostic * - 'secondary': Additional reference for comparison or validation @@ -406,6 +951,8 @@ export type ReferenceDatasetLink = { }; /** + * ResultOutputType + * * Types of supported outputs * * These map to the categories of output in the CMEC output bundle @@ -413,71 +960,288 @@ export type ReferenceDatasetLink = { export type ResultOutputType = 'plot' | 'data' | 'html'; /** + * ScalarValue + * * A flattened representation of a scalar diagnostic value * * This includes the dimensions and the value of the diagnostic */ export type ScalarValue = { + /** + * Dimensions + */ dimensions: { [key: string]: string; }; + /** + * Value + */ value: number | number; + /** + * Attributes + */ attributes?: { [key: string]: string | number | number; } | null; + /** + * Id + */ id: number; + /** + * Execution Group Id + */ execution_group_id: number; + /** + * Execution Id + */ execution_id: number; + /** + * Is Outlier + */ is_outlier?: boolean | null; - verification_status?: ('verified' | 'unverified') | null; + /** + * Verification Status + */ + verification_status?: 'verified' | 'unverified' | null; }; /** + * SeriesValue + * * A flattened representation of a series diagnostic value * * This includes the dimensions, values array, index array, and index name */ export type SeriesValue = { + /** + * Id + */ id: number; + /** + * Dimensions + */ dimensions: { [key: string]: string; }; + /** + * Values + */ values: Array; + /** + * Index + */ index?: Array | null; + /** + * Index Name + */ index_name?: string | null; + /** + * Attributes + */ attributes?: { [key: string]: string | number; } | null; + /** + * Execution Group Id + */ execution_group_id: number; + /** + * Execution Id + */ execution_id: number; }; +/** + * ThemeDetail + */ export type ThemeDetail = { + /** + * Slug + */ slug: string; + /** + * Title + */ title: string; + /** + * Description + */ description?: string | null; + /** + * Collections + */ collections: Array; + /** + * Explorer Cards + */ explorer_cards: Array; }; +/** + * ThemeSummary + */ export type ThemeSummary = { + /** + * Slug + */ slug: string; + /** + * Title + */ title: string; + /** + * Description + */ description?: string | null; + /** + * Collection Count + */ collection_count: number; + /** + * Card Count + */ card_count: number; }; +/** + * ValidationError + */ export type ValidationError = { + /** + * Location + */ loc: Array; + /** + * Message + */ msg: string; + /** + * Error Type + */ type: string; + /** + * Input + */ input?: unknown; + /** + * Context + */ ctx?: { [key: string]: unknown; }; }; +/** + * Collection[Dataset] + */ +export type CollectionDatasetWritable = { + /** + * Data + */ + data: Array; + /** + * Total Count + */ + total_count?: number | null; +}; + +/** + * Collection[DiagnosticSummary] + */ +export type CollectionDiagnosticSummaryWritable = { + /** + * Data + */ + data: Array; + /** + * Total Count + */ + total_count?: number | null; +}; + +/** + * Collection[ExecutionGroup] + */ +export type CollectionExecutionGroupWritable = { + /** + * Data + */ + data: Array; + /** + * Total Count + */ + total_count?: number | null; +}; + +/** + * Collection[Execution] + */ +export type CollectionExecutionWritable = { + /** + * Data + */ + data: Array; + /** + * Total Count + */ + total_count?: number | null; +}; + +/** + * Dataset + */ +export type DatasetWritable = { + /** + * Id + */ + id: number; + /** + * Slug + */ + slug: string; + /** + * Dataset Type + */ + dataset_type: string; + metadata: Cmip6DatasetMetadata | null; +}; + +/** + * ExecutionStats + * + * Statistics for execution groups and their success rates. + */ +export type ExecutionStatsWritable = { + /** + * Total Execution Groups + */ + total_execution_groups: number; + /** + * Successful Execution Groups + */ + successful_execution_groups: number; + /** + * Failed Execution Groups + */ + failed_execution_groups: number; + /** + * Scalar Value Count + */ + scalar_value_count: number; + /** + * Series Value Count + */ + series_value_count: number; + /** + * Total Datasets + */ + total_datasets: number; + /** + * Total Files + */ + total_files: number; +}; + export type Cmip7AssessmentFastTrackAftListAftDiagnosticsData = { body?: never; path?: never; @@ -487,6 +1251,8 @@ export type Cmip7AssessmentFastTrackAftListAftDiagnosticsData = { export type Cmip7AssessmentFastTrackAftListAftDiagnosticsResponses = { /** + * Response Cmip7 Assessment Fast Track (Aft)-List Aft Diagnostics + * * Successful Response */ 200: Array; @@ -497,6 +1263,9 @@ export type Cmip7AssessmentFastTrackAftListAftDiagnosticsResponse = Cmip7Assessm export type Cmip7AssessmentFastTrackAftGetAftDiagnosticData = { body?: never; path: { + /** + * Aft Id + */ aft_id: string; }; query?: never; @@ -525,17 +1294,29 @@ export type DatasetsListData = { body?: never; path?: never; query?: { + /** + * Offset + */ offset?: number; + /** + * Limit + */ limit?: number; /** + * Name Contains + * * Filter datasets by name */ name_contains?: string; /** + * Dataset Type + * * Filter datasets by the type of dataset */ dataset_type?: string; /** + * Facets + * * Filter datasets by facets (JSON string) */ facets?: string; @@ -556,7 +1337,7 @@ export type DatasetsListResponses = { /** * Successful Response */ - 200: CollectionDatasetReadable; + 200: CollectionDataset; }; export type DatasetsListResponse = DatasetsListResponses[keyof DatasetsListResponses]; @@ -564,6 +1345,9 @@ export type DatasetsListResponse = DatasetsListResponses[keyof DatasetsListRespo export type DatasetsGetData = { body?: never; path: { + /** + * Slug + */ slug: string; }; query?: never; @@ -591,10 +1375,19 @@ export type DatasetsGetResponse = DatasetsGetResponses[keyof DatasetsGetResponse export type DatasetsExecutionsData = { body?: never; path: { + /** + * Dataset Id + */ dataset_id: number; }; query?: { + /** + * Offset + */ offset?: number; + /** + * Limit + */ limit?: number; }; url: '/api/v1/datasets/{dataset_id}/executions'; @@ -613,7 +1406,7 @@ export type DatasetsExecutionsResponses = { /** * Successful Response */ - 200: CollectionExecutionGroupReadable; + 200: CollectionExecutionGroup; }; export type DatasetsExecutionsResponse = DatasetsExecutionsResponses[keyof DatasetsExecutionsResponses]; @@ -629,7 +1422,7 @@ export type DiagnosticsListResponses = { /** * Successful Response */ - 200: CollectionDiagnosticSummaryReadable; + 200: CollectionDiagnosticSummary; }; export type DiagnosticsListResponse = DiagnosticsListResponses[keyof DiagnosticsListResponses]; @@ -653,7 +1446,13 @@ export type DiagnosticsFacetsResponse = DiagnosticsFacetsResponses[keyof Diagnos export type DiagnosticsGetData = { body?: never; path: { + /** + * Provider Slug + */ provider_slug: string; + /** + * Diagnostic Slug + */ diagnostic_slug: string; }; query?: never; @@ -681,7 +1480,13 @@ export type DiagnosticsGetResponse = DiagnosticsGetResponses[keyof DiagnosticsGe export type DiagnosticsListExecutionGroupsData = { body?: never; path: { + /** + * Provider Slug + */ provider_slug: string; + /** + * Diagnostic Slug + */ diagnostic_slug: string; }; query?: never; @@ -701,7 +1506,7 @@ export type DiagnosticsListExecutionGroupsResponses = { /** * Successful Response */ - 200: CollectionExecutionGroupReadable; + 200: CollectionExecutionGroup; }; export type DiagnosticsListExecutionGroupsResponse = DiagnosticsListExecutionGroupsResponses[keyof DiagnosticsListExecutionGroupsResponses]; @@ -709,7 +1514,13 @@ export type DiagnosticsListExecutionGroupsResponse = DiagnosticsListExecutionGro export type DiagnosticsListExecutionsData = { body?: never; path: { + /** + * Provider Slug + */ provider_slug: string; + /** + * Diagnostic Slug + */ diagnostic_slug: string; }; query?: never; @@ -729,7 +1540,7 @@ export type DiagnosticsListExecutionsResponses = { /** * Successful Response */ - 200: CollectionExecutionReadable; + 200: CollectionExecution; }; export type DiagnosticsListExecutionsResponse = DiagnosticsListExecutionsResponses[keyof DiagnosticsListExecutionsResponses]; @@ -737,7 +1548,13 @@ export type DiagnosticsListExecutionsResponse = DiagnosticsListExecutionsRespons export type DiagnosticsListMetricValuesData = { body?: never; path: { + /** + * Provider Slug + */ provider_slug: string; + /** + * Diagnostic Slug + */ diagnostic_slug: string; }; query: { @@ -745,28 +1562,43 @@ export type DiagnosticsListMetricValuesData = { * Type of metric values to return */ value_type: MetricValueType; + /** + * Format + */ format?: string | null; /** + * Offset + * * Number of items to skip for pagination */ offset?: number; /** + * Limit + * * Maximum number of items to return */ limit?: number; /** + * Detect Outliers + * * Outlier detection method: 'off' or 'iqr' */ detect_outliers?: 'off' | 'iqr'; /** + * Include Unverified + * * Include unverified (outlier) values */ include_unverified?: boolean; /** + * Isolate Ids + * * Comma-separated list of metric value IDs to isolate */ isolate_ids?: string | null; /** + * Exclude Ids + * * Comma-separated list of metric value IDs to exclude */ exclude_ids?: string | null; @@ -803,7 +1635,7 @@ export type ExecutionsGetExecutionStatisticsResponses = { /** * Successful Response */ - 200: ExecutionStatsReadable; + 200: ExecutionStats; }; export type ExecutionsGetExecutionStatisticsResponse = ExecutionsGetExecutionStatisticsResponses[keyof ExecutionsGetExecutionStatisticsResponses]; @@ -812,12 +1644,33 @@ export type ExecutionsListRecentExecutionGroupsData = { body?: never; path?: never; query?: { + /** + * Limit + */ limit?: number; + /** + * Offset + */ offset?: number; + /** + * Diagnostic Name Contains + */ diagnostic_name_contains?: string | null; + /** + * Provider Name Contains + */ provider_name_contains?: string | null; + /** + * Dirty + */ dirty?: boolean | null; + /** + * Successful + */ successful?: boolean | null; + /** + * Source Id + */ source_id?: string | null; }; url: '/api/v1/executions/'; @@ -836,7 +1689,7 @@ export type ExecutionsListRecentExecutionGroupsResponses = { /** * Successful Response */ - 200: CollectionExecutionGroupReadable; + 200: CollectionExecutionGroup; }; export type ExecutionsListRecentExecutionGroupsResponse = ExecutionsListRecentExecutionGroupsResponses[keyof ExecutionsListRecentExecutionGroupsResponses]; @@ -844,6 +1697,9 @@ export type ExecutionsListRecentExecutionGroupsResponse = ExecutionsListRecentEx export type ExecutionsGetData = { body?: never; path: { + /** + * Group Id + */ group_id: string; }; query?: never; @@ -871,9 +1727,15 @@ export type ExecutionsGetResponse = ExecutionsGetResponses[keyof ExecutionsGetRe export type ExecutionsExecutionData = { body?: never; path: { + /** + * Group Id + */ group_id: string; }; query?: { + /** + * Execution Id + */ execution_id?: string | null; }; url: '/api/v1/executions/{group_id}/execution'; @@ -900,9 +1762,15 @@ export type ExecutionsExecutionResponse = ExecutionsExecutionResponses[keyof Exe export type ExecutionsExecutionDatasetsData = { body?: never; path: { + /** + * Group Id + */ group_id: string; }; query?: { + /** + * Execution Id + */ execution_id?: string | null; }; url: '/api/v1/executions/{group_id}/datasets'; @@ -921,7 +1789,7 @@ export type ExecutionsExecutionDatasetsResponses = { /** * Successful Response */ - 200: CollectionDatasetReadable; + 200: CollectionDataset; }; export type ExecutionsExecutionDatasetsResponse = ExecutionsExecutionDatasetsResponses[keyof ExecutionsExecutionDatasetsResponses]; @@ -929,9 +1797,15 @@ export type ExecutionsExecutionDatasetsResponse = ExecutionsExecutionDatasetsRes export type ExecutionsExecutionLogsData = { body?: never; path: { + /** + * Group Id + */ group_id: string; }; query?: { + /** + * Execution Id + */ execution_id?: string | null; }; url: '/api/v1/executions/{group_id}/logs'; @@ -956,9 +1830,15 @@ export type ExecutionsExecutionLogsResponses = { export type ExecutionsMetricBundleData = { body?: never; path: { + /** + * Group Id + */ group_id: string; }; query?: { + /** + * Execution Id + */ execution_id?: string | null; }; url: '/api/v1/executions/{group_id}/metric_bundle'; @@ -985,36 +1865,57 @@ export type ExecutionsMetricBundleResponse = ExecutionsMetricBundleResponses[key export type ExecutionsListMetricValuesData = { body?: never; path: { + /** + * Group Id + */ group_id: string; }; query: { + /** + * Execution Id + */ execution_id?: string | null; /** * Type of metric values to return */ value_type: MetricValueType; + /** + * Format + */ format?: string | null; /** + * Offset + * * Number of items to skip for pagination */ offset?: number; /** + * Limit + * * Maximum number of items to return */ limit?: number; /** + * Detect Outliers + * * Outlier detection method: 'off' or 'iqr' */ detect_outliers?: 'off' | 'iqr'; /** + * Include Unverified + * * Include unverified (outlier) values */ include_unverified?: boolean; /** + * Isolate Ids + * * Comma-separated list of metric value IDs to isolate */ isolate_ids?: string | null; /** + * Exclude Ids + * * Comma-separated list of metric value IDs to exclude */ exclude_ids?: string | null; @@ -1043,9 +1944,15 @@ export type ExecutionsListMetricValuesResponse = ExecutionsListMetricValuesRespo export type ExecutionsExecutionArchiveData = { body?: never; path: { + /** + * Group Id + */ group_id: string; }; query?: { + /** + * Execution Id + */ execution_id?: string | null; }; url: '/api/v1/executions/{group_id}/archive'; @@ -1076,6 +1983,8 @@ export type ExplorerListCollectionsData = { export type ExplorerListCollectionsResponses = { /** + * Response Explorer-List Collections + * * Successful Response */ 200: Array; @@ -1086,6 +1995,9 @@ export type ExplorerListCollectionsResponse = ExplorerListCollectionsResponses[k export type ExplorerGetCollectionData = { body?: never; path: { + /** + * Collection Id + */ collection_id: string; }; query?: never; @@ -1119,6 +2031,8 @@ export type ExplorerListThemesData = { export type ExplorerListThemesResponses = { /** + * Response Explorer-List Themes + * * Successful Response */ 200: Array; @@ -1129,6 +2043,9 @@ export type ExplorerListThemesResponse = ExplorerListThemesResponses[keyof Explo export type ExplorerGetThemeData = { body?: never; path: { + /** + * Theme Slug + */ theme_slug: string; }; query?: never; @@ -1156,6 +2073,9 @@ export type ExplorerGetThemeResponse = ExplorerGetThemeResponses[keyof ExplorerG export type ResultsGetResultData = { body?: never; path: { + /** + * Result Id + */ result_id: number; }; query?: never; @@ -1187,13 +2107,11 @@ export type UtilsHealthCheckData = { export type UtilsHealthCheckResponses = { /** + * Response Utils-Health Check + * * Successful Response */ 200: boolean; }; export type UtilsHealthCheckResponse = UtilsHealthCheckResponses[keyof UtilsHealthCheckResponses]; - -export type ClientOptions = { - baseUrl: `${string}://${string}` | (string & {}); -}; \ No newline at end of file diff --git a/frontend/src/components/execution/executionLogs/executionLogContainer.tsx b/frontend/src/components/execution/executionLogs/executionLogContainer.tsx index 77e5bbb..f027cb3 100644 --- a/frontend/src/components/execution/executionLogs/executionLogContainer.tsx +++ b/frontend/src/components/execution/executionLogs/executionLogContainer.tsx @@ -14,6 +14,26 @@ import { Skeleton } from "@/components/ui/skeleton.tsx"; import { downloadTextFile } from "@/lib/downloadUtils.ts"; import { ExecutionLogView, type LogMessage } from "./executionLogView.tsx"; +function isValidationError( + error: unknown, +): error is { detail: Array<{ msg: string }> } { + if (typeof error !== "object" || error === null || !("detail" in error)) { + return false; + } + const { detail } = error as { detail: unknown }; + return ( + Array.isArray(detail) && + detail.length > 0 && + typeof detail[0]?.msg === "string" + ); +} + +function formatValidationError(error: { + detail: Array<{ msg: string }>; +}): string { + return error.detail.map((d) => d.msg).join("; "); +} + interface ExecutionLogContainerProps { groupId: string; executionId?: string; @@ -123,7 +143,12 @@ export function ExecutionLogContainer({ Error Loading Logs - An error occurred while loading the logs: {error.message} + An error occurred while loading the logs:{" "} + {error instanceof Error + ? error.message + : isValidationError(error) + ? formatValidationError(error) + : JSON.stringify(error)} diff --git a/frontend/src/lib/apiEndpoint.test.ts b/frontend/src/lib/apiEndpoint.test.ts index 4a5cbee..11c2277 100644 --- a/frontend/src/lib/apiEndpoint.test.ts +++ b/frontend/src/lib/apiEndpoint.test.ts @@ -26,12 +26,19 @@ const localStorageMock = (() => { }; })(); +let originalViteBaseUrl: string | undefined; + beforeEach(() => { vi.stubGlobal("localStorage", localStorageMock); localStorageMock.clear(); + originalViteBaseUrl = import.meta.env.VITE_BASE_URL; + delete import.meta.env.VITE_BASE_URL; }); afterEach(() => { + if (originalViteBaseUrl !== undefined) { + import.meta.env.VITE_BASE_URL = originalViteBaseUrl; + } vi.unstubAllGlobals(); });