From 9ee0dda3fdec4b3faedabb4ce84a5671cfb90dab Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 18 Mar 2026 00:40:40 +0200 Subject: [PATCH] Delete dead code, refactor webapp This change removes some dead code, refactors some webapp code to simplify logic, adds some new tests. Signed-off-by: Gabriel Adrian Samfira --- webapp/package-lock.json | 596 +++++++++++++- webapp/package.json | 1 + .../pool-creation-anti-duplication.test.ts | 200 ----- webapp/src/lib/api/client.ts | 26 +- webapp/src/lib/api/generated-client.ts | 2 +- webapp/src/lib/components/CodeEditor.svelte | 73 +- .../lib/components/ControllerInfoCard.svelte | 4 +- .../components/CreateOrganizationModal.svelte | 3 +- .../CreatePoolModal.anti-duplication.md | 113 --- .../components/CreatePoolModal.simple.test.ts | 171 ---- .../src/lib/components/CreatePoolModal.svelte | 10 +- .../components/CreateRepositoryModal.svelte | 3 +- .../lib/components/CreateScaleSetModal.svelte | 10 +- .../src/lib/components/UpdatePoolModal.svelte | 88 +- .../lib/components/UpdateScaleSetModal.svelte | 85 +- .../lib/components/cells/EntityCell.svelte | 8 +- .../lib/components/cells/GenericCell.svelte | 32 +- webapp/src/lib/stores/eager-cache.ts | 449 +++-------- webapp/src/lib/stores/toast.ts | 24 +- webapp/src/lib/stores/websocket.ts | 26 +- .../[id]/page.integration.test.ts | 253 ++++++ .../routes/credentials/page.render.test.ts | 217 ----- webapp/src/routes/credentials/page.test.ts | 618 -------------- .../src/routes/endpoints/page.render.test.ts | 189 ----- webapp/src/routes/endpoints/page.test.ts | 536 ------------- .../enterprises/[id]/page.render.test.ts | 164 ---- .../src/routes/enterprises/[id]/page.test.ts | 454 ----------- .../routes/enterprises/page.render.test.ts | 179 ----- webapp/src/routes/enterprises/page.test.ts | 528 ------------ webapp/src/routes/init/page.render.test.ts | 639 --------------- webapp/src/routes/init/page.test.ts | 574 ------------- .../routes/instances/[id]/page.render.test.ts | 461 ----------- webapp/src/routes/instances/[id]/page.test.ts | 560 ------------- .../src/routes/instances/page.render.test.ts | 211 ----- webapp/src/routes/instances/page.test.ts | 413 ---------- webapp/src/routes/login/page.render.test.ts | 497 ------------ webapp/src/routes/login/page.test.ts | 481 ----------- .../routes/objects/[id]/page.render.test.ts | 214 ----- webapp/src/routes/objects/page.render.test.ts | 176 ---- .../organizations/[id]/page.render.test.ts | 188 ----- .../routes/organizations/[id]/page.test.ts | 531 ------------- .../routes/organizations/page.render.test.ts | 180 ----- webapp/src/routes/organizations/page.test.ts | 551 ------------- webapp/src/routes/page.integration.test.ts | 336 ++++++++ .../pools/[id]/page.integration.test.ts | 256 ++++++ webapp/src/routes/pools/page.render.test.ts | 527 ------------ webapp/src/routes/pools/page.test.ts | 751 ------------------ webapp/src/routes/pools/pool-creation.test.ts | 168 ---- .../repositories/[id]/page.render.test.ts | 189 ----- .../src/routes/repositories/[id]/page.test.ts | 532 ------------- .../repositories/[id]/pool-creation.test.ts | 228 ------ .../routes/repositories/page.render.test.ts | 158 ---- webapp/src/routes/repositories/page.test.ts | 483 ----------- .../scalesets/[id]/page.integration.test.ts | 231 ++++++ .../src/routes/scalesets/page.render.test.ts | 528 ------------ webapp/src/routes/scalesets/page.test.ts | 663 ---------------- .../templates/[id]/page.integration.test.ts | 163 ++++ .../templates/create/page.integration.test.ts | 138 ++++ .../routes/templates/page.integration.test.ts | 229 ++++++ webapp/src/test/mocks.ts | 51 -- 60 files changed, 2353 insertions(+), 14016 deletions(-) delete mode 100644 webapp/src/integration/pool-creation-anti-duplication.test.ts delete mode 100644 webapp/src/lib/components/CreatePoolModal.anti-duplication.md delete mode 100644 webapp/src/lib/components/CreatePoolModal.simple.test.ts create mode 100644 webapp/src/routes/credentials/[forge_type]/[id]/page.integration.test.ts delete mode 100644 webapp/src/routes/credentials/page.render.test.ts delete mode 100644 webapp/src/routes/credentials/page.test.ts delete mode 100644 webapp/src/routes/endpoints/page.render.test.ts delete mode 100644 webapp/src/routes/endpoints/page.test.ts delete mode 100644 webapp/src/routes/enterprises/[id]/page.render.test.ts delete mode 100644 webapp/src/routes/enterprises/[id]/page.test.ts delete mode 100644 webapp/src/routes/enterprises/page.render.test.ts delete mode 100644 webapp/src/routes/enterprises/page.test.ts delete mode 100644 webapp/src/routes/init/page.render.test.ts delete mode 100644 webapp/src/routes/init/page.test.ts delete mode 100644 webapp/src/routes/instances/[id]/page.render.test.ts delete mode 100644 webapp/src/routes/instances/[id]/page.test.ts delete mode 100644 webapp/src/routes/instances/page.render.test.ts delete mode 100644 webapp/src/routes/instances/page.test.ts delete mode 100644 webapp/src/routes/login/page.render.test.ts delete mode 100644 webapp/src/routes/login/page.test.ts delete mode 100644 webapp/src/routes/objects/[id]/page.render.test.ts delete mode 100644 webapp/src/routes/objects/page.render.test.ts delete mode 100644 webapp/src/routes/organizations/[id]/page.render.test.ts delete mode 100644 webapp/src/routes/organizations/[id]/page.test.ts delete mode 100644 webapp/src/routes/organizations/page.render.test.ts delete mode 100644 webapp/src/routes/organizations/page.test.ts create mode 100644 webapp/src/routes/page.integration.test.ts create mode 100644 webapp/src/routes/pools/[id]/page.integration.test.ts delete mode 100644 webapp/src/routes/pools/page.render.test.ts delete mode 100644 webapp/src/routes/pools/page.test.ts delete mode 100644 webapp/src/routes/pools/pool-creation.test.ts delete mode 100644 webapp/src/routes/repositories/[id]/page.render.test.ts delete mode 100644 webapp/src/routes/repositories/[id]/page.test.ts delete mode 100644 webapp/src/routes/repositories/[id]/pool-creation.test.ts delete mode 100644 webapp/src/routes/repositories/page.render.test.ts delete mode 100644 webapp/src/routes/repositories/page.test.ts create mode 100644 webapp/src/routes/scalesets/[id]/page.integration.test.ts delete mode 100644 webapp/src/routes/scalesets/page.render.test.ts delete mode 100644 webapp/src/routes/scalesets/page.test.ts create mode 100644 webapp/src/routes/templates/[id]/page.integration.test.ts create mode 100644 webapp/src/routes/templates/create/page.integration.test.ts create mode 100644 webapp/src/routes/templates/page.integration.test.ts delete mode 100644 webapp/src/test/mocks.ts diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 6d7cd910b..d299ef475 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -38,6 +38,7 @@ "@testing-library/svelte": "^5.2.0-next.3", "@testing-library/user-event": "^14.6.1", "@types/node": "^24.2.0", + "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", "autoprefixer": "^10.4.16", "happy-dom": "^20.0.2", @@ -115,16 +116,42 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-identifier": { + "node_modules/@babel/helper-string-parser": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", @@ -135,6 +162,30 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@biomejs/js-api": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@biomejs/js-api/-/js-api-1.0.0.tgz", @@ -980,6 +1031,109 @@ "node": ">=18" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -993,6 +1147,16 @@ "node": ">=18.0.0" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", @@ -1031,9 +1195,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1304,6 +1468,17 @@ "url": "https://opencollective.com/openapi_generator" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@playwright/test": { "version": "1.56.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", @@ -2342,6 +2517,40 @@ "dev": true, "license": "MIT" }, + "node_modules/@vitest/coverage-v8": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^1.0.2", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "std-env": "^3.9.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, "node_modules/@vitest/expect": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", @@ -2575,6 +2784,25 @@ "node": ">=12" } }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz", + "integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3181,6 +3409,21 @@ "node": ">= 0.6" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -3379,6 +3622,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/easy-table": { "version": "1.1.0", "dev": true, @@ -3691,6 +3941,23 @@ "dev": true, "license": "ISC" }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -3992,6 +4259,13 @@ "node": ">=18" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -4087,6 +4361,67 @@ "@types/estree": "^1.0.6" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/iterare": { "version": "1.2.1", "dev": true, @@ -4095,6 +4430,22 @@ "node": ">=6" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jiti": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", @@ -4552,6 +4903,34 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4800,6 +5179,13 @@ "dev": true, "license": "MIT" }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", @@ -4813,6 +5199,16 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-scurry": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", @@ -5436,6 +5832,19 @@ "node": ">=v12.22.7" } }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/set-cookie-parser": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz", @@ -5443,6 +5852,29 @@ "dev": true, "license": "MIT" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/shell-quote": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", @@ -5593,6 +6025,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "dev": true, @@ -5604,6 +6052,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -5993,6 +6455,93 @@ "node": ">=18" } }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -6467,6 +7016,22 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -6499,6 +7064,25 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", diff --git a/webapp/package.json b/webapp/package.json index c8e77f6d7..856cfe514 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -24,6 +24,7 @@ "@testing-library/svelte": "^5.2.0-next.3", "@testing-library/user-event": "^14.6.1", "@types/node": "^24.2.0", + "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", "autoprefixer": "^10.4.16", "happy-dom": "^20.0.2", diff --git a/webapp/src/integration/pool-creation-anti-duplication.test.ts b/webapp/src/integration/pool-creation-anti-duplication.test.ts deleted file mode 100644 index 126ddcad8..000000000 --- a/webapp/src/integration/pool-creation-anti-duplication.test.ts +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Integration tests to prevent duplicate pool creation issue - * - * These tests verify that: - * 1. Entity detail pages don't make duplicate API calls - * 2. Global pools page handles creation correctly - * 3. The conditional logic in CreatePoolModal works as expected - */ -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Core test: Verify the conditional logic exists and works -describe('Pool Creation Anti-Duplication Integration', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - describe('Conditional Logic Verification', () => { - it('should have conditional API call logic based on initialEntityType', async () => { - // Mock the CreatePoolModal to test its conditional logic - const mockCreatePoolModal = await import('$lib/components/CreatePoolModal.svelte'); - - // This test verifies that the modal component has the logic to decide - // whether to make API calls or let parent components handle them - expect(mockCreatePoolModal).toBeDefined(); - }); - - it('should prevent duplicate pool creation through architecture', () => { - // The architecture prevents duplication by: - // 1. Entity pages: Parent handles API calls, modal just validates and dispatches - // 2. Global page: Modal handles API calls since no parent entity context - - const scenarios = [ - { - name: 'Repository detail page', - hasInitialEntity: true, - expectedAPICallLocation: 'parent' - }, - { - name: 'Organization detail page', - hasInitialEntity: true, - expectedAPICallLocation: 'parent' - }, - { - name: 'Enterprise detail page', - hasInitialEntity: true, - expectedAPICallLocation: 'parent' - }, - { - name: 'Global pools page', - hasInitialEntity: false, - expectedAPICallLocation: 'modal' - } - ]; - - scenarios.forEach(scenario => { - if (scenario.hasInitialEntity) { - // Entity pages: Modal should NOT make API calls - expect(scenario.expectedAPICallLocation).toBe('parent'); - } else { - // Global page: Modal SHOULD make API calls - expect(scenario.expectedAPICallLocation).toBe('modal'); - } - }); - }); - }); - - describe('API Call Prevention Rules', () => { - it('should follow the rule: one source of truth per scenario', () => { - const rules = { - 'entity-detail-page': { - modalMakesAPICall: false, - parentMakesAPICall: true, - reason: 'Entity is pre-known, parent handles creation' - }, - 'global-pools-page': { - modalMakesAPICall: true, - parentMakesAPICall: false, - reason: 'No pre-selected entity, modal handles everything' - } - }; - - // Verify rules are consistent - Object.values(rules).forEach(rule => { - // Each scenario should have exactly one source making API calls - const apiCallSources = [rule.modalMakesAPICall, rule.parentMakesAPICall]; - const activeSourcesCount = apiCallSources.filter(Boolean).length; - expect(activeSourcesCount).toBe(1); - }); - }); - - it('should prevent race conditions through sequential handling', () => { - // The fix ensures: - // 1. Only one component makes the API call - // 2. Success/error handling is centralized - // 3. No race conditions between modal and parent - - const preventionMechanisms = { - conditionalAPICall: 'Modal checks initialEntityType props', - singleSubmitEvent: 'Only one submit event dispatched', - clearResponsibility: 'Each component has defined role' - }; - - Object.entries(preventionMechanisms).forEach(([mechanism, description]) => { - expect(description).toContain(mechanism === 'conditionalAPICall' ? 'Modal checks' : 'one'); - }); - }); - }); - - describe('Error Handling Consistency', () => { - it('should handle errors appropriately per scenario', () => { - const errorHandling = { - 'entity-page-api-error': { - handledBy: 'parent', - action: 'show toast, keep modal open', - apiCallMadeBy: 'parent' - }, - 'global-page-api-error': { - handledBy: 'modal', - action: 'show error in modal', - apiCallMadeBy: 'modal' - } - }; - - Object.entries(errorHandling).forEach(([scenario, handling]) => { - // Error should be handled by the same component that made the API call - expect(handling.handledBy).toBe(handling.apiCallMadeBy); - }); - }); - }); - - describe('Regression Prevention', () => { - it('should prevent the specific duplicate issue that was fixed', () => { - // The original bug: Both modal AND parent were calling createRepositoryPool - // The fix: Only parent calls API when initialEntityType is provided - - const originalBug = { - description: 'Both modal and parent called createRepositoryPool', - symptoms: 'Two identical pools created', - rootCause: 'No conditional logic in modal submission' - }; - - const fix = { - description: 'Conditional API calls based on initialEntityType', - prevention: 'Only one component makes API call per scenario', - verification: 'Unit tests verify API call counts' - }; - - // Verify the fix addresses the root cause - expect(fix.description).toContain('Conditional'); - expect(fix.prevention).toContain('one component'); - expect(originalBug.rootCause).toContain('No conditional logic'); - }); - - it('should maintain backward compatibility', () => { - // The fix should not break existing functionality - const compatibility = { - globalPoolsPage: 'Still works, modal handles creation', - entityDetailPages: 'Still works, parent handles creation', - modalInterface: 'Still works with same props and events', - apiInterface: 'Still works with same API calls, just different caller' - }; - - Object.values(compatibility).forEach(requirement => { - expect(requirement).toContain('works'); - }); - }); - }); - - describe('Future Duplication Prevention', () => { - it('should have clear patterns for adding new entity types', () => { - // When adding new entity types, developers should follow: - const patterns = { - modalLogic: 'Add new case to entity type switch statement', - parentHandler: 'Create handleCreatePool function in parent', - conditionalCheck: 'Use initialEntityType to determine API caller', - errorHandling: 'Handle errors in the component making API call' - }; - - // These patterns prevent accidental duplication - Object.values(patterns).forEach(pattern => { - expect(pattern).toBeDefined(); - }); - }); - - it('should make it easy to identify API call responsibility', () => { - // Clear responsibility matrix - const responsibilities = { - 'CreatePoolModal with initialEntityType': 'Validate form, dispatch event', - 'CreatePoolModal without initialEntityType': 'Validate form, make API call', - 'Parent with CreatePoolModal (entity page)': 'Handle API call and success/error', - 'Parent with CreatePoolModal (global page)': 'Handle success message only' - }; - - // Each scenario has clear responsibility - Object.values(responsibilities).forEach(responsibility => { - expect(responsibility).toMatch(/^(Validate|Handle|dispatch)/); - }); - }); - }); -}); \ No newline at end of file diff --git a/webapp/src/lib/api/client.ts b/webapp/src/lib/api/client.ts index 47fdef77a..08ccb5651 100644 --- a/webapp/src/lib/api/client.ts +++ b/webapp/src/lib/api/client.ts @@ -76,29 +76,5 @@ export interface APIError { details?: string; } -// GarmApiClient now extends/wraps the generated client -export class GarmApiClient extends GeneratedGarmApiClient { - constructor(baseUrl: string = '') { - super(baseUrl); - } - - // All methods are inherited from GeneratedGarmApiClient - // This class now acts as a simple wrapper for backward compatibility - - // Explicitly expose template methods for TypeScript - declare listTemplates: (osType?: string, partialName?: string, forgeType?: string) => Promise; - declare getTemplate: (id: number) => Promise