diff --git a/README.md b/README.md index b66c55a..2671b1c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,23 @@ # openapi-gen Generate Effect http clients from openapi specification. + +## Usage + +```bash +npx @tim-smart/openapi-gen --spec --name > src/Client.ts +``` + +## Options + +| Option | Alias | Description | Default | +| ------------- | ----- | --------------------------------------------- | -------- | +| `--spec` | `-s` | The OpenAPI spec file path or URL (json/yaml) | Required | +| `--name` | `-n` | The name of the generated client | `Client` | +| `--type-only` | `-t` | Generate a type-only client without schemas | `false` | + +## Example + +```bash +npx @tim-smart/openapi-gen --spec ./openapi.yaml > src/Client.ts +``` diff --git a/package.json b/package.json index 64fc02f..2200675 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "version": "changeset version", "release": "pnpm run build && changeset publish", - "test": "pnpm coverage", + "test": "vitest run", "clean": "rm -rf dist/*", "build": "tsup && pnpm copy-package-json", "build:ts": "tsup", @@ -29,6 +29,7 @@ "@effect/cli": "^0.61.3", "@effect/platform": "^0.82.3", "@effect/platform-node": "^0.81.0", + "@effect/vitest": "^0.27.0", "@types/node": "^22.15.18", "@types/swagger2openapi": "^7.0.4", "effect": "^3.15.2", @@ -36,7 +37,8 @@ "swagger2openapi": "^7.0.8", "tsup": "^8.4.0", "tsx": "^4.19.4", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vitest": "^4.0.8" }, "dependencies": { "yaml": "^2.8.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40bf5c7..30b6bfc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,6 +27,9 @@ importers: '@effect/platform-node': specifier: ^0.81.0 version: 0.81.0(@effect/cluster@0.34.0(@effect/platform@0.82.3(effect@3.15.2))(@effect/rpc@0.59.4(@effect/platform@0.82.3(effect@3.15.2))(effect@3.15.2))(@effect/sql@0.35.3(@effect/experimental@0.46.3(@effect/platform@0.82.3(effect@3.15.2))(effect@3.15.2))(@effect/platform@0.82.3(effect@3.15.2))(effect@3.15.2))(effect@3.15.2))(@effect/platform@0.82.3(effect@3.15.2))(@effect/rpc@0.59.4(@effect/platform@0.82.3(effect@3.15.2))(effect@3.15.2))(@effect/sql@0.35.3(@effect/experimental@0.46.3(@effect/platform@0.82.3(effect@3.15.2))(effect@3.15.2))(@effect/platform@0.82.3(effect@3.15.2))(effect@3.15.2))(effect@3.15.2) + '@effect/vitest': + specifier: ^0.27.0 + version: 0.27.0(effect@3.15.2)(vitest@4.0.10(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0)) '@types/node': specifier: ^22.15.18 version: 22.15.18 @@ -44,13 +47,16 @@ importers: version: 7.0.8 tsup: specifier: ^8.4.0 - version: 8.4.0(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0) + version: 8.4.0(postcss@8.5.6)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.4 version: 4.19.4 typescript: specifier: ^5.8.3 version: 5.8.3 + vitest: + specifier: ^4.0.8 + version: 4.0.10(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0) publishDirectory: dist packages: @@ -202,6 +208,12 @@ packages: peerDependencies: effect: ^3.15.2 + '@effect/vitest@0.27.0': + resolution: {integrity: sha512-8bM7n9xlMUYw9GqPIVgXFwFm2jf27m/R7psI64PGpwU5+26iwyxp9eAXEsfT5S6lqztYfpQQ1Ubp5o6HfNYzJQ==} + peerDependencies: + effect: ^3.19.0 + vitest: ^3.2.0 + '@esbuild/aix-ppc64@0.25.1': resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} engines: {node: '>=18'} @@ -524,6 +536,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -670,51 +685,106 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.35.0': resolution: {integrity: sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.35.0': resolution: {integrity: sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.35.0': resolution: {integrity: sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.35.0': resolution: {integrity: sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.35.0': resolution: {integrity: sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.35.0': resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.35.0': resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.35.0': resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==} cpu: [loong64] @@ -725,47 +795,111 @@ packages: cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.35.0': resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.35.0': resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.35.0': resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.35.0': resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.35.0': resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.35.0': resolution: {integrity: sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.35.0': resolution: {integrity: sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -775,6 +909,35 @@ packages: '@types/swagger2openapi@7.0.4': resolution: {integrity: sha512-ffMqzciTDihOKH4Q//9Ond1yb5JP1P5FC/aFPsLK4blea1Fwk2aYctiNCkAh5etDYFswFXS+5LV/vuGkf+PU6A==} + '@vitest/expect@4.0.10': + resolution: {integrity: sha512-3QkTX/lK39FBNwARCQRSQr0TP9+ywSdxSX+LgbJ2M1WmveXP72anTbnp2yl5fH+dU6SUmBzNMrDHs80G8G2DZg==} + + '@vitest/mocker@4.0.10': + resolution: {integrity: sha512-e2OfdexYkjkg8Hh3L9NVEfbwGXq5IZbDovkf30qW2tOh7Rh9sVtmSr2ztEXOFbymNxS4qjzLXUQIvATvN4B+lg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.10': + resolution: {integrity: sha512-99EQbpa/zuDnvVjthwz5bH9o8iPefoQZ63WV8+bsRJZNw3qQSvSltfut8yu1Jc9mqOYi7pEbsKxYTi/rjaq6PA==} + + '@vitest/runner@4.0.10': + resolution: {integrity: sha512-EXU2iSkKvNwtlL8L8doCpkyclw0mc/t4t9SeOnfOFPyqLmQwuceMPA4zJBa6jw0MKsZYbw7kAn+gl7HxrlB8UQ==} + + '@vitest/snapshot@4.0.10': + resolution: {integrity: sha512-2N4X2ZZl7kZw0qeGdQ41H0KND96L3qX1RgwuCfy6oUsF2ISGD/HpSbmms+CkIOsQmg2kulwfhJ4CI0asnZlvkg==} + + '@vitest/spy@4.0.10': + resolution: {integrity: sha512-AsY6sVS8OLb96GV5RoG8B6I35GAbNrC49AO+jNRF9YVGb/g9t+hzNm1H6kD0NDp8tt7VJLs6hb7YMkDXqu03iw==} + + '@vitest/utils@4.0.10': + resolution: {integrity: sha512-kOuqWnEwZNtQxMKg3WmPK1vmhZu9WcoX69iwWjVz+jvKTsF1emzsv3eoPcDr6ykA3qP2bsCQE7CwqfNtAVzsmg==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -805,6 +968,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -832,6 +999,10 @@ packages: call-me-maybe@1.0.2: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + engines: {node: '>=18'} + chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} @@ -878,6 +1049,15 @@ packages: supports-color: optional: true + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -915,6 +1095,9 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} @@ -937,6 +1120,13 @@ packages: engines: {node: '>=4'} hasBin: true + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -966,6 +1156,15 @@ packages: picomatch: optional: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1100,6 +1299,9 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1141,6 +1343,11 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} @@ -1236,6 +1443,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1247,6 +1457,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -1273,6 +1487,10 @@ packages: yaml: optional: true + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -1327,6 +1545,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -1364,6 +1587,9 @@ packages: should@13.2.3: resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -1372,6 +1598,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -1382,6 +1612,12 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1422,6 +1658,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -1429,6 +1668,14 @@ packages: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -1497,6 +1744,80 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true + vite@7.2.2: + resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + 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 + + vitest@4.0.10: + resolution: {integrity: sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.10 + '@vitest/browser-preview': 4.0.10 + '@vitest/browser-webdriverio': 4.0.10 + '@vitest/ui': 4.0.10 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -1514,6 +1835,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1803,6 +2129,11 @@ snapshots: dependencies: effect: 3.15.2 + '@effect/vitest@0.27.0(effect@3.15.2)(vitest@4.0.10(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0))': + dependencies: + effect: 3.15.2 + vitest: 4.0.10(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0) + '@esbuild/aix-ppc64@0.25.1': optional: true @@ -1976,6 +2307,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -2095,64 +2428,139 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.35.0': optional: true + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + '@rollup/rollup-android-arm64@4.35.0': optional: true + '@rollup/rollup-android-arm64@4.53.3': + optional: true + '@rollup/rollup-darwin-arm64@4.35.0': optional: true + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + '@rollup/rollup-darwin-x64@4.35.0': optional: true + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + '@rollup/rollup-freebsd-arm64@4.35.0': optional: true + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + '@rollup/rollup-freebsd-x64@4.35.0': optional: true + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.35.0': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.35.0': optional: true + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-arm64-musl@4.35.0': optional: true + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.35.0': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.35.0': optional: true + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-x64-gnu@4.35.0': optional: true + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-x64-musl@4.35.0': optional: true + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.35.0': optional: true + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.35.0': optional: true + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + '@rollup/rollup-win32-x64-msvc@4.35.0': optional: true + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + '@standard-schema/spec@1.0.0': {} + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.6': {} + '@types/estree@1.0.8': {} + '@types/node@12.20.55': {} '@types/node@22.15.18': @@ -2164,6 +2572,45 @@ snapshots: '@types/node': 22.15.18 openapi-types: 12.1.3 + '@vitest/expect@4.0.10': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.10 + '@vitest/utils': 4.0.10 + chai: 6.2.1 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.10(vite@7.2.2(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 4.0.10 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.2(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0) + + '@vitest/pretty-format@4.0.10': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.10': + dependencies: + '@vitest/utils': 4.0.10 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.10': + dependencies: + '@vitest/pretty-format': 4.0.10 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.10': {} + + '@vitest/utils@4.0.10': + dependencies: + '@vitest/pretty-format': 4.0.10 + tinyrainbow: 3.0.3 + ansi-colors@4.1.3: {} ansi-regex@5.0.1: {} @@ -2184,6 +2631,8 @@ snapshots: array-union@2.1.0: {} + assertion-error@2.0.1: {} + balanced-match@1.0.2: {} better-path-resolve@1.0.0: @@ -2207,6 +2656,8 @@ snapshots: call-me-maybe@1.0.2: {} + chai@6.2.1: {} + chardet@0.7.0: {} chokidar@4.0.3: @@ -2243,6 +2694,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.3: + dependencies: + ms: 2.1.3 + detect-indent@6.1.0: {} detect-libc@1.0.3: {} @@ -2272,6 +2727,8 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + es-module-lexer@1.7.0: {} + es6-promise@3.3.1: {} esbuild@0.25.1: @@ -2334,6 +2791,12 @@ snapshots: esprima@4.0.1: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + expect-type@1.2.2: {} + extendable-error@0.1.7: {} external-editor@3.1.0: @@ -2364,6 +2827,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -2488,6 +2955,10 @@ snapshots: lru-cache@10.4.3: {} + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + merge2@1.4.1: {} micromatch@4.0.8: @@ -2531,6 +3002,8 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nanoid@3.3.11: {} + node-addon-api@7.1.1: {} node-fetch-h2@2.3.0: @@ -2622,23 +3095,34 @@ snapshots: path-type@4.0.0: {} + pathe@2.0.3: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.2: {} + picomatch@4.0.3: {} + pify@4.0.1: {} pirates@4.0.6: {} - postcss-load-config@6.0.1(tsx@4.19.4)(yaml@2.8.0): + postcss-load-config@6.0.1(postcss@8.5.6)(tsx@4.19.4)(yaml@2.8.0): dependencies: lilconfig: 3.1.3 optionalDependencies: + postcss: 8.5.6 tsx: 4.19.4 yaml: 2.8.0 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prettier@2.8.8: {} prettier@3.5.3: {} @@ -2695,6 +3179,34 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.35.0 fsevents: 2.3.3 + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -2735,10 +3247,14 @@ snapshots: should-type-adaptors: 1.1.0 should-util: 1.0.1 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} slash@3.0.0: {} + source-map-js@1.2.1: {} + source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 @@ -2750,6 +3266,10 @@ snapshots: sprintf-js@1.0.3: {} + stackback@0.0.2: {} + + std-env@3.10.0: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -2808,6 +3328,8 @@ snapshots: dependencies: any-promise: 1.3.0 + tinybench@2.9.0: {} + tinyexec@0.3.2: {} tinyglobby@0.2.12: @@ -2815,6 +3337,13 @@ snapshots: fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyrainbow@3.0.3: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -2835,7 +3364,7 @@ snapshots: ts-interface-checker@0.1.13: {} - tsup@8.4.0(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0): + tsup@8.4.0(postcss@8.5.6)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0): dependencies: bundle-require: 5.1.0(esbuild@0.25.1) cac: 6.7.14 @@ -2845,7 +3374,7 @@ snapshots: esbuild: 0.25.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(tsx@4.19.4)(yaml@2.8.0) + postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@4.19.4)(yaml@2.8.0) resolve-from: 5.0.0 rollup: 4.35.0 source-map: 0.8.0-beta.0 @@ -2854,6 +3383,7 @@ snapshots: tinyglobby: 0.2.12 tree-kill: 1.2.2 optionalDependencies: + postcss: 8.5.6 typescript: 5.8.3 transitivePeerDependencies: - jiti @@ -2878,6 +3408,58 @@ snapshots: uuid@11.1.0: {} + vite@7.2.2(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0): + dependencies: + esbuild: 0.25.4 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.15.18 + fsevents: 2.3.3 + tsx: 4.19.4 + yaml: 2.8.0 + + vitest@4.0.10(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0): + dependencies: + '@vitest/expect': 4.0.10 + '@vitest/mocker': 4.0.10(vite@7.2.2(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/pretty-format': 4.0.10 + '@vitest/runner': 4.0.10 + '@vitest/snapshot': 4.0.10 + '@vitest/spy': 4.0.10 + '@vitest/utils': 4.0.10 + debug: 4.4.3 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.2(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.15.18 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + webidl-conversions@3.0.1: {} webidl-conversions@4.0.2: {} @@ -2897,6 +3479,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 diff --git a/scripts/copy-package-json.ts b/scripts/copy-package-json.ts index e86ed4f..25005a8 100644 --- a/scripts/copy-package-json.ts +++ b/scripts/copy-package-json.ts @@ -11,7 +11,7 @@ const read = pipe( name: json.name, version: json.version, description: json.description, - bin: "main.js", + bin: "bin.js", repository: json.repository, author: json.author, license: json.license, diff --git a/src/JsonSchemaGen.ts b/src/JsonSchemaGen.ts index deb197e..56df2c6 100644 --- a/src/JsonSchemaGen.ts +++ b/src/JsonSchemaGen.ts @@ -160,9 +160,17 @@ const make = Effect.gen(function* () { isClass, isEnum, }) - return toSource(importName, Object.keys(schema).length ? schema : { - properties: {}, - } as JsonSchema.JsonSchema, name, topLevel).pipe( + return toSource( + importName, + Object.keys(schema).length + ? schema + : ({ + properties: {}, + } as JsonSchema.JsonSchema), + name, + topLevel, + true, + ).pipe( Option.map((source) => transformer.onTopLevel({ importName, @@ -208,6 +216,7 @@ const make = Effect.gen(function* () { schema: JsonSchema.JsonSchema, currentIdentifier: string, topLevel = false, + isRoot = false, ): Option.Option => { schema = cleanupSchema(schema) if ("properties" in schema) { @@ -258,11 +267,15 @@ const make = Effect.gen(function* () { }), ) } else if ("enum" in schema) { - if (!topLevel && enums.has(currentIdentifier)) { + if (!isRoot && !topLevel && enums.has(currentIdentifier)) { return Option.some( transformer.onRef({ importName, name: currentIdentifier }), ) - } else if (!topLevel && enums.has(currentIdentifier + "Enum")) { + } else if ( + !isRoot && + !topLevel && + enums.has(currentIdentifier + "Enum") + ) { return Option.some( transformer.onRef({ importName, name: currentIdentifier + "Enum" }), ) @@ -286,9 +299,10 @@ const make = Effect.gen(function* () { { type: "object", ...schema } as any, currentIdentifier, topLevel, + isRoot, ) } else if ("allOf" in schema) { - if (store.has(currentIdentifier)) { + if (!isRoot && !topLevel && store.has(currentIdentifier)) { return Option.some( transformer.onRef({ importName, name: currentIdentifier }), ) @@ -303,6 +317,7 @@ const make = Effect.gen(function* () { flattened, currentIdentifier + "Enum", topLevel, + isRoot, ) } else if ("anyOf" in schema || "oneOf" in schema) { let itemSchemas = diff --git a/src/OpenApi.ts b/src/OpenApi.ts index b84f981..86d04f4 100644 --- a/src/OpenApi.ts +++ b/src/OpenApi.ts @@ -295,15 +295,15 @@ ${clientErrorSource(name)}` let options: Array = [] if (operation.params && !operation.payload) { args.push( - `options${operation.paramsOptional ? "?" : ""}: typeof ${operation.params}.Encoded${operation.paramsOptional ? " | undefined" : ""}`, + `options${operation.paramsOptional ? "?" : ""}: S.Schema.Encoded${operation.paramsOptional ? " | undefined" : ""}`, ) } else if (operation.params) { options.push( - `readonly params${operation.paramsOptional ? "?" : ""}: typeof ${operation.params}.Encoded${operation.paramsOptional ? " | undefined" : ""}`, + `readonly params${operation.paramsOptional ? "?" : ""}: S.Schema.Encoded${operation.paramsOptional ? " | undefined" : ""}`, ) } if (operation.payload) { - const type = `typeof ${operation.payload}.Encoded` + const type = `S.Schema.Encoded` if (!operation.params) { args.push(`options: ${type}`) } else { @@ -316,14 +316,15 @@ ${clientErrorSource(name)}` let success = "void" if (operation.successSchemas.size > 0) { success = Array.from(operation.successSchemas.values()) - .map((schema) => `typeof ${schema}.Type`) + .map((schema) => `S.Schema.Type`) .join(" | ") } const errors = ["HttpClientError.HttpClientError", "ParseError"] if (operation.errorSchemas.size > 0) { errors.push( ...Array.from(operation.errorSchemas.values()).map( - (schema) => `${name}Error<"${schema}", typeof ${schema}.Type>`, + (schema) => + `${name}Error<"${schema}", S.Schema.Type>`, ), ) } @@ -349,7 +350,7 @@ ${clientErrorSource(name)}` (response: HttpClientResponse.HttpClientResponse) => Effect.flatMap( HttpClientResponse.schemaBodyJson(schema)(response), - (cause) => Effect.fail(${name}Error(tag, cause, response)), + (cause: A) => Effect.fail(${name}Error(tag, cause, response)), ) return { httpClient, @@ -616,7 +617,7 @@ const processPath = (path: string) => { const commonSource = `const unexpectedStatus = (response: HttpClientResponse.HttpClientResponse) => Effect.flatMap( Effect.orElseSucceed(response.json, () => "Unexpected status code"), - (description) => + (description: unknown) => Effect.fail( new HttpClientError.ResponseError({ request: response.request, @@ -633,7 +634,7 @@ const commonSource = `const unexpectedStatus = (response: HttpClientResponse.Htt ) => Effect.Effect = options.transformClient ? (f) => (request) => Effect.flatMap( - Effect.flatMap(options.transformClient!(httpClient), (client) => + Effect.flatMap(options.transformClient!(httpClient), (client: HttpClient.HttpClient) => client.execute(request), ), f, @@ -654,7 +655,16 @@ class ${name}ErrorImpl extends Data.Error<{ cause: any request: HttpClientRequest.HttpClientRequest response: HttpClientResponse.HttpClientResponse -}> {} +}> { + constructor(args: { + _tag: string + cause: any + request: HttpClientRequest.HttpClientRequest + response: HttpClientResponse.HttpClientResponse + }) { + super(args as any) + } +} export const ${name}Error = ( tag: Tag, diff --git a/src/bin.ts b/src/bin.ts new file mode 100644 index 0000000..5420a85 --- /dev/null +++ b/src/bin.ts @@ -0,0 +1,5 @@ +import * as Effect from "effect/Effect" +import * as NodeRuntime from "@effect/platform-node/NodeRuntime" +import { run, Env } from "./main.js" + +run(process.argv).pipe(Effect.provide(Env), NodeRuntime.runMain) diff --git a/src/main.ts b/src/main.ts index 0f7f918..e71966d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,14 +3,21 @@ import * as Console from "effect/Console" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as NodeContext from "@effect/platform-node/NodeContext" -import * as NodeRuntime from "@effect/platform-node/NodeRuntime" import * as Command from "@effect/cli/Command" import * as CliConfig from "@effect/cli/CliConfig" +import * as FileSystem from "@effect/platform/FileSystem" +import * as FetchHttpClient from "@effect/platform/FetchHttpClient" +import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem" import { OpenApi } from "./OpenApi.js" +import * as Yaml from "yaml" +import type { OpenAPISpec } from "@effect/platform/OpenApi" +import * as HttpClient from "@effect/platform/HttpClient" -const spec = Options.fileParse("spec").pipe( +const spec = Options.text("spec").pipe( Options.withAlias("s"), - Options.withDescription("The OpenAPI spec file to generate the client from"), + Options.withDescription( + "The OpenAPI spec file path or URL to generate the client from", + ), ) const name = Options.text("name").pipe( @@ -24,25 +31,67 @@ const typeOnly = Options.boolean("type-only").pipe( Options.withDescription("Generate a type-only client without schemas"), ) +const isUrl = (str: string): boolean => + str.startsWith("http://") || str.startsWith("https://") + const root = Command.make("openapigen", { spec, typeOnly, name }).pipe( - Command.withHandler(({ spec, typeOnly, name }) => - OpenApi.generate(spec as any, { name, typeOnly }).pipe( - Effect.flatMap(Console.log), - ), - ), + Command.withHandler((args) => main(args).pipe(Effect.flatMap(Console.log))), ) -const run = Command.run(root, { +const fileParsers: Record unknown> = { + json: (content: string) => JSON.parse(content), + yaml: (content: string) => Yaml.parse(content), + yml: (content: string) => Yaml.parse(content), +} + +export const main = ({ + spec: specInput, + typeOnly, + name, +}: { + spec: string + typeOnly: boolean + name: string +}) => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const http = yield* HttpClient.HttpClient + const extension = specInput.split(".").pop()?.toLowerCase() ?? "" + + const parser = fileParsers[extension] + + if (!parser) + return yield* Effect.fail(`Unsupported file format: ${extension}`) + + const specRawContent = isUrl(specInput) + ? yield* http + .get(specInput) + .pipe(Effect.andThen((response) => response.text)) + : yield* fs.readFileString(specInput) + + const parsedSpec = yield* Effect.try({ + try: () => parser(specRawContent), + catch: (error: unknown) => + new Error(`Failed to parse spec file: ${error}`), + }) + + return yield* OpenApi.generate(parsedSpec as unknown as OpenAPISpec, { + name, + typeOnly, + }) + }) + +export const run = Command.run(root, { name: "openapigen", version: "0.0.0", }) -const Env = Layer.mergeAll( +export const Env = Layer.mergeAll( NodeContext.layer, + NodeFileSystem.layer, + FetchHttpClient.layer, OpenApi.Live, CliConfig.layer({ showBuiltIns: false, }), ) - -run(process.argv).pipe(Effect.provide(Env), NodeRuntime.runMain) diff --git a/test/smoke.test.ts b/test/smoke.test.ts new file mode 100644 index 0000000..27065b3 --- /dev/null +++ b/test/smoke.test.ts @@ -0,0 +1,77 @@ +import { it, expect } from "@effect/vitest" +import * as Effect from "effect/Effect" +import { main, Env } from "../src/main.js" +import ts from "typescript" + +function typecheck(code: string) { + const fileName = "virtual-file.ts" + + const compilerOptions: ts.CompilerOptions = { + strict: true, + noEmit: true, + target: ts.ScriptTarget.ESNext, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Bundler, + skipLibCheck: true, + // add libs as needed, e.g.: + // lib: ["ES2021", "DOM"], + } + + const host = ts.createCompilerHost(compilerOptions) + + // Override how the source file is provided so it uses our string + host.getSourceFile = (name, languageVersion) => { + if (name === fileName) { + return ts.createSourceFile( + name, + code, + languageVersion, + true, + ts.ScriptKind.TS, + ) + } + // For lib files etc., fall back to default behavior + return ts.sys.readFile(name) + ? ts.createSourceFile(name, ts.sys.readFile(name)!, languageVersion, true) + : undefined + } + + // Avoid hitting the real FS for the virtual file + host.fileExists = (filePath) => + filePath === fileName || ts.sys.fileExists(filePath) + host.readFile = (filePath) => + filePath === fileName ? code : ts.sys.readFile(filePath) + + const program = ts.createProgram([fileName], compilerOptions, host) + + const diagnostics = [ + ...program.getSyntacticDiagnostics(), + ...program.getSemanticDiagnostics(), + ] + + return ts.formatDiagnosticsWithColorAndContext(diagnostics, { + ...host, + getCurrentDirectory: () => process.cwd(), + getCanonicalFileName: (f) => f, + getNewLine: () => "\n", + }) +} + +it.effect( + "revenuecat spec", + () => + Effect.gen(function* () { + const tsOutputFile = yield* main({ + spec: "https://www.revenuecat.com/docs/redocusaurus/plugin-redoc-0.yaml", + typeOnly: false, + name: "RevenueCat", + }) + + yield* Effect.log(tsOutputFile) + + const typecheckResult = typecheck(tsOutputFile) + + expect(typecheckResult).toBe("") + }).pipe(Effect.provide(Env)), + 30_000, +) diff --git a/tsup.config.ts b/tsup.config.ts index 070ceb9..9360066 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from "tsup" export default defineConfig({ - entry: ["src/main.ts"], + entry: ["src/bin.ts"], clean: true, publicDir: true, banner: { diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..e516a83 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['./test/**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + exclude: [], + globals: true, + coverage: { + provider: 'v8', + }, + }, +});