From f9ac79803989ae2ef392ee08746980dea1a2dc91 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 00:24:08 +0900 Subject: [PATCH 01/15] Update lib --- bun.lock | 468 +++++++++++++-------------- examples/next-webpack/package.json | 6 +- examples/next/package.json | 6 +- examples/rsbuild/package.json | 4 +- examples/vite/package.json | 8 +- package.json | 6 +- packages/core/package.json | 2 +- packages/fetch/package.json | 2 +- packages/generator/package.json | 2 +- packages/hookform/package.json | 16 +- packages/next-plugin/package.json | 2 +- packages/react-query/package.json | 4 +- packages/rsbuild-plugin/package.json | 2 +- packages/ui/package.json | 8 +- packages/utils/package.json | 2 +- packages/vite-plugin/package.json | 2 +- packages/webpack-plugin/package.json | 2 +- packages/zod/package.json | 6 +- 18 files changed, 260 insertions(+), 288 deletions(-) diff --git a/bun.lock b/bun.lock index 92e6514..9cba258 100644 --- a/bun.lock +++ b/bun.lock @@ -6,13 +6,13 @@ "name": "devup-api", "devDependencies": { "@biomejs/biome": "^2.3", - "@testing-library/react": "^16.3.1", + "@testing-library/react": "^16.3.2", "@testing-library/react-hooks": "^8.0.1", "@types/bun": "latest", "bun-test-env-dom": "^1.0.3", "husky": "^9", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "react": "^19.2.4", + "react-dom": "^19.2.4", }, }, "examples/next": { @@ -27,9 +27,9 @@ "@devup-api/zod": "workspace:*", "@devup-ui/react": "^1", "@tanstack/react-query": "^5", - "next": "^16.1.1", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "next": "^16.1.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", }, "devDependencies": { "@devup-ui/next-plugin": "^1", @@ -46,9 +46,9 @@ "@devup-api/fetch": "workspace:*", "@devup-api/next-plugin": "workspace:*", "@devup-ui/react": "^1", - "next": "^16.1.1", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "next": "^16.1.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", }, "devDependencies": { "@devup-ui/next-plugin": "^1", @@ -63,8 +63,8 @@ "version": "0.1.0", "dependencies": { "@devup-api/fetch": "workspace:*", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "react": "^19.2.4", + "react-dom": "^19.2.4", }, "devDependencies": { "@devup-api/rsbuild-plugin": "workspace:*", @@ -80,68 +80,68 @@ "version": "0.1.0", "dependencies": { "@devup-api/fetch": "workspace:*", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "react": "^19.2.4", + "react-dom": "^19.2.4", }, "devDependencies": { "@devup-api/vite-plugin": "workspace:*", "@types/react": "^19", "@types/react-dom": "^19", - "@vitejs/plugin-react": "^5.1.2", + "@vitejs/plugin-react": "^5.1.4", "typescript": "^5", - "vite": "^7.3.0", + "vite": "^7.3.1", }, }, "packages/core": { "name": "@devup-api/core", - "version": "0.1.10", + "version": "0.1.12", "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9", }, }, "packages/fetch": { "name": "@devup-api/fetch", - "version": "0.1.13", + "version": "0.1.15", "dependencies": { "@devup-api/core": "workspace:^", }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9", }, }, "packages/generator": { "name": "@devup-api/generator", - "version": "0.1.12", + "version": "0.1.16", "dependencies": { "@devup-api/core": "workspace:^", "@devup-api/utils": "workspace:^", }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "openapi-types": "^12.1", "typescript": "^5.9", }, }, "packages/hookform": { "name": "@devup-api/hookform", - "version": "0.0.1", + "version": "0.1.0", "dependencies": { "@devup-api/fetch": "workspace:^", "@devup-api/zod": "workspace:^", "@hookform/resolvers": ">=5.2.2", - "react-hook-form": ">=7.70.0", + "react-hook-form": ">=7.71.1", }, "devDependencies": { - "@tanstack/react-query": "^5.90.16", - "@testing-library/react": "^16.3.1", - "@types/node": "^25.0", + "@tanstack/react-query": "^5.90", + "@testing-library/react": "^16.3", + "@types/node": "^25.2", "@types/react": "^19.2", - "bun-test-env-dom": "^1.0.3", - "happy-dom": "^20.0.11", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "bun-test-env-dom": "^1.0", + "happy-dom": "^20.6", + "react": "^19.2", + "react-dom": "^19.2", "rollup-plugin-preserve-directives": "^0.4", "typescript": "^5.9", "vite": "^7.3", @@ -157,7 +157,7 @@ }, "packages/next-plugin": { "name": "@devup-api/next-plugin", - "version": "0.1.8", + "version": "0.1.9", "dependencies": { "@devup-api/core": "workspace:^", "@devup-api/generator": "workspace:^", @@ -165,7 +165,7 @@ "@devup-api/webpack-plugin": "workspace:^", }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "@types/webpack": "^5.28", "typescript": "^5.9", }, @@ -176,16 +176,16 @@ }, "packages/react-query": { "name": "@devup-api/react-query", - "version": "0.1.5", + "version": "0.1.8", "dependencies": { "@devup-api/fetch": "workspace:^", "@tanstack/react-query": ">=5.90", }, "devDependencies": { "@testing-library/react-hooks": "^8.0.1", - "@types/node": "^25.0", + "@types/node": "^25.2", "@types/react": "^19.2", - "happy-dom": "^20.0.11", + "happy-dom": "^20.6.1", "typescript": "^5.9", }, "peerDependencies": { @@ -195,14 +195,14 @@ }, "packages/rsbuild-plugin": { "name": "@devup-api/rsbuild-plugin", - "version": "0.1.8", + "version": "0.1.9", "dependencies": { "@devup-api/core": "workspace:^", "@devup-api/generator": "workspace:^", "@devup-api/utils": "workspace:^", }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9", }, "peerDependencies": { @@ -212,42 +212,42 @@ }, "packages/ui": { "name": "@devup-api/ui", - "version": "0.0.1", + "version": "0.1.0", "dependencies": { "@devup-api/hookform": "workspace:^", }, "devDependencies": { - "@types/node": "^25.0", - "@types/react": "^19.2.7", - "react": "^19.2.3", + "@types/node": "^25.2", + "@types/react": "^19.2", + "react": "^19.2", "rollup-plugin-preserve-directives": "^0.4", "typescript": "^5.9", "vite": "^7.3", "vite-plugin-dts": "^4.5", }, "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || >=19.0.0", }, }, "packages/utils": { "name": "@devup-api/utils", - "version": "0.1.6", + "version": "0.1.7", "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "openapi-types": "^12.1", "typescript": "^5.9", }, }, "packages/vite-plugin": { "name": "@devup-api/vite-plugin", - "version": "0.1.8", + "version": "0.1.9", "dependencies": { "@devup-api/core": "workspace:^", "@devup-api/generator": "workspace:^", "@devup-api/utils": "workspace:^", }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "openapi-types": "^12.1", "typescript": "^5.9", }, @@ -258,14 +258,14 @@ }, "packages/webpack-plugin": { "name": "@devup-api/webpack-plugin", - "version": "0.1.8", + "version": "0.1.9", "dependencies": { "@devup-api/core": "workspace:^", "@devup-api/generator": "workspace:^", "@devup-api/utils": "workspace:^", }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "@types/webpack": "^5.28", "typescript": "^5.9", }, @@ -275,41 +275,41 @@ }, "packages/zod": { "name": "@devup-api/zod", - "version": "0.0.1", + "version": "0.1.0", "dependencies": { "@devup-api/fetch": "workspace:^", "zod": ">=4", }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9", - "zod": "^4.3.5", + "zod": "^4.3", }, "peerDependencies": { - "zod": "^3.0.0", + "zod": "^4.0", }, }, }, "packages": { "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], - "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="], + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], - "@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="], + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], - "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="], + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], @@ -317,39 +317,39 @@ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], + "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], - "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], - "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], - "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], - "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], - "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], - "@biomejs/biome": ["@biomejs/biome@2.3.10", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.10", "@biomejs/cli-darwin-x64": "2.3.10", "@biomejs/cli-linux-arm64": "2.3.10", "@biomejs/cli-linux-arm64-musl": "2.3.10", "@biomejs/cli-linux-x64": "2.3.10", "@biomejs/cli-linux-x64-musl": "2.3.10", "@biomejs/cli-win32-arm64": "2.3.10", "@biomejs/cli-win32-x64": "2.3.10" }, "bin": { "biome": "bin/biome" } }, "sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ=="], + "@biomejs/biome": ["@biomejs/biome@2.3.15", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.15", "@biomejs/cli-darwin-x64": "2.3.15", "@biomejs/cli-linux-arm64": "2.3.15", "@biomejs/cli-linux-arm64-musl": "2.3.15", "@biomejs/cli-linux-x64": "2.3.15", "@biomejs/cli-linux-x64-musl": "2.3.15", "@biomejs/cli-win32-arm64": "2.3.15", "@biomejs/cli-win32-x64": "2.3.15" }, "bin": { "biome": "bin/biome" } }, "sha512-u+jlPBAU2B45LDkjjNNYpc1PvqrM/co4loNommS9/sl9oSxsAQKsNZejYuUztvToB5oXi1tN/e62iNd6ESiY3g=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-SDCdrJ4COim1r8SNHg19oqT50JfkI/xGZHSyC6mGzMfKrpNe/217Eq6y98XhNTc0vGWDjznSDNXdUc6Kg24jbw=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-RkyeSosBtn3C3Un8zQnl9upX0Qbq4E3QmBa0qjpOh1MebRbHhNlRC16jk8HdTe/9ym5zlfnpbb8cKXzW+vlTxw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-FN83KxrdVWANOn5tDmW6UBC0grojchbGmcEz6JkRs2YY6DY63sTZhwkQ56x6YtKhDVV1Unz7FJexy8o7KwuIhg=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-SSSIj2yMkFdSkXqASzIBdjySBXOe65RJlhKEDlri7MN19RC4cpez+C0kEwPrhXOTgJbwQR9QH1F4+VnHkC35pg=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.10", "", { "os": "linux", "cpu": "x64" }, "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.15", "", { "os": "linux", "cpu": "x64" }, "sha512-T8n9p8aiIKOrAD7SwC7opiBM1LYGrE5G3OQRXWgbeo/merBk8m+uxJ1nOXMPzfYyFLfPlKF92QS06KN1UW+Zbg=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.15", "", { "os": "linux", "cpu": "x64" }, "sha512-dbjPzTh+ijmmNwojFYbQNMFp332019ZDioBYAMMJj5Ux9d8MkM+u+J68SBJGVwVeSHMYj+T9504CoxEzQxrdNw=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-puMuenu/2brQdgqtQ7geNwQlNVxiABKEZJhMRX6AGWcmrMO8EObMXniFQywy2b81qmC+q+SDvlOpspNwz0WiOA=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.10", "", { "os": "win32", "cpu": "x64" }, "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.15", "", { "os": "win32", "cpu": "x64" }, "sha512-kDZr/hgg+igo5Emi0LcjlgfkoGZtgIpJKhnvKTRmMBv6FF/3SDyEV4khBwqNebZIyMZTzvpca9sQNSXJ39pI2A=="], "@devup-api/core": ["@devup-api/core@workspace:packages/core"], @@ -375,73 +375,75 @@ "@devup-api/zod": ["@devup-api/zod@workspace:packages/zod"], - "@devup-ui/next-plugin": ["@devup-ui/next-plugin@1.0.59", "", { "dependencies": { "@devup-ui/wasm": "1.0.51", "@devup-ui/webpack-plugin": "1.0.49", "glob": "^13.0", "next": "^16.0" } }, "sha512-u44tjZZROuWkny/b5ZRvmiNSRvpgvrs+91npGejTx3w7dp+9y17B3GajrNkEytglT5c/45ItAaOR3fwRYwWnbA=="], + "@devup-ui/next-plugin": ["@devup-ui/next-plugin@1.0.71", "", { "dependencies": { "@devup-ui/plugin-utils": "^1.0.3", "@devup-ui/wasm": "^1.0.65", "@devup-ui/webpack-plugin": "^1.0.57" }, "peerDependencies": { "next": "*" } }, "sha512-luBZGalnlOEzClKy0b08kk1W18CPTXeOermN3JLACxRglT7xOd6iLcz0ogu5VGDagvhiPADM22BxR3LcCjRxSA=="], + + "@devup-ui/plugin-utils": ["@devup-ui/plugin-utils@1.0.3", "", {}, "sha512-jhpsEdYgSBXentAgb7cE0IYxznDin2qNngsOQmfwuIHwG6Uaz4mfrKjj0+4p1vY1ged3XZBc7d0z8QmscC4k4w=="], - "@devup-ui/react": ["@devup-ui/react@1.0.29", "", { "dependencies": { "csstype-extra": "latest", "react": "^19.2" } }, "sha512-RGmnTOMsksV/IOiqU9yCQZW+hQ/WAgo4qVyh+gsbzZQc4tM3fDUKctOF72Wm2Be7n2sn+JVHhocEaAWNkPeIig=="], + "@devup-ui/react": ["@devup-ui/react@1.0.34", "", { "dependencies": { "csstype-extra": "latest", "react": "^19.2" } }, "sha512-49ut7ujlLCyjWaVxeGMZrlNhCkFpeTXcRjQOZBWLdgcI7j989UV/xojXFBxJfXzR9GqlsYkhzf/rj1ot2YFb1g=="], - "@devup-ui/wasm": ["@devup-ui/wasm@1.0.51", "", {}, "sha512-BYhdsf42OxGxe3VWxNrSC2uRfpTY5vqGRwWLSYHyOWbozuop4W04L/hGX0M2hkdz3jI9Q3DISSInMH2cIiT6CA=="], + "@devup-ui/wasm": ["@devup-ui/wasm@1.0.65", "", {}, "sha512-fyMETe1P+hMHcoQBZAeLO4oSbAzmj6Zdk97UCZ5lpwyBn+N+lrvN7IIyWsWOArZ7+Bx+1vQdD7p76Q++Tw1dWQ=="], - "@devup-ui/webpack-plugin": ["@devup-ui/webpack-plugin@1.0.49", "", { "dependencies": { "@devup-ui/wasm": "1.0.47" } }, "sha512-jvdmBvOPuwvdpqr/OvAQF6vw1jLyWfJ9PCytybLflMfudRq4AKd5wmB9IBLmQhAvdCo8itrn7uiWl+f4nwwl3g=="], + "@devup-ui/webpack-plugin": ["@devup-ui/webpack-plugin@1.0.57", "", { "dependencies": { "@devup-ui/plugin-utils": "^1.0.3", "@devup-ui/wasm": "^1.0.65" } }, "sha512-uSrkikYvr6VgxSDnYQy9IKYBZvAC+VGmc/WVccdvjJedmFzDeKUWgx9nMxjPESaYYDjlkgpXiZg396Grp4JDIg=="], - "@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], + "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], - "@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], - "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="], + "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.6.1", "", { "dependencies": { "@types/node": ">=20.0.0", "happy-dom": "^20.6.1" } }, "sha512-4Aji+soqukwUxq2DgHmkjxdGnG7hEiJuprqDlW4Wu6AQ0t8U9ItlICcM5to89pulIsEGrF1CkCoNrufQTcqb8A=="], "@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="], @@ -497,7 +499,7 @@ "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], - "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.1", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], @@ -511,7 +513,7 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@microsoft/api-extractor": ["@microsoft/api-extractor@7.55.2", "", { "dependencies": { "@microsoft/api-extractor-model": "7.32.2", "@microsoft/tsdoc": "~0.16.0", "@microsoft/tsdoc-config": "~0.18.0", "@rushstack/node-core-library": "5.19.1", "@rushstack/rig-package": "0.6.0", "@rushstack/terminal": "0.19.5", "@rushstack/ts-command-line": "5.1.5", "diff": "~8.0.2", "lodash": "~4.17.15", "minimatch": "10.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-1jlWO4qmgqYoVUcyh+oXYRztZde/pAi7cSVzBz/rc+S7CoVzDasy8QE13dx6sLG4VRo8SfkkLbFORR6tBw4uGQ=="], + "@microsoft/api-extractor": ["@microsoft/api-extractor@7.56.3", "", { "dependencies": { "@microsoft/api-extractor-model": "7.32.2", "@microsoft/tsdoc": "~0.16.0", "@microsoft/tsdoc-config": "~0.18.0", "@rushstack/node-core-library": "5.19.1", "@rushstack/rig-package": "0.6.0", "@rushstack/terminal": "0.21.0", "@rushstack/ts-command-line": "5.2.0", "diff": "~8.0.2", "lodash": "~4.17.23", "minimatch": "10.1.2", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-fRqok4aRNq5GpgGBv2fKlSSKbirPKTJ75vQefthB5x9dwt4Zz+AezUzdc1p/AG4wUBIgmhjcEwn/Rj+N4Wh4Mw=="], "@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.32.2", "", { "dependencies": { "@microsoft/tsdoc": "~0.16.0", "@microsoft/tsdoc-config": "~0.18.0", "@rushstack/node-core-library": "5.19.1" } }, "sha512-Ussc25rAalc+4JJs9HNQE7TuO9y6jpYQX9nWD1DhqUzYPBr3Lr7O9intf+ZY8kD5HnIqeIRJX7ccCT0QyBy2Ww=="], @@ -519,117 +521,123 @@ "@microsoft/tsdoc-config": ["@microsoft/tsdoc-config@0.18.0", "", { "dependencies": { "@microsoft/tsdoc": "0.16.0", "ajv": "~8.12.0", "jju": "~1.4.0", "resolve": "~1.22.2" } }, "sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw=="], - "@module-federation/error-codes": ["@module-federation/error-codes@0.21.6", "", {}, "sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ=="], + "@module-federation/error-codes": ["@module-federation/error-codes@0.22.0", "", {}, "sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug=="], - "@module-federation/runtime": ["@module-federation/runtime@0.21.6", "", { "dependencies": { "@module-federation/error-codes": "0.21.6", "@module-federation/runtime-core": "0.21.6", "@module-federation/sdk": "0.21.6" } }, "sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ=="], + "@module-federation/runtime": ["@module-federation/runtime@0.22.0", "", { "dependencies": { "@module-federation/error-codes": "0.22.0", "@module-federation/runtime-core": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA=="], - "@module-federation/runtime-core": ["@module-federation/runtime-core@0.21.6", "", { "dependencies": { "@module-federation/error-codes": "0.21.6", "@module-federation/sdk": "0.21.6" } }, "sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw=="], + "@module-federation/runtime-core": ["@module-federation/runtime-core@0.22.0", "", { "dependencies": { "@module-federation/error-codes": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA=="], - "@module-federation/runtime-tools": ["@module-federation/runtime-tools@0.21.6", "", { "dependencies": { "@module-federation/runtime": "0.21.6", "@module-federation/webpack-bundler-runtime": "0.21.6" } }, "sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q=="], + "@module-federation/runtime-tools": ["@module-federation/runtime-tools@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/webpack-bundler-runtime": "0.22.0" } }, "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA=="], - "@module-federation/sdk": ["@module-federation/sdk@0.21.6", "", {}, "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw=="], + "@module-federation/sdk": ["@module-federation/sdk@0.22.0", "", {}, "sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g=="], - "@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.21.6", "", { "dependencies": { "@module-federation/runtime": "0.21.6", "@module-federation/sdk": "0.21.6" } }, "sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ=="], + "@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA=="], "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], - "@next/env": ["@next/env@16.1.1", "", {}, "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA=="], + "@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.53", "", {}, "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.5", "", { "os": "android", "cpu": "arm" }, "sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.5", "", { "os": "android", "cpu": "arm64" }, "sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.5", "", { "os": "linux", "cpu": "arm" }, "sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.5", "", { "os": "linux", "cpu": "arm" }, "sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.5", "", { "os": "linux", "cpu": "none" }, "sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.5", "", { "os": "linux", "cpu": "none" }, "sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.5", "", { "os": "linux", "cpu": "none" }, "sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.5", "", { "os": "linux", "cpu": "x64" }, "sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.5", "", { "os": "none", "cpu": "arm64" }, "sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.5", "", { "os": "win32", "cpu": "x64" }, "sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.5", "", { "os": "win32", "cpu": "x64" }, "sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], - "@rsbuild/core": ["@rsbuild/core@1.6.15", "", { "dependencies": { "@rspack/core": "1.6.8", "@rspack/lite-tapable": "~1.1.0", "@swc/helpers": "^0.5.17", "core-js": "~3.47.0", "jiti": "^2.6.1" }, "bin": { "rsbuild": "bin/rsbuild.js" } }, "sha512-LvoOF53PL6zXgdzEhgnnP51S4FseDFH1bHrobK4EK6zZX/tN8qgf5tdlmN7h4OkMv/Qs1oUfvj0QcLWSstnnvA=="], + "@rsbuild/core": ["@rsbuild/core@1.7.3", "", { "dependencies": { "@rspack/core": "~1.7.5", "@rspack/lite-tapable": "~1.1.0", "@swc/helpers": "^0.5.18", "core-js": "~3.47.0", "jiti": "^2.6.1" }, "bin": { "rsbuild": "bin/rsbuild.js" } }, "sha512-kI1oQvCXbQYxUvQPnDLdjSX4gFsbrFNpuUj6jXEJ7IcJ74Q+n4oeFj74/8tKerhxhe0L90m/ZQfzLeN5ORGA9w=="], - "@rsbuild/plugin-react": ["@rsbuild/plugin-react@1.4.2", "", { "dependencies": { "@rspack/plugin-react-refresh": "^1.5.2", "react-refresh": "^0.18.0" }, "peerDependencies": { "@rsbuild/core": "1.x" } }, "sha512-2rJb5mOuqVof2aDq4SbB1E65+0n1vjhAADipC88jvZRNuTOulg79fh7R4tsCiBMI4VWq46gSpwekiK8G5bq6jg=="], + "@rsbuild/plugin-react": ["@rsbuild/plugin-react@1.4.5", "", { "dependencies": { "@rspack/plugin-react-refresh": "^1.6.0", "react-refresh": "^0.18.0" }, "peerDependencies": { "@rsbuild/core": "^1.0.0 || ^2.0.0-0" } }, "sha512-eS2sXCedgGA/7bLu8yVtn48eE/GyPbXx4Q7OcutB01IQ1D2y8WSMBys4nwfrecy19utvw4NPn4gYDy52316+vg=="], - "@rspack/binding": ["@rspack/binding@1.6.8", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.6.8", "@rspack/binding-darwin-x64": "1.6.8", "@rspack/binding-linux-arm64-gnu": "1.6.8", "@rspack/binding-linux-arm64-musl": "1.6.8", "@rspack/binding-linux-x64-gnu": "1.6.8", "@rspack/binding-linux-x64-musl": "1.6.8", "@rspack/binding-wasm32-wasi": "1.6.8", "@rspack/binding-win32-arm64-msvc": "1.6.8", "@rspack/binding-win32-ia32-msvc": "1.6.8", "@rspack/binding-win32-x64-msvc": "1.6.8" } }, "sha512-lUeL4mbwGo+nqRKqFDCm9vH2jv9FNMVt1X8jqayWRcOCPlj/2UVMEFgqjR7Pp2vlvnTKq//31KbDBJmDZq31RQ=="], + "@rspack/binding": ["@rspack/binding@1.7.6", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.7.6", "@rspack/binding-darwin-x64": "1.7.6", "@rspack/binding-linux-arm64-gnu": "1.7.6", "@rspack/binding-linux-arm64-musl": "1.7.6", "@rspack/binding-linux-x64-gnu": "1.7.6", "@rspack/binding-linux-x64-musl": "1.7.6", "@rspack/binding-wasm32-wasi": "1.7.6", "@rspack/binding-win32-arm64-msvc": "1.7.6", "@rspack/binding-win32-ia32-msvc": "1.7.6", "@rspack/binding-win32-x64-msvc": "1.7.6" } }, "sha512-/NrEcfo8Gx22hLGysanrV6gHMuqZSxToSci/3M4kzEQtF5cPjfOv5pqeLK/+B6cr56ul/OmE96cCdWcXeVnFjQ=="], - "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.6.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-e8CTQtzaeGnf+BIzR7wRMUwKfIg0jd/sxMRc1Vd0bCMHBhSN9EsGoMuJJaKeRrSmy2nwMCNWHIG+TvT1CEKg+A=="], + "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.7.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NZ9AWtB1COLUX1tA9HQQvWpTy07NSFfKBU8A6ylWd5KH8AePZztpNgLLAVPTuNO4CZXYpwcoclf8jG/luJcQdQ=="], - "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.6.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-ku1XpTEPt6Za11zhpFWhfwrTQogcgi9RJrOUVC4FESiPO9aKyd4hJ+JiPgLY0MZOqsptK6vEAgOip+uDVXrCpg=="], + "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.7.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-J2g6xk8ZS7uc024dNTGTHxoFzFovAZIRixUG7PiciLKTMP78svbSSWrmW6N8oAsAkzYfJWwQpVgWfFNRHvYxSw=="], - "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.6.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-fvZX6xZPvBT8qipSpvkKMX5M7yd2BSpZNCZXcefw6gA3uC7LI3gu+er0LrDXY1PtPzVuHTyDx+abwWpagV3PiQ=="], + "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.7.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-eQfcsaxhFrv5FmtaA7+O1F9/2yFDNIoPZzV/ZvqvFz5bBXVc4FAm/1fVpBg8Po/kX1h0chBc7Xkpry3cabFW8w=="], - "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.6.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-++XMKcMNrt59HcFBLnRaJcn70k3X0GwkAegZBVpel8xYIAgvoXT5+L8P1ExId/yTFxqedaz8DbcxQnNmMozviw=="], + "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.7.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-DfQXKiyPIl7i1yECHy4eAkSmlUzzsSAbOjgMuKn7pudsWf483jg0UUYutNgXSlBjc/QSUp7906Cg8oty9OfwPA=="], - "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.6.8", "", { "os": "linux", "cpu": "x64" }, "sha512-tv3BWkTE1TndfX+DsE1rSTg8fBevCxujNZ3MlfZ22Wfy9x1FMXTJlWG8VIOXmaaJ1wUHzv8S7cE2YUUJ2LuiCg=="], + "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.7.6", "", { "os": "linux", "cpu": "x64" }, "sha512-NdA+2X3lk2GGrMMnTGyYTzM3pn+zNjaqXqlgKmFBXvjfZqzSsKq3pdD1KHZCd5QHN+Fwvoszj0JFsquEVhE1og=="], - "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.6.8", "", { "os": "linux", "cpu": "x64" }, "sha512-DCGgZ5/in1O3FjHWqXnDsncRy+48cMhfuUAAUyl0yDj1NpsZu9pP+xfGLvGcQTiYrVl7IH9Aojf1eShP/77WGA=="], + "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.7.6", "", { "os": "linux", "cpu": "x64" }, "sha512-rEy6MHKob02t/77YNgr6dREyJ0e0tv1X6Xsg8Z5E7rPXead06zefUbfazj4RELYySWnM38ovZyJAkPx/gOn3VA=="], - "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@1.6.8", "", { "dependencies": { "@napi-rs/wasm-runtime": "1.0.7" }, "cpu": "none" }, "sha512-VUwdhl/lI4m6o1OGCZ9JwtMjTV/yLY5VZTQdEPKb40JMTlmZ5MBlr5xk7ByaXXYHr6I+qnqEm73iMKQvg6iknw=="], + "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@1.7.6", "", { "dependencies": { "@napi-rs/wasm-runtime": "1.0.7" }, "cpu": "none" }, "sha512-YupOrz0daSG+YBbCIgpDgzfMM38YpChv+afZpaxx5Ml7xPeAZIIdgWmLHnQ2rts73N2M1NspAiBwV00Xx0N4Vg=="], - "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.6.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-23YX7zlOZlub+nPGDBUzktb4D5D6ETUAluKjXEeHIZ9m7fSlEYBnGL66YE+3t1DHXGd0OqsdwlvrNGcyo6EXDQ=="], + "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.7.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-INj7aVXjBvlZ84kEhSK4kJ484ub0i+BzgnjDWOWM1K+eFYDZjLdAsQSS3fGGXwVc3qKbPIssFfnftATDMTEJHQ=="], - "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.6.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-cFgRE3APxrY4AEdooVk2LtipwNNT/9mrnjdC5lVbsIsz+SxvGbZR231bxDJEqP15+RJOaD07FO1sIjINFqXMEg=="], + "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.7.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-lXGvC+z67UMcw58In12h8zCa9IyYRmuptUBMItQJzu+M278aMuD1nETyGLL7e4+OZ2lvrnnBIcjXN1hfw2yRzw=="], - "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.6.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cIuhVsZYd3o3Neo1JSAhJYw6BDvlxaBoqvgwRkG1rs0ExFmEmgYyG7ip9pFKnKNWph/tmW3rDYypmEfjs1is7g=="], + "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.7.6", "", { "os": "win32", "cpu": "x64" }, "sha512-zeUxEc0ZaPpmaYlCeWcjSJUPuRRySiSHN23oJ2Xyw0jsQ01Qm4OScPdr0RhEOFuK/UE+ANyRtDo4zJsY52Hadw=="], - "@rspack/core": ["@rspack/core@1.6.8", "", { "dependencies": { "@module-federation/runtime-tools": "0.21.6", "@rspack/binding": "1.6.8", "@rspack/lite-tapable": "1.1.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-FolcIAH5FW4J2FET+qwjd1kNeFbCkd0VLuIHO0thyolEjaPSxw5qxG67DA7BZGm6PVcoiSgPLks1DL6eZ8c+fA=="], + "@rspack/core": ["@rspack/core@1.7.6", "", { "dependencies": { "@module-federation/runtime-tools": "0.22.0", "@rspack/binding": "1.7.6", "@rspack/lite-tapable": "1.1.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-Iax6UhrfZqJajA778c1d5DBFbSIqPOSrI34kpNIiNpWd8Jq7mFIa+Z60SQb5ZQDZuUxcCZikjz5BxinFjTkg7Q=="], "@rspack/lite-tapable": ["@rspack/lite-tapable@1.1.0", "", {}, "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw=="], - "@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@1.5.3", "", { "dependencies": { "error-stack-parser": "^2.1.4", "html-entities": "^2.6.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0", "webpack-hot-middleware": "2.x" }, "optionalPeers": ["webpack-hot-middleware"] }, "sha512-VOnQMf3YOHkTqJ0+BJbrYga4tQAWNwoAnkgwRauXB4HOyCc5wLfBs9DcOFla/2usnRT3Sq6CMVhXmdPobwAoTA=="], + "@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@1.6.1", "", { "dependencies": { "error-stack-parser": "^2.1.4", "html-entities": "^2.6.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0", "webpack-hot-middleware": "2.x" }, "optionalPeers": ["webpack-hot-middleware"] }, "sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw=="], "@rushstack/node-core-library": ["@rushstack/node-core-library@5.19.1", "", { "dependencies": { "ajv": "~8.13.0", "ajv-draft-04": "~1.0.0", "ajv-formats": "~3.0.1", "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", "semver": "~7.5.4" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ESpb2Tajlatgbmzzukg6zyAhH+sICqJR2CNXNhXcEbz6UGCQfrKCtkxOpJTftWc8RGouroHG0Nud1SJAszvpmA=="], @@ -637,23 +645,23 @@ "@rushstack/rig-package": ["@rushstack/rig-package@0.6.0", "", { "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, "sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw=="], - "@rushstack/terminal": ["@rushstack/terminal@0.19.5", "", { "dependencies": { "@rushstack/node-core-library": "5.19.1", "@rushstack/problem-matcher": "0.1.1", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-6k5tpdB88G0K7QrH/3yfKO84HK9ggftfUZ51p7fePyCE7+RLLHkWZbID9OFWbXuna+eeCFE7AkKnRMHMxNbz7Q=="], + "@rushstack/terminal": ["@rushstack/terminal@0.21.0", "", { "dependencies": { "@rushstack/node-core-library": "5.19.1", "@rushstack/problem-matcher": "0.1.1", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-cLaI4HwCNYmknM5ns4G+drqdEB6q3dCPV423+d3TZeBusYSSm09+nR7CnhzJMjJqeRcdMAaLnrA4M/3xDz4R3w=="], - "@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.1.5", "", { "dependencies": { "@rushstack/terminal": "0.19.5", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-YmrFTFUdHXblYSa+Xc9OO9FsL/XFcckZy0ycQ6q7VSBsVs5P0uD9vcges5Q9vctGlVdu27w+Ct6IuJ458V0cTQ=="], + "@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.2.0", "", { "dependencies": { "@rushstack/terminal": "0.21.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-lYxCX0nDdkDtCkVpvF0m25ymf66SaMWuppbD6b7MdkIzvGXKBXNIVZlwBH/C0YfkanrupnICWf2n4z3AKSfaHw=="], "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], - "@tanstack/query-core": ["@tanstack/query-core@5.90.16", "", {}, "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww=="], + "@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="], - "@tanstack/react-query": ["@tanstack/react-query@5.90.16", "", { "dependencies": { "@tanstack/query-core": "5.90.16" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ=="], + "@tanstack/react-query": ["@tanstack/react-query@5.90.21", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg=="], "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], - "@testing-library/react": ["@testing-library/react@16.3.1", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw=="], + "@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], "@testing-library/react-hooks": ["@testing-library/react-hooks@8.0.1", "", { "dependencies": { "@babel/runtime": "^7.12.5", "react-error-boundary": "^3.1.0" }, "peerDependencies": { "@types/react": "^16.9.0 || ^17.0.0", "react": "^16.9.0 || ^17.0.0", "react-dom": "^16.9.0 || ^17.0.0", "react-test-renderer": "^16.9.0 || ^17.0.0" }, "optionalPeers": ["@types/react", "react-dom", "react-test-renderer"] }, "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g=="], @@ -673,7 +681,7 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], - "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], "@types/eslint": ["@types/eslint@9.6.1", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag=="], @@ -683,9 +691,9 @@ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], + "@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="], - "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -693,23 +701,25 @@ "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.2", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.53", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@volar/language-core": ["@volar/language-core@2.4.27", "", { "dependencies": { "@volar/source-map": "2.4.27" } }, "sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.4", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA=="], - "@volar/source-map": ["@volar/source-map@2.4.27", "", {}, "sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg=="], + "@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="], - "@volar/typescript": ["@volar/typescript@2.4.27", "", { "dependencies": { "@volar/language-core": "2.4.27", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg=="], + "@volar/source-map": ["@volar/source-map@2.4.28", "", {}, "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ=="], - "@vue/compiler-core": ["@vue/compiler-core@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.26", "entities": "^7.0.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w=="], + "@volar/typescript": ["@volar/typescript@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw=="], - "@vue/compiler-dom": ["@vue/compiler-dom@3.5.26", "", { "dependencies": { "@vue/compiler-core": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A=="], + "@vue/compiler-core": ["@vue/compiler-core@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.28", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.28", "", { "dependencies": { "@vue/compiler-core": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA=="], "@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="], "@vue/language-core": ["@vue/language-core@2.2.0", "", { "dependencies": { "@volar/language-core": "~2.4.11", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^0.4.9", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw=="], - "@vue/shared": ["@vue/shared@3.5.26", "", {}, "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A=="], + "@vue/shared": ["@vue/shared@3.5.28", "", {}, "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="], "@webassemblyjs/ast": ["@webassemblyjs/ast@1.14.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ=="], @@ -769,7 +779,7 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.9", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], @@ -779,9 +789,9 @@ "bun-test-env-dom": ["bun-test-env-dom@1.0.3", "", { "dependencies": { "@happy-dom/global-registrator": ">=20.0", "@testing-library/dom": ">=10.4", "@testing-library/jest-dom": ">=6.9", "@testing-library/react": ">=16.3", "@testing-library/user-event": ">=14.6" } }, "sha512-Ozepvzk1s/bJSxABEjbI+Ztnm3CN1b0vRSvf0Qa0rTnuO7S0wKN2cUTsXdyIJuqE6OnlAhyoe2NGqkdeemz5/Q=="], - "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], - "caniuse-lite": ["caniuse-lite@1.0.30001760", "", {}, "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], "chrome-trace-event": ["chrome-trace-event@1.0.4", "", {}, "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ=="], @@ -791,7 +801,7 @@ "compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="], - "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + "confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], @@ -801,7 +811,7 @@ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - "csstype-extra": ["csstype-extra@0.1.21", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-+bNbaI4AB6Sh9MeWlzHe+OPPDttu8oM8g6oyOSfoDmzjn6jdaixBdo6Sbu64apL9WEUfiuXuvDiVfqoSnyolBA=="], + "csstype-extra": ["csstype-extra@0.1.22", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-btfAb2xzFMT/9lEK7YayWPvXLSjWm6iTs9DPwg8761ZaD3QP1UJUQTOAhL2Fq5PibUQ6PvCH48rYEOU2FWfXLQ=="], "de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="], @@ -811,21 +821,21 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], + "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], - "electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="], + "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="], - "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], + "enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="], - "entities": ["entities@7.0.0", "", {}, "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ=="], + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="], "es-module-lexer": ["es-module-lexer@2.0.0", "", {}, "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw=="], - "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], + "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -855,13 +865,11 @@ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], - "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "happy-dom": ["happy-dom@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "@types/whatwg-mimetype": "^3.0.2", "whatwg-mimetype": "^3.0.0" } }, "sha512-QsCdAUHAmiDeKeaNojb1OHOPF7NjcWPBR7obdu3NwH2a/oyQaLg5d0aaCy/9My6CdPChYF07dvz5chaXBGaD4g=="], + "happy-dom": ["happy-dom@20.6.1", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^6.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-+0vhESXXhFwkdjZnJ5DlmJIfUYGgIEEjzIjB+aKJbFuqlvvKyOi+XkI1fYbgYR9QCxG5T08koxsQ6HrQfa5gCQ=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -903,7 +911,7 @@ "local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="], - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], @@ -919,9 +927,7 @@ "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], - "minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], - - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "minimatch": ["minimatch@10.1.2", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.1" } }, "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw=="], "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], @@ -933,7 +939,7 @@ "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], - "next": ["next@16.1.1", "", { "dependencies": { "@next/env": "16.1.1", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.1", "@next/swc-darwin-x64": "16.1.1", "@next/swc-linux-arm64-gnu": "16.1.1", "@next/swc-linux-arm64-musl": "16.1.1", "@next/swc-linux-x64-gnu": "16.1.1", "@next/swc-linux-x64-musl": "16.1.1", "@next/swc-win32-arm64-msvc": "16.1.1", "@next/swc-win32-x64-msvc": "16.1.1", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w=="], + "next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="], "next-example": ["next-example@workspace:examples/next"], @@ -947,8 +953,6 @@ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -967,13 +971,13 @@ "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], - "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], - "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], "react-error-boundary": ["react-error-boundary@3.1.4", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "react": ">=16.13.1" } }, "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA=="], - "react-hook-form": ["react-hook-form@7.70.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-COOMajS4FI3Wuwrs3GPpi/Jeef/5W1DRR84Yl5/ShlT3dKVFUfoGiEZ/QE6Uw8P4T2/CLJdcTVYKvWBMQTEpvw=="], + "react-hook-form": ["react-hook-form@7.71.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w=="], "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], @@ -985,7 +989,7 @@ "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - "rollup": ["rollup@4.53.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.5", "@rollup/rollup-android-arm64": "4.53.5", "@rollup/rollup-darwin-arm64": "4.53.5", "@rollup/rollup-darwin-x64": "4.53.5", "@rollup/rollup-freebsd-arm64": "4.53.5", "@rollup/rollup-freebsd-x64": "4.53.5", "@rollup/rollup-linux-arm-gnueabihf": "4.53.5", "@rollup/rollup-linux-arm-musleabihf": "4.53.5", "@rollup/rollup-linux-arm64-gnu": "4.53.5", "@rollup/rollup-linux-arm64-musl": "4.53.5", "@rollup/rollup-linux-loong64-gnu": "4.53.5", "@rollup/rollup-linux-ppc64-gnu": "4.53.5", "@rollup/rollup-linux-riscv64-gnu": "4.53.5", "@rollup/rollup-linux-riscv64-musl": "4.53.5", "@rollup/rollup-linux-s390x-gnu": "4.53.5", "@rollup/rollup-linux-x64-gnu": "4.53.5", "@rollup/rollup-linux-x64-musl": "4.53.5", "@rollup/rollup-openharmony-arm64": "4.53.5", "@rollup/rollup-win32-arm64-msvc": "4.53.5", "@rollup/rollup-win32-ia32-msvc": "4.53.5", "@rollup/rollup-win32-x64-gnu": "4.53.5", "@rollup/rollup-win32-x64-msvc": "4.53.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ=="], + "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], "rollup-plugin-preserve-directives": ["rollup-plugin-preserve-directives@0.4.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "magic-string": "^0.30.5" }, "peerDependencies": { "rollup": "2.x || 3.x || 4.x" } }, "sha512-gx4nBxYm5BysmEQS+e2tAMrtFxrGvk+Pe5ppafRibQi0zlW7VYAbEGk6IKDw9sJGPdFWgVTE0o4BU4cdG0Fylg=="], @@ -1027,7 +1031,7 @@ "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], - "terser": ["terser@5.44.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw=="], + "terser": ["terser@5.46.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg=="], "terser-webpack-plugin": ["terser-webpack-plugin@5.3.16", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q=="], @@ -1037,7 +1041,7 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], @@ -1047,7 +1051,7 @@ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - "vite": ["vite@7.3.0", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg=="], + "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], "vite-example": ["vite-example@workspace:examples/vite"], @@ -1055,17 +1059,19 @@ "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], - "watchpack": ["watchpack@2.4.4", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA=="], + "watchpack": ["watchpack@2.5.1", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg=="], - "webpack": ["webpack@5.104.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.4", "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "terser-webpack-plugin": "^5.3.16", "watchpack": "^2.4.4", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-5DeICTX8BVgNp6afSPYXAFjskIgWGlygQH58bcozPOXgo2r/6xx39Y1+cULZ3gTxUYQP88jmwLj2anu4Xaq84g=="], + "webpack": ["webpack@5.105.2", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.19.0", "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "terser-webpack-plugin": "^5.3.16", "watchpack": "^2.5.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw=="], "webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], + "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], + "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - "zod": ["zod@4.3.5", "", {}, "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -1073,70 +1079,36 @@ "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@devup-api/zod/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - - "@devup-ui/next-plugin/next": ["next@16.0.10", "", { "dependencies": { "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.10", "@next/swc-darwin-x64": "16.0.10", "@next/swc-linux-arm64-gnu": "16.0.10", "@next/swc-linux-arm64-musl": "16.0.10", "@next/swc-linux-x64-gnu": "16.0.10", "@next/swc-linux-x64-musl": "16.0.10", "@next/swc-win32-arm64-msvc": "16.0.10", "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA=="], - - "@devup-ui/webpack-plugin/@devup-ui/wasm": ["@devup-ui/wasm@1.0.47", "", {}, "sha512-RPktfdg53bK5BqAyhfs9hA5vzAiH0D63w60S+ACaoIPXpqQaQp2Lh9pl3Mi6E+8KA0Div/hoQCLfYxuAefodrg=="], - - "@happy-dom/global-registrator/@types/node": ["@types/node@20.19.27", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug=="], - "@microsoft/api-extractor/typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], - "@rsbuild/core/@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], + "@rsbuild/core/@swc/helpers": ["@swc/helpers@0.5.18", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ=="], "@rushstack/node-core-library/ajv": ["ajv@8.13.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.4.1" } }, "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA=="], + "@testing-library/jest-dom/aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + "@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + "@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "@vue/language-core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], "esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], - - "happy-dom/@types/node": ["@types/node@20.19.27", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug=="], - "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], - "path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], - "schema-utils/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], "schema-utils/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="], - "sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "@devup-ui/next-plugin/next/@next/env": ["@next/env@16.0.10", "", {}, "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang=="], - - "@devup-ui/next-plugin/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg=="], - - "@devup-ui/next-plugin/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw=="], - - "@devup-ui/next-plugin/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw=="], - - "@devup-ui/next-plugin/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw=="], - - "@devup-ui/next-plugin/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA=="], - - "@devup-ui/next-plugin/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g=="], - - "@devup-ui/next-plugin/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.0.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg=="], - - "@devup-ui/next-plugin/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.10", "", { "os": "win32", "cpu": "x64" }, "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q=="], - - "@devup-ui/next-plugin/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], - - "@happy-dom/global-registrator/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "happy-dom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], } } diff --git a/examples/next-webpack/package.json b/examples/next-webpack/package.json index 9b49123..bce79dd 100644 --- a/examples/next-webpack/package.json +++ b/examples/next-webpack/package.json @@ -9,9 +9,9 @@ "lint": "next lint" }, "dependencies": { - "next": "^16.1.1", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "next": "^16.1.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", "@devup-api/next-plugin": "workspace:*", "@devup-api/fetch": "workspace:*", "@devup-ui/react": "^1" diff --git a/examples/next/package.json b/examples/next/package.json index 18307b7..c755544 100644 --- a/examples/next/package.json +++ b/examples/next/package.json @@ -9,9 +9,9 @@ "lint": "next lint" }, "dependencies": { - "next": "^16.1.1", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "next": "^16.1.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", "@devup-api/next-plugin": "workspace:*", "@devup-api/fetch": "workspace:*", "@devup-api/react-query": "workspace:*", diff --git a/examples/rsbuild/package.json b/examples/rsbuild/package.json index 06169fd..3a0b427 100644 --- a/examples/rsbuild/package.json +++ b/examples/rsbuild/package.json @@ -9,8 +9,8 @@ "preview": "rsbuild preview" }, "dependencies": { - "react": "^19.2.3", - "react-dom": "^19.2.3", + "react": "^19.2.4", + "react-dom": "^19.2.4", "@devup-api/fetch": "workspace:*" }, "devDependencies": { diff --git a/examples/vite/package.json b/examples/vite/package.json index 37c45a9..62bc79c 100644 --- a/examples/vite/package.json +++ b/examples/vite/package.json @@ -9,16 +9,16 @@ "preview": "vite preview" }, "dependencies": { - "react": "^19.2.3", - "react-dom": "^19.2.3", + "react": "^19.2.4", + "react-dom": "^19.2.4", "@devup-api/fetch": "workspace:*" }, "devDependencies": { "@types/react": "^19", "@types/react-dom": "^19", - "@vitejs/plugin-react": "^5.1.2", + "@vitejs/plugin-react": "^5.1.4", "typescript": "^5", - "vite": "^7.3.0", + "vite": "^7.3.1", "@devup-api/vite-plugin": "workspace:*" } } diff --git a/package.json b/package.json index 1784e0b..0eb41ca 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,13 @@ "private": true, "devDependencies": { "@biomejs/biome": "^2.3", - "@testing-library/react": "^16.3.1", + "@testing-library/react": "^16.3.2", "@testing-library/react-hooks": "^8.0.1", "@types/bun": "latest", "bun-test-env-dom": "^1.0.3", "husky": "^9", - "react": "^19.2.3", - "react-dom": "^19.2.3" + "react": "^19.2.4", + "react-dom": "^19.2.4" }, "author": "JeongMin Oh", "license": "Apache-2.0", diff --git a/packages/core/package.json b/packages/core/package.json index 4048af7..025da03 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,7 +21,7 @@ "access": "public" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9" } } diff --git a/packages/fetch/package.json b/packages/fetch/package.json index 3245103..de93555 100644 --- a/packages/fetch/package.json +++ b/packages/fetch/package.json @@ -23,7 +23,7 @@ "@devup-api/core": "workspace:^" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9" } } diff --git a/packages/generator/package.json b/packages/generator/package.json index 23396f8..c32bb82 100644 --- a/packages/generator/package.json +++ b/packages/generator/package.json @@ -24,7 +24,7 @@ "@devup-api/utils": "workspace:^" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9", "openapi-types": "^12.1" } diff --git a/packages/hookform/package.json b/packages/hookform/package.json index 67db247..d0709bd 100644 --- a/packages/hookform/package.json +++ b/packages/hookform/package.json @@ -25,7 +25,7 @@ "@devup-api/fetch": "workspace:^", "@devup-api/zod": "workspace:^", "@hookform/resolvers": ">=5.2.2", - "react-hook-form": ">=7.70.0" + "react-hook-form": ">=7.71.1" }, "peerDependencies": { "@tanstack/react-query": ">=5.0.0", @@ -34,14 +34,14 @@ "zod": "*" }, "devDependencies": { - "@tanstack/react-query": "^5.90.16", - "@testing-library/react": "^16.3.1", - "@types/node": "^25.0", + "@tanstack/react-query": "^5.90", + "@testing-library/react": "^16.3", + "@types/node": "^25.2", "@types/react": "^19.2", - "bun-test-env-dom": "^1.0.3", - "happy-dom": "^20.0.11", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "bun-test-env-dom": "^1.0", + "happy-dom": "^20.6", + "react": "^19.2", + "react-dom": "^19.2", "typescript": "^5.9", "zod": "^4.3", "rollup-plugin-preserve-directives": "^0.4", diff --git a/packages/next-plugin/package.json b/packages/next-plugin/package.json index b09bf79..d29ef7c 100644 --- a/packages/next-plugin/package.json +++ b/packages/next-plugin/package.json @@ -30,7 +30,7 @@ "@devup-api/core": "*" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "@types/webpack": "^5.28", "typescript": "^5.9" } diff --git a/packages/react-query/package.json b/packages/react-query/package.json index a3ac593..dd72d48 100644 --- a/packages/react-query/package.json +++ b/packages/react-query/package.json @@ -29,9 +29,9 @@ }, "devDependencies": { "@testing-library/react-hooks": "^8.0.1", - "@types/node": "^25.0", + "@types/node": "^25.2", "@types/react": "^19.2", - "happy-dom": "^20.0.11", + "happy-dom": "^20.6.1", "typescript": "^5.9" } } diff --git a/packages/rsbuild-plugin/package.json b/packages/rsbuild-plugin/package.json index 289e14c..b2302dc 100644 --- a/packages/rsbuild-plugin/package.json +++ b/packages/rsbuild-plugin/package.json @@ -29,7 +29,7 @@ "@devup-api/core": "*" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9" } } diff --git a/packages/ui/package.json b/packages/ui/package.json index df0d8e7..e39e5c6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -25,15 +25,15 @@ "@devup-api/hookform": "workspace:^" }, "peerDependencies": { - "react": "^18.0.0 || ^19.0.0" + "react": "^18.0.0 || >=19.0.0" }, "devDependencies": { - "@types/node": "^25.0", - "@types/react": "^19.2.7", + "@types/node": "^25.2", + "@types/react": "^19.2", "rollup-plugin-preserve-directives": "^0.4", "vite": "^7.3", "vite-plugin-dts": "^4.5", - "react": "^19.2.3", + "react": "^19.2", "typescript": "^5.9" } } diff --git a/packages/utils/package.json b/packages/utils/package.json index e926e43..bac995a 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -20,7 +20,7 @@ "access": "public" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9", "openapi-types": "^12.1" } diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index 3af21a8..9205244 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -29,7 +29,7 @@ "@devup-api/core": "*" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "openapi-types": "^12.1", "typescript": "^5.9" } diff --git a/packages/webpack-plugin/package.json b/packages/webpack-plugin/package.json index 90c3b7c..c853b7a 100644 --- a/packages/webpack-plugin/package.json +++ b/packages/webpack-plugin/package.json @@ -28,7 +28,7 @@ "@devup-api/core": "*" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "@types/webpack": "^5.28", "typescript": "^5.9" } diff --git a/packages/zod/package.json b/packages/zod/package.json index cdd951e..84d4c7d 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -24,11 +24,11 @@ "zod": ">=4" }, "peerDependencies": { - "zod": "^3.0.0" + "zod": "^4.0" }, "devDependencies": { - "@types/node": "^25.0", + "@types/node": "^25.2", "typescript": "^5.9", - "zod": "^4.3.5" + "zod": "^4.3" } } From cabf093708e1e83ebd1783a37acb26798dc49784 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 00:54:17 +0900 Subject: [PATCH 02/15] feat(core): add bodyType to UrlMapValue for content type awareness - Add optional bodyType?: 'json' | 'form' | 'multipart' field to UrlMapValue - Field is optional for backward compatibility - Enables runtime to determine request body serialization strategy --- packages/core/src/url-map.ts | 1 + .../src/__tests__/generate-schema.test.ts | 169 + .../src/__tests__/generate-zod.test.ts | 217 +- packages/generator/src/generate-schema.ts | 36 +- packages/generator/src/generate-zod.ts | 33 +- test.json | 3279 +++++++++++++++++ 6 files changed, 3721 insertions(+), 14 deletions(-) create mode 100644 test.json diff --git a/packages/core/src/url-map.ts b/packages/core/src/url-map.ts index b9669fe..83136c4 100644 --- a/packages/core/src/url-map.ts +++ b/packages/core/src/url-map.ts @@ -1,4 +1,5 @@ export interface UrlMapValue { method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' url: string + bodyType?: 'json' | 'form' | 'multipart' } diff --git a/packages/generator/src/__tests__/generate-schema.test.ts b/packages/generator/src/__tests__/generate-schema.test.ts index c7dea1f..f5d2b26 100644 --- a/packages/generator/src/__tests__/generate-schema.test.ts +++ b/packages/generator/src/__tests__/generate-schema.test.ts @@ -5,6 +5,7 @@ import { extractParameters, extractRequestBody, formatTypeValue, + getRequestBodyContent, getTypeFromSchema, } from '../generate-schema' @@ -1185,3 +1186,171 @@ test('extractRequestBody handles requestBody with empty content', () => { } as OpenAPIV3_1.RequestBodyObject expect(extractRequestBody(requestBody, createDocument())).toBeUndefined() }) + +// Tests for form-urlencoded content type +test('extractRequestBody handles requestBody with application/x-www-form-urlencoded', () => { + const requestBody = { + content: { + 'application/x-www-form-urlencoded': { + schema: { + type: 'object' as const, + properties: { + name: { type: 'string' as const }, + email: { type: 'string' as const }, + }, + }, + }, + }, + } as OpenAPIV3_1.RequestBodyObject + expect(extractRequestBody(requestBody, createDocument())).toMatchSnapshot() +}) + +test('extractRequestBody handles requestBody $ref with application/x-www-form-urlencoded', () => { + const requestBody = { + $ref: '#/components/requestBodies/SubscribeForm', + } as OpenAPIV3_1.ReferenceObject + const document = { + components: { + requestBodies: { + SubscribeForm: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + type: 'object' as const, + properties: { + name: { type: 'string' as const }, + email: { type: 'string' as const }, + }, + }, + }, + }, + }, + }, + }, + } + expect( + extractRequestBody(requestBody, createDocument(document)), + ).toMatchSnapshot() +}) + +// Tests for multipart/form-data content type +test('extractRequestBody handles requestBody with multipart/form-data', () => { + const requestBody = { + content: { + 'multipart/form-data': { + schema: { + type: 'object' as const, + properties: { + name: { type: 'string' as const }, + document: { type: 'string' as const, format: 'binary' }, + }, + }, + }, + }, + } as OpenAPIV3_1.RequestBodyObject + expect(extractRequestBody(requestBody, createDocument())).toMatchSnapshot() +}) + +test('extractRequestBody handles requestBody $ref with multipart/form-data', () => { + const requestBody = { + $ref: '#/components/requestBodies/FileUpload', + } as OpenAPIV3_1.ReferenceObject + const document = { + components: { + requestBodies: { + FileUpload: { + content: { + 'multipart/form-data': { + schema: { + type: 'object' as const, + properties: { + file: { type: 'string' as const, format: 'binary' }, + }, + }, + }, + }, + }, + }, + }, + } + expect( + extractRequestBody(requestBody, createDocument(document)), + ).toMatchSnapshot() +}) + +// Tests for JSON priority over other content types +test('extractRequestBody prefers application/json over other content types', () => { + const requestBody = { + content: { + 'multipart/form-data': { + schema: { + type: 'object' as const, + properties: { + file: { type: 'string' as const, format: 'binary' }, + }, + }, + }, + 'application/json': { + schema: { + type: 'object' as const, + properties: { + name: { type: 'string' as const }, + }, + }, + }, + }, + } as OpenAPIV3_1.RequestBodyObject + // Should pick JSON content (no binary field) + const result = extractRequestBody(requestBody, createDocument()) + expect(result).toMatchSnapshot() +}) + +// Tests for getRequestBodyContent helper +test('getRequestBodyContent returns undefined for undefined content', () => { + expect(getRequestBodyContent(undefined)).toBeUndefined() +}) + +test('getRequestBodyContent returns json content when available', () => { + const content = { + 'application/json': { schema: { type: 'object' as const } }, + 'multipart/form-data': { schema: { type: 'object' as const } }, + } + expect(getRequestBodyContent(content)).toBe(content['application/json']) +}) + +test('getRequestBodyContent returns urlencoded content when no json', () => { + const content = { + 'application/x-www-form-urlencoded': { + schema: { type: 'object' as const }, + }, + 'multipart/form-data': { schema: { type: 'object' as const } }, + } + expect(getRequestBodyContent(content)).toBe( + content['application/x-www-form-urlencoded'], + ) +}) + +test('getRequestBodyContent returns multipart content when no json or urlencoded', () => { + const content = { + 'multipart/form-data': { schema: { type: 'object' as const } }, + } + expect(getRequestBodyContent(content)).toBe(content['multipart/form-data']) +}) + +test('getRequestBodyContent returns undefined for unsupported content types', () => { + const content = { + 'application/xml': { schema: { type: 'object' as const } }, + } + expect(getRequestBodyContent(content)).toBeUndefined() +}) + +// Tests for format: "binary" in getTypeFromSchema +test('getTypeFromSchema handles string with format binary', () => { + const schema = { type: 'string' as const, format: 'binary' } + expect(getTypeFromSchema(schema, createDocument())).toMatchSnapshot() +}) + +test('getTypeFromSchema handles nullable string with format binary', () => { + const schema = { type: ['string', 'null'] as any, format: 'binary' } + expect(getTypeFromSchema(schema, createDocument())).toMatchSnapshot() +}) diff --git a/packages/generator/src/__tests__/generate-zod.test.ts b/packages/generator/src/__tests__/generate-zod.test.ts index e288584..9ad4d7f 100644 --- a/packages/generator/src/__tests__/generate-zod.test.ts +++ b/packages/generator/src/__tests__/generate-zod.test.ts @@ -3157,7 +3157,7 @@ describe('generateZodSchemas - inline request body schema', () => { expect(result).toContain('post') }) - test('handles requestBody with no application/json content', () => { + test('handles requestBody with multipart/form-data content', () => { const result = generateZodSchemas({ 'openapi.json': createDocument({ paths: { @@ -3183,8 +3183,9 @@ describe('generateZodSchemas - inline request body schema', () => { }), }) - // Should handle gracefully when no application/json content + // Should process multipart/form-data content (inline schemas still produce pathSchemas structure) expect(result).toContain('pathSchemas') + expect(result).toContain('postPathSchemas') }) test('handles requestBody with empty content', () => { @@ -4084,3 +4085,215 @@ describe('generateZodSchemas - additional coverage', () => { expect(result).toContain('requestSchemas') }) }) + +// ============================================================================= +// Binary format handling +// ============================================================================= + +describe('generateZodSchemas - binary format handling', () => { + test('generates z.instanceof(File) for format: binary in runtime schemas', () => { + const result = generateZodSchemas({ + 'openapi.json': createDocument({ + paths: { + '/upload': { + post: { + operationId: 'uploadFile', + requestBody: { + content: { + 'multipart/form-data': { + schema: { + $ref: '#/components/schemas/FileUpload', + }, + }, + }, + }, + responses: { '200': { description: 'Success' } }, + }, + }, + }, + components: { + schemas: { + FileUpload: { + type: 'object', + properties: { + name: { type: 'string' }, + document: { type: 'string', format: 'binary' }, + }, + required: ['name'], + }, + }, + }, + }), + }) + + expect(result).toContain('requestSchemas') + expect(result).toContain('z.instanceof(File)') + expect(result).toContain('z.string()') + }) + + test('generates z.ZodType for format: binary in type declarations', () => { + const result = generateZodTypeDeclarations({ + 'openapi.json': createDocument({ + paths: { + '/upload': { + post: { + operationId: 'uploadFile', + requestBody: { + content: { + 'multipart/form-data': { + schema: { + $ref: '#/components/schemas/FileUpload', + }, + }, + }, + }, + responses: { '200': { description: 'Success' } }, + }, + }, + }, + components: { + schemas: { + FileUpload: { + type: 'object', + properties: { + name: { type: 'string' }, + document: { type: 'string', format: 'binary' }, + }, + required: ['name'], + }, + }, + }, + }), + }) + + expect(result).toContain('z.ZodType') + expect(result).toContain('z.ZodString') + }) +}) + +// ============================================================================= +// Content type support in request body schema collection +// ============================================================================= + +describe('generateZodSchemas - multi content type support', () => { + test('collects schema names from application/x-www-form-urlencoded', () => { + const result = generateZodSchemas({ + 'openapi.json': createDocument({ + paths: { + '/form': { + post: { + operationId: 'submitForm', + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + $ref: '#/components/schemas/FormInput', + }, + }, + }, + }, + responses: { '200': { description: 'Success' } }, + }, + }, + }, + components: { + schemas: { + FormInput: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string', format: 'email' }, + }, + required: ['name', 'email'], + }, + }, + }, + }), + }) + + expect(result).toContain('requestSchemas') + expect(result).toContain('FormInput') + }) + + test('collects schema names from multipart/form-data with $ref', () => { + const result = generateZodSchemas({ + 'openapi.json': createDocument({ + paths: { + '/upload': { + post: { + operationId: 'createUpload', + requestBody: { + content: { + 'multipart/form-data': { + schema: { + $ref: '#/components/schemas/UploadRequest', + }, + }, + }, + }, + responses: { '200': { description: 'Success' } }, + }, + }, + }, + components: { + schemas: { + UploadRequest: { + type: 'object', + properties: { + name: { type: 'string' }, + file: { type: 'string', format: 'binary' }, + }, + required: ['name', 'file'], + }, + }, + }, + }), + }) + + expect(result).toContain('requestSchemas') + expect(result).toContain('UploadRequest') + expect(result).toContain('z.instanceof(File)') + }) + + test('getRequestBodySchemaName returns schema name for urlencoded body', () => { + const result = generateZodSchemas({ + 'openapi.json': createDocument({ + paths: { + '/subscribe': { + post: { + operationId: 'subscribe', + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + $ref: '#/components/schemas/SubscribeRequest', + }, + }, + }, + }, + responses: { '200': { description: 'Success' } }, + }, + }, + }, + components: { + schemas: { + SubscribeRequest: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + required: ['name', 'email'], + }, + }, + }, + }), + }) + + // pathSchemas should map subscribe to SubscribeRequest schema + expect(result).toContain('requestSchemas') + expect(result).toContain('SubscribeRequest') + expect(result).toContain('pathSchemas') + expect(result).toContain('subscribe') + }) +}) diff --git a/packages/generator/src/generate-schema.ts b/packages/generator/src/generate-schema.ts index d6a6e1c..17098db 100644 --- a/packages/generator/src/generate-schema.ts +++ b/packages/generator/src/generate-schema.ts @@ -306,6 +306,13 @@ export function getTypeFromSchema( // Handle primitive types if (actualType === 'string') { + // Handle binary format for file upload fields + if (schemaObj.format === 'binary') { + return { + type: nullable ? 'File | Blob | null' : 'File | Blob', + default: schemaObj.default, + } + } return { type: nullable ? 'string | null' : 'string', default: schemaObj.default, @@ -723,6 +730,23 @@ export function extractParameters( return { pathParams, queryParams, headerParams } } +// Priority order: json > urlencoded > multipart +const CONTENT_TYPE_PRIORITY = [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data', +] as const + +export function getRequestBodyContent( + content: OpenAPIV3_1.RequestBodyObject['content'] | undefined, +): OpenAPIV3_1.MediaTypeObject | undefined { + if (!content) return undefined + for (const ct of CONTENT_TYPE_PRIORITY) { + if (content[ct]) return content[ct] + } + return undefined +} + /** * Extract request body from OpenAPI operation */ @@ -742,9 +766,9 @@ export function extractRequestBody( if (resolved && 'content' in resolved && resolved.content) { const content = resolved.content as OpenAPIV3_1.RequestBodyObject['content'] - const jsonContent = content['application/json'] - if (jsonContent && 'schema' in jsonContent && jsonContent.schema) { - return getTypeFromSchema(jsonContent.schema, document, { + const bodyContent = getRequestBodyContent(content) + if (bodyContent && 'schema' in bodyContent && bodyContent.schema) { + return getTypeFromSchema(bodyContent.schema, document, { defaultNonNullable: false, }).type } @@ -754,9 +778,9 @@ export function extractRequestBody( const content = requestBody.content if (content) { - const jsonContent = content['application/json'] - if (jsonContent && 'schema' in jsonContent && jsonContent.schema) { - return getTypeFromSchema(jsonContent.schema, document, { + const bodyContent = getRequestBodyContent(content) + if (bodyContent && 'schema' in bodyContent && bodyContent.schema) { + return getTypeFromSchema(bodyContent.schema, document, { defaultNonNullable: false, }).type } diff --git a/packages/generator/src/generate-zod.ts b/packages/generator/src/generate-zod.ts index ca043f6..b2d334e 100644 --- a/packages/generator/src/generate-zod.ts +++ b/packages/generator/src/generate-zod.ts @@ -158,6 +158,11 @@ function schemaToZod( // Handle primitive types if (primaryType === 'string') { + // Handle binary format for file upload fields + if (schemaObj.format === 'binary') { + return wrapNullable('z.instanceof(File)') + } + // Zod 4.0: Use top-level format validators instead of z.string().format() // Check format first to use top-level validators if (schemaObj.format === 'email') { @@ -401,6 +406,9 @@ function schemaToZodType( // Handle primitive types if (primaryType === 'string') { + if (schemaObj.format === 'binary') { + return wrapNullable('z.ZodType') + } return wrapNullable('z.ZodString') } @@ -564,9 +572,15 @@ function collectSchemaUsage( return extractSchemaNameFromRef(requestBody.$ref) } const content = requestBody.content - const jsonContent = content?.['application/json'] - if (jsonContent?.schema && '$ref' in jsonContent.schema) { - return extractSchemaNameFromRef(jsonContent.schema.$ref) + for (const ct of [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data', + ]) { + const bodyContent = content?.[ct] + if (bodyContent?.schema && '$ref' in bodyContent.schema) { + return extractSchemaNameFromRef(bodyContent.schema.$ref) + } } return null } @@ -604,9 +618,16 @@ function collectSchemaUsage( } } else { const content = operation.requestBody.content - const jsonContent = content?.['application/json'] - if (jsonContent?.schema) { - collectSchemaNames(jsonContent.schema, requestSchemaNames) + for (const ct of [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data', + ]) { + const bodyContent = content?.[ct] + if (bodyContent?.schema) { + collectSchemaNames(bodyContent.schema, requestSchemaNames) + break + } } } } diff --git a/test.json b/test.json new file mode 100644 index 0000000..1a42e1c --- /dev/null +++ b/test.json @@ -0,0 +1,3279 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "API", + "version": "0.1.0" + }, + "servers": [ + { + "url": "http://localhost:3000" + } + ], + "paths": { + "/": { + "get": { + "operationId": "root_endpoint", + "description": "Health check endpoint", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/complex-struct-body": { + "post": { + "operationId": "mod_file_with_complex_struct_body", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ComplexStructBody" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/complex-struct-body-with-rename": { + "post": { + "operationId": "mod_file_with_complex_struct_body_with_rename", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ComplexStructBodyWithRename" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/enums": { + "get": { + "operationId": "enum_endpoint", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Enum" + } + } + } + } + } + } + }, + "/enums/adjacently-tagged": { + "post": { + "operationId": "adjacently_tagged_endpoint", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdjacentlyTaggedResponse" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdjacentlyTaggedResponse" + } + } + } + } + } + } + }, + "/enums/enum2": { + "get": { + "operationId": "enum_endpoint2", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Enum2" + } + } + } + } + } + } + }, + "/enums/externally-tagged": { + "post": { + "operationId": "externally_tagged_endpoint", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExternallyTaggedEvent" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExternallyTaggedEvent" + } + } + } + } + } + } + }, + "/enums/internally-tagged": { + "post": { + "operationId": "internally_tagged_endpoint", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternallyTaggedMessage" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternallyTaggedMessage" + } + } + } + } + } + } + }, + "/enums/untagged": { + "post": { + "operationId": "untagged_endpoint", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UntaggedValue" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UntaggedValue" + } + } + } + } + } + } + }, + "/error": { + "get": { + "operationId": "error_endpoint", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/error/error-with-status": { + "get": { + "operationId": "error_endpoint_with_status_code", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/error/error-with-status2": { + "get": { + "operationId": "error_endpoint_with_status_code2", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse2" + } + } + } + }, + "404": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse2" + } + } + } + }, + "500": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse2" + } + } + } + } + } + } + }, + "/error/error2": { + "get": { + "operationId": "error_endpoint2", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse2" + } + } + } + } + } + } + }, + "/error/header-map": { + "get": { + "operationId": "header_map_endpoint", + "responses": { + "200": { + "description": "Successful response", + "headers": {}, + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse2" + } + } + } + } + } + } + }, + "/error/header-map2": { + "get": { + "operationId": "header_map_endpoint2", + "responses": { + "200": { + "description": "Successful response", + "headers": {}, + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse2" + } + } + } + } + } + } + }, + "/flatten": { + "post": { + "operationId": "list_users", + "tags": ["flatten"], + "description": "List users with pagination (demonstrates flatten for request/response)\n\nThe request accepts flattened pagination parameters (page, per_page)\nand returns a response with flattened metadata (total, has_more).", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserListRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserListResponse" + } + } + } + } + } + } + }, + "/flatten/search": { + "post": { + "operationId": "advanced_search", + "tags": ["flatten"], + "description": "Advanced search endpoint with multiple flatten fields", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdvancedSearchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchResponse" + } + } + } + } + } + } + }, + "/foo/foo": { + "post": { + "operationId": "signup", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignupRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignupResponse" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/form": { + "post": { + "operationId": "subscribe", + "tags": ["form"], + "description": "Subscribe to newsletter via form submission", + "requestBody": { + "required": true, + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SubscribeRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscribeResponse" + } + } + } + } + } + } + }, + "/form/contact": { + "post": { + "operationId": "contact", + "tags": ["form"], + "description": "Submit a contact form", + "requestBody": { + "required": true, + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ContactFormRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContactFormResponse" + } + } + } + } + } + } + }, + "/form/upload": { + "post": { + "operationId": "upload", + "tags": ["form"], + "description": "Upload a file via raw multipart form data", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContactFormResponse" + } + } + } + } + } + } + }, + "/generic/generic/{value}": { + "get": { + "operationId": "generic_endpoint", + "parameters": [ + { + "name": "value", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": ["value", "name"] + } + } + } + } + } + } + }, + "/generic/generic2": { + "get": { + "operationId": "generic_endpoint2", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/TestStruct" + } + }, + "required": ["value", "name"] + } + } + } + } + } + } + }, + "/generic/generic3": { + "get": { + "operationId": "generic_endpoint3", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/TestStruct" + }, + "value2": { + "type": "string" + } + }, + "required": ["value", "name", "value2"] + } + } + } + } + } + } + }, + "/generic/generic4": { + "get": { + "operationId": "generic_endpoint4", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "boolean" + }, + "value2": { + "type": "boolean" + } + }, + "required": ["value", "name", "value2"] + } + } + } + } + } + } + }, + "/generic/generic5": { + "get": { + "operationId": "generic_endpoint5", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContactResponse" + } + }, + "page": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "totalPage": { + "type": "integer" + } + }, + "required": ["items", "page", "size", "totalPage"] + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/health": { + "get": { + "operationId": "health", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/hello": { + "get": { + "operationId": "mod_file_endpoint", + "tags": ["hello"], + "description": "Hello!!", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/map-query": { + "get": { + "operationId": "mod_file_with_map_query", + "parameters": [ + { + "name": "name", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "age", + "in": "query", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "optional_age", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "nullable": true + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memos": { + "post": { + "operationId": "create_memo", + "description": "Create a new memo", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMemoRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMemoRequest" + } + } + } + } + } + }, + "put": { + "operationId": "update_memo", + "description": "Update a memo", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMemoRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMemoRequest" + } + } + } + } + } + } + }, + "/memos/format": { + "get": { + "operationId": "get_memo_format", + "description": "Get memo response format", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memos/{id}": { + "get": { + "operationId": "get_memo", + "description": "Get memo by id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemoResponse" + } + } + } + } + } + } + }, + "/memos/{id}/rel": { + "get": { + "operationId": "get_memo_rel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemoResponseRel" + } + } + } + } + } + } + }, + "/no-schema-query": { + "get": { + "operationId": "mod_file_with_no_schema_query", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/path/multi-path/{arg}/{var1}/{var2}": { + "get": { + "operationId": "mod_file_with_multi_path", + "parameters": [ + { + "name": "arg", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "var1", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "var2", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/path/multi-path/{var1}": { + "get": { + "operationId": "mod_file_with_test_struct", + "parameters": [ + { + "name": "var1", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/path/multi-path2/{arg}/{var1}/{var2}": { + "get": { + "operationId": "mod_file_with_multi_path_2", + "parameters": [ + { + "name": "arg", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "var1", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "var2", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/path/prefix/{var}": { + "get": { + "operationId": "prefix_variable", + "parameters": [ + { + "name": "var", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/struct-body": { + "post": { + "operationId": "mod_file_with_struct_body", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StructBody" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/struct-body-with-optional": { + "post": { + "operationId": "mod_file_with_struct_body_with_optional", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/struct-query": { + "get": { + "operationId": "mod_file_with_struct_query", + "parameters": [ + { + "name": "name", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "age", + "in": "query", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/test-struct": { + "get": { + "operationId": "mod_file_with_test_struct", + "parameters": [ + { + "name": "name", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "age", + "in": "query", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestStruct" + } + } + } + } + } + } + }, + "/third": { + "get": { + "operationId": "third_root_endpoint", + "description": "Third app root endpoint", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/third/hello": { + "get": { + "operationId": "third_hello_endpoint", + "tags": ["third"], + "description": "Third app hello endpoint", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/third/map-query": { + "get": { + "operationId": "third_map_query", + "tags": ["third"], + "parameters": [ + { + "name": "name", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "age", + "in": "query", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "optional_age", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "nullable": true + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/typed-form": { + "get": { + "operationId": "list_file_uploads", + "tags": ["typed-form"], + "description": "List all file uploads", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileUploadResponse" + } + } + } + } + } + } + }, + "post": { + "operationId": "create_file_upload", + "tags": ["typed-form"], + "description": "Create a new file upload with multipart form data", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CreateFileUploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileUploadResponse" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/typed-form/{id}": { + "put": { + "operationId": "update_file_upload", + "tags": ["typed-form"], + "description": "Update a file upload with multipart form data", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/UpdateFileUploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileUploadResponse" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "patch": { + "operationId": "patch_file_upload", + "tags": ["typed-form"], + "description": "Patch a file upload (partial update via schema_type! multipart)", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchFileUploadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileUploadResponse" + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/typed-header": { + "get": { + "operationId": "typed_header_jwt", + "parameters": [ + { + "name": "authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "post": { + "operationId": "typed_header", + "parameters": [ + { + "name": "user-agent", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "content-type", + "in": "header", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/users": { + "get": { + "operationId": "get_users", + "description": "Get all users (returns public response without internal_score)", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserPublicResponse" + } + } + } + } + } + } + }, + "post": { + "operationId": "create_user", + "description": "Create a new user\nRequest body uses CreateUserRequest (generated from User with only name, email)", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUserRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPublicResponse" + } + } + } + } + } + } + }, + "/users/dto/{id}": { + "get": { + "operationId": "get_user_dto", + "description": "Get user DTO (demonstrates field rename feature)\nThe Rust struct uses user_id/display_name, but JSON uses id/name", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserDTO" + } + } + } + } + } + } + }, + "/users/skip-response": { + "get": { + "operationId": "skip_response", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SkipResponse" + } + } + } + } + } + } + }, + "/users/summary": { + "get": { + "operationId": "get_users_summary", + "description": "Get user summaries (minimal fields for list views)", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserSummary" + } + } + } + } + } + } + } + }, + "/users/with-meta": { + "post": { + "operationId": "create_user_with_meta", + "description": "Create a new user with metadata (demonstrates `add` feature)\nRequest body uses CreateUserWithMeta (picks name/email, adds request_id/created_at)", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUserWithMeta" + } + } + } + }, + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUserWithMeta" + } + } + } + } + } + } + }, + "/users/{id}": { + "get": { + "operationId": "get_user", + "description": "Get user by ID (full internal view)", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AdjacentlyTaggedResponse": { + "description": "Adjacently tagged enum - serializes as `{\"type\": \"...\", \"data\": ...}`\nExample: `{\"type\": \"Success\", \"data\": {\"items\": [\"a\", \"b\"]}}`", + "oneOf": [ + { + "type": "object", + "description": "Successful response with items", + "properties": { + "data": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["items"] + }, + "type": { + "type": "string", + "enum": ["Success"] + } + }, + "required": ["type", "data"] + }, + { + "type": "object", + "description": "Error response with code and message", + "properties": { + "data": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": ["code", "message"] + }, + "type": { + "type": "string", + "enum": ["Error"] + } + }, + "required": ["type", "data"] + }, + { + "type": "object", + "description": "Empty response (unit variant)", + "properties": { + "type": { + "type": "string", + "enum": ["Empty"] + } + }, + "required": ["type"] + } + ], + "discriminator": { + "propertyName": "type" + } + }, + "AdvancedSearchRequest": { + "description": "Request combining multiple flattened structs", + "allOf": [ + { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Search query string" + } + }, + "required": ["query"] + }, + { + "$ref": "#/components/schemas/Pagination" + } + ] + }, + "ComplexStructBody": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "array": { + "type": "array", + "items": { + "type": "string" + } + }, + "map": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "nested_array": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nested_map": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nested_struct": { + "$ref": "#/components/schemas/StructBodyWithOptional" + }, + "nested_struct_array": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nested_struct_array_map": { + "type": "array", + "items": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + } + }, + "nested_struct_map": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nested_struct_map_array": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/StructBodyWithOptional" + }, + "type": "array" + } + } + }, + "required": [ + "name", + "age", + "nested_struct", + "array", + "map", + "nested_array", + "nested_map", + "nested_struct_array", + "nested_struct_map", + "nested_struct_array_map", + "nested_struct_map_array" + ] + }, + "ComplexStructBodyWithRename": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "array": { + "type": "array", + "items": { + "type": "string" + } + }, + "map": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "nestedArray": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nestedMap": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nestedStruct": { + "$ref": "#/components/schemas/StructBodyWithOptional" + }, + "nestedStructArray": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nestedStructArrayMap": { + "type": "array", + "items": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + } + }, + "nestedStructMap": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/StructBodyWithOptional" + } + }, + "nestedStructMapArray": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/StructBodyWithOptional" + }, + "type": "array" + } + } + }, + "required": [ + "name", + "age", + "nestedStruct", + "array", + "map", + "nestedArray", + "nestedMap", + "nestedStructArray", + "nestedStructMap", + "nestedStructArrayMap", + "nestedStructMapArray" + ] + }, + "ContactFormRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "message": { + "type": "string" + }, + "name": { + "type": "string" + }, + "subject": { + "type": "string", + "nullable": true + } + }, + "required": ["name", "email", "message"] + }, + "ContactFormResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "ticketId": { + "type": "string" + } + }, + "required": ["success", "ticketId"] + }, + "ContactResponse": { + "type": "object", + "properties": { + "adminReply": { + "type": "string", + "nullable": true + }, + "category": { + "type": "string", + "nullable": true + }, + "content": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "repliedAt": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string" + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "userId": { + "type": "integer" + } + }, + "required": ["id", "userId", "title", "content", "createdAt"] + }, + "CreateFileUploadRequest": { + "type": "object", + "properties": { + "document": { + "type": "string", + "format": "binary", + "nullable": true + }, + "name": { + "type": "string" + }, + "tags": { + "type": "string", + "nullable": true + }, + "thumbnail": { + "type": "string", + "format": "binary", + "nullable": true + } + }, + "required": ["name"] + }, + "CreateMemoRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": ["title", "content"] + }, + "CreateUserRequest": { + "type": "object", + "description": "Full user model with all fields", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": ["name", "email"] + }, + "CreateUserWithMeta": { + "type": "object", + "description": "Full user model with all fields", + "properties": { + "createdAt": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "requestId": { + "type": "string" + } + }, + "required": ["name", "email", "requestId"] + }, + "Enum": { + "type": "string", + "enum": ["A", "B", "C"] + }, + "Enum2": { + "description": "Enum2 Description", + "oneOf": [ + { + "type": "object", + "properties": { + "A": { + "type": "string" + } + }, + "required": ["A"] + }, + { + "type": "object", + "properties": { + "B": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["name", "age"] + } + }, + "required": ["B"] + }, + { + "type": "object", + "properties": { + "C": { + "type": "integer" + } + }, + "required": ["C"] + }, + { + "type": "object", + "properties": { + "D": { + "type": "boolean" + } + }, + "required": ["D"] + }, + { + "type": "object", + "properties": { + "E": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["E"] + }, + { + "type": "object", + "properties": { + "F": { + "type": "array", + "prefixItems": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["F"] + }, + { + "type": "object", + "properties": { + "G": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "type": "string" + } + } + }, + "required": ["G"] + }, + { + "type": "object", + "properties": { + "H": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "type": "string" + } + } + }, + "required": ["H"] + }, + { + "type": "object", + "properties": { + "I": { + "$ref": "#/components/schemas/TestStruct" + } + }, + "required": ["I"] + }, + { + "type": "object", + "properties": { + "J": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TestStruct" + } + } + }, + "required": ["J"] + }, + { + "type": "object", + "properties": { + "K": { + "type": "array", + "prefixItems": [ + { + "$ref": "#/components/schemas/TestStruct" + }, + { + "$ref": "#/components/schemas/TestStruct" + } + ], + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["K"] + }, + { + "type": "object", + "properties": { + "L": { + "type": "string", + "nullable": true + } + }, + "required": ["L"] + }, + { + "type": "object", + "properties": { + "M": { + "type": "array", + "items": { + "type": "string", + "nullable": true + } + } + }, + "required": ["M"] + }, + { + "type": "object", + "properties": { + "N": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "nullable": true, + "type": "string" + } + } + }, + "required": ["N"] + } + ] + }, + "ErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "error": { + "type": "string" + } + }, + "required": ["error", "code"] + }, + "ErrorResponse2": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "error": { + "type": "string" + } + }, + "required": ["error", "code"] + }, + "ExternallyTaggedEvent": { + "description": "Externally tagged enum (default) - serializes as `{\"VariantName\": ...}`\nExample: `{\"Created\": {\"id\": 1, \"name\": \"test\"}}`\nThis is included for comparison with the other representations.", + "oneOf": [ + { + "type": "object", + "description": "Item was created", + "properties": { + "Created": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name"] + } + }, + "required": ["Created"] + }, + { + "type": "object", + "description": "Item was updated", + "properties": { + "Updated": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + }, + "required": ["Updated"] + }, + { + "type": "object", + "description": "Item was deleted", + "properties": { + "Deleted": { + "type": "integer" + } + }, + "required": ["Deleted"] + } + ] + }, + "FileUploadResponse": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "documentUrl": { + "type": "string", + "nullable": true + }, + "id": { + "type": "integer" + }, + "isActive": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "thumbnailUrl": { + "type": "string", + "nullable": true + } + }, + "required": ["id", "name", "tags", "isActive", "createdAt"] + }, + "GenericStruct": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "object" + } + }, + "required": ["value", "name"] + }, + "GenericStruct2": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "object" + }, + "value2": { + "type": "object" + } + }, + "required": ["value", "name", "value2"] + }, + "InSkipResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"] + }, + "InternallyTaggedMessage": { + "description": "Internally tagged enum - serializes as `{\"type\": \"...\", ...fields...}`\nExample: `{\"type\": \"Request\", \"id\": 1, \"method\": \"GET\"}`", + "oneOf": [ + { + "type": "object", + "description": "A request message", + "properties": { + "id": { + "type": "integer" + }, + "method": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["Request"] + } + }, + "required": ["type", "id", "method"] + }, + { + "type": "object", + "description": "A response message", + "properties": { + "id": { + "type": "integer" + }, + "result": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "enum": ["Response"] + } + }, + "required": ["type", "id"] + }, + { + "type": "object", + "description": "A notification (no payload)", + "properties": { + "type": { + "type": "string", + "enum": ["Notification"] + } + }, + "required": ["type"] + } + ], + "discriminator": { + "propertyName": "type" + } + }, + "MapQuery": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "optional_age": { + "type": "integer", + "nullable": true + } + }, + "required": ["name", "age"] + }, + "MemoCommentSchema": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer" + }, + "memo": { + "$ref": "#/components/schemas/MemoSchema" + }, + "memoId": { + "type": "integer" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "user": { + "$ref": "#/components/schemas/UserSchema" + }, + "userId": { + "type": "integer" + } + }, + "required": [ + "id", + "userId", + "memoId", + "content", + "createdAt", + "updatedAt", + "user", + "memo" + ] + }, + "MemoResponse": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer" + }, + "status": { + "$ref": "#/components/schemas/MemoStatus" + }, + "title": { + "type": "string" + }, + "userId": { + "type": "integer" + } + }, + "required": ["id", "userId", "title", "content", "status", "createdAt"] + }, + "MemoResponseComments": { + "type": "object", + "properties": { + "memoComments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemoResponseComments_MemoComments" + } + } + }, + "required": ["memoComments"] + }, + "MemoResponseComments_MemoComments": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer" + }, + "memoId": { + "type": "integer" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "userId": { + "type": "integer" + } + }, + "required": [ + "id", + "userId", + "memoId", + "content", + "createdAt", + "updatedAt" + ] + }, + "MemoResponseRel": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer" + }, + "status": { + "$ref": "#/components/schemas/MemoStatus" + }, + "title": { + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/UserSchema" + }, + "userId": { + "type": "integer" + } + }, + "required": [ + "id", + "userId", + "title", + "content", + "status", + "createdAt", + "user" + ] + }, + "MemoSchema": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer" + }, + "status": { + "$ref": "#/components/schemas/MemoStatus" + }, + "title": { + "type": "string" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "user": { + "$ref": "#/components/schemas/UserSchema" + }, + "userId": { + "type": "integer" + } + }, + "required": [ + "id", + "userId", + "title", + "content", + "status", + "createdAt", + "updatedAt", + "user" + ] + }, + "MemoSnakeCase": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer" + }, + "user_id": { + "type": "integer" + } + }, + "required": ["id", "user_id", "created_at"] + }, + "MemoStatus": { + "type": "string", + "enum": ["draft", "published", "archived"] + }, + "PaginatedResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object" + } + }, + "page": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "totalPage": { + "type": "integer" + } + }, + "required": ["items", "page", "size", "totalPage"] + }, + "Pagination": { + "type": "object", + "description": "Common pagination parameters that can be reused across requests", + "properties": { + "page": { + "type": "integer", + "description": "Page number (1-indexed)", + "default": 1 + }, + "per_page": { + "type": "integer", + "description": "Items per page", + "default": 20 + } + } + }, + "PatchFileUploadRequest": { + "type": "object", + "properties": { + "isActive": { + "type": "boolean", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "tags": { + "type": "string", + "nullable": true + }, + "thumbnail": { + "type": "string", + "format": "binary", + "nullable": true + } + } + }, + "ResponseMeta": { + "type": "object", + "description": "Common metadata for responses", + "properties": { + "has_more": { + "type": "boolean", + "description": "Whether there are more pages" + }, + "total": { + "type": "integer", + "description": "Total number of items" + } + }, + "required": ["total", "has_more"] + }, + "SearchResponse": { + "description": "Response combining multiple flattened structs", + "allOf": [ + { + "type": "object", + "properties": { + "found": { + "type": "boolean", + "description": "Whether any results were found" + }, + "results": { + "type": "array", + "description": "Search results", + "items": { + "type": "string" + } + } + }, + "required": ["results", "found"] + }, + { + "$ref": "#/components/schemas/ResponseMeta" + } + ] + }, + "SignupRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": ["email", "password"] + }, + "SignupResponse": { + "type": "object", + "properties": { + "birthday": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "email": { + "type": "string" + }, + "gender": { + "type": "string", + "nullable": true + }, + "id": { + "type": "integer" + }, + "job": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string" + }, + "nickname": { + "type": "string", + "nullable": true + }, + "phoneNumber23": { + "type": "string" + } + }, + "required": ["id", "email", "name", "phoneNumber23", "createdAt"] + }, + "SkipResponse": { + "type": "object", + "properties": { + "email2": { + "type": "string", + "nullable": true + }, + "email4": { + "type": "string", + "nullable": true + }, + "email5": { + "type": "string", + "default": "" + }, + "email6": { + "type": "string", + "default": "default42" + }, + "in_skip": { + "$ref": "#/components/schemas/InSkipResponse" + }, + "in_skip2": { + "$ref": "#/components/schemas/InSkipResponse", + "nullable": true + }, + "in_skip3": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InSkipResponse" + } + }, + "in_skip4": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InSkipResponse" + }, + "nullable": true + }, + "in_skip5": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/InSkipResponse" + }, + "nullable": true + }, + "in_skip6": { + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": { + "$ref": "#/components/schemas/InSkipResponse" + }, + "nullable": true + }, + "name": { + "type": "string" + }, + "num": { + "type": "integer", + "default": 0 + } + }, + "required": ["name", "in_skip", "in_skip3"] + }, + "StructBody": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["name", "age"] + }, + "StructBodyWithOptional": { + "type": "object", + "properties": { + "age": { + "type": "integer", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + } + } + }, + "StructQuery": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["name", "age"] + }, + "SubscribeRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": ["name", "email"] + }, + "SubscribeResponse": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isSubscribed": { + "type": "boolean" + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name", "email", "isSubscribed"] + }, + "TestStruct": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["name", "age"] + }, + "ThirdMapQuery": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "optional_age": { + "type": "integer", + "nullable": true + } + }, + "required": ["name", "age"] + }, + "UntaggedValue": { + "description": "Untagged enum - serializes as just the variant data, no tag\nThe deserializer tries each variant in order until one matches.\nExample: `\"hello\"` or `42` or `{\"key\": \"value\"}`", + "oneOf": [ + { + "type": "string", + "description": "A string value" + }, + { + "type": "integer", + "description": "A numeric value" + }, + { + "type": "boolean", + "description": "A boolean value" + }, + { + "type": "object", + "description": "An object with key-value pairs", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": ["key", "value"] + } + ] + }, + "UpdateFileUploadRequest": { + "type": "object", + "properties": { + "document": { + "type": "string", + "format": "binary", + "nullable": true + }, + "is_active": { + "type": "boolean", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "tags": { + "type": "string", + "nullable": true + }, + "thumbnail": { + "type": "string", + "format": "binary", + "nullable": true + } + } + }, + "UpdateMemoRequest": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "title": { + "type": "string" + } + }, + "required": ["title", "content", "id"] + }, + "User": { + "type": "object", + "description": "Full user model with all fields", + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "internal_score": { + "type": "integer", + "description": "Internal field - should be omitted in public APIs", + "nullable": true + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name", "email"] + }, + "UserDTO": { + "type": "object", + "description": "Full user model with all fields", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name"] + }, + "UserItem": { + "type": "object", + "description": "Simple user representation", + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name", "email"] + }, + "UserListRequest": { + "description": "Request with flattened pagination parameters\n\nThe pagination fields (page, per_page) are merged into this struct's JSON representation.", + "allOf": [ + { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter users by name (optional)", + "nullable": true + }, + "sort": { + "type": "string", + "description": "Sort order: \"asc\" or \"desc\"" + } + } + }, + { + "$ref": "#/components/schemas/Pagination" + } + ] + }, + "UserListResponse": { + "description": "Paginated response with flattened metadata\n\nThe response meta fields (total, has_more) are merged into this struct's JSON representation.", + "allOf": [ + { + "type": "object", + "properties": { + "data": { + "type": "array", + "description": "List of users", + "items": { + "$ref": "#/components/schemas/UserItem" + } + } + }, + "required": ["data"] + }, + { + "$ref": "#/components/schemas/ResponseMeta" + } + ] + }, + "UserPublicResponse": { + "type": "object", + "description": "Full user model with all fields", + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name", "email"] + }, + "UserSchema": { + "type": "object", + "description": "User model", + "properties": { + "createdAt": { + "type": "string", + "format": "date-time", + "description": "Created at" + }, + "email": { + "type": "string", + "description": "User email" + }, + "id": { + "type": "integer", + "description": "User ID" + }, + "name": { + "type": "string", + "description": "User name" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "Updated at" + } + }, + "required": ["id", "email", "name", "createdAt", "updatedAt"] + }, + "UserSummary": { + "type": "object", + "description": "Full user model with all fields", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": ["id", "name"] + } + } + }, + "tags": [ + { + "name": "flatten" + }, + { + "name": "form" + }, + { + "name": "hello" + }, + { + "name": "typed-form" + }, + { + "name": "third" + } + ] +} From ab2a348b172061631f7269dc2b9cdf170a2bc794 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 00:54:49 +0900 Subject: [PATCH 03/15] feat(generator): support all request body content types and binary format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add CONTENT_TYPE_PRIORITY constant with json, urlencoded, multipart - Add getRequestBodyContent() helper to find schema across content types - Update extractRequestBody() to use helper in both and inline branches - Add format: binary → File | Blob type mapping in getTypeFromSchema() - Add 12 new test cases covering form, multipart, and binary content - All 108 tests pass with 7 new snapshots --- .../generate-schema.test.ts.snap | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/packages/generator/src/__tests__/__snapshots__/generate-schema.test.ts.snap b/packages/generator/src/__tests__/__snapshots__/generate-schema.test.ts.snap index 2cc5d04..d67a4c2 100644 --- a/packages/generator/src/__tests__/__snapshots__/generate-schema.test.ts.snap +++ b/packages/generator/src/__tests__/__snapshots__/generate-schema.test.ts.snap @@ -1052,3 +1052,74 @@ exports[`extractRequestBody handles requestBody $ref 1`] = ` }, } `; + +exports[`extractRequestBody handles requestBody with application/x-www-form-urlencoded 1`] = ` +{ + "email?": { + "default": undefined, + "type": "string", + }, + "name?": { + "default": undefined, + "type": "string", + }, +} +`; + +exports[`extractRequestBody handles requestBody $ref with application/x-www-form-urlencoded 1`] = ` +{ + "email?": { + "default": undefined, + "type": "string", + }, + "name?": { + "default": undefined, + "type": "string", + }, +} +`; + +exports[`extractRequestBody handles requestBody with multipart/form-data 1`] = ` +{ + "document?": { + "default": undefined, + "type": "File | Blob", + }, + "name?": { + "default": undefined, + "type": "string", + }, +} +`; + +exports[`extractRequestBody handles requestBody $ref with multipart/form-data 1`] = ` +{ + "file?": { + "default": undefined, + "type": "File | Blob", + }, +} +`; + +exports[`extractRequestBody prefers application/json over other content types 1`] = ` +{ + "name?": { + "default": undefined, + "type": "string", + }, +} +`; + +exports[`getTypeFromSchema handles string with format binary 1`] = ` +{ + "default": undefined, + "type": "File | Blob", +} +`; + +exports[`getTypeFromSchema handles nullable string with format binary 1`] = ` +{ + "default": undefined, + "type": "File | Blob | null", +} +`; From 1b4a94c753e376b21b8bf95cfc81af83fc919955 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 00:55:23 +0900 Subject: [PATCH 04/15] feat(generator): add Zod support for all content types and binary format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update getRequestBodySchemaName() to iterate all 3 content types - Update schema collection to check form and multipart content - Add format: binary → z.instanceof(File) in runtime schemas - Add format: binary → z.ZodType in type declarations - Update existing multipart test to expect schema processing - Add 5 new test cases for binary and multi-content-type support - All 126 tests pass with 100% coverage --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6a4f18e..8342c4a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json .DS_Store .claude +.sisyphus From 54948abee01250065ce0e08f63956c1d19615629 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 01:00:48 +0900 Subject: [PATCH 05/15] feat(generator): add interface generation for all content types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Import getRequestBodyContent helper from generate-schema - Update schema collection pass to use helper for all content types - Update body extraction pass to use helper for all content types - Add raw multipart handling: empty object schema → FormData | Record - Add 6 new test cases for form/multipart endpoints - All 52 tests pass with 54 snapshots --- .../generate-interface.test.ts.snap | 279 ++++++++++++ .../src/__tests__/generate-interface.test.ts | 401 ++++++++++++++++++ packages/generator/src/generate-interface.ts | 46 +- 3 files changed, 713 insertions(+), 13 deletions(-) diff --git a/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap b/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap index 9a2d1af..ad8bfdd 100644 --- a/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap +++ b/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap @@ -1752,3 +1752,282 @@ declare module "@devup-api/fetch" { } }" `; + +exports[`generateInterface handles urlencoded request body with $ref 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupPostApiStruct { + 'openapi.json': { + '/form': { + body: DevupObject<'request', 'openapi.json'>['SubscribeRequest']; + response?: { + ok?: boolean; + }; + }; + subscribe: { + body: DevupObject<'request', 'openapi.json'>['SubscribeRequest']; + response?: { + ok?: boolean; + }; + }; + } + } + + interface DevupRequestComponentStruct { + 'openapi.json': { + SubscribeRequest: { + name?: string; + email?: string; + }; + } + } + + interface DevupResponseComponentStruct {} + + interface DevupErrorComponentStruct {} +}" +`; + +exports[`generateInterface handles urlencoded request body with inline schema 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupPostApiStruct { + 'openapi.json': { + '/form/contact': { + body?: { + name?: string; + email?: string; + message?: string; + }; + response?: {}; + }; + contact: { + body?: { + name?: string; + email?: string; + message?: string; + }; + response?: {}; + }; + } + } + + interface DevupRequestComponentStruct {} + + interface DevupResponseComponentStruct {} + + interface DevupErrorComponentStruct {} +}" +`; + +exports[`generateInterface handles typed multipart request body with $ref 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupPostApiStruct { + 'openapi.json': { + '/typed-form': { + body: DevupObject<'request', 'openapi.json'>['CreateFileUploadRequest']; + response?: { + id?: string; + }; + }; + createFileUpload: { + body: DevupObject<'request', 'openapi.json'>['CreateFileUploadRequest']; + response?: { + id?: string; + }; + }; + } + } + + interface DevupRequestComponentStruct { + 'openapi.json': { + CreateFileUploadRequest: { + name: string; + document?: File | Blob; + tags?: Array; + thumbnail?: File | Blob; + }; + } + } + + interface DevupResponseComponentStruct {} + + interface DevupErrorComponentStruct {} +}" +`; + +exports[`generateInterface handles raw multipart with empty object schema 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupPostApiStruct { + 'openapi.json': { + '/form/upload': { + body: FormData | Record; + response?: { + url?: string; + }; + }; + upload: { + body: FormData | Record; + response?: { + url?: string; + }; + }; + } + } + + interface DevupRequestComponentStruct {} + + interface DevupResponseComponentStruct {} + + interface DevupErrorComponentStruct {} +}" +`; + +exports[`generateInterface handles multipart PUT and PATCH endpoints 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupPutApiStruct { + 'openapi.json': { + '/typed-form/{id}': { + params: { + id: string; + }; + body: DevupObject<'request', 'openapi.json'>['UpdateFileUploadRequest']; + response?: {}; + }; + updateFileUpload: { + params: { + id: string; + }; + body: DevupObject<'request', 'openapi.json'>['UpdateFileUploadRequest']; + response?: {}; + }; + } + } + + interface DevupPatchApiStruct { + 'openapi.json': { + '/typed-form/{id}': { + params: { + id: string; + }; + body: DevupObject<'request', 'openapi.json'>['PatchFileUploadRequest']; + response?: {}; + }; + patchFileUpload: { + params: { + id: string; + }; + body: DevupObject<'request', 'openapi.json'>['PatchFileUploadRequest']; + response?: {}; + }; + } + } + + interface DevupRequestComponentStruct { + 'openapi.json': { + UpdateFileUploadRequest: { + name: string; + document?: File | Blob; + }; + PatchFileUploadRequest: { + name?: string; + document?: File | Blob; + }; + } + } + + interface DevupResponseComponentStruct {} + + interface DevupErrorComponentStruct {} +}" +`; + +exports[`generateInterface preserves JSON endpoints alongside form/multipart endpoints 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupPostApiStruct { + 'openapi.json': { + '/users': { + body: DevupObject<'request', 'openapi.json'>['CreateUserRequest']; + response?: {}; + }; + createUser: { + body: DevupObject<'request', 'openapi.json'>['CreateUserRequest']; + response?: {}; + }; + '/form': { + body: DevupObject<'request', 'openapi.json'>['SubscribeRequest']; + response?: {}; + }; + subscribe: { + body: DevupObject<'request', 'openapi.json'>['SubscribeRequest']; + response?: {}; + }; + '/form/upload': { + body: FormData | Record; + response?: {}; + }; + upload: { + body: FormData | Record; + response?: {}; + }; + } + } + + interface DevupRequestComponentStruct { + 'openapi.json': { + CreateUserRequest: { + name?: string; + email?: string; + }; + SubscribeRequest: { + name?: string; + email?: string; + }; + } + } + + interface DevupResponseComponentStruct {} + + interface DevupErrorComponentStruct {} +}" +`; diff --git a/packages/generator/src/__tests__/generate-interface.test.ts b/packages/generator/src/__tests__/generate-interface.test.ts index bf5782b..f016cef 100644 --- a/packages/generator/src/__tests__/generate-interface.test.ts +++ b/packages/generator/src/__tests__/generate-interface.test.ts @@ -2054,3 +2054,404 @@ test('generateInterface handles error schema with inline enum', () => { expect(result).toContain('type ErrorResponseCode =') expect(result).toContain('"INVALID_INPUT"') }) + +// Test urlencoded request body with $ref +test('generateInterface handles urlencoded request body with $ref', () => { + const schema = { + paths: { + '/form': { + post: { + operationId: 'subscribe', + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + $ref: '#/components/schemas/SubscribeRequest', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { ok: { type: 'boolean' } }, + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + SubscribeRequest: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // Should use DevupObject reference for the urlencoded request body + expect(result).toContain( + "DevupObject<'request', 'openapi.json'>['SubscribeRequest']", + ) + // Should generate the request component + expect(result).toContain('SubscribeRequest') +}) + +// Test urlencoded request body with inline schema +test('generateInterface handles urlencoded request body with inline schema', () => { + const schema = { + paths: { + '/form/contact': { + post: { + operationId: 'contact', + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + message: { type: 'string' }, + }, + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { type: 'object', properties: {} }, + }, + }, + }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // Should generate body type with string properties + expect(result).toContain('name') + expect(result).toContain('email') + expect(result).toContain('message') +}) + +// Test typed multipart request body with $ref (includes binary fields) +test('generateInterface handles typed multipart request body with $ref', () => { + const schema = { + paths: { + '/typed-form': { + post: { + operationId: 'createFileUpload', + requestBody: { + content: { + 'multipart/form-data': { + schema: { + $ref: '#/components/schemas/CreateFileUploadRequest', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { id: { type: 'string' } }, + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + CreateFileUploadRequest: { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string' }, + document: { type: 'string', format: 'binary' }, + tags: { type: 'array', items: { type: 'string' } }, + thumbnail: { type: 'string', format: 'binary' }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // Should use DevupObject reference for the multipart request body + expect(result).toContain( + "DevupObject<'request', 'openapi.json'>['CreateFileUploadRequest']", + ) + // Should generate request component with binary fields as File | Blob + expect(result).toContain('File | Blob') +}) + +// Test raw multipart (empty object schema) +test('generateInterface handles raw multipart with empty object schema', () => { + const schema = { + paths: { + '/form/upload': { + post: { + operationId: 'upload', + requestBody: { + content: { + 'multipart/form-data': { + schema: { + type: 'object', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { url: { type: 'string' } }, + }, + }, + }, + }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // Raw multipart should use FormData | Record + expect(result).toContain('FormData | Record') +}) + +// Test typed multipart PUT and PATCH endpoints +test('generateInterface handles multipart PUT and PATCH endpoints', () => { + const schema = { + paths: { + '/typed-form/{id}': { + put: { + operationId: 'updateFileUpload', + parameters: [ + { + name: 'id', + in: 'path' as const, + required: true, + schema: { type: 'string' as const }, + }, + ], + requestBody: { + content: { + 'multipart/form-data': { + schema: { + $ref: '#/components/schemas/UpdateFileUploadRequest', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { type: 'object', properties: {} }, + }, + }, + }, + }, + }, + patch: { + operationId: 'patchFileUpload', + parameters: [ + { + name: 'id', + in: 'path' as const, + required: true, + schema: { type: 'string' as const }, + }, + ], + requestBody: { + content: { + 'multipart/form-data': { + schema: { + $ref: '#/components/schemas/PatchFileUploadRequest', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { type: 'object', properties: {} }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + UpdateFileUploadRequest: { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string' }, + document: { type: 'string', format: 'binary' }, + }, + }, + PatchFileUploadRequest: { + type: 'object', + properties: { + name: { type: 'string' }, + document: { type: 'string', format: 'binary' }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // Should use DevupObject references for both multipart request bodies + expect(result).toContain( + "DevupObject<'request', 'openapi.json'>['UpdateFileUploadRequest']", + ) + expect(result).toContain( + "DevupObject<'request', 'openapi.json'>['PatchFileUploadRequest']", + ) +}) + +// Test that JSON endpoints are unchanged when form/multipart endpoints exist alongside +test('generateInterface preserves JSON endpoints alongside form/multipart endpoints', () => { + const schema = { + paths: { + '/users': { + post: { + operationId: 'createUser', + requestBody: { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/CreateUserRequest', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { type: 'object', properties: {} }, + }, + }, + }, + }, + }, + }, + '/form': { + post: { + operationId: 'subscribe', + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + $ref: '#/components/schemas/SubscribeRequest', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { type: 'object', properties: {} }, + }, + }, + }, + }, + }, + }, + '/form/upload': { + post: { + operationId: 'upload', + requestBody: { + content: { + 'multipart/form-data': { + schema: { + type: 'object', + }, + }, + }, + }, + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { type: 'object', properties: {} }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + CreateUserRequest: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + }, + SubscribeRequest: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // JSON endpoint should use DevupObject reference + expect(result).toContain( + "DevupObject<'request', 'openapi.json'>['CreateUserRequest']", + ) + // Urlencoded endpoint should use DevupObject reference + expect(result).toContain( + "DevupObject<'request', 'openapi.json'>['SubscribeRequest']", + ) + // Raw multipart should use FormData | Record + expect(result).toContain('FormData | Record') +}) diff --git a/packages/generator/src/generate-interface.ts b/packages/generator/src/generate-interface.ts index df7383e..321705e 100644 --- a/packages/generator/src/generate-interface.ts +++ b/packages/generator/src/generate-interface.ts @@ -8,6 +8,7 @@ import { extractParameters, extractRequestBody, formatTypeValue, + getRequestBodyContent, getTypeFromSchema, } from './generate-schema' import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard' @@ -170,9 +171,9 @@ function generateSchemaInterface( } } else { const content = operation.requestBody.content - const jsonContent = content?.['application/json'] - if (jsonContent && 'schema' in jsonContent && jsonContent.schema) { - collectSchemaNames(jsonContent.schema, requestSchemaNames) + const bodyContent = getRequestBodyContent(content) + if (bodyContent && 'schema' in bodyContent && bodyContent.schema) { + collectSchemaNames(bodyContent.schema, requestSchemaNames) } } } @@ -270,12 +271,12 @@ function generateSchemaInterface( } } else { const content = operation.requestBody.content - const jsonContent = content?.['application/json'] - if (jsonContent && 'schema' in jsonContent && jsonContent.schema) { + const bodyContent = getRequestBodyContent(content) + if (bodyContent && 'schema' in bodyContent && bodyContent.schema) { // Check if schema is a direct reference to components.schemas - if ('$ref' in jsonContent.schema) { + if ('$ref' in bodyContent.schema) { const schemaName = extractSchemaNameFromRef( - jsonContent.schema.$ref, + bodyContent.schema.$ref, ) // Check if schema exists in components.schemas and is used in request body if ( @@ -295,12 +296,31 @@ function generateSchemaInterface( } } } else { - const requestBody = extractRequestBody( - operation.requestBody, - schema, - ) - if (requestBody !== undefined) { - requestBodyType = requestBody + // Check for raw multipart: multipart/form-data with empty/generic object schema + const schemaObj = bodyContent.schema as OpenAPIV3_1.SchemaObject + const isMultipart = + content?.['multipart/form-data'] !== undefined && + !content?.['application/json'] && + !content?.['application/x-www-form-urlencoded'] + const isEmptyObject = + schemaObj.type === 'object' && + (!schemaObj.properties || + Object.keys(schemaObj.properties).length === 0) && + !schemaObj.allOf && + !schemaObj.anyOf && + !schemaObj.oneOf + + if (isMultipart && isEmptyObject) { + // Raw multipart with no typed schema + requestBodyType = 'FormData | Record' + } else { + const requestBody = extractRequestBody( + operation.requestBody, + schema, + ) + if (requestBody !== undefined) { + requestBodyType = requestBody + } } } } From bbaf29abb200893b4eb9b6a6d4ae2840c6b12a46 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 01:08:01 +0900 Subject: [PATCH 06/15] feat(generator): emit bodyType in URL map for non-JSON endpoints --- .../src/__tests__/create-url-map.test.ts | 367 +++++++++++++++++- .../generator/src/__tests__/index.test.ts | 1 + packages/generator/src/create-url-map.ts | 54 ++- 3 files changed, 408 insertions(+), 14 deletions(-) diff --git a/packages/generator/src/__tests__/create-url-map.test.ts b/packages/generator/src/__tests__/create-url-map.test.ts index 9abf887..696ff9d 100644 --- a/packages/generator/src/__tests__/create-url-map.test.ts +++ b/packages/generator/src/__tests__/create-url-map.test.ts @@ -1,8 +1,8 @@ /** biome-ignore-all lint/style/noNonNullAssertion: test code */ -import { expect, test } from 'bun:test' +import { describe, expect, test } from 'bun:test' import type { UrlMapValue } from '@devup-api/core' import type { OpenAPIV3_1 } from 'openapi-types' -import { createUrlMap } from '../create-url-map' +import { createUrlMap, getBodyType } from '../create-url-map' test.each([ [ @@ -334,3 +334,366 @@ test.each([ expect(result['']).toHaveProperty(expectedKey) }) + +describe('bodyType emission', () => { + test('emits bodyType: form for application/x-www-form-urlencoded endpoint', () => { + const schema: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test API', version: '1.0.0' }, + paths: { + '/form': { + post: { + operationId: 'subscribe', + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + $ref: '#/components/schemas/SubscribeRequest', + }, + }, + }, + }, + responses: {}, + }, + }, + }, + } + + const result = createUrlMap({ '': schema }) + + expect(result['']!.subscribe).toEqual({ + method: 'POST', + url: '/form', + bodyType: 'form', + }) + expect(result['']!['/form']).toEqual({ + method: 'POST', + url: '/form', + bodyType: 'form', + }) + }) + + test('emits bodyType: multipart for multipart/form-data endpoint', () => { + const schema: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test API', version: '1.0.0' }, + paths: { + '/upload': { + post: { + operationId: 'upload_file', + requestBody: { + content: { + 'multipart/form-data': { + schema: { + type: 'object', + }, + }, + }, + }, + responses: {}, + }, + }, + }, + } + + const result = createUrlMap({ '': schema }) + + expect(result['']!.uploadFile).toEqual({ + method: 'POST', + url: '/upload', + bodyType: 'multipart', + }) + expect(result['']!['/upload']).toEqual({ + method: 'POST', + url: '/upload', + bodyType: 'multipart', + }) + }) + + test('does NOT emit bodyType for application/json endpoint', () => { + const schema: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test API', version: '1.0.0' }, + paths: { + '/users': { + post: { + operationId: 'create_user', + requestBody: { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/CreateUserRequest', + }, + }, + }, + }, + responses: {}, + }, + }, + }, + } + + const result = createUrlMap({ '': schema }) + + expect(result['']!.createUser).toEqual({ + method: 'POST', + url: '/users', + }) + expect(result['']!.createUser).not.toHaveProperty('bodyType') + expect(result['']!['/users']).not.toHaveProperty('bodyType') + }) + + test('does NOT emit bodyType for GET endpoint without requestBody', () => { + const schema: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test API', version: '1.0.0' }, + paths: { + '/users': { + get: { + operationId: 'get_users', + responses: {}, + }, + }, + }, + } + + const result = createUrlMap({ '': schema }) + + expect(result['']!.getUsers).toEqual({ + method: 'GET', + url: '/users', + }) + expect(result['']!.getUsers).not.toHaveProperty('bodyType') + }) + + test('resolves $ref in requestBody to detect content type', () => { + const schema: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test API', version: '1.0.0' }, + paths: { + '/form': { + post: { + operationId: 'submit_form', + requestBody: { + $ref: '#/components/requestBodies/FormBody', + }, + responses: {}, + }, + }, + }, + components: { + requestBodies: { + FormBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { + type: 'object', + properties: { + name: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }, + } + + const result = createUrlMap({ '': schema }) + + expect(result['']!.submitForm).toEqual({ + method: 'POST', + url: '/form', + bodyType: 'form', + }) + }) + + test('resolves $ref in requestBody for multipart content type', () => { + const schema: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test API', version: '1.0.0' }, + paths: { + '/upload': { + post: { + operationId: 'upload_file', + requestBody: { + $ref: '#/components/requestBodies/UploadBody', + }, + responses: {}, + }, + }, + }, + components: { + requestBodies: { + UploadBody: { + content: { + 'multipart/form-data': { + schema: { + type: 'object', + }, + }, + }, + }, + }, + }, + } + + const result = createUrlMap({ '': schema }) + + expect(result['']!.uploadFile).toEqual({ + method: 'POST', + url: '/upload', + bodyType: 'multipart', + }) + }) + + test('handles mixed endpoints with different body types', () => { + const schema: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test API', version: '1.0.0' }, + paths: { + '/users': { + post: { + operationId: 'create_user', + requestBody: { + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/User' }, + }, + }, + }, + responses: {}, + }, + }, + '/form': { + post: { + operationId: 'subscribe', + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { $ref: '#/components/schemas/SubscribeRequest' }, + }, + }, + }, + responses: {}, + }, + }, + '/upload': { + post: { + operationId: 'upload_file', + requestBody: { + content: { + 'multipart/form-data': { + schema: { type: 'object' }, + }, + }, + }, + responses: {}, + }, + }, + }, + } + + const result = createUrlMap({ '': schema }) + + // JSON endpoint: no bodyType + expect(result['']!.createUser).not.toHaveProperty('bodyType') + // Form endpoint: bodyType = 'form' + expect(result['']!.subscribe!.bodyType).toBe('form') + // Multipart endpoint: bodyType = 'multipart' + expect(result['']!.uploadFile!.bodyType).toBe('multipart') + }) +}) + +describe('getBodyType', () => { + const emptyDoc: OpenAPIV3_1.Document = { + openapi: '3.1.0', + info: { title: 'Test', version: '1.0.0' }, + paths: {}, + } + + test('returns undefined when no requestBody', () => { + expect(getBodyType({ responses: {} }, emptyDoc)).toBeUndefined() + }) + + test('returns undefined for JSON content', () => { + expect( + getBodyType( + { + requestBody: { + content: { + 'application/json': { schema: { type: 'object' } }, + }, + }, + responses: {}, + }, + emptyDoc, + ), + ).toBeUndefined() + }) + + test('returns form for urlencoded content', () => { + expect( + getBodyType( + { + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { type: 'object' }, + }, + }, + }, + responses: {}, + }, + emptyDoc, + ), + ).toBe('form') + }) + + test('returns multipart for multipart/form-data content', () => { + expect( + getBodyType( + { + requestBody: { + content: { + 'multipart/form-data': { schema: { type: 'object' } }, + }, + }, + responses: {}, + }, + emptyDoc, + ), + ).toBe('multipart') + }) + + test('returns undefined when requestBody has no content', () => { + expect( + getBodyType( + { + requestBody: { content: {} }, + responses: {}, + }, + emptyDoc, + ), + ).toBeUndefined() + }) + + test('prefers urlencoded over multipart when both present', () => { + expect( + getBodyType( + { + requestBody: { + content: { + 'application/x-www-form-urlencoded': { + schema: { type: 'object' }, + }, + 'multipart/form-data': { schema: { type: 'object' } }, + }, + }, + responses: {}, + }, + emptyDoc, + ), + ).toBe('form') + }) +}) diff --git a/packages/generator/src/__tests__/index.test.ts b/packages/generator/src/__tests__/index.test.ts index 20f6fb5..6223950 100644 --- a/packages/generator/src/__tests__/index.test.ts +++ b/packages/generator/src/__tests__/index.test.ts @@ -5,6 +5,7 @@ test('index.ts exports', () => { expect({ ...indexModule }).toEqual({ // URL map createUrlMap: expect.any(Function), + getBodyType: expect.any(Function), // Interface generation generateInterface: expect.any(Function), // Zod generation diff --git a/packages/generator/src/create-url-map.ts b/packages/generator/src/create-url-map.ts index 9ee967a..84fbdd2 100644 --- a/packages/generator/src/create-url-map.ts +++ b/packages/generator/src/create-url-map.ts @@ -2,6 +2,41 @@ import type { DevupApiTypeGeneratorOptions, UrlMapValue } from '@devup-api/core' import type { OpenAPIV3_1 } from 'openapi-types' import { convertCase } from './convert-case' +function resolveRequestBodyRef( + ref: string, + document: OpenAPIV3_1.Document, +): OpenAPIV3_1.RequestBodyObject | undefined { + // Expected format: #/components/requestBodies/Name + const parts = ref.replace('#/', '').split('/') + let current: unknown = document + for (const part of parts) { + if (current == null || typeof current !== 'object') return undefined + current = (current as Record)[part] + } + return current as OpenAPIV3_1.RequestBodyObject | undefined +} + +export function getBodyType( + operation: OpenAPIV3_1.OperationObject, + document: OpenAPIV3_1.Document, +): 'form' | 'multipart' | undefined { + const requestBody = operation.requestBody + if (!requestBody) return undefined + + let content: OpenAPIV3_1.RequestBodyObject['content'] | undefined + if ('$ref' in requestBody) { + const resolved = resolveRequestBodyRef(requestBody.$ref, document) + content = resolved?.content + } else { + content = requestBody.content + } + + if (!content) return undefined + if (content['application/x-www-form-urlencoded']) return 'form' + if (content['multipart/form-data']) return 'multipart' + return undefined +} + export function createUrlMap( schemas: Record, options?: DevupApiTypeGeneratorOptions, @@ -20,18 +55,8 @@ export function createUrlMap( // Convert param name based on case type return `{${convertCase(param, convertCaseType)}}` }) - if (operation.operationId) { - urlMap[convertCase(operation.operationId, convertCaseType)] = { - method: method.toUpperCase() as - | 'GET' - | 'POST' - | 'PUT' - | 'DELETE' - | 'PATCH', - url: normalizedPath, - } - } - urlMap[normalizedPath] = { + const bodyType = getBodyType(operation, schema) + const value: UrlMapValue = { method: method.toUpperCase() as | 'GET' | 'POST' @@ -39,7 +64,12 @@ export function createUrlMap( | 'DELETE' | 'PATCH', url: normalizedPath, + ...(bodyType && { bodyType }), + } + if (operation.operationId) { + urlMap[convertCase(operation.operationId, convertCaseType)] = value } + urlMap[normalizedPath] = value } } urlMaps[serverName] = urlMap From eb053a9b2f634867c371d23b89ff263b0fc88ee7 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 01:19:01 +0900 Subject: [PATCH 07/15] feat(fetch): auto-serialize request bodies based on content type --- .../fetch/src/__tests__/api-body-type.test.ts | 222 ++++++++++++++++++ packages/fetch/src/__tests__/utils.test.ts | 124 +++++++++- packages/fetch/src/api.ts | 32 ++- packages/fetch/src/utils.ts | 30 +++ 4 files changed, 401 insertions(+), 7 deletions(-) create mode 100644 packages/fetch/src/__tests__/api-body-type.test.ts diff --git a/packages/fetch/src/__tests__/api-body-type.test.ts b/packages/fetch/src/__tests__/api-body-type.test.ts new file mode 100644 index 0000000..494b9e5 --- /dev/null +++ b/packages/fetch/src/__tests__/api-body-type.test.ts @@ -0,0 +1,222 @@ +/** biome-ignore-all lint/suspicious/noExplicitAny: any is used to allow for flexibility in the type */ +import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test' +import type { UrlMapValue } from '@devup-api/core' + +// Mock the url-map module to return custom bodyType values +const mockUrlMap: Record> = { + 'openapi.json': { + submitForm: { method: 'POST', url: '/submit', bodyType: 'form' }, + uploadFile: { method: 'POST', url: '/upload', bodyType: 'multipart' }, + jsonEndpoint: { method: 'POST', url: '/json', bodyType: 'json' }, + }, +} + +mock.module('../url-map', () => ({ + DEVUP_API_URL_MAP: mockUrlMap, + getApiEndpointInfo: (key: string, serverName: string): UrlMapValue => { + const result = mockUrlMap[serverName]?.[key] ?? { + method: 'GET' as const, + url: key, + } + result.url ||= key + return result + }, +})) + +// Import DevupApi AFTER mock.module so it picks up the mocked url-map +const { DevupApi } = await import('../api') + +const originalFetch = globalThis.fetch + +beforeEach(() => { + globalThis.fetch = mock(() => + Promise.resolve( + new Response(JSON.stringify({ success: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }), + ), + ) as unknown as typeof fetch +}) + +afterEach(() => { + globalThis.fetch = originalFetch +}) + +describe('bodyType-aware serialization', () => { + test('bodyType form: plain object body serialized as URLSearchParams', async () => { + const api = new DevupApi( + 'https://api.example.com', + undefined, + 'openapi.json', + ) + const mockFetch = globalThis.fetch as unknown as ReturnType + + await api.post( + 'submitForm' as never, + { body: { name: 'test', age: 25 } } as never, + ) + + expect(mockFetch).toHaveBeenCalledTimes(1) + const call = mockFetch.mock.calls[0] + expect(call).toBeDefined() + if (call) { + const request = call[0] as Request + expect(request.headers.get('Content-Type')).toBe( + 'application/x-www-form-urlencoded', + ) + const body = await request.text() + const params = new URLSearchParams(body) + expect(params.get('name')).toBe('test') + expect(params.get('age')).toBe('25') + } + }) + + test('bodyType multipart: plain object body serialized as FormData', async () => { + const api = new DevupApi( + 'https://api.example.com', + undefined, + 'openapi.json', + ) + const mockFetch = globalThis.fetch as unknown as ReturnType + + await api.post( + 'uploadFile' as never, + { body: { name: 'test', value: 123 } } as never, + ) + + expect(mockFetch).toHaveBeenCalledTimes(1) + const call = mockFetch.mock.calls[0] + expect(call).toBeDefined() + if (call) { + const request = call[0] as Request + // Content-Type should NOT be explicitly set for multipart + // (browser auto-sets with boundary) + const contentType = request.headers.get('Content-Type') + expect( + contentType === null || contentType.includes('multipart/form-data'), + ).toBe(true) + expect(request.body).not.toBeNull() + } + }) + + test('bodyType undefined (default): plain object body serialized as JSON', async () => { + const api = new DevupApi( + 'https://api.example.com', + undefined, + 'openapi.json', + ) + const mockFetch = globalThis.fetch as unknown as ReturnType + + // Use a path that is NOT in the URL map, so bodyType will be undefined + await api.post( + '/test' as never, + { body: { name: 'test', value: 123 } } as never, + ) + + expect(mockFetch).toHaveBeenCalledTimes(1) + const call = mockFetch.mock.calls[0] + expect(call).toBeDefined() + if (call) { + const request = call[0] as Request + expect(request.headers.get('Content-Type')).toBe('application/json') + const body = await request.text() + expect(body).toBe(JSON.stringify({ name: 'test', value: 123 })) + } + }) + + test('bodyType json: plain object body serialized as JSON', async () => { + const api = new DevupApi( + 'https://api.example.com', + undefined, + 'openapi.json', + ) + const mockFetch = globalThis.fetch as unknown as ReturnType + + await api.post('jsonEndpoint' as never, { body: { name: 'test' } } as never) + + expect(mockFetch).toHaveBeenCalledTimes(1) + const call = mockFetch.mock.calls[0] + expect(call).toBeDefined() + if (call) { + const request = call[0] as Request + expect(request.headers.get('Content-Type')).toBe('application/json') + const body = await request.text() + expect(body).toBe(JSON.stringify({ name: 'test' })) + } + }) + + test('FormData body passthrough regardless of bodyType', async () => { + const api = new DevupApi( + 'https://api.example.com', + undefined, + 'openapi.json', + ) + const mockFetch = globalThis.fetch as unknown as ReturnType + const formData = new FormData() + formData.append('file', 'test') + + await api.post('submitForm' as never, { body: formData } as never) + + expect(mockFetch).toHaveBeenCalledTimes(1) + const call = mockFetch.mock.calls[0] + expect(call).toBeDefined() + if (call) { + const request = call[0] as Request + // FormData should be passed through as-is, not re-serialized + expect(request.body).not.toBeNull() + expect(request.body).toBeDefined() + } + }) + + test('bodyType form: nested objects are JSON.stringified in URLSearchParams', async () => { + const api = new DevupApi( + 'https://api.example.com', + undefined, + 'openapi.json', + ) + const mockFetch = globalThis.fetch as unknown as ReturnType + + await api.post( + 'submitForm' as never, + { body: { data: { nested: true }, items: [1, 2] } } as never, + ) + + expect(mockFetch).toHaveBeenCalledTimes(1) + const call = mockFetch.mock.calls[0] + expect(call).toBeDefined() + if (call) { + const request = call[0] as Request + const body = await request.text() + const params = new URLSearchParams(body) + expect(params.get('data')).toBe(JSON.stringify({ nested: true })) + expect(params.get('items')).toBe(JSON.stringify([1, 2])) + } + }) + + test('bodyType form: null/undefined values are skipped', async () => { + const api = new DevupApi( + 'https://api.example.com', + undefined, + 'openapi.json', + ) + const mockFetch = globalThis.fetch as unknown as ReturnType + + await api.post( + 'submitForm' as never, + { body: { name: 'test', empty: null, missing: undefined } } as never, + ) + + expect(mockFetch).toHaveBeenCalledTimes(1) + const call = mockFetch.mock.calls[0] + expect(call).toBeDefined() + if (call) { + const request = call[0] as Request + const body = await request.text() + const params = new URLSearchParams(body) + expect(params.get('name')).toBe('test') + expect(params.has('empty')).toBe(false) + expect(params.has('missing')).toBe(false) + } + }) +}) diff --git a/packages/fetch/src/__tests__/utils.test.ts b/packages/fetch/src/__tests__/utils.test.ts index e43b935..281be2f 100644 --- a/packages/fetch/src/__tests__/utils.test.ts +++ b/packages/fetch/src/__tests__/utils.test.ts @@ -1,5 +1,11 @@ -import { expect, test } from 'bun:test' -import { getApiEndpoint, getQueryString, isPlainObject } from '../utils' +import { describe, expect, test } from 'bun:test' +import { + getApiEndpoint, + getQueryString, + isPlainObject, + objectToFormData, + objectToURLSearchParams, +} from '../utils' test.each([ [{}, true], @@ -160,3 +166,117 @@ test.each([ ) expect(result.toString()).toBe(expected) }) + +describe('objectToURLSearchParams', () => { + test('converts simple key-value pairs', () => { + const params = objectToURLSearchParams({ name: 'test', age: 25 }) + expect(params.get('name')).toBe('test') + expect(params.get('age')).toBe('25') + }) + + test('skips null values', () => { + const params = objectToURLSearchParams({ name: 'test', skip: null }) + expect(params.get('name')).toBe('test') + expect(params.has('skip')).toBe(false) + }) + + test('skips undefined values', () => { + const params = objectToURLSearchParams({ name: 'test', skip: undefined }) + expect(params.get('name')).toBe('test') + expect(params.has('skip')).toBe(false) + }) + + test('JSON.stringifies nested objects', () => { + const nested = { key: 'value' } + const params = objectToURLSearchParams({ data: nested }) + expect(params.get('data')).toBe(JSON.stringify(nested)) + }) + + test('JSON.stringifies arrays', () => { + const arr = [1, 2, 3] + const params = objectToURLSearchParams({ items: arr }) + expect(params.get('items')).toBe(JSON.stringify(arr)) + }) + + test('converts boolean values to string', () => { + const params = objectToURLSearchParams({ active: true, deleted: false }) + expect(params.get('active')).toBe('true') + expect(params.get('deleted')).toBe('false') + }) + + test('converts number values to string', () => { + const params = objectToURLSearchParams({ count: 42, price: 9.99 }) + expect(params.get('count')).toBe('42') + expect(params.get('price')).toBe('9.99') + }) + + test('handles empty object', () => { + const params = objectToURLSearchParams({}) + expect(params.toString()).toBe('') + }) +}) + +describe('objectToFormData', () => { + test('converts simple key-value pairs', () => { + const formData = objectToFormData({ name: 'test', age: 25 }) + expect(formData.get('name')).toBe('test') + expect(formData.get('age')).toBe('25') + }) + + test('skips null values', () => { + const formData = objectToFormData({ name: 'test', skip: null }) + expect(formData.get('name')).toBe('test') + expect(formData.has('skip')).toBe(false) + }) + + test('skips undefined values', () => { + const formData = objectToFormData({ name: 'test', skip: undefined }) + expect(formData.get('name')).toBe('test') + expect(formData.has('skip')).toBe(false) + }) + + test('JSON.stringifies nested objects', () => { + const nested = { key: 'value' } + const formData = objectToFormData({ data: nested }) + expect(formData.get('data')).toBe(JSON.stringify(nested)) + }) + + test('JSON.stringifies arrays', () => { + const arr = [1, 2, 3] + const formData = objectToFormData({ items: arr }) + expect(formData.get('items')).toBe(JSON.stringify(arr)) + }) + + test('appends Blob values directly', () => { + const blob = new Blob(['hello'], { type: 'text/plain' }) + const formData = objectToFormData({ file: blob }) + const result = formData.get('file') + expect(result).toBeInstanceOf(Blob) + }) + + test('appends File values directly', () => { + const file = new File(['content'], 'test.txt', { type: 'text/plain' }) + const formData = objectToFormData({ document: file }) + const result = formData.get('document') + expect(result).toBeInstanceOf(File) + expect((result as File).name).toBe('test.txt') + }) + + test('converts boolean values to string', () => { + const formData = objectToFormData({ active: true, deleted: false }) + expect(formData.get('active')).toBe('true') + expect(formData.get('deleted')).toBe('false') + }) + + test('converts number values to string', () => { + const formData = objectToFormData({ count: 42, price: 9.99 }) + expect(formData.get('count')).toBe('42') + expect(formData.get('price')).toBe('9.99') + }) + + test('handles empty object', () => { + const formData = objectToFormData({}) + const entries = Array.from(formData.entries()) + expect(entries).toHaveLength(0) + }) +}) diff --git a/packages/fetch/src/api.ts b/packages/fetch/src/api.ts index 16af265..19da778 100644 --- a/packages/fetch/src/api.ts +++ b/packages/fetch/src/api.ts @@ -22,7 +22,13 @@ import type { } from '@devup-api/core' import { convertResponse } from './response-converter' import { getApiEndpointInfo } from './url-map' -import { getApiEndpoint, getQueryString, isPlainObject } from './utils' +import { + getApiEndpoint, + getQueryString, + isPlainObject, + objectToFormData, + objectToURLSearchParams, +} from './utils' // biome-ignore lint/suspicious/noExplicitAny: any is used to allow for flexibility in the type export type DevupApiResponse = @@ -217,7 +223,7 @@ export class DevupApi> { ): Promise< DevupApiResponse, ExtractValue> > { - const { method, url } = getApiEndpointInfo(path, this.serverName) + const { method, url, bodyType } = getApiEndpointInfo(path, this.serverName) const { middleware = [], query, @@ -237,13 +243,29 @@ export class DevupApi> { headers: mergedHeaders, } if (body) { - if (isPlainObject(body)) { + if (!isPlainObject(body)) { + // Non-plain-object passthrough (FormData, Blob, string, etc.) + requestOptions.body = body + } else if (bodyType === 'form') { + // URLSearchParams serialization + requestOptions.body = objectToURLSearchParams( + body as Record, + ) + if (!requestOptions.headers.has('Content-Type')) { + requestOptions.headers.set( + 'Content-Type', + 'application/x-www-form-urlencoded', + ) + } + } else if (bodyType === 'multipart') { + // FormData serialization — do NOT set Content-Type (browser sets it with boundary) + requestOptions.body = objectToFormData(body as Record) + } else { + // Default: JSON serialization (bodyType === 'json' or undefined) requestOptions.body = JSON.stringify(body) if (!requestOptions.headers.has('Content-Type')) { requestOptions.headers.set('Content-Type', 'application/json') } - } else { - requestOptions.body = body } } const queryString = query ? `?${getQueryString(query).toString()}` : '' diff --git a/packages/fetch/src/utils.ts b/packages/fetch/src/utils.ts index 17e2991..a8d39dc 100644 --- a/packages/fetch/src/utils.ts +++ b/packages/fetch/src/utils.ts @@ -19,6 +19,36 @@ export function getApiEndpoint( return ret } +export function objectToURLSearchParams( + obj: Record, +): URLSearchParams { + const params = new URLSearchParams() + for (const [key, value] of Object.entries(obj)) { + if (value === null || value === undefined) continue + if (typeof value === 'object') { + params.append(key, JSON.stringify(value)) + } else { + params.append(key, String(value)) + } + } + return params +} + +export function objectToFormData(obj: Record): FormData { + const formData = new FormData() + for (const [key, value] of Object.entries(obj)) { + if (value === null || value === undefined) continue + if (value instanceof File || value instanceof Blob) { + formData.append(key, value) + } else if (typeof value === 'object') { + formData.append(key, JSON.stringify(value)) + } else { + formData.append(key, String(value)) + } + } + return formData +} + export function getQueryString( query: NonNullable, ): URLSearchParams { From 33e306691817cfccdb4190a5b334fe76ab5fb75c Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 01:31:56 +0900 Subject: [PATCH 08/15] Add test --- bun.lock | 1 + examples/next/app/page.tsx | 17 +++++++++++++++++ examples/next/next.config.ts | 2 +- test.json => examples/next/openapi3.json | 0 examples/next/package.json | 3 ++- 5 files changed, 21 insertions(+), 2 deletions(-) rename test.json => examples/next/openapi3.json (100%) diff --git a/bun.lock b/bun.lock index 9cba258..8478261 100644 --- a/bun.lock +++ b/bun.lock @@ -30,6 +30,7 @@ "next": "^16.1.6", "react": "^19.2.4", "react-dom": "^19.2.4", + "zod": "^4.3", }, "devDependencies": { "@devup-ui/next-plugin": "^1", diff --git a/examples/next/app/page.tsx b/examples/next/app/page.tsx index 32d0e2e..8aa1098 100644 --- a/examples/next/app/page.tsx +++ b/examples/next/app/page.tsx @@ -14,6 +14,10 @@ const api2 = createApi({ baseUrl: 'https://api.example2.com', serverName: 'openapi2.json', }) +const api3 = createApi({ + baseUrl: 'https://api.example2.com', + serverName: 'openapi3.json', +}) const queryClient = createQueryClient(api) @@ -80,6 +84,19 @@ export default function Home() { .then((res) => { console.log(res) }) + api3.POST('/form', { + body: { + email: 'name', + name: 'John Doe', + }, + }) + + api3.POST('/typed-form', { + body: { + name: 'John Doe', + tags: 'tag1,tag2', + }, + }) }, [mutateAsync]) return ( diff --git a/examples/next/next.config.ts b/examples/next/next.config.ts index ef237a9..9e694da 100644 --- a/examples/next/next.config.ts +++ b/examples/next/next.config.ts @@ -6,7 +6,7 @@ const config = devupApi( reactStrictMode: true, }, { - openapiFiles: ['./openapi.json', './openapi2.json'], + openapiFiles: ['./openapi.json', './openapi2.json', './openapi3.json'], }, ) diff --git a/test.json b/examples/next/openapi3.json similarity index 100% rename from test.json rename to examples/next/openapi3.json diff --git a/examples/next/package.json b/examples/next/package.json index c755544..6a1ead3 100644 --- a/examples/next/package.json +++ b/examples/next/package.json @@ -19,7 +19,8 @@ "@devup-ui/react": "^1", "@tanstack/react-query": "^5", "@devup-api/ui": "workspace:*", - "@devup-api/hookform": "workspace:*" + "@devup-api/hookform": "workspace:*", + "zod": "^4.3" }, "devDependencies": { "@devup-ui/next-plugin": "^1", From d7888aca1ff1c58733f91823fccbb475da68094a Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 02:06:53 +0900 Subject: [PATCH 09/15] refactor(generator): create openapi-utils.ts with shared utilities --- packages/generator/src/openapi-utils.ts | 105 ++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 packages/generator/src/openapi-utils.ts diff --git a/packages/generator/src/openapi-utils.ts b/packages/generator/src/openapi-utils.ts new file mode 100644 index 0000000..29e63b2 --- /dev/null +++ b/packages/generator/src/openapi-utils.ts @@ -0,0 +1,105 @@ +import type { OpenAPIV3_1 } from 'openapi-types' + +// ============================================================================= +// Constants +// ============================================================================= + +/** + * Content type priority order: json > urlencoded > multipart. + * Used to determine which content type to use when multiple are available. + */ +export const CONTENT_TYPE_PRIORITY = [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data', +] as const + +// ============================================================================= +// $ref Resolution +// ============================================================================= + +/** + * Resolve a JSON `$ref` reference within an OpenAPI document. + * Handles `#/` prefix stripping, path segment traversal, and null safety. + */ +export function resolveRef( + ref: string, + document: OpenAPIV3_1.Document, +): T | null { + if (!ref.startsWith('#/')) { + return null + } + + const parts = ref.slice(2).split('/') + let current: unknown = document + + for (const part of parts) { + if (current && typeof current === 'object' && part in current) { + current = (current as Record)[part] + } else { + return null + } + } + + if (current && typeof current === 'object' && !('$ref' in current)) { + return current as T + } + + return null +} + +// ============================================================================= +// Content Type Helpers +// ============================================================================= + +/** + * Get the first matching request body content from priority-ordered content types. + * Returns the `MediaTypeObject` for the highest-priority content type found. + */ +export function getRequestBodyContent( + content: OpenAPIV3_1.RequestBodyObject['content'] | undefined, +): OpenAPIV3_1.MediaTypeObject | undefined { + if (!content) return undefined + for (const ct of CONTENT_TYPE_PRIORITY) { + if (content[ct]) return content[ct] + } + return undefined +} + +// ============================================================================= +// Schema Name Extraction +// ============================================================================= + +/** + * Extract the schema name from a `$ref` string pointing to `#/components/schemas/`. + */ +export function extractSchemaNameFromRef(ref: string): string | null { + if (ref.startsWith('#/components/schemas/')) { + return ref.replace('#/components/schemas/', '') + } + return null +} + +// ============================================================================= +// Server Name Normalization +// ============================================================================= + +/** + * Normalize server name by removing the `./` prefix. + */ +export function normalizeServerName(serverName: string): string { + return serverName.replace(/^\.\//, '') +} + +// ============================================================================= +// Status Code Classification +// ============================================================================= + +/** + * Check if an HTTP status code represents an error response (4xx, 5xx, or 'default'). + */ +export function isErrorStatusCode(statusCode: string): boolean { + if (statusCode === 'default') return true + const code = parseInt(statusCode, 10) + return code >= 400 && code < 600 +} From 75462a7d3588b50d7fe9c51c48efff1c1048e15c Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 02:07:28 +0900 Subject: [PATCH 10/15] refactor(generator): migrate all generators to use shared openapi-utils --- .../src/__tests__/generate-schema.test.ts | 2 +- .../generator/src/__tests__/index.test.ts | 11 +++ packages/generator/src/create-url-map.ts | 20 +--- packages/generator/src/generate-interface.ts | 2 +- packages/generator/src/generate-schema.ts | 54 +---------- packages/generator/src/generate-zod.ts | 91 +++---------------- packages/generator/src/index.ts | 1 + 7 files changed, 37 insertions(+), 144 deletions(-) diff --git a/packages/generator/src/__tests__/generate-schema.test.ts b/packages/generator/src/__tests__/generate-schema.test.ts index f5d2b26..6128e0a 100644 --- a/packages/generator/src/__tests__/generate-schema.test.ts +++ b/packages/generator/src/__tests__/generate-schema.test.ts @@ -5,9 +5,9 @@ import { extractParameters, extractRequestBody, formatTypeValue, - getRequestBodyContent, getTypeFromSchema, } from '../generate-schema' +import { getRequestBodyContent } from '../openapi-utils' const createDocument = ( document: Partial = {}, diff --git a/packages/generator/src/__tests__/index.test.ts b/packages/generator/src/__tests__/index.test.ts index 6223950..82a47a9 100644 --- a/packages/generator/src/__tests__/index.test.ts +++ b/packages/generator/src/__tests__/index.test.ts @@ -20,5 +20,16 @@ test('index.ts exports', () => { parseCrudConfigsFromMultiple: expect.any(Function), parseDevupOperations: expect.any(Function), parseDevupTag: expect.any(Function), + // OpenAPI utilities + CONTENT_TYPE_PRIORITY: [ + 'application/json', + 'application/x-www-form-urlencoded', + 'multipart/form-data', + ], + resolveRef: expect.any(Function), + getRequestBodyContent: expect.any(Function), + extractSchemaNameFromRef: expect.any(Function), + normalizeServerName: expect.any(Function), + isErrorStatusCode: expect.any(Function), }) }) diff --git a/packages/generator/src/create-url-map.ts b/packages/generator/src/create-url-map.ts index 84fbdd2..15b874e 100644 --- a/packages/generator/src/create-url-map.ts +++ b/packages/generator/src/create-url-map.ts @@ -1,20 +1,7 @@ import type { DevupApiTypeGeneratorOptions, UrlMapValue } from '@devup-api/core' import type { OpenAPIV3_1 } from 'openapi-types' import { convertCase } from './convert-case' - -function resolveRequestBodyRef( - ref: string, - document: OpenAPIV3_1.Document, -): OpenAPIV3_1.RequestBodyObject | undefined { - // Expected format: #/components/requestBodies/Name - const parts = ref.replace('#/', '').split('/') - let current: unknown = document - for (const part of parts) { - if (current == null || typeof current !== 'object') return undefined - current = (current as Record)[part] - } - return current as OpenAPIV3_1.RequestBodyObject | undefined -} +import { resolveRef } from './openapi-utils' export function getBodyType( operation: OpenAPIV3_1.OperationObject, @@ -25,7 +12,10 @@ export function getBodyType( let content: OpenAPIV3_1.RequestBodyObject['content'] | undefined if ('$ref' in requestBody) { - const resolved = resolveRequestBodyRef(requestBody.$ref, document) + const resolved = resolveRef( + requestBody.$ref, + document, + ) content = resolved?.content } else { content = requestBody.content diff --git a/packages/generator/src/generate-interface.ts b/packages/generator/src/generate-interface.ts index 321705e..3e7f616 100644 --- a/packages/generator/src/generate-interface.ts +++ b/packages/generator/src/generate-interface.ts @@ -8,9 +8,9 @@ import { extractParameters, extractRequestBody, formatTypeValue, - getRequestBodyContent, getTypeFromSchema, } from './generate-schema' +import { getRequestBodyContent } from './openapi-utils' import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard' export interface ParameterDefinition diff --git a/packages/generator/src/generate-schema.ts b/packages/generator/src/generate-schema.ts index 17098db..dd81fff 100644 --- a/packages/generator/src/generate-schema.ts +++ b/packages/generator/src/generate-schema.ts @@ -1,5 +1,6 @@ import type { OpenAPIV3_1 } from 'openapi-types' import type { ParameterDefinition } from './generate-interface' +import { getRequestBodyContent, resolveRef } from './openapi-utils' import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard' /** @@ -111,34 +112,6 @@ function getNonNullType( return types.find((t) => t !== 'null') } -/** - * Resolve $ref reference in OpenAPI schema - */ -function resolveSchemaRef< - T extends OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ParameterObject, ->(ref: string, document: OpenAPIV3_1.Document): T | null { - if (!ref.startsWith('#/')) { - return null - } - - const parts = ref.slice(2).split('/') - let current: unknown = document - - for (const part of parts) { - if (current && typeof current === 'object' && part in current) { - current = (current as Record)[part] - } else { - return null - } - } - - if (current && typeof current === 'object' && !('$ref' in current)) { - return current as T - } - - return null -} - /** * Options for component reference handling */ @@ -179,7 +152,7 @@ export function getTypeFromSchema( ? schema.$ref.replace('#/components/schemas/', '') : undefined - const resolved = resolveSchemaRef( + const resolved = resolveRef( schema.$ref, document, ) @@ -369,7 +342,7 @@ export function getTypeFromSchema( // Need to resolve $ref if present to check for default let hasDefault = false if ('$ref' in value) { - const resolved = resolveSchemaRef( + const resolved = resolveRef( value.$ref, document, ) @@ -672,7 +645,7 @@ export function extractParameters( for (const param of allParams) { if ('$ref' in param) { // Resolve $ref parameter - const resolved = resolveSchemaRef( + const resolved = resolveRef( param.$ref, document, ) @@ -730,23 +703,6 @@ export function extractParameters( return { pathParams, queryParams, headerParams } } -// Priority order: json > urlencoded > multipart -const CONTENT_TYPE_PRIORITY = [ - 'application/json', - 'application/x-www-form-urlencoded', - 'multipart/form-data', -] as const - -export function getRequestBodyContent( - content: OpenAPIV3_1.RequestBodyObject['content'] | undefined, -): OpenAPIV3_1.MediaTypeObject | undefined { - if (!content) return undefined - for (const ct of CONTENT_TYPE_PRIORITY) { - if (content[ct]) return content[ct] - } - return undefined -} - /** * Extract request body from OpenAPI operation */ @@ -762,7 +718,7 @@ export function extractRequestBody( } if ('$ref' in requestBody) { - const resolved = resolveSchemaRef(requestBody.$ref, document) + const resolved = resolveRef(requestBody.$ref, document) if (resolved && 'content' in resolved && resolved.content) { const content = resolved.content as OpenAPIV3_1.RequestBodyObject['content'] diff --git a/packages/generator/src/generate-zod.ts b/packages/generator/src/generate-zod.ts index b2d334e..a85be6e 100644 --- a/packages/generator/src/generate-zod.ts +++ b/packages/generator/src/generate-zod.ts @@ -1,66 +1,15 @@ import type { DevupApiTypeGeneratorOptions } from '@devup-api/core' import type { OpenAPIV3_1 } from 'openapi-types' import { convertCase } from './convert-case' +import { + CONTENT_TYPE_PRIORITY, + extractSchemaNameFromRef, + isErrorStatusCode, + normalizeServerName, + resolveRef, +} from './openapi-utils' import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard' -// ============================================================================= -// Helper Functions -// ============================================================================= - -/** - * Normalize server name by removing ./ prefix - */ -function normalizeServerName(serverName: string): string { - return serverName.replace(/^\.\//, '') -} - -/** - * Resolve $ref reference in OpenAPI schema - */ -function resolveSchemaRef< - T extends OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ParameterObject, ->(ref: string, document: OpenAPIV3_1.Document): T | null { - if (!ref.startsWith('#/')) { - return null - } - - const parts = ref.slice(2).split('/') - let current: unknown = document - - for (const part of parts) { - if (current && typeof current === 'object' && part in current) { - current = (current as Record)[part] - } else { - return null - } - } - - if (current && typeof current === 'object' && !('$ref' in current)) { - return current as T - } - - return null -} - -/** - * Extract schema name from $ref - */ -function extractSchemaNameFromRef(ref: string): string | null { - if (ref.startsWith('#/components/schemas/')) { - return ref.replace('#/components/schemas/', '') - } - return null -} - -/** - * Check if status code is an error response - */ -function isErrorStatusCode(statusCode: string): boolean { - if (statusCode === 'default') return true - const code = parseInt(statusCode, 10) - return code >= 400 && code < 600 -} - // ============================================================================= // OpenAPI to Zod Conversion // ============================================================================= @@ -83,10 +32,7 @@ function schemaToZod( // Return lazy reference for circular dependencies return `z.lazy(() => ${schemaRefs.get(schemaName)})` } - const resolved = resolveSchemaRef( - schema.$ref, - document, - ) + const resolved = resolveRef(schema.$ref, document) if (resolved) { return schemaToZod(resolved, document, schemaRefs, options) } @@ -262,7 +208,7 @@ function schemaToZod( // Check for default value let hasDefault = false if ('$ref' in value) { - const resolved = resolveSchemaRef( + const resolved = resolveRef( value.$ref, document, ) @@ -328,10 +274,7 @@ function schemaToZodType( // Return a lazy type reference return `z.ZodLazy` } - const resolved = resolveSchemaRef( - schema.$ref, - document, - ) + const resolved = resolveRef(schema.$ref, document) if (resolved) { return schemaToZodType(resolved, document, options) } @@ -442,7 +385,7 @@ function schemaToZodType( // Check for default value let hasDefault = false if ('$ref' in value) { - const resolved = resolveSchemaRef( + const resolved = resolveRef( value.$ref, document, ) @@ -572,11 +515,7 @@ function collectSchemaUsage( return extractSchemaNameFromRef(requestBody.$ref) } const content = requestBody.content - for (const ct of [ - 'application/json', - 'application/x-www-form-urlencoded', - 'multipart/form-data', - ]) { + for (const ct of CONTENT_TYPE_PRIORITY) { const bodyContent = content?.[ct] if (bodyContent?.schema && '$ref' in bodyContent.schema) { return extractSchemaNameFromRef(bodyContent.schema.$ref) @@ -618,11 +557,7 @@ function collectSchemaUsage( } } else { const content = operation.requestBody.content - for (const ct of [ - 'application/json', - 'application/x-www-form-urlencoded', - 'multipart/form-data', - ]) { + for (const ct of CONTENT_TYPE_PRIORITY) { const bodyContent = content?.[ct] if (bodyContent?.schema) { collectSchemaNames(bodyContent.schema, requestSchemaNames) diff --git a/packages/generator/src/index.ts b/packages/generator/src/index.ts index ac52774..ef9a697 100644 --- a/packages/generator/src/index.ts +++ b/packages/generator/src/index.ts @@ -3,4 +3,5 @@ export * from './crud-types' export * from './generate-crud-config' export * from './generate-interface' export * from './generate-zod' +export * from './openapi-utils' export * from './parse-crud-tags' From 020480e67b999724ccca14bf6a1bb1d5a7992069 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 02:08:47 +0900 Subject: [PATCH 11/15] refactor(generator): remove getBodyType from public exports --- .../src/__tests__/create-url-map.test.ts | 95 +------------------ .../generator/src/__tests__/index.test.ts | 1 - packages/generator/src/create-url-map.ts | 2 +- 3 files changed, 3 insertions(+), 95 deletions(-) diff --git a/packages/generator/src/__tests__/create-url-map.test.ts b/packages/generator/src/__tests__/create-url-map.test.ts index 696ff9d..e9a95d0 100644 --- a/packages/generator/src/__tests__/create-url-map.test.ts +++ b/packages/generator/src/__tests__/create-url-map.test.ts @@ -2,7 +2,7 @@ import { describe, expect, test } from 'bun:test' import type { UrlMapValue } from '@devup-api/core' import type { OpenAPIV3_1 } from 'openapi-types' -import { createUrlMap, getBodyType } from '../create-url-map' +import { createUrlMap } from '../create-url-map' test.each([ [ @@ -605,95 +605,4 @@ describe('bodyType emission', () => { }) }) -describe('getBodyType', () => { - const emptyDoc: OpenAPIV3_1.Document = { - openapi: '3.1.0', - info: { title: 'Test', version: '1.0.0' }, - paths: {}, - } - - test('returns undefined when no requestBody', () => { - expect(getBodyType({ responses: {} }, emptyDoc)).toBeUndefined() - }) - - test('returns undefined for JSON content', () => { - expect( - getBodyType( - { - requestBody: { - content: { - 'application/json': { schema: { type: 'object' } }, - }, - }, - responses: {}, - }, - emptyDoc, - ), - ).toBeUndefined() - }) - - test('returns form for urlencoded content', () => { - expect( - getBodyType( - { - requestBody: { - content: { - 'application/x-www-form-urlencoded': { - schema: { type: 'object' }, - }, - }, - }, - responses: {}, - }, - emptyDoc, - ), - ).toBe('form') - }) - - test('returns multipart for multipart/form-data content', () => { - expect( - getBodyType( - { - requestBody: { - content: { - 'multipart/form-data': { schema: { type: 'object' } }, - }, - }, - responses: {}, - }, - emptyDoc, - ), - ).toBe('multipart') - }) - - test('returns undefined when requestBody has no content', () => { - expect( - getBodyType( - { - requestBody: { content: {} }, - responses: {}, - }, - emptyDoc, - ), - ).toBeUndefined() - }) - - test('prefers urlencoded over multipart when both present', () => { - expect( - getBodyType( - { - requestBody: { - content: { - 'application/x-www-form-urlencoded': { - schema: { type: 'object' }, - }, - 'multipart/form-data': { schema: { type: 'object' } }, - }, - }, - responses: {}, - }, - emptyDoc, - ), - ).toBe('form') - }) -}) +// getBodyType is now internal (not exported) — tested indirectly through createUrlMap tests above diff --git a/packages/generator/src/__tests__/index.test.ts b/packages/generator/src/__tests__/index.test.ts index 82a47a9..65d6d9d 100644 --- a/packages/generator/src/__tests__/index.test.ts +++ b/packages/generator/src/__tests__/index.test.ts @@ -5,7 +5,6 @@ test('index.ts exports', () => { expect({ ...indexModule }).toEqual({ // URL map createUrlMap: expect.any(Function), - getBodyType: expect.any(Function), // Interface generation generateInterface: expect.any(Function), // Zod generation diff --git a/packages/generator/src/create-url-map.ts b/packages/generator/src/create-url-map.ts index 15b874e..14c9574 100644 --- a/packages/generator/src/create-url-map.ts +++ b/packages/generator/src/create-url-map.ts @@ -3,7 +3,7 @@ import type { OpenAPIV3_1 } from 'openapi-types' import { convertCase } from './convert-case' import { resolveRef } from './openapi-utils' -export function getBodyType( +function getBodyType( operation: OpenAPIV3_1.OperationObject, document: OpenAPIV3_1.Document, ): 'form' | 'multipart' | undefined { From 10956a5c6ccfa70058957bf7171fc59a8e875d2c Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 02:17:41 +0900 Subject: [PATCH 12/15] refactor(generator): deduplicate generate-interface.ts with shared utils and content extraction helper --- packages/generator/src/generate-interface.ts | 373 ++++++------------- 1 file changed, 108 insertions(+), 265 deletions(-) diff --git a/packages/generator/src/generate-interface.ts b/packages/generator/src/generate-interface.ts index 3e7f616..c0c9263 100644 --- a/packages/generator/src/generate-interface.ts +++ b/packages/generator/src/generate-interface.ts @@ -10,7 +10,12 @@ import { formatTypeValue, getTypeFromSchema, } from './generate-schema' -import { getRequestBodyContent } from './openapi-utils' +import { + extractSchemaNameFromRef, + getRequestBodyContent, + isErrorStatusCode, + normalizeServerName, +} from './openapi-utils' import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard' export interface ParameterDefinition @@ -27,17 +32,88 @@ export interface EndpointDefinition { error?: unknown } -// Helper function to extract schema names from $ref -function extractSchemaNameFromRef(ref: string): string | null { - if (ref.startsWith('#/components/schemas/')) { - return ref.replace('#/components/schemas/', '') +// Extract schema type from a response/error object's JSON content. +// Shared logic for both success response and error response extraction. +function extractContentType( + responseObj: + | OpenAPIV3_1.ResponseObject + | OpenAPIV3_1.ReferenceObject + | undefined, + componentType: 'response' | 'error', + schemaNames: Set, + schema: OpenAPIV3_1.Document, + serverName: string, + enumContext: ReturnType, + options?: DevupApiTypeGeneratorOptions, +): unknown { + if (!responseObj) return undefined + if ('$ref' in responseObj) { + // ResponseObject reference - skip for now + return undefined + } + if (!('content' in responseObj)) return undefined + + const content = responseObj.content + const jsonContent = content?.['application/json'] + if (!jsonContent || !('schema' in jsonContent) || !jsonContent.schema) { + return undefined + } + + const contextLabel = componentType === 'response' ? 'Response' : 'Error' + const responseDefaultNonNullable = options?.responseDefaultNonNullable ?? true + + const extractInlineType = ( + schemaRef: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject, + ): unknown => { + const inlineContext = createSchemaContext(contextLabel) + const { type: schemaType } = getTypeFromSchema(schemaRef, schema, { + defaultNonNullable: responseDefaultNonNullable, + context: inlineContext, + serverName, + componentType, + usedSchemaNames: schemaNames, + }) + for (const [enumName, enumDef] of inlineContext.enums) { + if (!enumContext.enums.has(enumName)) { + enumContext.enums.set(enumName, enumDef) + } + } + return schemaType + } + + // Check if schema is a direct reference to components.schemas + if ('$ref' in jsonContent.schema) { + const schemaName = extractSchemaNameFromRef(jsonContent.schema.$ref) + if ( + schemaName && + schema.components?.schemas?.[schemaName] && + schemaNames.has(schemaName) + ) { + return `DevupObject<'${componentType}', '${serverName}'>['${schemaName}']` + } + return extractInlineType(jsonContent.schema) + } + + // Check if it's an array with items referencing a component schema + const schemaObj = jsonContent.schema as OpenAPIV3_1.SchemaObject + if ( + schemaObj.type === 'array' && + schemaObj.items && + '$ref' in schemaObj.items + ) { + const schemaName = extractSchemaNameFromRef(schemaObj.items.$ref) + if ( + schemaName && + schema.components?.schemas?.[schemaName] && + schemaNames.has(schemaName) + ) { + return `Array['${schemaName}']>` + } + return extractInlineType(jsonContent.schema) } - return null -} -// Helper function to normalize server name by removing ./ prefix -function normalizeServerName(serverName: string): string { - return serverName.replace(/^\.\//, '') + // Extract schema type (inline schema) + return extractInlineType(jsonContent.schema) } // Generate interface for a single schema @@ -142,13 +218,6 @@ function generateSchemaInterface( const responseSchemaNames = new Set() const errorSchemaNames = new Set() - // Helper function to check if a status code is an error response - const isErrorStatusCode = (statusCode: string): boolean => { - if (statusCode === 'default') return true - const code = parseInt(statusCode, 10) - return code >= 400 && code < 600 - } - // First, collect schema names used in request body and responses if (schema.paths) { for (const pathItem of Object.values(schema.paths)) { @@ -332,7 +401,6 @@ function generateSchemaInterface( // Extract response // Check if response uses a component schema - let responseType: unknown if (operation.responses) { // Prefer 200 response, fallback to first available response const successResponse = @@ -340,135 +408,22 @@ function generateSchemaInterface( operation.responses['201'] || Object.values(operation.responses)[0] - if (successResponse) { - if ('$ref' in successResponse) { - // ResponseObject reference - skip for now - // Could resolve if needed - } else if ('content' in successResponse) { - const content = successResponse.content - const jsonContent = content?.['application/json'] - if ( - jsonContent && - 'schema' in jsonContent && - jsonContent.schema - ) { - // Check if schema is a direct reference to components.schemas - if ('$ref' in jsonContent.schema) { - const schemaName = extractSchemaNameFromRef( - jsonContent.schema.$ref, - ) - // Check if schema exists in components.schemas and is used in response - if ( - schemaName && - schema.components?.schemas?.[schemaName] && - responseSchemaNames.has(schemaName) - ) { - // Use component reference - responseType = `DevupObject<'response', '${serverName}'>['${schemaName}']` - } else { - // Extract schema type with response options - const responseDefaultNonNullable = - options?.responseDefaultNonNullable ?? true - const inlineContext = createSchemaContext('Response') - const { type: schemaType } = getTypeFromSchema( - jsonContent.schema, - schema, - { - defaultNonNullable: responseDefaultNonNullable, - context: inlineContext, - serverName, - componentType: 'response', - usedSchemaNames: responseSchemaNames, - }, - ) - // Merge enums - for (const [enumName, enumDef] of inlineContext.enums) { - if (!enumContext.enums.has(enumName)) { - enumContext.enums.set(enumName, enumDef) - } - } - responseType = schemaType - } - } else { - // Check if it's an array with items referencing a component schema - const schemaObj = - jsonContent.schema as OpenAPIV3_1.SchemaObject - if ( - schemaObj.type === 'array' && - schemaObj.items && - '$ref' in schemaObj.items - ) { - const schemaName = extractSchemaNameFromRef( - schemaObj.items.$ref, - ) - // Check if schema exists in components.schemas and is used in response - if ( - schemaName && - schema.components?.schemas?.[schemaName] && - responseSchemaNames.has(schemaName) - ) { - // Use component reference for array items - responseType = `Array['${schemaName}']>` - } else { - // Extract schema type with response options - const responseDefaultNonNullable = - options?.responseDefaultNonNullable ?? true - const inlineContext = createSchemaContext('Response') - const { type: schemaType } = getTypeFromSchema( - jsonContent.schema, - schema, - { - defaultNonNullable: responseDefaultNonNullable, - context: inlineContext, - serverName, - componentType: 'response', - usedSchemaNames: responseSchemaNames, - }, - ) - // Merge enums - for (const [enumName, enumDef] of inlineContext.enums) { - if (!enumContext.enums.has(enumName)) { - enumContext.enums.set(enumName, enumDef) - } - } - responseType = schemaType - } - } else { - // Extract schema type with response options - const responseDefaultNonNullable = - options?.responseDefaultNonNullable ?? true - const inlineContext = createSchemaContext('Response') - const { type: schemaType } = getTypeFromSchema( - jsonContent.schema, - schema, - { - defaultNonNullable: responseDefaultNonNullable, - context: inlineContext, - serverName, - componentType: 'response', - usedSchemaNames: responseSchemaNames, - }, - ) - // Merge enums - for (const [enumName, enumDef] of inlineContext.enums) { - if (!enumContext.enums.has(enumName)) { - enumContext.enums.set(enumName, enumDef) - } - } - responseType = schemaType - } - } - } - } + const responseType = extractContentType( + successResponse, + 'response', + responseSchemaNames, + schema, + serverName, + enumContext, + options, + ) + if (responseType !== undefined) { + endpoint.response = responseType } } - if (responseType !== undefined) { - endpoint.response = responseType - } // Extract error // Check if error uses a component schema - let errorType: unknown if (operation.responses) { // Find error responses (4xx, 5xx, or default) const errorResponse = @@ -483,131 +438,19 @@ function generateSchemaInterface( isErrorStatusCode(statusCode), )?.[1] - if (errorResponse) { - if ('$ref' in errorResponse) { - // ResponseObject reference - skip for now - // Could resolve if needed - } else if ('content' in errorResponse) { - const content = errorResponse.content - const jsonContent = content?.['application/json'] - if ( - jsonContent && - 'schema' in jsonContent && - jsonContent.schema - ) { - // Check if schema is a direct reference to components.schemas - if ('$ref' in jsonContent.schema) { - const schemaName = extractSchemaNameFromRef( - jsonContent.schema.$ref, - ) - // Check if schema exists in components.schemas and is used in error - if ( - schemaName && - schema.components?.schemas?.[schemaName] && - errorSchemaNames.has(schemaName) - ) { - // Use component reference - errorType = `DevupObject<'error', '${serverName}'>['${schemaName}']` - } else { - // Extract schema type with response options - const responseDefaultNonNullable = - options?.responseDefaultNonNullable ?? true - const inlineContext = createSchemaContext('Error') - const { type: schemaType } = getTypeFromSchema( - jsonContent.schema, - schema, - { - defaultNonNullable: responseDefaultNonNullable, - context: inlineContext, - serverName, - componentType: 'error', - usedSchemaNames: errorSchemaNames, - }, - ) - // Merge enums - for (const [enumName, enumDef] of inlineContext.enums) { - if (!enumContext.enums.has(enumName)) { - enumContext.enums.set(enumName, enumDef) - } - } - errorType = schemaType - } - } else { - // Check if it's an array with items referencing a component schema - const schemaObj = - jsonContent.schema as OpenAPIV3_1.SchemaObject - if ( - schemaObj.type === 'array' && - schemaObj.items && - '$ref' in schemaObj.items - ) { - const schemaName = extractSchemaNameFromRef( - schemaObj.items.$ref, - ) - // Check if schema exists in components.schemas and is used in error - if ( - schemaName && - schema.components?.schemas?.[schemaName] && - errorSchemaNames.has(schemaName) - ) { - // Use component reference for array items - errorType = `Array['${schemaName}']>` - } else { - // Extract schema type with response options - const responseDefaultNonNullable = - options?.responseDefaultNonNullable ?? true - const inlineContext = createSchemaContext('Error') - const { type: schemaType } = getTypeFromSchema( - jsonContent.schema, - schema, - { - defaultNonNullable: responseDefaultNonNullable, - context: inlineContext, - serverName, - componentType: 'error', - usedSchemaNames: errorSchemaNames, - }, - ) - // Merge enums - for (const [enumName, enumDef] of inlineContext.enums) { - if (!enumContext.enums.has(enumName)) { - enumContext.enums.set(enumName, enumDef) - } - } - errorType = schemaType - } - } else { - // Extract schema type with response options - const responseDefaultNonNullable = - options?.responseDefaultNonNullable ?? true - const inlineContext = createSchemaContext('Error') - const { type: schemaType } = getTypeFromSchema( - jsonContent.schema, - schema, - { - defaultNonNullable: responseDefaultNonNullable, - context: inlineContext, - serverName, - componentType: 'error', - usedSchemaNames: errorSchemaNames, - }, - ) - // Merge enums - for (const [enumName, enumDef] of inlineContext.enums) { - if (!enumContext.enums.has(enumName)) { - enumContext.enums.set(enumName, enumDef) - } - } - errorType = schemaType - } - } - } - } + const errorType = extractContentType( + errorResponse, + 'error', + errorSchemaNames, + schema, + serverName, + enumContext, + options, + ) + if (errorType !== undefined) { + endpoint.error = errorType } } - if (errorType !== undefined) { - endpoint.error = errorType - } // Generate path key (normalize path by replacing {param} with converted param and removing slashes) const normalizedPath = path.replace(/\{([^}]+)\}/g, (_, param) => { From 4c35b827c90d217b2008b348218e623725e52864 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 02:23:40 +0900 Subject: [PATCH 13/15] Add test --- .../generate-interface.test.ts.snap | 69 +++++++++++++++++ .../src/__tests__/generate-interface.test.ts | 77 +++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap b/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap index ad8bfdd..c017752 100644 --- a/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap +++ b/packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap @@ -1714,6 +1714,75 @@ declare module "@devup-api/fetch" { }" `; +exports[`generateInterface handles response with non-JSON content type only 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupGetApiStruct { + 'openapi.json': { + '/files/{id}': { + params: { + id: string; + }; + }; + downloadFile: { + params: { + id: string; + }; + }; + } + } + + interface DevupRequestComponentStruct {} + + interface DevupResponseComponentStruct {} + + interface DevupErrorComponentStruct {} +}" +`; + +exports[`generateInterface handles response schema with inline enum 1`] = ` +"import "@devup-api/fetch"; +import type { DevupObject } from "@devup-api/fetch"; + +declare module "@devup-api/fetch" { + type ItemResponseStatus = "active" | "inactive" | "archived" + + interface DevupApiServers { + 'openapi.json': never + } + + interface DevupGetApiStruct { + 'openapi.json': { + '/items': { + response: DevupObject<'response', 'openapi.json'>['ItemResponse']; + }; + listItems: { + response: DevupObject<'response', 'openapi.json'>['ItemResponse']; + }; + } + } + + interface DevupRequestComponentStruct {} + + interface DevupResponseComponentStruct { + 'openapi.json': { + ItemResponse: { + name?: string; + status?: ItemResponseStatus; + }; + } + } + + interface DevupErrorComponentStruct {} +}" +`; + exports[`generateInterface handles error schema with inline enum 1`] = ` "import "@devup-api/fetch"; import type { DevupObject } from "@devup-api/fetch"; diff --git a/packages/generator/src/__tests__/generate-interface.test.ts b/packages/generator/src/__tests__/generate-interface.test.ts index f016cef..3800489 100644 --- a/packages/generator/src/__tests__/generate-interface.test.ts +++ b/packages/generator/src/__tests__/generate-interface.test.ts @@ -2003,6 +2003,83 @@ test('generateInterface handles request schema with inline enum', () => { }) // Test error schema with inline enum (lines 705-707 coverage) +test('generateInterface handles response with non-JSON content type only', () => { + const schema = { + paths: { + '/files/{id}': { + get: { + operationId: 'downloadFile', + parameters: [ + { + name: 'id', + in: 'path', + required: true, + schema: { type: 'string' }, + }, + ], + responses: { + '200': { + description: 'File content', + content: { + 'text/plain': { + schema: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // Should not have a typed response since there's no application/json + expect(result).not.toContain("DevupObject<'response'") +}) + +test('generateInterface handles response schema with inline enum', () => { + const schema = { + paths: { + '/items': { + get: { + operationId: 'listItems', + responses: { + '200': { + description: 'Success', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/ItemResponse', + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + ItemResponse: { + type: 'object', + properties: { + name: { type: 'string' }, + status: { + type: 'string', + enum: ['active', 'inactive', 'archived'], + }, + }, + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + expect(result).toMatchSnapshot() + // Inline enum in response schema should generate type alias + expect(result).toContain('type ItemResponseStatus =') + expect(result).toContain('"active"') +}) + test('generateInterface handles error schema with inline enum', () => { const schema = { paths: { From 1ee14026c2be3b4b7047ba8e7df072d8fd3a2aff Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 03:07:16 +0900 Subject: [PATCH 14/15] Refactor --- packages/core/src/api-struct.ts | 28 ++ .../generator/src/__tests__/index.test.ts | 3 + .../generator/src/generate-crud-config.ts | 17 +- packages/generator/src/generate-interface.ts | 90 ++--- packages/generator/src/generate-schema.ts | 47 +-- packages/generator/src/generate-zod.ts | 91 +---- packages/generator/src/openapi-utils.ts | 111 +++++++ packages/generator/src/parse-crud-tags.ts | 27 +- packages/next-plugin/src/plugin.ts | 51 ++- packages/react-query/src/query-client.ts | 132 +------- packages/rsbuild-plugin/src/plugin.ts | 56 ++-- .../src/__tests__/generate-devup.test.ts | 312 ++++++++++++++++++ packages/utils/src/generate-devup.ts | 149 +++++++++ packages/utils/src/index.ts | 1 + packages/vite-plugin/src/plugin.ts | 75 ++--- packages/webpack-plugin/src/plugin.ts | 54 ++- 16 files changed, 756 insertions(+), 488 deletions(-) create mode 100644 packages/utils/src/__tests__/generate-devup.test.ts create mode 100644 packages/utils/src/generate-devup.ts diff --git a/packages/core/src/api-struct.ts b/packages/core/src/api-struct.ts index 08f436c..867bcd1 100644 --- a/packages/core/src/api-struct.ts +++ b/packages/core/src/api-struct.ts @@ -76,6 +76,34 @@ export type DevupDeleteApiStructKey = ConditionalKeys< export type DevupPatchApiStructKey = ConditionalKeys< DevupPatchApiStructScope > +export type DevupApiMethodKeys = + | 'get' + | 'post' + | 'put' + | 'delete' + | 'patch' + | 'GET' + | 'POST' + | 'PUT' + | 'DELETE' + | 'PATCH' + +export type DevupApiMethodScope< + S extends string, + M extends DevupApiMethodKeys, +> = { + get: DevupGetApiStructScope + post: DevupPostApiStructScope + put: DevupPutApiStructScope + delete: DevupDeleteApiStructScope + patch: DevupPatchApiStructScope + GET: DevupGetApiStructScope + POST: DevupPostApiStructScope + PUT: DevupPutApiStructScope + DELETE: DevupDeleteApiStructScope + PATCH: DevupPatchApiStructScope +}[M] + export type DevupApiStructScope = DevupGetApiStructScope & DevupPostApiStructScope & DevupPutApiStructScope & diff --git a/packages/generator/src/__tests__/index.test.ts b/packages/generator/src/__tests__/index.test.ts index 65d6d9d..e69224b 100644 --- a/packages/generator/src/__tests__/index.test.ts +++ b/packages/generator/src/__tests__/index.test.ts @@ -30,5 +30,8 @@ test('index.ts exports', () => { extractSchemaNameFromRef: expect.any(Function), normalizeServerName: expect.any(Function), isErrorStatusCode: expect.any(Function), + isNullableSchema: expect.any(Function), + getPrimaryType: expect.any(Function), + collectSchemaNames: expect.any(Function), }) }) diff --git a/packages/generator/src/generate-crud-config.ts b/packages/generator/src/generate-crud-config.ts index 2638dee..d7eaa33 100644 --- a/packages/generator/src/generate-crud-config.ts +++ b/packages/generator/src/generate-crud-config.ts @@ -1,18 +1,9 @@ +import { toPascal } from '@devup-api/utils' import type { OpenAPIV3_1 } from 'openapi-types' import type { CrudConfig, CrudField } from './crud-types' import { parseCrudConfigsFromMultiple } from './parse-crud-tags' import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard' -/** - * Convert string to PascalCase for component names - */ -function toPascalCase(str: string): string { - return str - .split(/[-_]/) - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join('') -} - /** * Convert string to title case for labels */ @@ -162,7 +153,7 @@ function generateFieldsComponent( */ function generateCrudComponent(name: string, config: CrudConfig): string[] { const lines: string[] = [] - const componentName = `${toPascalCase(name)}Crud` + const componentName = `${toPascal(name)}Crud` const fieldsComponentName = `${componentName}Fields` // Determine edit method and operationId @@ -235,7 +226,7 @@ export function generateCrudConfigCode( // Generate component for each CRUD group for (const [name, config] of Object.entries(configs)) { - const componentName = `${toPascalCase(name)}Crud` + const componentName = `${toPascal(name)}Crud` // Get fields from create endpoint (primary form fields) const fields = config.create.fields ?? [] @@ -259,7 +250,7 @@ export function generateCrudConfigCode( // Default export lines.push('export default {') for (const name of Object.keys(configs)) { - const componentName = `${toPascalCase(name)}Crud` + const componentName = `${toPascal(name)}Crud` lines.push(` ${name}: ${componentName},`) } lines.push('};') diff --git a/packages/generator/src/generate-interface.ts b/packages/generator/src/generate-interface.ts index c0c9263..2762acf 100644 --- a/packages/generator/src/generate-interface.ts +++ b/packages/generator/src/generate-interface.ts @@ -11,6 +11,7 @@ import { getTypeFromSchema, } from './generate-schema' import { + collectSchemaNames, extractSchemaNameFromRef, getRequestBodyContent, isErrorStatusCode, @@ -145,73 +146,10 @@ function generateSchemaInterface( } as const const convertCaseType = options?.convertCase ?? 'camel' - // Helper function to collect schema names from a schema object - // Recursively traverses into referenced schemas to find all nested $refs - const collectSchemaNames = ( - schemaObj: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject, - targetSet: Set, - visited: Set = new Set(), - ): void => { - if ('$ref' in schemaObj) { - const schemaName = extractSchemaNameFromRef(schemaObj.$ref) - if (schemaName) { - // Avoid infinite recursion for circular references - if (visited.has(schemaName)) { - return - } - targetSet.add(schemaName) - visited.add(schemaName) - - // Recursively collect from the referenced schema - const referencedSchema = schema.components?.schemas?.[schemaName] - if (referencedSchema) { - collectSchemaNames( - referencedSchema as - | OpenAPIV3_1.SchemaObject - | OpenAPIV3_1.ReferenceObject, - targetSet, - visited, - ) - } - } - return - } - - const schemaObjTyped = schemaObj as OpenAPIV3_1.SchemaObject - - // Check allOf, anyOf, oneOf - if (schemaObjTyped.allOf) { - schemaObjTyped.allOf.forEach((s) => { - collectSchemaNames(s, targetSet, visited) - }) - } - if (schemaObjTyped.anyOf) { - schemaObjTyped.anyOf.forEach((s) => { - collectSchemaNames(s, targetSet, visited) - }) - } - if (schemaObjTyped.oneOf) { - schemaObjTyped.oneOf.forEach((s) => { - collectSchemaNames(s, targetSet, visited) - }) - } - - // Check properties - if (schemaObjTyped.properties) { - Object.values(schemaObjTyped.properties).forEach((prop) => { - collectSchemaNames(prop, targetSet, visited) - }) - } - - // Check items (for arrays) - if ( - schemaObjTyped.type === 'array' && - 'items' in schemaObjTyped && - schemaObjTyped.items - ) { - collectSchemaNames(schemaObjTyped.items, targetSet, visited) - } - } + const collectOpts = { + followComponentRefs: true, + document: schema, + } as const // Track which schemas are used in request body and responses const requestSchemaNames = new Set() @@ -242,7 +180,11 @@ function generateSchemaInterface( const content = operation.requestBody.content const bodyContent = getRequestBodyContent(content) if (bodyContent && 'schema' in bodyContent && bodyContent.schema) { - collectSchemaNames(bodyContent.schema, requestSchemaNames) + collectSchemaNames( + bodyContent.schema, + requestSchemaNames, + collectOpts, + ) } } } @@ -272,9 +214,17 @@ function generateSchemaInterface( jsonContent.schema ) { if (isError) { - collectSchemaNames(jsonContent.schema, errorSchemaNames) + collectSchemaNames( + jsonContent.schema, + errorSchemaNames, + collectOpts, + ) } else { - collectSchemaNames(jsonContent.schema, responseSchemaNames) + collectSchemaNames( + jsonContent.schema, + responseSchemaNames, + collectOpts, + ) } } } diff --git a/packages/generator/src/generate-schema.ts b/packages/generator/src/generate-schema.ts index dd81fff..2c90bf4 100644 --- a/packages/generator/src/generate-schema.ts +++ b/packages/generator/src/generate-schema.ts @@ -1,6 +1,11 @@ import type { OpenAPIV3_1 } from 'openapi-types' import type { ParameterDefinition } from './generate-interface' -import { getRequestBodyContent, resolveRef } from './openapi-utils' +import { + getPrimaryType, + getRequestBodyContent, + isNullableSchema, + resolveRef, +} from './openapi-utils' import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard' /** @@ -83,35 +88,6 @@ function generateEnumName( return name } -/** - * Check if a schema is nullable (OpenAPI 3.0 or 3.1) - * OpenAPI 3.0: uses `nullable: true` - * OpenAPI 3.1: uses type array like `["string", "null"]` - */ -function isNullable(schema: OpenAPIV3_1.SchemaObject): boolean { - // OpenAPI 3.0 style: nullable: true - if ('nullable' in schema && schema.nullable === true) { - return true - } - - // OpenAPI 3.1 style: type is an array containing "null" - if (Array.isArray(schema.type) && schema.type.includes('null')) { - return true - } - - return false -} - -/** - * Get the non-null type from OpenAPI 3.1 type array - * e.g., ["string", "null"] -> "string" - */ -function getNonNullType( - types: (OpenAPIV3_1.NonArraySchemaObjectType | 'array')[], -): OpenAPIV3_1.NonArraySchemaObjectType | 'array' | undefined { - return types.find((t) => t !== 'null') -} - /** * Options for component reference handling */ @@ -241,7 +217,7 @@ export function getTypeFromSchema( } // Check if schema is nullable - const nullable = isNullable(schemaObj) + const nullable = isNullableSchema(schemaObj) // Handle enum if (schemaObj.enum) { @@ -273,9 +249,7 @@ export function getTypeFromSchema( } // Get the actual type (handle OpenAPI 3.1 type arrays) - const actualType = Array.isArray(schemaObj.type) - ? getNonNullType(schemaObj.type) - : schemaObj.type + const actualType = getPrimaryType(schemaObj) // Handle primitive types if (actualType === 'string') { @@ -718,7 +692,10 @@ export function extractRequestBody( } if ('$ref' in requestBody) { - const resolved = resolveRef(requestBody.$ref, document) + const resolved = resolveRef( + requestBody.$ref, + document, + ) if (resolved && 'content' in resolved && resolved.content) { const content = resolved.content as OpenAPIV3_1.RequestBodyObject['content'] diff --git a/packages/generator/src/generate-zod.ts b/packages/generator/src/generate-zod.ts index a85be6e..a8a0257 100644 --- a/packages/generator/src/generate-zod.ts +++ b/packages/generator/src/generate-zod.ts @@ -3,8 +3,11 @@ import type { OpenAPIV3_1 } from 'openapi-types' import { convertCase } from './convert-case' import { CONTENT_TYPE_PRIORITY, + collectSchemaNames, extractSchemaNameFromRef, + getPrimaryType, isErrorStatusCode, + isNullableSchema, normalizeServerName, resolveRef, } from './openapi-utils' @@ -41,37 +44,14 @@ function schemaToZod( const schemaObj = schema as OpenAPIV3_1.SchemaObject - // Handle nullable (OpenAPI 3.0 uses 'nullable', OpenAPI 3.1 uses type array with 'null') - const isNullable = (): boolean => { - // Check for OpenAPI 3.0 style nullable - if ('nullable' in schemaObj && schemaObj.nullable === true) { - return true - } - // Check for OpenAPI 3.1 style nullable (type: ["string", "null"]) - if (Array.isArray(schemaObj.type) && schemaObj.type.includes('null')) { - return true - } - return false - } - const wrapNullable = (zodStr: string): string => { - if (isNullable()) { + if (isNullableSchema(schemaObj)) { return `${zodStr}.nullable()` } return zodStr } - // Helper to get the primary type from OpenAPI 3.1 type array - const getPrimaryType = (): string | undefined => { - if (Array.isArray(schemaObj.type)) { - // Filter out 'null' to get the primary type - const nonNullTypes = schemaObj.type.filter((t) => t !== 'null') - return nonNullTypes[0] - } - return schemaObj.type - } - - const primaryType = getPrimaryType() + const primaryType = getPrimaryType(schemaObj) // Handle allOf (intersection) if (schemaObj.allOf) { @@ -283,35 +263,14 @@ function schemaToZodType( const schemaObj = schema as OpenAPIV3_1.SchemaObject - // Handle nullable - const isNullable = (): boolean => { - if ('nullable' in schemaObj && schemaObj.nullable === true) { - return true - } - if (Array.isArray(schemaObj.type) && schemaObj.type.includes('null')) { - return true - } - return false - } - const wrapNullable = (zodType: string): string => { - if (isNullable()) { + if (isNullableSchema(schemaObj)) { return `z.ZodNullable<${zodType}>` } return zodType } - // Helper to get the primary type from OpenAPI 3.1 type array - const getPrimaryType = (): string | undefined => { - if (Array.isArray(schemaObj.type)) { - // Filter out 'null' to get the primary type - const nonNullTypes = schemaObj.type.filter((t) => t !== 'null') - return nonNullTypes[0] - } - return schemaObj.type - } - - const primaryType = getPrimaryType() + const primaryType = getPrimaryType(schemaObj) // Handle allOf (intersection) if (schemaObj.allOf) { @@ -471,42 +430,6 @@ function collectSchemaUsage( } const convertCaseType = options?.convertCase ?? 'camel' - const collectSchemaNames = ( - schemaObj: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject, - targetSet: Set, - ): void => { - if ('$ref' in schemaObj) { - const schemaName = extractSchemaNameFromRef(schemaObj.$ref) - if (schemaName) { - targetSet.add(schemaName) - } - return - } - - const s = schemaObj as OpenAPIV3_1.SchemaObject - - if (s.allOf) - s.allOf.forEach((sub) => { - collectSchemaNames(sub, targetSet) - }) - if (s.anyOf) - s.anyOf.forEach((sub) => { - collectSchemaNames(sub, targetSet) - }) - if (s.oneOf) - s.oneOf.forEach((sub) => { - collectSchemaNames(sub, targetSet) - }) - if (s.properties) { - Object.values(s.properties).forEach((prop) => { - collectSchemaNames(prop, targetSet) - }) - } - if (s.type === 'array' && 'items' in s && s.items) { - collectSchemaNames(s.items, targetSet) - } - } - // Helper to get direct schema name from request body const getRequestBodySchemaName = ( requestBody: OpenAPIV3_1.RequestBodyObject | OpenAPIV3_1.ReferenceObject, diff --git a/packages/generator/src/openapi-utils.ts b/packages/generator/src/openapi-utils.ts index 29e63b2..a41b7e6 100644 --- a/packages/generator/src/openapi-utils.ts +++ b/packages/generator/src/openapi-utils.ts @@ -103,3 +103,114 @@ export function isErrorStatusCode(statusCode: string): boolean { const code = parseInt(statusCode, 10) return code >= 400 && code < 600 } + +// ============================================================================= +// Nullable Detection +// ============================================================================= + +/** + * Check if a schema is nullable (OpenAPI 3.0 or 3.1). + * OpenAPI 3.0: uses `nullable: true` + * OpenAPI 3.1: uses type array like `["string", "null"]` + */ +export function isNullableSchema(schema: OpenAPIV3_1.SchemaObject): boolean { + if ('nullable' in schema && schema.nullable === true) { + return true + } + if (Array.isArray(schema.type) && schema.type.includes('null')) { + return true + } + return false +} + +// ============================================================================= +// Type Extraction +// ============================================================================= + +/** + * Get the primary (non-null) type from an OpenAPI schema. + * For OpenAPI 3.1 type arrays like `["string", "null"]`, returns `"string"`. + * For simple type strings, returns the type directly. + */ +export function getPrimaryType( + schema: OpenAPIV3_1.SchemaObject, +): string | undefined { + if (Array.isArray(schema.type)) { + return schema.type.find((t) => t !== 'null') + } + return schema.type +} + +// ============================================================================= +// Schema Name Collection +// ============================================================================= + +/** + * Recursively collect component schema names referenced by a schema object. + * + * When `followComponentRefs` is true (and `document` is provided), follows + * `$ref` into `components.schemas` to discover transitive dependencies, + * with circular-reference protection via the `visited` set. + * + * When `followComponentRefs` is false (default), only collects the direct + * schema name from `$ref` without following into the referenced schema. + */ +export function collectSchemaNames( + schemaObj: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject, + targetSet: Set, + options?: { + followComponentRefs?: boolean + document?: OpenAPIV3_1.Document + visited?: Set + }, +): void { + if ('$ref' in schemaObj) { + const schemaName = extractSchemaNameFromRef(schemaObj.$ref) + if (schemaName) { + targetSet.add(schemaName) + + if (options?.followComponentRefs && options.document) { + const visited = options.visited ?? new Set() + if (visited.has(schemaName)) return + visited.add(schemaName) + + const referencedSchema = options.document.components?.schemas?.[ + schemaName + ] as OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject | undefined + if (referencedSchema) { + collectSchemaNames(referencedSchema, targetSet, { + ...options, + visited, + }) + } + } + } + return + } + + const s = schemaObj as OpenAPIV3_1.SchemaObject + + if (s.allOf) { + for (const sub of s.allOf) { + collectSchemaNames(sub, targetSet, options) + } + } + if (s.anyOf) { + for (const sub of s.anyOf) { + collectSchemaNames(sub, targetSet, options) + } + } + if (s.oneOf) { + for (const sub of s.oneOf) { + collectSchemaNames(sub, targetSet, options) + } + } + if (s.properties) { + for (const prop of Object.values(s.properties)) { + collectSchemaNames(prop, targetSet, options) + } + } + if (s.type === 'array' && 'items' in s && s.items) { + collectSchemaNames(s.items, targetSet, options) + } +} diff --git a/packages/generator/src/parse-crud-tags.ts b/packages/generator/src/parse-crud-tags.ts index 2230461..8ff28a0 100644 --- a/packages/generator/src/parse-crud-tags.ts +++ b/packages/generator/src/parse-crud-tags.ts @@ -7,6 +7,7 @@ import type { DevupOperation, ParsedDevupTag, } from './crud-types' +import { resolveRef } from './openapi-utils' /** * Regex pattern for devup tags: devup:{name}:{mode} @@ -77,30 +78,6 @@ function getExpectedModeForMethod(method: string): CrudMode | null { } } -/** - * Resolve a schema reference - */ -function resolveSchemaRef( - ref: string, - document: OpenAPIV3_1.Document, -): OpenAPIV3_1.SchemaObject | null { - // Format: #/components/schemas/SchemaName - const parts = ref.split('/') - if ( - parts.length !== 4 || - parts[1] !== 'components' || - parts[2] !== 'schemas' - ) { - return null - } - const schemaName = parts[3] - return ( - (document.components?.schemas?.[ - schemaName as string - ] as OpenAPIV3_1.SchemaObject) ?? null - ) -} - /** * Extract fields from an OpenAPI schema */ @@ -111,7 +88,7 @@ function extractFieldsFromSchema( // Resolve reference if needed let resolvedSchema: OpenAPIV3_1.SchemaObject if ('$ref' in schema) { - const resolved = resolveSchemaRef(schema.$ref, document) + const resolved = resolveRef(schema.$ref, document) if (!resolved) return [] resolvedSchema = resolved } else { diff --git a/packages/next-plugin/src/plugin.ts b/packages/next-plugin/src/plugin.ts index 4c8fd54..6fd132d 100644 --- a/packages/next-plugin/src/plugin.ts +++ b/packages/next-plugin/src/plugin.ts @@ -1,4 +1,4 @@ -import { join, relative, resolve } from 'node:path' +import { relative, resolve } from 'node:path' import type { DevupApiOptions } from '@devup-api/core' import { createUrlMap, @@ -10,6 +10,9 @@ import { } from '@devup-api/generator' import { createTmpDir, + type DevupGenerators, + type DevupIOSync, + generateDevupArtifacts, normalizeOpenapiFiles, readOpenapis, writeInterface, @@ -31,39 +34,25 @@ export function devupApi( const isTurbo = process.env.TURBOPACK === '1' || process.env.TURBOPACK === 'auto' if (isTurbo) { - const tempDir = createTmpDir(options?.tempDir) - const openapiFiles = normalizeOpenapiFiles(options?.openapiFiles) - const schemas = readOpenapis(openapiFiles) - - // Generate API interface file - writeInterface( - join(tempDir, 'api.d.ts'), - generateInterface(schemas, options), - ) - - // Generate Zod schemas file - writeInterface( - join(tempDir, 'zod-schemas.js'), - generateZodSchemas(schemas, options), - ) - - // Generate Zod type declarations - writeInterface( - join(tempDir, 'zod.d.ts'), - generateZodTypeDeclarations(schemas, options), - ) + const io: DevupIOSync = { + createTmpDir, + normalizeOpenapiFiles, + readOpenapis, + writeInterface, + } - // Generate CRUD configs file - writeInterface( - join(tempDir, 'crud-configs.jsx'), - generateCrudConfigCode(schemas), - ) + const generators: DevupGenerators = { + generateInterface, + generateZodSchemas, + generateZodTypeDeclarations, + generateCrudConfigCode, + generateCrudConfigTypes, + createUrlMap, + } - // Generate CRUD config type declarations - writeInterface(join(tempDir, 'ui.d.ts'), generateCrudConfigTypes(schemas)) + const { tempDir, urlMap } = generateDevupArtifacts(io, generators, options) - // Create urlMap and set environment variable - const urlMap = createUrlMap(schemas, options) + // Set environment variable config.env ??= {} if (urlMap && Object.keys(urlMap).length > 0) { Object.assign(config.env, { diff --git a/packages/react-query/src/query-client.ts b/packages/react-query/src/query-client.ts index b7a32bc..006bb61 100644 --- a/packages/react-query/src/query-client.ts +++ b/packages/react-query/src/query-client.ts @@ -4,14 +4,11 @@ import type { ConditionalApiOption, ConditionalKeys, DevupApi, + DevupApiMethodKeys, + DevupApiMethodScope, DevupApiRequestInit, DevupApiResponse, DevupApiServers, - DevupDeleteApiStructScope, - DevupGetApiStructScope, - DevupPatchApiStructScope, - DevupPostApiStructScope, - DevupPutApiStructScope, ExtractValue, } from '@devup-api/fetch' import { @@ -40,29 +37,8 @@ export class DevupQueryClient> { } useQuery< - M extends - | 'get' - | 'post' - | 'put' - | 'delete' - | 'patch' - | 'GET' - | 'POST' - | 'PUT' - | 'DELETE' - | 'PATCH', - ST extends { - get: DevupGetApiStructScope - post: DevupPostApiStructScope - put: DevupPutApiStructScope - delete: DevupDeleteApiStructScope - patch: DevupPatchApiStructScope - GET: DevupGetApiStructScope - POST: DevupPostApiStructScope - PUT: DevupPutApiStructScope - DELETE: DevupDeleteApiStructScope - PATCH: DevupPatchApiStructScope - }[M], + M extends DevupApiMethodKeys, + ST extends DevupApiMethodScope, T extends ConditionalKeys, O extends Additional, D extends ExtractValue, @@ -105,29 +81,8 @@ export class DevupQueryClient> { } useMutation< - M extends - | 'get' - | 'post' - | 'put' - | 'delete' - | 'patch' - | 'GET' - | 'POST' - | 'PUT' - | 'DELETE' - | 'PATCH', - ST extends { - get: DevupGetApiStructScope - post: DevupPostApiStructScope - put: DevupPutApiStructScope - delete: DevupDeleteApiStructScope - patch: DevupPatchApiStructScope - GET: DevupGetApiStructScope - POST: DevupPostApiStructScope - PUT: DevupPutApiStructScope - DELETE: DevupDeleteApiStructScope - PATCH: DevupPatchApiStructScope - }[M], + M extends DevupApiMethodKeys, + ST extends DevupApiMethodScope, T extends ConditionalKeys, O extends Additional, D extends ExtractValue, @@ -160,29 +115,8 @@ export class DevupQueryClient> { } useSuspenseQuery< - M extends - | 'get' - | 'post' - | 'put' - | 'delete' - | 'patch' - | 'GET' - | 'POST' - | 'PUT' - | 'DELETE' - | 'PATCH', - ST extends { - get: DevupGetApiStructScope - post: DevupPostApiStructScope - put: DevupPutApiStructScope - delete: DevupDeleteApiStructScope - patch: DevupPatchApiStructScope - GET: DevupGetApiStructScope - POST: DevupPostApiStructScope - PUT: DevupPutApiStructScope - DELETE: DevupDeleteApiStructScope - PATCH: DevupPatchApiStructScope - }[M], + M extends DevupApiMethodKeys, + ST extends DevupApiMethodScope, T extends ConditionalKeys, O extends Additional, D extends ExtractValue, @@ -225,29 +159,8 @@ export class DevupQueryClient> { } useInfiniteQuery< - M extends - | 'get' - | 'post' - | 'put' - | 'delete' - | 'patch' - | 'GET' - | 'POST' - | 'PUT' - | 'DELETE' - | 'PATCH', - ST extends { - get: DevupGetApiStructScope - post: DevupPostApiStructScope - put: DevupPutApiStructScope - delete: DevupDeleteApiStructScope - patch: DevupPatchApiStructScope - GET: DevupGetApiStructScope - POST: DevupPostApiStructScope - PUT: DevupPutApiStructScope - DELETE: DevupDeleteApiStructScope - PATCH: DevupPatchApiStructScope - }[M], + M extends DevupApiMethodKeys, + ST extends DevupApiMethodScope, T extends ConditionalKeys, O extends Additional, D extends ExtractValue, @@ -302,29 +215,8 @@ export class DevupQueryClient> { } useQueries< - M extends - | 'get' - | 'post' - | 'put' - | 'delete' - | 'patch' - | 'GET' - | 'POST' - | 'PUT' - | 'DELETE' - | 'PATCH', - ST extends { - get: DevupGetApiStructScope - post: DevupPostApiStructScope - put: DevupPutApiStructScope - delete: DevupDeleteApiStructScope - patch: DevupPatchApiStructScope - GET: DevupGetApiStructScope - POST: DevupPostApiStructScope - PUT: DevupPutApiStructScope - DELETE: DevupDeleteApiStructScope - PATCH: DevupPatchApiStructScope - }[M], + M extends DevupApiMethodKeys, + ST extends DevupApiMethodScope, T extends ConditionalKeys, O extends Additional, D extends ExtractValue, diff --git a/packages/rsbuild-plugin/src/plugin.ts b/packages/rsbuild-plugin/src/plugin.ts index 82dbe25..19ceaad 100644 --- a/packages/rsbuild-plugin/src/plugin.ts +++ b/packages/rsbuild-plugin/src/plugin.ts @@ -1,4 +1,4 @@ -import { join, resolve } from 'node:path' +import { resolve } from 'node:path' import type { DevupApiOptions } from '@devup-api/core' import { createUrlMap, @@ -10,6 +10,9 @@ import { } from '@devup-api/generator' import { createTmpDirAsync, + type DevupGenerators, + type DevupIOAsync, + generateDevupArtifactsAsync, normalizeOpenapiFiles, readOpenapiAsync, writeInterfaceAsync, @@ -22,43 +25,28 @@ export function devupApiRsbuildPlugin( return { name: 'devup-api', async setup(build) { - const tempDir = await createTmpDirAsync(options?.tempDir) - const openapiFiles = normalizeOpenapiFiles(options?.openapiFiles) - const schemas = await readOpenapiAsync(openapiFiles) + const io: DevupIOAsync = { + createTmpDirAsync, + normalizeOpenapiFiles, + readOpenapiAsync, + writeInterfaceAsync, + } - // Generate interface file - await writeInterfaceAsync( - join(tempDir, 'api.d.ts'), - generateInterface(schemas, options), - ) - - // Generate Zod schemas file - await writeInterfaceAsync( - join(tempDir, 'zod-schemas.js'), - generateZodSchemas(schemas, options), - ) - - // Generate Zod type declarations - await writeInterfaceAsync( - join(tempDir, 'zod.d.ts'), - generateZodTypeDeclarations(schemas, options), - ) - - // Generate CRUD configs file - await writeInterfaceAsync( - join(tempDir, 'crud-configs.jsx'), - generateCrudConfigCode(schemas), - ) + const generators: DevupGenerators = { + generateInterface, + generateZodSchemas, + generateZodTypeDeclarations, + generateCrudConfigCode, + generateCrudConfigTypes, + createUrlMap, + } - // Generate CRUD config type declarations - await writeInterfaceAsync( - join(tempDir, 'ui.d.ts'), - generateCrudConfigTypes(schemas), + const { tempDir, urlMap } = await generateDevupArtifactsAsync( + io, + generators, + options, ) - // Create urlMap and set environment variable - const urlMap = createUrlMap(schemas, options) - // Get absolute paths for virtual modules const zodSchemasPath = resolve(tempDir, 'zod-schemas.js') const crudConfigsPath = resolve(tempDir, 'crud-configs.jsx') diff --git a/packages/utils/src/__tests__/generate-devup.test.ts b/packages/utils/src/__tests__/generate-devup.test.ts new file mode 100644 index 0000000..b3dbe4d --- /dev/null +++ b/packages/utils/src/__tests__/generate-devup.test.ts @@ -0,0 +1,312 @@ +import { expect, mock, test } from 'bun:test' +import { join } from 'node:path' +import type { OpenAPIV3_1 } from 'openapi-types' +import type { + DevupGenerators, + DevupIOAsync, + DevupIOSync, +} from '../generate-devup' +import { + generateDevupArtifacts, + generateDevupArtifactsAsync, +} from '../generate-devup' + +const mockSchema = { + openapi: '3.1.0', + paths: { + '/users': { + get: { + operationId: 'getUsers', + responses: { + '200': { + content: { + 'application/json': { + schema: { type: 'array', items: { type: 'string' } }, + }, + }, + }, + }, + }, + }, + }, +} as unknown as OpenAPIV3_1.Document + +const mockSchemas: Record = { + 'openapi.json': mockSchema, +} + +const mockUrlMap = { getUsers: { method: 'GET', url: '/users' } } + +function createMockGenerators(): DevupGenerators<{ + openapiFiles?: string | string[] + tempDir?: string +}> { + return { + generateInterface: mock(() => 'interface-content'), + generateZodSchemas: mock(() => 'zod-schemas-content'), + generateZodTypeDeclarations: mock(() => 'zod-types-content'), + generateCrudConfigCode: mock(() => 'crud-config-content'), + generateCrudConfigTypes: mock(() => 'crud-types-content'), + createUrlMap: mock(() => mockUrlMap), + } +} + +function createMockIOAsync(): DevupIOAsync { + return { + createTmpDirAsync: mock(async () => 'df'), + normalizeOpenapiFiles: mock(() => ['openapi.json']), + readOpenapiAsync: mock(async () => mockSchemas), + writeInterfaceAsync: mock(async () => {}), + } +} + +function createMockIOSync(): DevupIOSync { + return { + createTmpDir: mock(() => 'df'), + normalizeOpenapiFiles: mock(() => ['openapi.json']), + readOpenapis: mock(() => mockSchemas), + writeInterface: mock(() => {}), + } +} + +// ============================================================================= +// generateDevupArtifactsAsync +// ============================================================================= + +test('generateDevupArtifactsAsync returns correct artifacts', async () => { + const io = createMockIOAsync() + const generators = createMockGenerators() + + const result = await generateDevupArtifactsAsync(io, generators) + + expect(result.tempDir).toBe('df') + expect(result.schemas).toBe(mockSchemas) + expect(result.files.interface).toBe('interface-content') + expect(result.files.zodSchemas).toBe('zod-schemas-content') + expect(result.files.zodTypes).toBe('zod-types-content') + expect(result.files.crudConfig).toBe('crud-config-content') + expect(result.files.crudTypes).toBe('crud-types-content') + expect(result.urlMap).toBe(mockUrlMap) +}) + +test('generateDevupArtifactsAsync calls IO functions correctly', async () => { + const io = createMockIOAsync() + const generators = createMockGenerators() + const options = { tempDir: 'custom-dir', openapiFiles: 'api.json' } + + await generateDevupArtifactsAsync(io, generators, options) + + expect(io.createTmpDirAsync).toHaveBeenCalledWith('custom-dir') + expect(io.normalizeOpenapiFiles).toHaveBeenCalledWith('api.json') + expect(io.readOpenapiAsync).toHaveBeenCalledWith(['openapi.json']) +}) + +test('generateDevupArtifactsAsync calls all generators with schemas and options', async () => { + const io = createMockIOAsync() + const generators = createMockGenerators() + const options = { tempDir: 'df', openapiFiles: 'openapi.json' } + + await generateDevupArtifactsAsync(io, generators, options) + + expect(generators.generateInterface).toHaveBeenCalledWith( + mockSchemas, + options, + ) + expect(generators.generateZodSchemas).toHaveBeenCalledWith( + mockSchemas, + options, + ) + expect(generators.generateZodTypeDeclarations).toHaveBeenCalledWith( + mockSchemas, + options, + ) + expect(generators.generateCrudConfigCode).toHaveBeenCalledWith(mockSchemas) + expect(generators.generateCrudConfigTypes).toHaveBeenCalledWith(mockSchemas) + expect(generators.createUrlMap).toHaveBeenCalledWith(mockSchemas, options) +}) + +test('generateDevupArtifactsAsync writes all 5 files to tempDir', async () => { + const io = createMockIOAsync() + const generators = createMockGenerators() + + await generateDevupArtifactsAsync(io, generators) + + expect(io.writeInterfaceAsync).toHaveBeenCalledTimes(5) + expect(io.writeInterfaceAsync).toHaveBeenCalledWith( + join('df', 'api.d.ts'), + 'interface-content', + ) + expect(io.writeInterfaceAsync).toHaveBeenCalledWith( + join('df', 'zod-schemas.js'), + 'zod-schemas-content', + ) + expect(io.writeInterfaceAsync).toHaveBeenCalledWith( + join('df', 'zod.d.ts'), + 'zod-types-content', + ) + expect(io.writeInterfaceAsync).toHaveBeenCalledWith( + join('df', 'crud-configs.jsx'), + 'crud-config-content', + ) + expect(io.writeInterfaceAsync).toHaveBeenCalledWith( + join('df', 'ui.d.ts'), + 'crud-types-content', + ) +}) + +test('generateDevupArtifactsAsync passes undefined options when none provided', async () => { + const io = createMockIOAsync() + const generators = createMockGenerators() + + await generateDevupArtifactsAsync(io, generators) + + expect(io.createTmpDirAsync).toHaveBeenCalledWith(undefined) + expect(io.normalizeOpenapiFiles).toHaveBeenCalledWith(undefined) + expect(generators.generateInterface).toHaveBeenCalledWith( + mockSchemas, + undefined, + ) + expect(generators.createUrlMap).toHaveBeenCalledWith(mockSchemas, undefined) +}) + +// ============================================================================= +// generateDevupArtifacts (sync) +// ============================================================================= + +test('generateDevupArtifacts returns correct artifacts', () => { + const io = createMockIOSync() + const generators = createMockGenerators() + + const result = generateDevupArtifacts(io, generators) + + expect(result.tempDir).toBe('df') + expect(result.schemas).toBe(mockSchemas) + expect(result.files.interface).toBe('interface-content') + expect(result.files.zodSchemas).toBe('zod-schemas-content') + expect(result.files.zodTypes).toBe('zod-types-content') + expect(result.files.crudConfig).toBe('crud-config-content') + expect(result.files.crudTypes).toBe('crud-types-content') + expect(result.urlMap).toBe(mockUrlMap) +}) + +test('generateDevupArtifacts calls IO functions correctly', () => { + const io = createMockIOSync() + const generators = createMockGenerators() + const options = { tempDir: 'custom-dir', openapiFiles: 'api.json' } + + generateDevupArtifacts(io, generators, options) + + expect(io.createTmpDir).toHaveBeenCalledWith('custom-dir') + expect(io.normalizeOpenapiFiles).toHaveBeenCalledWith('api.json') + expect(io.readOpenapis).toHaveBeenCalledWith(['openapi.json']) +}) + +test('generateDevupArtifacts calls all generators with schemas and options', () => { + const io = createMockIOSync() + const generators = createMockGenerators() + const options = { tempDir: 'df', openapiFiles: 'openapi.json' } + + generateDevupArtifacts(io, generators, options) + + expect(generators.generateInterface).toHaveBeenCalledWith( + mockSchemas, + options, + ) + expect(generators.generateZodSchemas).toHaveBeenCalledWith( + mockSchemas, + options, + ) + expect(generators.generateZodTypeDeclarations).toHaveBeenCalledWith( + mockSchemas, + options, + ) + expect(generators.generateCrudConfigCode).toHaveBeenCalledWith(mockSchemas) + expect(generators.generateCrudConfigTypes).toHaveBeenCalledWith(mockSchemas) + expect(generators.createUrlMap).toHaveBeenCalledWith(mockSchemas, options) +}) + +test('generateDevupArtifacts writes all 5 files to tempDir', () => { + const io = createMockIOSync() + const generators = createMockGenerators() + + generateDevupArtifacts(io, generators) + + expect(io.writeInterface).toHaveBeenCalledTimes(5) + expect(io.writeInterface).toHaveBeenCalledWith( + join('df', 'api.d.ts'), + 'interface-content', + ) + expect(io.writeInterface).toHaveBeenCalledWith( + join('df', 'zod-schemas.js'), + 'zod-schemas-content', + ) + expect(io.writeInterface).toHaveBeenCalledWith( + join('df', 'zod.d.ts'), + 'zod-types-content', + ) + expect(io.writeInterface).toHaveBeenCalledWith( + join('df', 'crud-configs.jsx'), + 'crud-config-content', + ) + expect(io.writeInterface).toHaveBeenCalledWith( + join('df', 'ui.d.ts'), + 'crud-types-content', + ) +}) + +test('generateDevupArtifacts passes undefined options when none provided', () => { + const io = createMockIOSync() + const generators = createMockGenerators() + + generateDevupArtifacts(io, generators) + + expect(io.createTmpDir).toHaveBeenCalledWith(undefined) + expect(io.normalizeOpenapiFiles).toHaveBeenCalledWith(undefined) + expect(generators.generateInterface).toHaveBeenCalledWith( + mockSchemas, + undefined, + ) + expect(generators.createUrlMap).toHaveBeenCalledWith(mockSchemas, undefined) +}) + +test('generateDevupArtifacts uses custom tempDir in file paths', () => { + const io = createMockIOSync() + ;(io.createTmpDir as ReturnType).mockReturnValue('custom-temp') + const generators = createMockGenerators() + + const result = generateDevupArtifacts(io, generators, { + tempDir: 'custom-temp', + }) + + expect(result.tempDir).toBe('custom-temp') + expect(io.writeInterface).toHaveBeenCalledWith( + join('custom-temp', 'api.d.ts'), + 'interface-content', + ) + expect(io.writeInterface).toHaveBeenCalledWith( + join('custom-temp', 'zod-schemas.js'), + 'zod-schemas-content', + ) +}) + +test('generateDevupArtifactsAsync uses custom tempDir in file paths', async () => { + const io = createMockIOAsync() + ;(io.createTmpDirAsync as ReturnType).mockResolvedValue( + 'custom-temp', + ) + const generators = createMockGenerators() + + const result = await generateDevupArtifactsAsync(io, generators, { + tempDir: 'custom-temp', + }) + + expect(result.tempDir).toBe('custom-temp') + expect(io.writeInterfaceAsync).toHaveBeenCalledWith( + join('custom-temp', 'api.d.ts'), + 'interface-content', + ) + expect(io.writeInterfaceAsync).toHaveBeenCalledWith( + join('custom-temp', 'zod-schemas.js'), + 'zod-schemas-content', + ) +}) diff --git a/packages/utils/src/generate-devup.ts b/packages/utils/src/generate-devup.ts new file mode 100644 index 0000000..bb14a97 --- /dev/null +++ b/packages/utils/src/generate-devup.ts @@ -0,0 +1,149 @@ +import { join } from 'node:path' +import type { OpenAPIV3_1 } from 'openapi-types' + +/** + * Minimal options interface for the generation pipeline. + * Defined locally to avoid depending on @devup-api/core. + */ +interface GenerateDevupOptions { + openapiFiles?: string | string[] + tempDir?: string +} + +/** + * Generator functions injected by the caller. + * This decouples @devup-api/utils from @devup-api/generator. + */ +export interface DevupGenerators { + generateInterface: ( + schemas: Record, + options?: TOptions, + ) => string + generateZodSchemas: ( + schemas: Record, + options?: TOptions, + ) => string + generateZodTypeDeclarations: ( + schemas: Record, + options?: TOptions, + ) => string + generateCrudConfigCode: ( + schemas: Record, + ) => string + generateCrudConfigTypes: ( + schemas: Record, + ) => string + createUrlMap: ( + schemas: Record, + options?: TOptions, + ) => Record +} + +/** + * IO functions for the async pipeline. + */ +export interface DevupIOAsync { + createTmpDirAsync: (tempDir?: string) => Promise + normalizeOpenapiFiles: (openapiFiles?: string[] | string) => string[] + readOpenapiAsync: ( + openapiFiles: string[], + ) => Promise> + writeInterfaceAsync: (filePath: string, content: string) => Promise +} + +/** + * IO functions for the sync pipeline. + */ +export interface DevupIOSync { + createTmpDir: (tempDir?: string) => string + normalizeOpenapiFiles: (openapiFiles?: string[] | string) => string[] + readOpenapis: (openapiFiles: string[]) => Record + writeInterface: (filePath: string, content: string) => void +} + +/** + * Generated file contents from the pipeline. + */ +export interface DevupGeneratedFiles { + interface: string + zodSchemas: string + zodTypes: string + crudConfig: string + crudTypes: string +} + +/** + * Result of the generation pipeline. + */ +export interface DevupArtifacts { + tempDir: string + schemas: Record + files: DevupGeneratedFiles + urlMap: Record +} + +/** + * Async generation pipeline: read schemas → generate all outputs → write files → create URL map. + * Used by Vite, Webpack, and Rsbuild plugins. + */ +export async function generateDevupArtifactsAsync< + TOptions extends GenerateDevupOptions, +>( + io: DevupIOAsync, + generators: DevupGenerators, + options?: TOptions, +): Promise { + const tempDir = await io.createTmpDirAsync(options?.tempDir) + const openapiFiles = io.normalizeOpenapiFiles(options?.openapiFiles) + const schemas = await io.readOpenapiAsync(openapiFiles) + + const files: DevupGeneratedFiles = { + interface: generators.generateInterface(schemas, options), + zodSchemas: generators.generateZodSchemas(schemas, options), + zodTypes: generators.generateZodTypeDeclarations(schemas, options), + crudConfig: generators.generateCrudConfigCode(schemas), + crudTypes: generators.generateCrudConfigTypes(schemas), + } + + await Promise.all([ + io.writeInterfaceAsync(join(tempDir, 'api.d.ts'), files.interface), + io.writeInterfaceAsync(join(tempDir, 'zod-schemas.js'), files.zodSchemas), + io.writeInterfaceAsync(join(tempDir, 'zod.d.ts'), files.zodTypes), + io.writeInterfaceAsync(join(tempDir, 'crud-configs.jsx'), files.crudConfig), + io.writeInterfaceAsync(join(tempDir, 'ui.d.ts'), files.crudTypes), + ]) + + const urlMap = generators.createUrlMap(schemas, options) + return { tempDir, schemas, files, urlMap } +} + +/** + * Sync generation pipeline: read schemas → generate all outputs → write files → create URL map. + * Used by Next.js Turbopack plugin. + */ +export function generateDevupArtifacts( + io: DevupIOSync, + generators: DevupGenerators, + options?: TOptions, +): DevupArtifacts { + const tempDir = io.createTmpDir(options?.tempDir) + const openapiFiles = io.normalizeOpenapiFiles(options?.openapiFiles) + const schemas = io.readOpenapis(openapiFiles) + + const files: DevupGeneratedFiles = { + interface: generators.generateInterface(schemas, options), + zodSchemas: generators.generateZodSchemas(schemas, options), + zodTypes: generators.generateZodTypeDeclarations(schemas, options), + crudConfig: generators.generateCrudConfigCode(schemas), + crudTypes: generators.generateCrudConfigTypes(schemas), + } + + io.writeInterface(join(tempDir, 'api.d.ts'), files.interface) + io.writeInterface(join(tempDir, 'zod-schemas.js'), files.zodSchemas) + io.writeInterface(join(tempDir, 'zod.d.ts'), files.zodTypes) + io.writeInterface(join(tempDir, 'crud-configs.jsx'), files.crudConfig) + io.writeInterface(join(tempDir, 'ui.d.ts'), files.crudTypes) + + const urlMap = generators.createUrlMap(schemas, options) + return { tempDir, schemas, files, urlMap } +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 31e3081..767fb41 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,4 +1,5 @@ export * from './create-tmp-dir' +export * from './generate-devup' export * from './read-openapi' export * from './to-camel' export * from './to-pascal' diff --git a/packages/vite-plugin/src/plugin.ts b/packages/vite-plugin/src/plugin.ts index 3201e44..dd48000 100644 --- a/packages/vite-plugin/src/plugin.ts +++ b/packages/vite-plugin/src/plugin.ts @@ -1,4 +1,3 @@ -import { join } from 'node:path' import type { DevupApiOptions } from '@devup-api/core' import { createUrlMap, @@ -10,11 +9,14 @@ import { } from '@devup-api/generator' import { createTmpDirAsync, + type DevupArtifacts, + type DevupGenerators, + type DevupIOAsync, + generateDevupArtifactsAsync, normalizeOpenapiFiles, readOpenapiAsync, writeInterfaceAsync, } from '@devup-api/utils' -import type { OpenAPIV3_1 } from 'openapi-types' import type { Plugin } from 'vite' const VIRTUAL_ZOD_MODULE = '@devup-api/zod' @@ -24,18 +26,29 @@ const VIRTUAL_UI_MODULE = '@devup-api/ui/crud' const RESOLVED_VIRTUAL_UI_MODULE = `\0${VIRTUAL_UI_MODULE}` export function devupApi(options?: DevupApiOptions): Plugin { - let cachedSchemas: Record | null = null - let zodSchemasCode: string | null = null - let crudConfigCode: string | null = null + let artifacts: DevupArtifacts | null = null - const getSchemas = async (): Promise< - Record - > => { - if (!cachedSchemas) { - const openapiFiles = normalizeOpenapiFiles(options?.openapiFiles) - cachedSchemas = await readOpenapiAsync(openapiFiles) + const io: DevupIOAsync = { + createTmpDirAsync, + normalizeOpenapiFiles, + readOpenapiAsync, + writeInterfaceAsync, + } + + const generators: DevupGenerators = { + generateInterface, + generateZodSchemas, + generateZodTypeDeclarations, + generateCrudConfigCode, + generateCrudConfigTypes, + createUrlMap, + } + + const getArtifacts = async (): Promise => { + if (!artifacts) { + artifacts = await generateDevupArtifactsAsync(io, generators, options) } - return cachedSchemas + return artifacts } return { @@ -55,50 +68,24 @@ export function devupApi(options?: DevupApiOptions): Plugin { // Load virtual module content async load(id) { if (id === RESOLVED_VIRTUAL_ZOD_MODULE) { - if (!zodSchemasCode) { - const schemas = await getSchemas() - zodSchemasCode = generateZodSchemas(schemas, options) - } - return zodSchemasCode + const { files } = await getArtifacts() + return files.zodSchemas } if (id === RESOLVED_VIRTUAL_UI_MODULE) { - if (!crudConfigCode) { - const schemas = await getSchemas() - crudConfigCode = generateCrudConfigCode(schemas) - } - return crudConfigCode + const { files } = await getArtifacts() + return files.crudConfig } return null }, // Generate type definitions async configResolved() { - const tempDir = await createTmpDirAsync(options?.tempDir) - const schemas = await getSchemas() - - // Write API interface definitions - await writeInterfaceAsync( - join(tempDir, 'api.d.ts'), - generateInterface(schemas, options), - ) - - // Write Zod type declarations - await writeInterfaceAsync( - join(tempDir, 'zod.d.ts'), - generateZodTypeDeclarations(schemas, options), - ) - - // Write CRUD config type declarations - await writeInterfaceAsync( - join(tempDir, 'ui.d.ts'), - generateCrudConfigTypes(schemas), - ) + await getArtifacts() }, // Inject URL map as environment variable async config() { - const schemas = await getSchemas() - const urlMap = createUrlMap(schemas, options) + const { urlMap } = await getArtifacts() const define: Record = {} if (urlMap && Object.keys(urlMap).length > 0) { // json stringify twice to avoid JSON.parse error diff --git a/packages/webpack-plugin/src/plugin.ts b/packages/webpack-plugin/src/plugin.ts index 49df231..e812cea 100644 --- a/packages/webpack-plugin/src/plugin.ts +++ b/packages/webpack-plugin/src/plugin.ts @@ -1,4 +1,4 @@ -import { join, resolve } from 'node:path' +import { resolve } from 'node:path' import type { DevupApiOptions } from '@devup-api/core' import { createUrlMap, @@ -10,6 +10,9 @@ import { } from '@devup-api/generator' import { createTmpDirAsync, + type DevupGenerators, + type DevupIOAsync, + generateDevupArtifactsAsync, normalizeOpenapiFiles, readOpenapiAsync, writeInterfaceAsync, @@ -38,42 +41,29 @@ export class devupApiWebpackPlugin { try { this.initialized = true - const tempDir = await createTmpDirAsync(this.options?.tempDir) - const openapiFiles = normalizeOpenapiFiles(this.options?.openapiFiles) - const schemas = await readOpenapiAsync(openapiFiles) - - // Generate interface file - await writeInterfaceAsync( - join(tempDir, 'api.d.ts'), - generateInterface(schemas, this.options), - ) - - // Generate Zod schemas file - await writeInterfaceAsync( - join(tempDir, 'zod-schemas.js'), - generateZodSchemas(schemas, this.options), - ) - - // Generate Zod type declarations - await writeInterfaceAsync( - join(tempDir, 'zod.d.ts'), - generateZodTypeDeclarations(schemas, this.options), - ) + const io: DevupIOAsync = { + createTmpDirAsync, + normalizeOpenapiFiles, + readOpenapiAsync, + writeInterfaceAsync, + } - // Generate CRUD configs file - await writeInterfaceAsync( - join(tempDir, 'crud-configs.jsx'), - generateCrudConfigCode(schemas), - ) + const generators: DevupGenerators = { + generateInterface, + generateZodSchemas, + generateZodTypeDeclarations, + generateCrudConfigCode, + generateCrudConfigTypes, + createUrlMap, + } - // Generate CRUD config type declarations - await writeInterfaceAsync( - join(tempDir, 'ui.d.ts'), - generateCrudConfigTypes(schemas), + const { tempDir, urlMap } = await generateDevupArtifactsAsync( + io, + generators, + this.options, ) // Create urlMap and set environment variable - const urlMap = createUrlMap(schemas, this.options) const define: Record = {} if (urlMap && Object.keys(urlMap).length > 0) { define['process.env.DEVUP_API_URL_MAP'] = JSON.stringify( From b1494b614b65c442b441cb5aa973a888826adf8e Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 14 Feb 2026 03:20:27 +0900 Subject: [PATCH 15/15] Refactor --- .changepacks/changepack_log_9cW0V-lNDA7Tux1nIoFro.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changepacks/changepack_log_9cW0V-lNDA7Tux1nIoFro.json diff --git a/.changepacks/changepack_log_9cW0V-lNDA7Tux1nIoFro.json b/.changepacks/changepack_log_9cW0V-lNDA7Tux1nIoFro.json new file mode 100644 index 0000000..e9af7b5 --- /dev/null +++ b/.changepacks/changepack_log_9cW0V-lNDA7Tux1nIoFro.json @@ -0,0 +1 @@ +{"changes":{"packages/ui/package.json":"Patch","packages/vite-plugin/package.json":"Patch","packages/webpack-plugin/package.json":"Patch","packages/utils/package.json":"Patch","packages/next-plugin/package.json":"Patch","packages/zod/package.json":"Patch","packages/fetch/package.json":"Patch","packages/generator/package.json":"Patch","packages/core/package.json":"Patch","packages/hookform/package.json":"Patch","packages/react-query/package.json":"Patch","packages/rsbuild-plugin/package.json":"Patch"},"note":"Support form and Refactor","date":"2026-02-13T18:20:24.793922700Z"} \ No newline at end of file