From 253870f645a3ffff8d8952bdd542b89f03eab14d Mon Sep 17 00:00:00 2001 From: Breimerct Date: Sun, 15 Jun 2025 05:48:45 -0500 Subject: [PATCH 1/3] feat: add Vitest for testing and coverage support --- .gitignore | 1 + package-lock.json | 1028 ++++++++++++++++++++++++++++++++++++- package.json | 15 +- src/app/app.ts | 4 +- src/client/http-client.ts | 8 +- src/helpers/utils.ts | 4 +- tsconfig.json | 11 +- tsconfig.test.json | 15 + vitest.config.ts | 32 ++ 9 files changed, 1084 insertions(+), 34 deletions(-) create mode 100644 tsconfig.test.json create mode 100644 vitest.config.ts diff --git a/.gitignore b/.gitignore index 31bb7ca..8add2b3 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ ehthumbs.db Thumbs.db # Testing +coverage # Compiled files diff --git a/package-lock.json b/package-lock.json index dd3e453..4c008a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,9 @@ }, "devDependencies": { "@commitlint/config-conventional": "^19.8.1", + "@types/node": "^24.0.1", + "@vitest/coverage-v8": "^3.2.3", + "@vitest/ui": "^3.2.3", "husky": "^9.1.7", "lint-staged": "^16.1.1", "nodemon": "^3.1.10", @@ -20,7 +23,93 @@ "prettier": "^3.5.3", "ts-node": "^10.9.2", "tsup": "^8.5.0", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vitest": "^3.2.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "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/@commitlint/config-conventional": { @@ -507,6 +596,16 @@ "node": ">=12" } }, + "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.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -694,6 +793,13 @@ "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.43.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz", @@ -1002,6 +1108,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, "node_modules/@types/conventional-commits-parser": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", @@ -1012,6 +1128,13 @@ "@types/node": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -1029,6 +1152,177 @@ "undici-types": "~7.8.0" } }, + "node_modules/@vitest/coverage-v8": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.3.tgz", + "integrity": "sha512-D1QKzngg8PcDoCE8FHSZhREDuEy+zcKmMiMafYse41RZpBE5EDJyKOTdqK3RQfsV2S2nyKor5KCs8PyPRFqKPg==", + "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.3", + "vitest": "3.2.3" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.3.tgz", + "integrity": "sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.3", + "@vitest/utils": "3.2.3", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.3.tgz", + "integrity": "sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.3", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.3.tgz", + "integrity": "sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.3.tgz", + "integrity": "sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.3", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.3.tgz", + "integrity": "sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.3", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.3.tgz", + "integrity": "sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.2.3.tgz", + "integrity": "sha512-9aR2tY/WT7GRHGEH/9sSIipJqeA21Eh3C6xmiOVmfyBCFmezUSUFLalpaSmRHlRzWCKQU10yz3AHhKuYcdnZGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.3", + "fflate": "^0.8.2", + "flatted": "^3.3.3", + "pathe": "^2.0.3", + "sirv": "^3.0.1", + "tinyglobby": "^0.2.14", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "3.2.3" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.3.tgz", + "integrity": "sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.3", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1132,6 +1426,39 @@ "dev": true, "license": "MIT" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", + "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1202,6 +1529,23 @@ "node": ">=8" } }, + "node_modules/chai": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -1215,6 +1559,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1432,6 +1786,16 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1482,6 +1846,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", @@ -1523,6 +1894,16 @@ "@esbuild/win32-x64": "0.25.5" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -1530,6 +1911,16 @@ "dev": true, "license": "MIT" }, + "node_modules/expect-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", + "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -1553,6 +1944,13 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1578,6 +1976,13 @@ "rollup": "^4.34.8" } }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "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", @@ -1705,6 +2110,13 @@ "node": ">=4" } }, + "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/husky": { "version": "9.1.7", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", @@ -1801,36 +2213,131 @@ "dev": true, "license": "ISC" }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "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": "BlueOak-1.0.0", + "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": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "engines": { + "node": ">=10" } }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-lib-source-maps/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { @@ -2059,6 +2566,13 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -2076,6 +2590,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/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -2146,6 +2688,16 @@ "ufo": "^1.5.4" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2178,6 +2730,25 @@ "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" } }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -2349,6 +2920,16 @@ "dev": true, "license": "MIT" }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2404,6 +2985,35 @@ "pathe": "^2.0.1" } }, + "node_modules/postcss": { + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", + "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postcss-load-config": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", @@ -2603,6 +3213,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -2629,6 +3246,21 @@ "node": ">=10" } }, + "node_modules/sirv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", + "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -2672,6 +3304,30 @@ "node": ">= 8" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -2786,6 +3442,19 @@ "node": ">=8" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -2822,6 +3491,47 @@ "node": ">=4" } }, + "node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "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/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -2845,6 +3555,13 @@ "node": ">=0.8" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -2897,6 +3614,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinypool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", + "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2910,6 +3657,16 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/touch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", @@ -3116,6 +3873,218 @@ "dev": true, "license": "MIT" }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.3.tgz", + "integrity": "sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.3.tgz", + "integrity": "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.3", + "@vitest/mocker": "3.2.3", + "@vitest/pretty-format": "^3.2.3", + "@vitest/runner": "3.2.3", + "@vitest/snapshot": "3.2.3", + "@vitest/spy": "3.2.3", + "@vitest/utils": "3.2.3", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.0", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.3", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.3", + "@vitest/ui": "3.2.3", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -3160,6 +4129,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/package.json b/package.json index 4b83dbd..b0a43d9 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,12 @@ "format": "prettier --write .", "format:check": "prettier --check .", "check": "npm run lint && npm run format:check", - "prepare": "husky" + "prepare": "husky", + "test": "vitest run", + "test:watch": "vitest", + "test:ui": "vitest --ui", + "test:coverage": "vitest run --coverage", + "test:ci": "vitest run --coverage --reporter=verbose" }, "typesVersions": { "*": { @@ -63,6 +68,9 @@ ], "devDependencies": { "@commitlint/config-conventional": "^19.8.1", + "@types/node": "^24.0.1", + "@vitest/coverage-v8": "^3.2.3", + "@vitest/ui": "^3.2.3", "husky": "^9.1.7", "lint-staged": "^16.1.1", "nodemon": "^3.1.10", @@ -70,7 +78,8 @@ "prettier": "^3.5.3", "ts-node": "^10.9.2", "tsup": "^8.5.0", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vitest": "^3.2.3" }, "dependencies": { "node-fetch": "^3.3.2" @@ -86,4 +95,4 @@ "@commitlint/config-conventional" ] } -} \ No newline at end of file +} diff --git a/src/app/app.ts b/src/app/app.ts index 6f0bc99..f9378bc 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,5 +1,5 @@ -import { HttpClient } from '../client'; -import { HttpClientConfig, HttpResponse, RequestConfig } from '../types'; +import { HttpClient } from '@/client'; +import { HttpClientConfig, HttpResponse, RequestConfig } from '@/types'; const defaultHttpClient = new HttpClient(); diff --git a/src/client/http-client.ts b/src/client/http-client.ts index 3ba8d3a..03d6745 100644 --- a/src/client/http-client.ts +++ b/src/client/http-client.ts @@ -5,7 +5,7 @@ import { createHttpError, createTimeoutSignal, getFetch, -} from '../helpers'; +} from '@/helpers'; import { QueryParams, HttpHeaders, @@ -15,7 +15,7 @@ import { RequestConfig, HttpClientConfig, Interceptors, -} from '../types'; +} from '@/types'; export class HttpClient { private config: HttpClientConfig; @@ -253,4 +253,8 @@ export class HttpClient { async request(config: RequestConfig): Promise> { return this.makeRequest(config); } + + getConfig(): HttpClientConfig { + return this.config; + } } diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 91a905c..830d858 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -1,5 +1,5 @@ -import { HttpCode } from '../constants'; -import { HttpError, HttpHeaders, QueryParams } from '../types/http-client'; +import { HttpCode } from '@/constants'; +import { HttpError, HttpHeaders, QueryParams } from '@/types'; export function buildURL(baseURL: string = '', endpoint: string, params?: QueryParams): string { const cleanBaseURL = baseURL.replace(/\/$/, ''); diff --git a/tsconfig.json b/tsconfig.json index e5899c8..c8131bb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,8 +39,10 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ /* Module Resolution Options */ "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + "baseUrl": "./" /* Base directory to resolve non-absolute module names. */, + "paths": { + "@/*": ["src/*"] + } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ @@ -60,6 +62,7 @@ "skipLibCheck": true /* Skip type checking of declaration files. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, + "include": ["src/**/*"], "exclude": [ "node_modules", "dist", @@ -67,7 +70,7 @@ "src/__mocks__", "src/**/*.test.ts", "src/**/*.spec.ts", - "tsup.config.ts", - "oxlint.ts" + "*.config.ts", + "vitest.config.ts" ] } diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..d7349ee --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "types": ["vitest/globals", "node"], + + "baseUrl": "./", + "paths": { + "@/*": ["src/*"], + "@tests/*": ["tests/*"] + } + }, + "include": ["src/**/*", "tests/**/*", "vitest.config.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..d19b3ad --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,32 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + // Entorno de testing + environment: 'node', + + // Patrones de archivos de test + include: ['tests/**/*.{test,spec}.{js,ts}', 'src/**/*.{test,spec}.{js,ts}'], + + // Excluir archivos + exclude: ['node_modules', 'dist', 'build'], + + // Coverage + coverage: { + provider: 'v8', // o 'c8' + reporter: ['text', 'json', 'html'], + include: ['src/**/*.ts'], + exclude: [ + 'src/**/*.d.ts', + 'src/index.ts', // archivo de entrada principal + 'src/**/*.test.ts', + 'src/**/*.spec.ts', + ], + }, + + // Configuración adicional + globals: true, // Para usar expect, describe, it sin importar + clearMocks: true, + restoreMocks: true, + }, +}); From 670b704e19df377e36ada436bb6ba07b4442d6d6 Mon Sep 17 00:00:00 2001 From: Breimerct Date: Sun, 15 Jun 2025 05:48:59 -0500 Subject: [PATCH 2/3] feat: add comprehensive tests for HTTP client, helpers, and types --- tests/app/app.test.ts | 119 +++++++++++++++++++++++ tests/client/http-client.test.ts | 160 ++++++++++++++++++++++++++++++ tests/helpers/helpers.test.ts | 82 ++++++++++++++++ tests/types/types.test.ts | 162 +++++++++++++++++++++++++++++++ 4 files changed, 523 insertions(+) create mode 100644 tests/app/app.test.ts create mode 100644 tests/client/http-client.test.ts create mode 100644 tests/helpers/helpers.test.ts create mode 100644 tests/types/types.test.ts diff --git a/tests/app/app.test.ts b/tests/app/app.test.ts new file mode 100644 index 0000000..5ef4e40 --- /dev/null +++ b/tests/app/app.test.ts @@ -0,0 +1,119 @@ +import { createBrexClient, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT } from '../../src/app'; + +import { describe, it, expect } from 'vitest'; + +const baseURL = 'https://jsonplaceholder.typicode.com/'; + +describe('Base app HTTP Client', () => { + it('should create a Brex client with default config', async () => { + const client = createBrexClient({ baseURL }); + + const response = await client.request({ + method: 'GET', + url: '/users', + }); + + expect(client).toBeDefined(); + expect(response.content).not.toBeNull(); + expect(response.status).toBe(200); + }); + + it('should handle GET request', async () => { + const response = await GET(`${baseURL}users/1`); + + expect(response.content).not.toBeNull(); + expect(response.status).toBe(200); + expect(response.content.id).toBe(1); + }); + + it('should handle POST request', async () => { + const response = await POST(`${baseURL}users`, { + name: 'John Doe', + email: 'john.doe@example.com', + phone: '123-456-7890', + website: 'https://example.com', + }); + + expect(response.content).not.toBeNull(); + expect(response.status).toBe(201); + expect(response.content.name).toBe('John Doe'); + expect(response.content.email).toBe('john.doe@example.com'); + expect(response.content.phone).toBe('123-456-7890'); + expect(response.content.website).toBe('https://example.com'); + }); + + it('should handle PUT request', async () => { + const response = await PUT(`${baseURL}users/1`, { + name: 'Jane Doe', + email: 'jane.doe@example.com', + phone: '987-654-3210', + website: 'https://jane-doe.com', + }); + + expect(response.content).not.toBeNull(); + expect(response.status).toBe(200); + expect(response.content.name).toBe('Jane Doe'); + expect(response.content.email).toBe('jane.doe@example.com'); + expect(response.content.phone).toBe('987-654-3210'); + expect(response.content.website).toBe('https://jane-doe.com'); + }); + + it('should handle PATCH request', async () => { + const response = await PATCH(`${baseURL}users/1`, { + name: 'Updated Name', + }); + + expect(response.content).not.toBeNull(); + expect(response.status).toBe(200); + expect(response.content.name).toBe('Updated Name'); + }); + + it('should handle DELETE request', async () => { + const response = await DELETE(`${baseURL}users/1`); + + expect(response.content).not.toBeNull(); + expect(response.status).toBe(200); + }); + + it('should handle OPTIONS request', async () => { + const response = await OPTIONS(`${baseURL}`); + + expect(response.status).toBe(204); + }); + + it('should handle HEAD request', async () => { + const response = await HEAD(`${baseURL}`); + + expect(response.status).toBe(200); + }); + + it('should handle request with custom headers', async () => { + const response = await GET(`${baseURL}`, { + headers: { 'Custom-Header': 'CustomValue' }, + }); + + expect(response.status).toBe(200); + }); + + it('should handle request with query parameters', async () => { + const response = await GET(`${baseURL}users`, { + params: { _limit: 1 }, + }); + + expect(response.content).not.toBeNull(); + expect(response.status).toBe(200); + expect(response.content.length).toBe(1); + }); + + it('should handle request with timeout', async () => { + const client = createBrexClient({ baseURL, timeout: 1000 }); + + const response = await client.request({ + method: 'GET', + url: '/users/1', + }); + + expect(response.content).not.toBeNull(); + expect(response.status).toBe(200); + }); +}); diff --git a/tests/client/http-client.test.ts b/tests/client/http-client.test.ts new file mode 100644 index 0000000..fa004bc --- /dev/null +++ b/tests/client/http-client.test.ts @@ -0,0 +1,160 @@ +import { HttpClient } from '../../src/client'; +import { describe, it, expect } from 'vitest'; +import { RequestConfig } from '../../src/types'; + +const baseURL = 'https://jsonplaceholder.typicode.com/'; + +describe('HttpClient', () => { + it('should create an instance', () => { + const client = new HttpClient(); + expect(client).toBeDefined(); + }); + + it('should handle request with default config', async () => { + const client = new HttpClient({ baseURL }); + const response = await client.request({ method: 'GET', url: '/users' }); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + }); + + it('should handle GET request', async () => { + const client = new HttpClient({ baseURL }); + const response = await client.get('/users/1'); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + expect(response.content.id).toBe(1); + }); + + it('should handle POST request', async () => { + const client = new HttpClient({ baseURL }); + const response = await client.post('/users', { + name: 'John Doe', + email: 'john.doe@example.com', + }); + + expect(response.status).toBe(201); + expect(response.content).not.toBeNull(); + expect(response.content.name).toBe('John Doe'); + expect(response.content.email).toBe('john.doe@example.com'); + }); + + it('should handle PUT request', async () => { + const client = new HttpClient({ baseURL }); + const response = await client.put('/users/1', { + name: 'Jane Doe', + email: 'jane.doe@example.com', + phone: '987-654-3210', + website: 'https://jane-doe.com', + }); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + }); + + it('should handle PATCH request', async () => { + const client = new HttpClient({ baseURL }); + const response = await client.patch('/users/1', { + name: 'Jane Smith', + }); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + expect(response.content.name).toBe('Jane Smith'); + }); + + it('should handle DELETE request', async () => { + const client = new HttpClient({ baseURL }); + const response = await client.delete('/users/1'); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + }); + + it('should handle request with custom headers', async () => { + const client = new HttpClient({ baseURL }); + const response = await client.request({ + method: 'GET', + url: '/users', + headers: { 'Custom-Header': 'CustomValue' }, + }); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + }); + + it('should handle request with interceptors', async () => { + const client = new HttpClient({ baseURL }); + let _config: Partial = {}; + + client.addRequestInterceptor((config) => { + config.headers = { ...config.headers, 'X-Intercepted': 'true' }; + _config = config; + return config; + }); + + const response = await client.get('/users/1'); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + expect(response.content.id).toBe(1); + expect(_config.headers?.['X-Intercepted']).toBe('true'); + expect(_config.url).toBe('/users/1'); + }); + + it('should handle response interceptors', async () => { + const client = new HttpClient({ baseURL }); + let _response; + + client.addResponseInterceptor((response) => { + response.content = { ...response.content, intercepted: true }; + _response = response.content; + return response; + }); + + const response = await client.get('/users/1'); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + expect(response.content.id).toBe(1); + expect(_response.intercepted).toBe(true); + }); + + it('should handle request with timeout', async () => { + const client = new HttpClient({ baseURL, timeout: 1000 }); + + const response = await client.request({ + method: 'GET', + url: '/users/1', + }); + + expect(response.status).toBe(200); + expect(response.content).not.toBeNull(); + }); + + it('should validate config', async () => { + const client = new HttpClient({}); + client + .setBaseURL(baseURL) + .setHeader('Authorization', 'Bearer token') + .setHeaders({ 'Custom-Header': 'CustomValue' }) + .setParams({ page: 1, limit: 10 }) + .setParam('sort', 'asc') + .setTimeout(5000); + + const config = client.getConfig(); + + expect(config.baseURL).toBe(baseURL); + expect(config.headers).toEqual({ + Authorization: 'Bearer token', + 'Custom-Header': 'CustomValue', + }); + expect(config.params).toEqual({ + page: 1, + limit: 10, + sort: 'asc', + }); + expect(config.timeout).toBe(5000); + }); +}); diff --git a/tests/helpers/helpers.test.ts b/tests/helpers/helpers.test.ts new file mode 100644 index 0000000..e88bb02 --- /dev/null +++ b/tests/helpers/helpers.test.ts @@ -0,0 +1,82 @@ +import { HttpCode } from '../../src/constants'; +import { + buildURL, + mergeHeaders, + mergeParams, + createTimeoutSignal, + createHttpError, + getFetch, +} from '../../src/helpers'; +import { describe, it, expect } from 'vitest'; + +describe('Helpers', () => { + it('should build url', () => { + const url = buildURL('https://api.example.com/', '/users', { page: 1, limit: 10 }); + expect(url).toBe('https://api.example.com/users?page=1&limit=10'); + }); + + it('should merge headers', () => { + const headers1 = { 'Content-Type': 'application/json' }; + const headers2 = { Authorization: 'Bearer token' }; + const mergedHeaders = mergeHeaders(headers1, headers2); + + expect(mergedHeaders).toEqual({ + 'Content-Type': 'application/json', + Authorization: 'Bearer token', + }); + }); + + it('should merge params', () => { + const params1 = { page: 1, limit: 10 }; + const params2 = { sort: 'asc' }; + const mergedParams = mergeParams(params1, params2); + + expect(mergedParams).toEqual({ + page: 1, + limit: 10, + sort: 'asc', + }); + }); + + it('should create timeout signal', () => { + const signal = createTimeoutSignal(1000); + expect(signal).toBeInstanceOf(AbortSignal); + expect(signal.aborted).toBe(false); + + // Wait for the timeout to complete + return new Promise((resolve) => { + setTimeout(() => { + expect(signal.aborted).toBe(true); + resolve(true); + }, 1000); + }); + }); + + it('should create HTTP error', () => { + const error = new Error('Test error'); + const httpError = createHttpError(error, 404); + + expect(httpError).toEqual({ + message: 'Test error', + status: 404, + code: HttpCode[404], + }); + }); + + it('should create HTTP error with unknown error', () => { + const httpError = createHttpError(null, 500); + + expect(httpError).toEqual({ + message: 'Unknown error', + status: 500, + code: HttpCode[500], + }); + }); + + it('should get fetch function', () => { + const fetchFn = getFetch(); + + expect(fetchFn).toBeDefined(); + expect(typeof fetchFn).toBe('function'); + }); +}); diff --git a/tests/types/types.test.ts b/tests/types/types.test.ts new file mode 100644 index 0000000..0f4e883 --- /dev/null +++ b/tests/types/types.test.ts @@ -0,0 +1,162 @@ +import { describe, it, expect } from 'vitest'; +import type { + HttpMethod, + HttpHeaders, + QueryParams, + HttpError, + HttpResponse, + RequestInterceptor, + ResponseInterceptor, + Interceptors, + RequestConfig, + HttpClientConfig, +} from '../../src/types'; + +describe('HTTP Client Types', () => { + it('should accept valid HTTP methods', () => { + const validMethods: HttpMethod[] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']; + expect(validMethods).toHaveLength(7); + expect(validMethods.includes('GET')).toBeTruthy(); + }); + + it('should allow standard HTTP headers', () => { + const headers: HttpHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer token123', + Accept: 'application/json', + 'X-API-Key': 'api-key-123', + }; + + expect(headers['Content-Type']).toBe('application/json'); + expect(headers.Authorization).toBe('Bearer token123'); + }); + + it('should allow custom headers', () => { + const headers: HttpHeaders = { + 'X-Custom-Header': 'custom-value', + 'X-Another-Header': 'another-value', + }; + + expect(headers['X-Custom-Header']).toBe('custom-value'); + expect(headers['X-Another-Header']).toBe('another-value'); + }); + + it('should allow valid query parameters', () => { + const params: QueryParams = { + page: 1, + limit: 10, + search: 'test', + isActive: true, + optional: undefined, + }; + + expect(params.page).toBe(1); + expect(params.limit).toBe(10); + expect(params.search).toBe('test'); + expect(params.isActive).toBe(true); + expect(params.optional).toBeUndefined(); + }); + + it('should have the correct structure', () => { + const error: HttpError = { + message: 'Not Found', + status: 404, + code: 'RESOURCE_NOT_FOUND', + }; + + expect(error.message).toBe('Not Found'); + expect(error.status).toBe(404); + expect(error.code).toBe('RESOURCE_NOT_FOUND'); + }); + + it('should handle successful responses', () => { + const response: HttpResponse<{ name: string }> = { + content: { name: 'John Doe' }, + error: null, + status: 200, + }; + + expect(response.content.name).toBe('John Doe'); + expect(response.error).toBeNull(); + expect(response.status).toBe(200); + }); + + it('should handle error responses', () => { + const response: HttpResponse = { + content: null, + error: { + message: 'Server Error', + status: 500, + code: 'INTERNAL_SERVER_ERROR', + }, + status: 500, + }; + + expect(response.content).toBeNull(); + expect(response.error?.message).toBe('Server Error'); + expect(response.status).toBe(500); + }); + + it('should have request and response interceptors', () => { + const requestInterceptor: RequestInterceptor = (config) => config; + const responseInterceptor: ResponseInterceptor = (response) => response; + + const interceptors: Interceptors = { + request: [requestInterceptor], + response: [responseInterceptor], + }; + + expect(interceptors.request).toHaveLength(1); + expect(interceptors.response).toHaveLength(1); + }); + + it('should have required and optional properties', () => { + const config: RequestConfig = { + url: '/users', + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + params: { page: 1 }, + body: { name: 'John' }, + timeout: 5000, + }; + + expect(config.url).toBe('/users'); + expect(config.method).toBe('GET'); + expect(config.headers?.['Content-Type']).toBe('application/json'); + expect(config.params?.page).toBe(1); + expect(config.body.name).toBe('John'); + expect(config.timeout).toBe(5000); + }); + + it('should configure client with all options', () => { + const config: HttpClientConfig = { + baseURL: 'https://api.example.com', + headers: { 'Content-Type': 'application/json' }, + params: { version: 1 }, + timeout: 10000, + interceptors: { + request: [(config) => config], + response: [(response) => response], + }, + }; + + expect(config.baseURL).toBe('https://api.example.com'); + expect(config.headers?.['Content-Type']).toBe('application/json'); + expect(config.params?.version).toBe(1); + expect(config.timeout).toBe(10000); + expect(config.interceptors?.request).toHaveLength(1); + expect(config.interceptors?.response).toHaveLength(1); + }); + + it('should allow minimal configuration', () => { + const config: HttpClientConfig = { + baseURL: 'https://api.example.com', + }; + + expect(config.baseURL).toBe('https://api.example.com'); + expect(config.headers).toBeUndefined(); + expect(config.params).toBeUndefined(); + expect(config.timeout).toBeUndefined(); + expect(config.interceptors).toBeUndefined(); + }); +}); From efbedb21ced3eeaf5530dae6c2647a1e41ac99dd Mon Sep 17 00:00:00 2001 From: Breimerct Date: Sun, 15 Jun 2025 05:50:48 -0500 Subject: [PATCH 3/3] feat: add test execution step in GitHub Actions workflow before building the project --- .github/workflows/publish.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0073f20..b4f8b78 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -95,7 +95,11 @@ jobs: }; fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2)); " + # execute tests if they exist + - name: Run tests + run: npm run test || echo "No tests found, skipping..." + # Build the project - name: Build project run: npm run build:tsup