fork of hey-api/openapi-ts because I need some additional things

Merge pull request #1316 from hey-api/chore/zod-plugin

authored by

Lubos and committed by
GitHub
55e36cda 3e514d64

+1470 -279
+5
.changeset/curly-ravens-kneel.md
··· 1 + --- 2 + '@hey-api/docs': patch 3 + --- 4 + 5 + docs: add Plugins page
+5
.changeset/strange-rice-relate.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + fix: add input.exclude option
+5
.changeset/wet-laws-tell.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + fix: make Zod plugin available in plugins options
+5 -1
docs/.vitepress/config/en.ts
··· 48 48 { 49 49 items: [ 50 50 { 51 + link: '/openapi-ts/plugins', 52 + text: 'Introduction', 53 + }, 54 + { 51 55 link: '/openapi-ts/fastify', 52 56 text: 'Fastify', 53 57 }, ··· 57 61 }, 58 62 { 59 63 link: '/openapi-ts/zod', 60 - text: 'Zod <span class="soon">soon</span>', 64 + text: 'Zod', 61 65 }, 62 66 ], 63 67 text: 'Plugins',
+6
docs/embed.ts
··· 39 39 'openapi-ts.config.ts,src/client/@tanstack/react-query.gen.ts,src/client/types.gen.ts,src/App.tsx', 40 40 view: 'editor', 41 41 }); 42 + case 'hey-api-client-fetch-plugin-zod-example': 43 + return await sdk.embedProjectId(container, projectId, { 44 + height: 700, 45 + openFile: 'openapi-ts.config.ts,src/client/zod.gen.ts,src/App.tsx', 46 + view: 'editor', 47 + }); 42 48 case 'hey-api-example': 43 49 return await sdk.embedProjectId(container, projectId, { 44 50 height: 700,
+19 -3
docs/openapi-ts/configuration.md
··· 55 55 Filters work only with the [experimental parser](#parser) which is currently an opt-in feature. 56 56 ::: 57 57 58 - If you work with large specifications and want to generate output from their subset, set `input.include` to a regular expression string matching against resource references. 58 + If you work with large specifications and want to generate output from their subset, you can use regular expressions to select the relevant definitions. Set `input.include` to match resource references to be included or `input.exclude` to match resource references to be excluded. When both regular expressions match the same definition, `input.exclude` takes precedence over `input.include`. 59 59 60 - ```js 60 + ::: code-group 61 + 62 + ```js [include] 61 63 export default { 62 64 client: '@hey-api/client-fetch', 63 65 experimentalParser: true, // [!code ++] 64 66 input: { 67 + // match only the schema named `foo` and `GET` operation for the `/api/v1/foo` path // [!code ++] 65 68 include: '^(#/components/schemas/foo|#/paths/api/v1/foo/get)$', // [!code ++] 66 69 path: 'path/to/openapi.json', 67 70 }, ··· 69 72 }; 70 73 ``` 71 74 72 - The configuration above will process only the schema named `foo` and `GET` operation for the `/api/v1/foo` path. 75 + ```js [exclude] 76 + export default { 77 + client: '@hey-api/client-fetch', 78 + experimentalParser: true, // [!code ++] 79 + input: { 80 + // match everything except for the schema named `foo` and `GET` operation for the `/api/v1/foo` path // [!code ++] 81 + exclude: '^(#/components/schemas/foo|#/paths/api/v1/foo/get)$', // [!code ++] 82 + path: 'path/to/openapi.json', 83 + }, 84 + output: 'src/client', 85 + }; 86 + ``` 87 + 88 + ::: 73 89 74 90 ## Output 75 91
+4
docs/openapi-ts/output.md
··· 388 388 389 389 Client package files are located in the `client` folder. This folder will include different files depending on which client you're using. This folder isn't generated by default. If you want to bundle client packages into your output, read the [Bundling](/openapi-ts/clients/fetch#bundling) section. 390 390 391 + ## Plugins 392 + 393 + The default output generated by Hey API plugins already allows you to build robust clients. However, you might be working with third-party packages and wishing to automate more of your boilerplate. The [Plugins](/openapi-ts/plugins) page covers this topic and more. 394 + 391 395 <!--@include: ../examples.md--> 392 396 <!--@include: ../sponsorship.md-->
+164
docs/openapi-ts/plugins.md
··· 1 + --- 2 + title: Plugins 3 + description: Learn about and discover available plugins. 4 + --- 5 + 6 + # Plugins 7 + 8 + Every generated file in your output is created by a plugin. You already learned about the default plugins in [Output](/openapi-ts/output). This page contains all native plugins and shows you how to create your own. 9 + 10 + ## Hey API 11 + 12 + Apart from being responsible for the default output, Hey API plugins are the foundation for other plugins. Instead of creating their own primitives, other plugins can reuse the artifacts from Hey API plugins. This results in smaller output and a better user experience. 13 + 14 + - `@hey-api/schemas` - export OpenAPI definitions as JavaScript objects 15 + - `@hey-api/services` - robust and polished SDKs 16 + - `@hey-api/transformers` - response data transformer functions 17 + - `@hey-api/types` - TypeScript interfaces and enums 18 + 19 + ## Third Party 20 + 21 + These plugins help reduce boilerplate associated with third-party dependencies. Hey API natively supports the most popular packages. Please open an issue on [GitHub](https://github.com/hey-api/openapi-ts/issues) if you'd like us to support your favorite package. 22 + 23 + - [`fastify`](/openapi-ts/fastify) - TypeScript interface for Fastify route handlers 24 + - [`@tanstack/angular-query-experimental`](/openapi-ts/tanstack-query) - TanStack Query functions and query keys 25 + - [`@tanstack/react-query`](/openapi-ts/tanstack-query) - TanStack Query functions and query keys 26 + - [`@tanstack/solid-query`](/openapi-ts/tanstack-query) - TanStack Query functions and query keys 27 + - [`@tanstack/svelte-query`](/openapi-ts/tanstack-query) - TanStack Query functions and query keys 28 + - [`@tanstack/vue-query`](/openapi-ts/tanstack-query) - TanStack Query functions and query keys 29 + - [`zod`](/openapi-ts/zod) - Zod schemas to validate your data 30 + 31 + ## Community 32 + 33 + Featured community plugins. 34 + 35 + - [add plugin](https://github.com/hey-api/openapi-ts/pulls) 36 + 37 + ## Custom 38 + 39 + ::: warning 40 + Plugins API is in development. The interface might change before it becomes stable. We encourage you to leave feedback on [GitHub](https://github.com/hey-api/openapi-ts/issues). 41 + ::: 42 + 43 + If the existing plugins do not handle your use case or you're working with proprietary packages, you might want to create your own plugin. 44 + 45 + ### Configuration 46 + 47 + We recommend following the design pattern of the native plugins. First, create a `my-plugin` folder for your plugin files. Inside, create a barrel file `index.ts` exporting the plugin's API. 48 + 49 + ::: code-group 50 + 51 + ```ts [index.ts] 52 + export { defaultConfig, defineConfig } from './config'; 53 + export type { Config } from './types'; 54 + ``` 55 + 56 + ::: 57 + 58 + `index.ts` references 2 files, so we need to create them. `types.d.ts` contains the TypeScript interface for your plugin's options. It must have the `name` and `output` fields, everything else will become your plugin's configuration options. 59 + 60 + ::: code-group 61 + 62 + ```ts [types.d.ts] 63 + export interface Config { 64 + /** 65 + * Plugin name. Must be unique. 66 + */ 67 + name: 'my-plugin'; 68 + /** 69 + * Name of the generated file. 70 + * @default 'my-plugin' 71 + */ 72 + output?: string; 73 + /** 74 + * A custom option for your plugin. 75 + */ 76 + myOption?: boolean; 77 + } 78 + ``` 79 + 80 + ::: 81 + 82 + `config.ts` contains the runtime configuration for your plugin. It must implement the `Config` interface from `types.d.ts` and additional plugin metadata defined in the `PluginConfig` interface. 83 + 84 + ::: code-group 85 + 86 + ```ts [config.ts] 87 + import type { DefineConfig, PluginConfig } from '@hey-api/openapi-ts/plugins'; 88 + 89 + import { handler } from './plugin'; 90 + import type { Config } from './types'; 91 + 92 + export const defaultConfig: PluginConfig<Config> = { 93 + _dependencies: ['@hey-api/types'], 94 + _handler: handler, 95 + _handlerLegacy: () => {}, 96 + name: 'my-plugin', 97 + output: 'my-plugin', 98 + }; 99 + 100 + /** 101 + * Type helper for `my-plugin` plugin, returns {@link PluginConfig} object 102 + */ 103 + export const defineConfig: DefineConfig<Config> = (config) => ({ 104 + ...defaultConfig, 105 + ...config, 106 + }); 107 + ``` 108 + 109 + ::: 110 + 111 + In the `config.ts` above, we define a `my-plugin` plugin which will generate a `my-plugin.gen.ts` output file. We also demonstrate declaring `@hey-api/types` as a dependency for our plugin, so we can safely import artifacts from `types.gen.ts`. 112 + 113 + Lastly, we define the `_handler` method which will be responsible for generating our custom output. We just need to create the remaining `plugin.ts` file. 114 + 115 + ::: code-group 116 + 117 + ```ts [plugin.ts] 118 + import type { PluginHandler } from '@hey-api/openapi-ts/plugins'; 119 + 120 + import type { Config } from './types'; 121 + 122 + export const handler: PluginHandler<Config> = ({ context, plugin }) => { 123 + // create a file for our output 124 + const file = context.createFile({ 125 + id: plugin.name, 126 + path: plugin.output, 127 + }); 128 + 129 + context.subscribe('before', () => { 130 + // do something before parsing the input 131 + }); 132 + 133 + context.subscribe('operation', ({ operation }) => { 134 + // do something with the operation model 135 + }); 136 + 137 + context.subscribe('schema', ({ operation }) => { 138 + // do something with the schema model 139 + }); 140 + 141 + context.subscribe('after', () => { 142 + // do something after parsing the input 143 + }); 144 + }; 145 + ``` 146 + 147 + ::: 148 + 149 + And that's it! We can now register our plugin in the Hey API configuration. 150 + 151 + ```js 152 + import { defineConfig } from './src/my-plugin'; 153 + 154 + export default { 155 + client: '@hey-api/client-fetch', 156 + input: 'path/to/openapi.json', 157 + output: 'src/client', 158 + plugins: [ 159 + defineConfig({ 160 + myOption: true, 161 + }), 162 + ], 163 + }; 164 + ```
+1 -1
docs/openapi-ts/transformers.md
··· 46 46 }; 47 47 ``` 48 48 49 - This will generate types that use `Date` instead of `string` and appropriate transformers. Note that third party date packages are not supported at the moment. 49 + This will generate types that use `Date` instead of `string` and appropriate transformers. Note that third-party date packages are not supported at the moment. 50 50 51 51 ## Example 52 52
+43
docs/openapi-ts/zod.md
··· 10 10 ::: 11 11 12 12 [Zod](https://zod.dev/) is a TypeScript-first schema validation library with static type inference. 13 + 14 + <button class="buttonLink" @click="(event) => embedProject('hey-api-client-fetch-plugin-zod-example')(event)"> 15 + Live demo 16 + </button> 17 + 18 + ## Features 19 + 20 + - seamless integration with `@hey-api/openapi-ts` ecosystem 21 + - Zod schemas for requests, responses, and reusable components 22 + 23 + ## Installation 24 + 25 + ::: warning 26 + Zod plugin works only with the [experimental parser](/openapi-ts/configuration#parser) which is currently an opt-in feature. 27 + ::: 28 + 29 + Ensure you have already [configured](/openapi-ts/get-started) `@hey-api/openapi-ts`. Update your configuration to use the Zod plugin. 30 + 31 + ```js 32 + export default { 33 + client: '@hey-api/client-fetch', 34 + experimentalParser: true, // [!code ++] 35 + input: 'path/to/openapi.json', 36 + output: 'src/client', 37 + plugins: [ 38 + // ...other plugins 39 + 'zod', // [!code ++] 40 + ], 41 + }; 42 + ``` 43 + 44 + You can now generate Zod artifacts. 🎉 45 + 46 + ## Output 47 + 48 + The Zod plugin will generate the following artifacts, depending on the input specification. 49 + 50 + ## Schemas 51 + 52 + More information will be provided as we finalize the plugin. 53 + 54 + <!--@include: ../examples.md--> 55 + <!--@include: ../sponsorship.md-->
+1
packages/openapi-ts/src/compiler/index.ts
··· 48 48 parameterDeclaration: types.createParameterDeclaration, 49 49 propertyAccessExpression: types.createPropertyAccessExpression, 50 50 propertyAccessExpressions: transform.createPropertyAccessExpressions, 51 + propertyAssignment: types.createPropertyAssignment, 51 52 returnFunctionCall: _return.createReturnFunctionCall, 52 53 returnStatement: _return.createReturnStatement, 53 54 returnVariable: _return.createReturnVariable,
+12 -4
packages/openapi-ts/src/compiler/types.ts
··· 549 549 ) { 550 550 initializer = createIdentifier({ text: value.value as string }); 551 551 } 552 - assignment = ts.factory.createPropertyAssignment( 553 - value.key, 552 + assignment = createPropertyAssignment({ 554 553 initializer, 555 - ); 554 + name: value.key, 555 + }); 556 556 } 557 557 558 558 addLeadingComments({ ··· 598 598 const assignment = 599 599 shorthand && canShorthand 600 600 ? ts.factory.createShorthandPropertyAssignment(value) 601 - : ts.factory.createPropertyAssignment(key, initializer); 601 + : createPropertyAssignment({ initializer, name: key }); 602 602 603 603 return assignment; 604 604 }) ··· 885 885 multiLine?: boolean; 886 886 statements: Array<ts.Statement>; 887 887 }) => ts.factory.createBlock(statements, multiLine); 888 + 889 + export const createPropertyAssignment = ({ 890 + initializer, 891 + name, 892 + }: { 893 + initializer: ts.Expression; 894 + name: string | ts.PropertyName; 895 + }) => ts.factory.createPropertyAssignment(name, initializer);
+82 -11
packages/openapi-ts/src/openApi/3.0.x/parser/index.ts
··· 17 17 export const parseV3_0_X = (context: IRContext<OpenApiV3_0_X>) => { 18 18 const operationIds = new Map<string, string>(); 19 19 20 - const regexp = context.config.input.include 20 + const excludeRegExp = context.config.input.exclude 21 + ? new RegExp(context.config.input.exclude) 22 + : undefined; 23 + const includeRegExp = context.config.input.include 21 24 ? new RegExp(context.config.input.include) 22 25 : undefined; 23 26 ··· 56 59 }; 57 60 58 61 const $refDelete = `#/paths${path}/delete`; 59 - if (finalPathItem.delete && canProcessRef($refDelete, regexp)) { 62 + if ( 63 + finalPathItem.delete && 64 + canProcessRef({ 65 + $ref: $refDelete, 66 + excludeRegExp, 67 + includeRegExp, 68 + }) 69 + ) { 60 70 parseOperation({ 61 71 ...operationArgs, 62 72 method: 'delete', ··· 75 85 } 76 86 77 87 const $refGet = `#/paths${path}/get`; 78 - if (finalPathItem.get && canProcessRef($refGet, regexp)) { 88 + if ( 89 + finalPathItem.get && 90 + canProcessRef({ 91 + $ref: $refGet, 92 + excludeRegExp, 93 + includeRegExp, 94 + }) 95 + ) { 79 96 parseOperation({ 80 97 ...operationArgs, 81 98 method: 'get', ··· 94 111 } 95 112 96 113 const $refHead = `#/paths${path}/head`; 97 - if (finalPathItem.head && canProcessRef($refHead, regexp)) { 114 + if ( 115 + finalPathItem.head && 116 + canProcessRef({ 117 + $ref: $refHead, 118 + excludeRegExp, 119 + includeRegExp, 120 + }) 121 + ) { 98 122 parseOperation({ 99 123 ...operationArgs, 100 124 method: 'head', ··· 113 137 } 114 138 115 139 const $refOptions = `#/paths${path}/options`; 116 - if (finalPathItem.options && canProcessRef($refOptions, regexp)) { 140 + if ( 141 + finalPathItem.options && 142 + canProcessRef({ 143 + $ref: $refOptions, 144 + excludeRegExp, 145 + includeRegExp, 146 + }) 147 + ) { 117 148 parseOperation({ 118 149 ...operationArgs, 119 150 method: 'options', ··· 132 163 } 133 164 134 165 const $refPatch = `#/paths${path}/patch`; 135 - if (finalPathItem.patch && canProcessRef($refPatch, regexp)) { 166 + if ( 167 + finalPathItem.patch && 168 + canProcessRef({ 169 + $ref: $refPatch, 170 + excludeRegExp, 171 + includeRegExp, 172 + }) 173 + ) { 136 174 parseOperation({ 137 175 ...operationArgs, 138 176 method: 'patch', ··· 151 189 } 152 190 153 191 const $refPost = `#/paths${path}/post`; 154 - if (finalPathItem.post && canProcessRef($refPost, regexp)) { 192 + if ( 193 + finalPathItem.post && 194 + canProcessRef({ 195 + $ref: $refPost, 196 + excludeRegExp, 197 + includeRegExp, 198 + }) 199 + ) { 155 200 parseOperation({ 156 201 ...operationArgs, 157 202 method: 'post', ··· 170 215 } 171 216 172 217 const $refPut = `#/paths${path}/put`; 173 - if (finalPathItem.put && canProcessRef($refPut, regexp)) { 218 + if ( 219 + finalPathItem.put && 220 + canProcessRef({ 221 + $ref: $refPut, 222 + excludeRegExp, 223 + includeRegExp, 224 + }) 225 + ) { 174 226 parseOperation({ 175 227 ...operationArgs, 176 228 method: 'put', ··· 189 241 } 190 242 191 243 const $refTrace = `#/paths${path}/trace`; 192 - if (finalPathItem.trace && canProcessRef($refTrace, regexp)) { 244 + if ( 245 + finalPathItem.trace && 246 + canProcessRef({ 247 + $ref: $refTrace, 248 + excludeRegExp, 249 + includeRegExp, 250 + }) 251 + ) { 193 252 parseOperation({ 194 253 ...operationArgs, 195 254 method: 'trace', ··· 212 271 if (context.spec.components) { 213 272 for (const name in context.spec.components.parameters) { 214 273 const $ref = `#/components/parameters/${name}`; 215 - if (!canProcessRef($ref, regexp)) { 274 + if ( 275 + !canProcessRef({ 276 + $ref, 277 + excludeRegExp, 278 + includeRegExp, 279 + }) 280 + ) { 216 281 continue; 217 282 } 218 283 ··· 231 296 232 297 for (const name in context.spec.components.schemas) { 233 298 const $ref = `#/components/schemas/${name}`; 234 - if (!canProcessRef($ref, regexp)) { 299 + if ( 300 + !canProcessRef({ 301 + $ref, 302 + excludeRegExp, 303 + includeRegExp, 304 + }) 305 + ) { 235 306 continue; 236 307 } 237 308
+82 -11
packages/openapi-ts/src/openApi/3.1.x/parser/index.ts
··· 17 17 export const parseV3_1_X = (context: IRContext<OpenApiV3_1_X>) => { 18 18 const operationIds = new Map<string, string>(); 19 19 20 - const regexp = context.config.input.include 20 + const excludeRegExp = context.config.input.exclude 21 + ? new RegExp(context.config.input.exclude) 22 + : undefined; 23 + const includeRegExp = context.config.input.include 21 24 ? new RegExp(context.config.input.include) 22 25 : undefined; 23 26 ··· 49 52 }; 50 53 51 54 const $refDelete = `#/paths${path}/delete`; 52 - if (finalPathItem.delete && canProcessRef($refDelete, regexp)) { 55 + if ( 56 + finalPathItem.delete && 57 + canProcessRef({ 58 + $ref: $refDelete, 59 + excludeRegExp, 60 + includeRegExp, 61 + }) 62 + ) { 53 63 parseOperation({ 54 64 ...operationArgs, 55 65 method: 'delete', ··· 68 78 } 69 79 70 80 const $refGet = `#/paths${path}/get`; 71 - if (finalPathItem.get && canProcessRef($refGet, regexp)) { 81 + if ( 82 + finalPathItem.get && 83 + canProcessRef({ 84 + $ref: $refGet, 85 + excludeRegExp, 86 + includeRegExp, 87 + }) 88 + ) { 72 89 parseOperation({ 73 90 ...operationArgs, 74 91 method: 'get', ··· 87 104 } 88 105 89 106 const $refHead = `#/paths${path}/head`; 90 - if (finalPathItem.head && canProcessRef($refHead, regexp)) { 107 + if ( 108 + finalPathItem.head && 109 + canProcessRef({ 110 + $ref: $refHead, 111 + excludeRegExp, 112 + includeRegExp, 113 + }) 114 + ) { 91 115 parseOperation({ 92 116 ...operationArgs, 93 117 method: 'head', ··· 106 130 } 107 131 108 132 const $refOptions = `#/paths${path}/options`; 109 - if (finalPathItem.options && canProcessRef($refOptions, regexp)) { 133 + if ( 134 + finalPathItem.options && 135 + canProcessRef({ 136 + $ref: $refOptions, 137 + excludeRegExp, 138 + includeRegExp, 139 + }) 140 + ) { 110 141 parseOperation({ 111 142 ...operationArgs, 112 143 method: 'options', ··· 125 156 } 126 157 127 158 const $refPatch = `#/paths${path}/patch`; 128 - if (finalPathItem.patch && canProcessRef($refPatch, regexp)) { 159 + if ( 160 + finalPathItem.patch && 161 + canProcessRef({ 162 + $ref: $refPatch, 163 + excludeRegExp, 164 + includeRegExp, 165 + }) 166 + ) { 129 167 parseOperation({ 130 168 ...operationArgs, 131 169 method: 'patch', ··· 144 182 } 145 183 146 184 const $refPost = `#/paths${path}/post`; 147 - if (finalPathItem.post && canProcessRef($refPost, regexp)) { 185 + if ( 186 + finalPathItem.post && 187 + canProcessRef({ 188 + $ref: $refPost, 189 + excludeRegExp, 190 + includeRegExp, 191 + }) 192 + ) { 148 193 parseOperation({ 149 194 ...operationArgs, 150 195 method: 'post', ··· 163 208 } 164 209 165 210 const $refPut = `#/paths${path}/put`; 166 - if (finalPathItem.put && canProcessRef($refPut, regexp)) { 211 + if ( 212 + finalPathItem.put && 213 + canProcessRef({ 214 + $ref: $refPut, 215 + excludeRegExp, 216 + includeRegExp, 217 + }) 218 + ) { 167 219 parseOperation({ 168 220 ...operationArgs, 169 221 method: 'put', ··· 182 234 } 183 235 184 236 const $refTrace = `#/paths${path}/trace`; 185 - if (finalPathItem.trace && canProcessRef($refTrace, regexp)) { 237 + if ( 238 + finalPathItem.trace && 239 + canProcessRef({ 240 + $ref: $refTrace, 241 + excludeRegExp, 242 + includeRegExp, 243 + }) 244 + ) { 186 245 parseOperation({ 187 246 ...operationArgs, 188 247 method: 'trace', ··· 205 264 if (context.spec.components) { 206 265 for (const name in context.spec.components.parameters) { 207 266 const $ref = `#/components/parameters/${name}`; 208 - if (!canProcessRef($ref, regexp)) { 267 + if ( 268 + !canProcessRef({ 269 + $ref, 270 + excludeRegExp, 271 + includeRegExp, 272 + }) 273 + ) { 209 274 continue; 210 275 } 211 276 ··· 224 289 225 290 for (const name in context.spec.components.schemas) { 226 291 const $ref = `#/components/schemas/${name}`; 227 - if (!canProcessRef($ref, regexp)) { 292 + if ( 293 + !canProcessRef({ 294 + $ref, 295 + excludeRegExp, 296 + includeRegExp, 297 + }) 298 + ) { 228 299 continue; 229 300 } 230 301
+26 -4
packages/openapi-ts/src/openApi/shared/utils/filter.ts
··· 1 - export const canProcessRef = ($ref: string, regexp?: RegExp): boolean => { 2 - if (!regexp) { 1 + /** 2 + * Exclude takes precedence over include. 3 + */ 4 + export const canProcessRef = ({ 5 + $ref, 6 + excludeRegExp, 7 + includeRegExp, 8 + }: { 9 + $ref: string; 10 + excludeRegExp?: RegExp; 11 + includeRegExp?: RegExp; 12 + }): boolean => { 13 + if (!excludeRegExp && !includeRegExp) { 3 14 return true; 4 15 } 5 16 6 - regexp.lastIndex = 0; 7 - return regexp.test($ref); 17 + if (excludeRegExp) { 18 + excludeRegExp.lastIndex = 0; 19 + if (excludeRegExp.test($ref)) { 20 + return false; 21 + } 22 + } 23 + 24 + if (includeRegExp) { 25 + includeRegExp.lastIndex = 0; 26 + return includeRegExp.test($ref); 27 + } 28 + 29 + return true; 8 30 };
+2 -2
packages/openapi-ts/src/plugins/index.ts
··· 55 55 | UserConfig<TanStackSolidQuery> 56 56 | UserConfig<TanStackSvelteQuery> 57 57 | UserConfig<TanStackVueQuery> 58 - | UserConfig<Fastify>; 59 - // | UserConfig<Zod> 58 + | UserConfig<Fastify> 59 + | UserConfig<Zod>; 60 60 61 61 export type ClientPlugins = 62 62 | PluginConfig<HeyApiSchemas>
+220 -114
packages/openapi-ts/src/plugins/zod/plugin.ts
··· 1 - import type ts from 'typescript'; 1 + import ts from 'typescript'; 2 2 3 3 import { compiler } from '../../compiler'; 4 4 import type { IRContext } from '../../ir/context'; ··· 15 15 16 16 const zodId = 'zod'; 17 17 18 + const digitsRegExp = /^\d+$/; 19 + 20 + // frequently used identifiers 21 + const optionalIdentifier = compiler.identifier({ text: 'optional' }); 22 + const zIdentifier = compiler.identifier({ text: 'z' }); 23 + 18 24 const arrayTypeToZodSchema = ({ 19 25 context, 20 26 namespace, ··· 27 33 if (!schema.items) { 28 34 const expression = compiler.callExpression({ 29 35 functionName: compiler.propertyAccessExpression({ 30 - expression: 'z', 31 - name: 'array', 36 + expression: zIdentifier, 37 + name: compiler.identifier({ text: schema.type }), 32 38 }), 33 39 parameters: [ 34 40 unknownTypeToZodSchema({ ··· 57 63 if (itemExpressions.length === 1) { 58 64 const expression = compiler.callExpression({ 59 65 functionName: compiler.propertyAccessExpression({ 60 - expression: 'z', 61 - name: 'array', 66 + expression: zIdentifier, 67 + name: compiler.identifier({ text: schema.type }), 62 68 }), 63 69 parameters: itemExpressions, 64 70 }); ··· 77 83 78 84 const expression = compiler.callExpression({ 79 85 functionName: compiler.propertyAccessExpression({ 80 - expression: 'z', 81 - name: 'array', 86 + expression: zIdentifier, 87 + name: compiler.identifier({ text: schema.type }), 82 88 }), 83 89 parameters: [ 84 90 unknownTypeToZodSchema({ ··· 109 115 110 116 const expression = compiler.callExpression({ 111 117 functionName: compiler.propertyAccessExpression({ 112 - expression: 'z', 113 - name: 'boolean', 118 + expression: zIdentifier, 119 + name: compiler.identifier({ text: schema.type }), 114 120 }), 115 121 }); 116 122 return expression; 117 123 }; 118 124 125 + const enumTypeToZodSchema = ({ 126 + context, 127 + namespace, 128 + schema, 129 + }: { 130 + context: IRContext; 131 + namespace: Array<ts.Statement>; 132 + schema: SchemaWithType<'enum'>; 133 + }): ts.Expression => { 134 + const enumMembers: Array<ts.LiteralExpression> = []; 135 + 136 + for (const item of schema.items ?? []) { 137 + // Zod supports only string enums 138 + if (item.type === 'string' && typeof item.const === 'string') { 139 + enumMembers.push( 140 + compiler.stringLiteral({ 141 + text: item.const, 142 + }), 143 + ); 144 + } 145 + } 146 + 147 + if (!enumMembers.length) { 148 + return unknownTypeToZodSchema({ 149 + context, 150 + namespace, 151 + schema: { 152 + type: 'unknown', 153 + }, 154 + }); 155 + } 156 + 157 + const enumExpression = compiler.callExpression({ 158 + functionName: compiler.propertyAccessExpression({ 159 + expression: zIdentifier, 160 + name: compiler.identifier({ text: schema.type }), 161 + }), 162 + parameters: [ 163 + compiler.arrayLiteralExpression({ 164 + elements: enumMembers, 165 + multiLine: false, 166 + }), 167 + ], 168 + }); 169 + 170 + return enumExpression; 171 + }; 172 + 119 173 const neverTypeToZodSchema = ({ 120 174 schema, 121 175 }: { ··· 125 179 }) => { 126 180 const expression = compiler.callExpression({ 127 181 functionName: compiler.propertyAccessExpression({ 128 - expression: 'z', 129 - name: schema.type, 182 + expression: zIdentifier, 183 + name: compiler.identifier({ text: schema.type }), 130 184 }), 131 185 }); 132 186 return expression; ··· 141 195 }) => { 142 196 const expression = compiler.callExpression({ 143 197 functionName: compiler.propertyAccessExpression({ 144 - expression: 'z', 145 - name: schema.type, 198 + expression: zIdentifier, 199 + name: compiler.identifier({ text: schema.type }), 146 200 }), 147 201 }); 148 202 return expression; ··· 166 220 167 221 const expression = compiler.callExpression({ 168 222 functionName: compiler.propertyAccessExpression({ 169 - expression: 'z', 170 - name: 'number', 223 + expression: zIdentifier, 224 + name: compiler.identifier({ text: schema.type }), 171 225 }), 172 226 }); 173 227 return expression; 174 228 }; 175 229 176 230 const objectTypeToZodSchema = ({ 177 - // context, 231 + context, 178 232 // namespace, 179 - // eslint-disable-next-line @typescript-eslint/no-unused-vars 233 + 180 234 schema, 181 235 }: { 182 236 context: IRContext; 183 237 namespace: Array<ts.Statement>; 184 238 schema: SchemaWithType<'object'>; 185 239 }) => { 240 + const properties: Array<ts.PropertyAssignment> = []; 241 + 186 242 // let indexProperty: Property | undefined; 187 243 // const schemaProperties: Array<Property> = []; 188 244 // let indexPropertyItems: Array<IRSchemaObject> = []; 189 - // const required = schema.required ?? []; 245 + const required = schema.required ?? []; 190 246 // let hasOptionalProperties = false; 191 247 192 - // for (const name in schema.properties) { 193 - // const property = schema.properties[name]; 194 - // const isRequired = required.includes(name); 195 - // digitsRegExp.lastIndex = 0; 196 - // schemaProperties.push({ 197 - // comment: parseSchemaJsDoc({ schema: property }), 198 - // isReadOnly: property.accessScope === 'read', 199 - // isRequired, 200 - // name: digitsRegExp.test(name) 201 - // ? ts.factory.createNumericLiteral(name) 202 - // : name, 203 - // type: schemaToZodSchema({ 204 - // $ref: `${irRef}${name}`, 205 - // context, 206 - // namespace, 207 - // schema: property, 208 - // }), 209 - // }); 210 - // // indexPropertyItems.push(property); 211 - // if (!isRequired) { 212 - // hasOptionalProperties = true; 213 - // } 214 - // } 248 + for (const name in schema.properties) { 249 + const property = schema.properties[name]; 250 + const isRequired = required.includes(name); 251 + 252 + let propertyExpression = schemaToZodSchema({ 253 + context, 254 + schema: property, 255 + }); 256 + 257 + if (property.accessScope === 'read') { 258 + propertyExpression = compiler.callExpression({ 259 + functionName: compiler.propertyAccessExpression({ 260 + expression: propertyExpression, 261 + name: compiler.identifier({ text: 'readonly' }), 262 + }), 263 + }); 264 + } 265 + 266 + if (!isRequired) { 267 + propertyExpression = compiler.callExpression({ 268 + functionName: compiler.propertyAccessExpression({ 269 + expression: propertyExpression, 270 + name: optionalIdentifier, 271 + }), 272 + }); 273 + } 274 + 275 + digitsRegExp.lastIndex = 0; 276 + let propertyName = digitsRegExp.test(name) 277 + ? ts.factory.createNumericLiteral(name) 278 + : name; 279 + // TODO: parser - abstract safe property name logic 280 + if ( 281 + ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && 282 + !name.startsWith("'") && 283 + !name.endsWith("'") 284 + ) { 285 + propertyName = `'${name}'`; 286 + } 287 + properties.push( 288 + compiler.propertyAssignment({ 289 + initializer: propertyExpression, 290 + name: propertyName, 291 + }), 292 + ); 293 + 294 + // indexPropertyItems.push(property); 295 + // if (!isRequired) { 296 + // hasOptionalProperties = true; 297 + // } 298 + } 215 299 216 300 // if ( 217 301 // schema.additionalProperties && ··· 253 337 // }); 254 338 const expression = compiler.callExpression({ 255 339 functionName: compiler.propertyAccessExpression({ 256 - expression: 'z', 257 - name: 'object', 340 + expression: zIdentifier, 341 + name: compiler.identifier({ text: schema.type }), 258 342 }), 259 - parameters: [ 260 - // TODO: parser - handle parameters 261 - compiler.objectExpression({ 262 - multiLine: true, 263 - obj: [], 264 - }), 265 - ], 343 + parameters: [ts.factory.createObjectLiteralExpression(properties, true)], 266 344 }); 267 345 return expression; 268 346 }; ··· 274 352 namespace: Array<ts.Statement>; 275 353 schema: SchemaWithType<'string'>; 276 354 }) => { 355 + let stringExpression = compiler.callExpression({ 356 + functionName: compiler.propertyAccessExpression({ 357 + expression: zIdentifier, 358 + name: compiler.identifier({ text: schema.type }), 359 + }), 360 + }); 361 + 277 362 if (schema.const !== undefined) { 278 363 // TODO: parser - add constant 279 364 // return compiler.literalTypeNode({ ··· 282 367 } 283 368 284 369 if (schema.format) { 285 - // TODO: parser - add format 286 - // if (schema.format === 'binary') { 287 - // return compiler.typeUnionNode({ 288 - // types: [ 289 - // compiler.typeReferenceNode({ 290 - // typeName: 'Blob', 291 - // }), 292 - // compiler.typeReferenceNode({ 293 - // typeName: 'File', 294 - // }), 295 - // ], 296 - // }); 297 - // } 298 - // if (schema.format === 'date-time' || schema.format === 'date') { 299 - // // TODO: parser - add ability to skip type transformers 300 - // if (context.config.plugins['@hey-api/transformers']?.dates) { 301 - // return compiler.typeReferenceNode({ typeName: 'Date' }); 302 - // } 303 - // } 370 + switch (schema.format) { 371 + case 'date-time': 372 + stringExpression = compiler.callExpression({ 373 + functionName: compiler.propertyAccessExpression({ 374 + expression: stringExpression, 375 + name: compiler.identifier({ text: 'datetime' }), 376 + }), 377 + }); 378 + break; 379 + case 'ipv4': 380 + case 'ipv6': 381 + stringExpression = compiler.callExpression({ 382 + functionName: compiler.propertyAccessExpression({ 383 + expression: stringExpression, 384 + name: compiler.identifier({ text: 'ip' }), 385 + }), 386 + }); 387 + break; 388 + case 'uri': 389 + stringExpression = compiler.callExpression({ 390 + functionName: compiler.propertyAccessExpression({ 391 + expression: stringExpression, 392 + name: compiler.identifier({ text: 'url' }), 393 + }), 394 + }); 395 + break; 396 + case 'date': 397 + case 'email': 398 + case 'time': 399 + case 'uuid': 400 + stringExpression = compiler.callExpression({ 401 + functionName: compiler.propertyAccessExpression({ 402 + expression: stringExpression, 403 + name: compiler.identifier({ text: schema.format }), 404 + }), 405 + }); 406 + break; 407 + } 304 408 } 305 409 306 - const expression = compiler.callExpression({ 307 - functionName: compiler.propertyAccessExpression({ 308 - expression: 'z', 309 - name: 'string', 310 - }), 311 - }); 312 - return expression; 410 + return stringExpression; 313 411 }; 314 412 315 413 const undefinedTypeToZodSchema = ({ ··· 321 419 }) => { 322 420 const expression = compiler.callExpression({ 323 421 functionName: compiler.propertyAccessExpression({ 324 - expression: 'z', 325 - name: schema.type, 422 + expression: zIdentifier, 423 + name: compiler.identifier({ text: schema.type }), 326 424 }), 327 425 }); 328 426 return expression; ··· 337 435 }) => { 338 436 const expression = compiler.callExpression({ 339 437 functionName: compiler.propertyAccessExpression({ 340 - expression: 'z', 341 - name: schema.type, 438 + expression: zIdentifier, 439 + name: compiler.identifier({ text: schema.type }), 342 440 }), 343 441 }); 344 442 return expression; ··· 353 451 }) => { 354 452 const expression = compiler.callExpression({ 355 453 functionName: compiler.propertyAccessExpression({ 356 - expression: 'z', 357 - name: schema.type, 454 + expression: zIdentifier, 455 + name: compiler.identifier({ text: schema.type }), 358 456 }), 359 457 }); 360 458 return expression; ··· 370 468 context: IRContext; 371 469 namespace: Array<ts.Statement>; 372 470 schema: IRSchemaObject; 373 - // @ts-expect-error 374 471 }): ts.Expression => { 375 472 switch (schema.type as Required<IRSchemaObject>['type']) { 376 473 case 'array': ··· 386 483 schema: schema as SchemaWithType<'boolean'>, 387 484 }); 388 485 case 'enum': 389 - // TODO: parser - handle enum 390 - // return enumTypeToIdentifier({ 391 - // $ref, 392 - // context, 393 - // namespace, 394 - // schema: schema as SchemaWithType<'enum'>, 395 - // }); 396 - break; 486 + return enumTypeToZodSchema({ 487 + context, 488 + namespace, 489 + schema: schema as SchemaWithType<'enum'>, 490 + }); 397 491 case 'never': 398 492 return neverTypeToZodSchema({ 399 493 context, ··· 425 519 schema: schema as SchemaWithType<'string'>, 426 520 }); 427 521 case 'tuple': 428 - // TODO: parser - handle tuple 429 - // return tupleTypeToIdentifier({ 430 - // context, 431 - // namespace, 432 - // schema: schema as SchemaWithType<'tuple'>, 433 - // }); 434 - break; 522 + // TODO: parser - temporary unknown while not handled 523 + return unknownTypeToZodSchema({ 524 + context, 525 + namespace, 526 + schema: { 527 + type: 'unknown', 528 + }, 529 + }); 530 + // TODO: parser - handle tuple 531 + // return tupleTypeToIdentifier({ 532 + // context, 533 + // namespace, 534 + // schema: schema as SchemaWithType<'tuple'>, 535 + // }); 435 536 case 'undefined': 436 537 return undefinedTypeToZodSchema({ 437 538 context, ··· 494 595 schema, 495 596 }); 496 597 } else if (schema.items) { 598 + // TODO: parser - temporary unknown while not handled 599 + expression = unknownTypeToZodSchema({ 600 + context, 601 + namespace, 602 + schema: { 603 + type: 'unknown', 604 + }, 605 + }); 606 + 497 607 // TODO: parser - handle items 498 608 // schema = deduplicateSchema({ schema }); 499 609 // if (schema.items) { ··· 527 637 } 528 638 529 639 // emit nodes only if $ref points to a reusable component 530 - if ($ref && isRefOpenApiComponent($ref) && expression) { 531 - // enum handler emits its own artifacts 532 - if (schema.type !== 'enum') { 533 - const identifier = file.identifier({ 534 - $ref, 535 - create: true, 536 - namespace: 'value', 537 - }); 538 - const statement = compiler.constVariable({ 539 - exportConst: true, 540 - expression, 541 - name: identifier.name || '', 542 - }); 543 - file.add(statement); 544 - } 640 + if ($ref && isRefOpenApiComponent($ref)) { 641 + const identifier = file.identifier({ 642 + $ref, 643 + create: true, 644 + namespace: 'value', 645 + }); 646 + const statement = compiler.constVariable({ 647 + exportConst: true, 648 + expression, 649 + name: identifier.name || '', 650 + }); 651 + file.add(statement); 545 652 } 546 653 547 - // @ts-expect-error 548 654 return expression; 549 655 }; 550 656
+13 -1
packages/openapi-ts/src/types/config.ts
··· 82 82 | Record<string, unknown> 83 83 | { 84 84 /** 85 + * Prevent parts matching the regular expression from being processed. 86 + * You can select both operations and components by reference within 87 + * the bundled input. In case of conflicts, `exclude` takes precedence 88 + * over `include`. 89 + * 90 + * @example 91 + * operation: '^#/paths/api/v1/foo/get$' 92 + * schema: '^#/components/schemas/Foo$' 93 + */ 94 + exclude?: string; 95 + /** 85 96 * Process only parts matching the regular expression. You can select both 86 - * operations and components by reference within the bundled input. 97 + * operations and components by reference within the bundled input. In 98 + * case of conflicts, `exclude` takes precedence over `include`. 87 99 * 88 100 * @example 89 101 * operation: '^#/paths/api/v1/foo/get$'
+386 -62
packages/openapi-ts/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts
··· 30 30 31 31 export const SimpleFile = z.string(); 32 32 33 - export const SimpleReference = z.object({}); 33 + export const SimpleReference = z.object({ 34 + prop: z.string().optional() 35 + }); 36 + 37 + export const SimpleStringWithPattern = z.unknown(); 38 + 39 + export const EnumWithStrings = z.enum([ 40 + 'Success', 41 + 'Warning', 42 + 'Error', 43 + "'Single Quote'", 44 + '"Double Quotes"', 45 + 'Non-ascii: øæåôöØÆÅÔÖ字符串' 46 + ]); 47 + 48 + export const EnumWithReplacedCharacters = z.enum([ 49 + "'Single Quote'", 50 + '"Double Quotes"', 51 + 'øæåôöØÆÅÔÖ字符串', 52 + '' 53 + ]); 54 + 55 + export const EnumWithNumbers = z.unknown(); 34 56 35 57 export const EnumFromDescription = z.number(); 58 + 59 + export const EnumWithExtensions = z.unknown(); 60 + 61 + export const EnumWithXEnumNames = z.unknown(); 36 62 37 63 export const ArrayWithNumbers = z.array(z.number()); 38 64 ··· 40 66 41 67 export const ArrayWithStrings = z.array(z.string()); 42 68 43 - export const ArrayWithReferences = z.array(z.object({})); 69 + export const ArrayWithReferences = z.array(z.object({ 70 + prop: z.string().optional() 71 + })); 44 72 45 - export const ArrayWithArray = z.array(z.array(z.object({}))); 73 + export const ArrayWithArray = z.array(z.array(z.object({ 74 + prop: z.string().optional() 75 + }))); 46 76 47 - export const ArrayWithProperties = z.array(z.object({})); 77 + export const ArrayWithProperties = z.array(z.object({ 78 + '16x16': camelCaseCommentWithBreaks.optional(), 79 + bar: z.string().optional() 80 + })); 48 81 49 82 export const ArrayWithAnyOfProperties = z.array(z.unknown()); 50 83 51 - export const AnyOfAnyAndNull = z.object({}); 84 + export const AnyOfAnyAndNull = z.object({ 85 + data: z.unknown().optional() 86 + }); 52 87 53 - export const AnyOfArrays = z.object({}); 88 + export const AnyOfArrays = z.object({ 89 + results: z.array(z.unknown()).optional() 90 + }); 54 91 55 92 export const DictionaryWithString = z.object({}); 56 93 57 - export const DictionaryWithPropertiesAndAdditionalProperties = z.object({}); 94 + export const DictionaryWithPropertiesAndAdditionalProperties = z.object({ 95 + foo: z.number().optional(), 96 + bar: z.boolean().optional() 97 + }); 58 98 59 99 export const DictionaryWithReference = z.object({}); 60 100 ··· 64 104 65 105 export const DictionaryWithProperties = z.object({}); 66 106 67 - export const ModelWithInteger = z.object({}); 107 + export const ModelWithInteger = z.object({ 108 + prop: z.number().optional() 109 + }); 68 110 69 - export const ModelWithBoolean = z.object({}); 111 + export const ModelWithBoolean = z.object({ 112 + prop: z.boolean().optional() 113 + }); 70 114 71 - export const ModelWithString = z.object({}); 115 + export const ModelWithString = z.object({ 116 + prop: z.string().optional() 117 + }); 72 118 73 - export const ModelWithStringError = z.object({}); 119 + export const ModelWithStringError = z.object({ 120 + prop: z.string().optional() 121 + }); 74 122 75 123 export const Model_From_Zendesk = z.string(); 76 124 77 - export const ModelWithNullableString = z.object({}); 125 + export const ModelWithNullableString = z.object({ 126 + nullableProp1: z.unknown().optional(), 127 + nullableRequiredProp1: z.unknown(), 128 + nullableProp2: z.unknown().optional(), 129 + nullableRequiredProp2: z.unknown(), 130 + 'foo_bar-enum': z.enum([ 131 + 'Success', 132 + 'Warning', 133 + 'Error', 134 + 'ØÆÅ字符串' 135 + ]).optional() 136 + }); 78 137 79 - export const ModelWithEnum = z.object({}); 138 + export const ModelWithEnum = z.object({ 139 + 'foo_bar-enum': z.enum([ 140 + 'Success', 141 + 'Warning', 142 + 'Error', 143 + 'ØÆÅ字符串' 144 + ]).optional(), 145 + statusCode: z.enum([ 146 + '100', 147 + '200 FOO', 148 + '300 FOO_BAR', 149 + '400 foo-bar', 150 + '500 foo.bar', 151 + '600 foo&bar' 152 + ]).optional(), 153 + bool: z.unknown().optional() 154 + }); 80 155 81 - export const ModelWithEnumWithHyphen = z.object({}); 156 + export const ModelWithEnumWithHyphen = z.object({ 157 + 'foo-bar-baz-qux': z.enum([ 158 + '3.0' 159 + ]).optional() 160 + }); 82 161 83 - export const ModelWithEnumFromDescription = z.object({}); 162 + export const ModelWithEnumFromDescription = z.object({ 163 + test: z.number().optional() 164 + }); 84 165 85 - export const ModelWithNestedEnums = z.object({}); 166 + export const ModelWithNestedEnums = z.object({ 167 + dictionaryWithEnum: z.object({}).optional(), 168 + dictionaryWithEnumFromDescription: z.object({}).optional(), 169 + arrayWithEnum: z.array(z.enum([ 170 + 'Success', 171 + 'Warning', 172 + 'Error' 173 + ])).optional(), 174 + arrayWithDescription: z.array(z.number()).optional(), 175 + 'foo_bar-enum': z.enum([ 176 + 'Success', 177 + 'Warning', 178 + 'Error', 179 + 'ØÆÅ字符串' 180 + ]).optional() 181 + }); 86 182 87 - export const ModelWithReference = z.object({}); 183 + export const ModelWithReference = z.object({ 184 + prop: z.object({ 185 + required: z.string(), 186 + requiredAndReadOnly: z.string().readonly(), 187 + requiredAndNullable: z.unknown(), 188 + string: z.string().optional(), 189 + number: z.number().optional(), 190 + boolean: z.boolean().optional(), 191 + reference: ModelWithString.optional(), 192 + 'property with space': z.string().optional(), 193 + default: z.string().optional(), 194 + try: z.string().optional(), 195 + '@namespace.string': z.string().readonly().optional(), 196 + '@namespace.integer': z.number().readonly().optional() 197 + }).optional() 198 + }); 88 199 89 - export const ModelWithArrayReadOnlyAndWriteOnly = z.object({}); 200 + export const ModelWithArrayReadOnlyAndWriteOnly = z.object({ 201 + prop: z.array(z.object({ 202 + foo: z.string(), 203 + bar: z.string().readonly(), 204 + baz: z.string() 205 + })).optional(), 206 + propWithFile: z.array(z.string()).optional(), 207 + propWithNumber: z.array(z.number()).optional() 208 + }); 90 209 91 - export const ModelWithArray = z.object({}); 210 + export const ModelWithArray = z.object({ 211 + prop: z.array(ModelWithString).optional(), 212 + propWithFile: z.array(z.string()).optional(), 213 + propWithNumber: z.array(z.number()).optional() 214 + }); 92 215 93 - export const ModelWithDictionary = z.object({}); 216 + export const ModelWithDictionary = z.object({ 217 + prop: z.object({}).optional() 218 + }); 94 219 95 - export const DeprecatedModel = z.object({}); 220 + export const DeprecatedModel = z.object({ 221 + prop: z.string().optional() 222 + }); 96 223 97 - export const ModelWithCircularReference = z.object({}); 224 + export const CompositionWithOneOf = z.object({ 225 + propA: z.unknown().optional() 226 + }); 98 227 99 - export const CompositionWithOneOf = z.object({}); 228 + export const CompositionWithOneOfAnonymous = z.object({ 229 + propA: z.unknown().optional() 230 + }); 100 231 101 - export const CompositionWithOneOfAnonymous = z.object({}); 232 + export const ModelCircle = z.object({ 233 + kind: z.string(), 234 + radius: z.number().optional() 235 + }); 102 236 103 - export const ModelCircle = z.object({}); 237 + export const ModelSquare = z.object({ 238 + kind: z.string(), 239 + sideLength: z.number().optional() 240 + }); 104 241 105 - export const ModelSquare = z.object({}); 242 + export const CompositionWithOneOfDiscriminator = z.unknown(); 106 243 107 - export const CompositionWithAnyOf = z.object({}); 244 + export const CompositionWithAnyOf = z.object({ 245 + propA: z.unknown().optional() 246 + }); 108 247 109 - export const CompositionWithAnyOfAnonymous = z.object({}); 248 + export const CompositionWithAnyOfAnonymous = z.object({ 249 + propA: z.unknown().optional() 250 + }); 110 251 111 - export const CompositionWithNestedAnyAndTypeNull = z.object({}); 252 + export const CompositionWithNestedAnyAndTypeNull = z.object({ 253 + propA: z.unknown().optional() 254 + }); 112 255 113 - export const CompositionWithNestedAnyOfAndNull = z.object({}); 256 + export const _3e_num_1Период = z.enum([ 257 + 'Bird', 258 + 'Dog' 259 + ]); 114 260 115 - export const CompositionWithOneOfAndNullable = z.object({}); 261 + export const ConstValue = z.enum([ 262 + 'ConstValue' 263 + ]); 116 264 117 - export const CompositionWithOneOfAndSimpleDictionary = z.object({}); 265 + export const CompositionWithNestedAnyOfAndNull = z.object({ 266 + propA: z.unknown().optional() 267 + }); 118 268 119 - export const CompositionWithOneOfAndSimpleArrayDictionary = z.object({}); 269 + export const CompositionWithOneOfAndNullable = z.object({ 270 + propA: z.unknown().optional() 271 + }); 120 272 121 - export const CompositionWithOneOfAndComplexArrayDictionary = z.object({}); 273 + export const CompositionWithOneOfAndSimpleDictionary = z.object({ 274 + propA: z.unknown().optional() 275 + }); 122 276 123 - export const CompositionWithAllOfAndNullable = z.object({}); 277 + export const CompositionWithOneOfAndSimpleArrayDictionary = z.object({ 278 + propA: z.unknown().optional() 279 + }); 124 280 125 - export const CompositionWithAnyOfAndNullable = z.object({}); 281 + export const CompositionWithOneOfAndComplexArrayDictionary = z.object({ 282 + propA: z.unknown().optional() 283 + }); 126 284 127 - export const CompositionBaseModel = z.object({}); 285 + export const CompositionWithAllOfAndNullable = z.object({ 286 + propA: z.unknown().optional() 287 + }); 128 288 129 - export const ModelWithProperties = z.object({}); 289 + export const CompositionWithAnyOfAndNullable = z.object({ 290 + propA: z.unknown().optional() 291 + }); 130 292 131 - export const ModelWithNestedProperties = z.object({}); 293 + export const CompositionBaseModel = z.object({ 294 + firstName: z.string().optional(), 295 + lastname: z.string().optional() 296 + }); 132 297 133 - export const ModelWithDuplicateProperties = z.object({}); 298 + export const CompositionExtendedModel = z.unknown(); 134 299 135 - export const ModelWithOrderedProperties = z.object({}); 300 + export const ModelWithProperties = z.object({ 301 + required: z.string(), 302 + requiredAndReadOnly: z.string().readonly(), 303 + requiredAndNullable: z.unknown(), 304 + string: z.string().optional(), 305 + number: z.number().optional(), 306 + boolean: z.boolean().optional(), 307 + reference: ModelWithString.optional(), 308 + 'property with space': z.string().optional(), 309 + default: z.string().optional(), 310 + try: z.string().optional(), 311 + '@namespace.string': z.string().readonly().optional(), 312 + '@namespace.integer': z.number().readonly().optional() 313 + }); 136 314 137 - export const ModelWithDuplicateImports = z.object({}); 315 + export const ModelWithNestedProperties = z.object({ 316 + first: z.unknown().readonly() 317 + }); 138 318 139 - export const ModelWithPattern = z.object({}); 319 + export const ModelWithDuplicateProperties = z.object({ 320 + prop: ModelWithString.optional() 321 + }); 140 322 141 - export const File = z.object({}); 323 + export const ModelWithOrderedProperties = z.object({ 324 + zebra: z.string().optional(), 325 + apple: z.string().optional(), 326 + hawaii: z.string().optional() 327 + }); 142 328 143 - export const _default = z.object({}); 329 + export const ModelWithDuplicateImports = z.object({ 330 + propA: ModelWithString.optional(), 331 + propB: ModelWithString.optional(), 332 + propC: ModelWithString.optional() 333 + }); 144 334 145 - export const Pageable = z.object({}); 335 + export const ModelThatExtends = z.unknown(); 336 + 337 + export const ModelThatExtendsExtends = z.unknown(); 338 + 339 + export const ModelWithPattern = z.object({ 340 + key: z.string(), 341 + name: z.string(), 342 + enabled: z.boolean().readonly().optional(), 343 + modified: z.string().datetime().readonly().optional(), 344 + id: z.string().optional(), 345 + text: z.string().optional(), 346 + patternWithSingleQuotes: z.string().optional(), 347 + patternWithNewline: z.string().optional(), 348 + patternWithBacktick: z.string().optional() 349 + }); 350 + 351 + export const File = z.object({ 352 + id: z.string().readonly().optional(), 353 + updated_at: z.string().datetime().readonly().optional(), 354 + created_at: z.string().datetime().readonly().optional(), 355 + mime: z.string(), 356 + file: z.string().url().readonly().optional() 357 + }); 358 + 359 + export const _default = z.object({ 360 + name: z.string().optional() 361 + }); 362 + 363 + export const Pageable = z.object({ 364 + page: z.number().optional(), 365 + size: z.number().optional(), 366 + sort: z.array(z.string()).optional() 367 + }); 146 368 147 369 export const FreeFormObjectWithoutAdditionalProperties = z.object({}); 148 370 ··· 150 372 151 373 export const FreeFormObjectWithAdditionalPropertiesEqEmptyObject = z.object({}); 152 374 153 - export const ModelWithConst = z.object({}); 375 + export const ModelWithConst = z.object({ 376 + String: z.enum([ 377 + 'String' 378 + ]).optional(), 379 + number: z.unknown().optional(), 380 + null: z.unknown().optional(), 381 + withType: z.enum([ 382 + 'Some string' 383 + ]).optional() 384 + }); 154 385 155 - export const ModelWithAdditionalPropertiesEqTrue = z.object({}); 386 + export const ModelWithAdditionalPropertiesEqTrue = z.object({ 387 + prop: z.string().optional() 388 + }); 156 389 157 - export const NestedAnyOfArraysNullable = z.object({}); 390 + export const NestedAnyOfArraysNullable = z.object({ 391 + nullableArray: z.unknown().optional() 392 + }); 393 + 394 + export const CompositionWithOneOfAndProperties = z.unknown(); 395 + 396 + export const NullableObject = z.unknown(); 158 397 159 398 export const CharactersInDescription = z.string(); 160 399 161 - export const ModelWithNullableObject = z.object({}); 400 + export const ModelWithNullableObject = z.object({ 401 + data: NullableObject.optional() 402 + }); 162 403 163 - export const ModelWithNestedArrayEnumsData = z.object({}); 404 + export const ModelWithOneOfEnum = z.unknown(); 164 405 165 - export const ModelWithNestedArrayEnums = z.object({}); 406 + export const ModelWithNestedArrayEnumsDataFoo = z.enum([ 407 + 'foo', 408 + 'bar' 409 + ]); 166 410 167 - export const ModelWithNestedCompositionEnums = z.object({}); 411 + export const ModelWithNestedArrayEnumsDataBar = z.enum([ 412 + 'baz', 413 + 'qux' 414 + ]); 168 415 169 - export const ModelWithReadOnlyAndWriteOnly = z.object({}); 416 + export const ModelWithNestedArrayEnumsData = z.object({ 417 + foo: z.array(ModelWithNestedArrayEnumsDataFoo).optional(), 418 + bar: z.array(ModelWithNestedArrayEnumsDataBar).optional() 419 + }); 420 + 421 + export const ModelWithNestedArrayEnums = z.object({ 422 + array_strings: z.array(z.string()).optional(), 423 + data: ModelWithNestedArrayEnumsData.optional() 424 + }); 425 + 426 + export const ModelWithNestedCompositionEnums = z.object({ 427 + foo: ModelWithNestedArrayEnumsDataFoo.optional() 428 + }); 429 + 430 + export const ModelWithReadOnlyAndWriteOnly = z.object({ 431 + foo: z.string(), 432 + bar: z.string().readonly(), 433 + baz: z.string() 434 + }); 435 + 436 + export const ModelWithConstantSizeArray = z.unknown(); 437 + 438 + export const ModelWithAnyOfConstantSizeArray = z.unknown(); 170 439 171 440 export const ModelWithPrefixItemsConstantSizeArray = z.array(z.unknown()); 172 441 173 - export const ModelWithNumericEnumUnion = z.object({}); 442 + export const ModelWithAnyOfConstantSizeArrayNullable = z.unknown(); 174 443 175 - export const ModelWithBackticksInDescription = z.object({}); 444 + export const ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.unknown(); 445 + 446 + export const ModelWithAnyOfConstantSizeArrayAndIntersect = z.unknown(); 447 + 448 + export const ModelWithNumericEnumUnion = z.object({ 449 + value: z.unknown().optional() 450 + }); 451 + 452 + export const ModelWithBackticksInDescription = z.object({ 453 + template: z.string().optional() 454 + }); 455 + 456 + export const ModelWithOneOfAndProperties = z.unknown(); 176 457 177 458 export const ParameterSimpleParameterUnused = z.string(); 178 459 ··· 186 467 187 468 export const _import = z.string(); 188 469 189 - export const SchemaWithFormRestrictedKeys = z.object({}); 470 + export const SchemaWithFormRestrictedKeys = z.object({ 471 + description: z.string().optional(), 472 + 'x-enum-descriptions': z.string().optional(), 473 + 'x-enum-varnames': z.string().optional(), 474 + 'x-enumNames': z.string().optional(), 475 + title: z.string().optional(), 476 + object: z.object({ 477 + description: z.string().optional(), 478 + 'x-enum-descriptions': z.string().optional(), 479 + 'x-enum-varnames': z.string().optional(), 480 + 'x-enumNames': z.string().optional(), 481 + title: z.string().optional() 482 + }).optional(), 483 + array: z.array(z.object({ 484 + description: z.string().optional(), 485 + 'x-enum-descriptions': z.string().optional(), 486 + 'x-enum-varnames': z.string().optional(), 487 + 'x-enumNames': z.string().optional(), 488 + title: z.string().optional() 489 + })).optional() 490 + }); 190 491 191 - export const io_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({}); 492 + export const io_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({ 493 + preconditions: z.object({ 494 + resourceVersion: z.string().optional(), 495 + uid: z.string().optional() 496 + }).optional() 497 + }); 192 498 193 - export const io_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({}); 499 + export const io_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({ 500 + resourceVersion: z.string().optional(), 501 + uid: z.string().optional() 502 + }); 194 503 195 504 export const AdditionalPropertiesUnknownIssue = z.object({}); 196 505 197 506 export const AdditionalPropertiesUnknownIssue2 = z.object({}); 198 507 199 - export const AdditionalPropertiesIntegerIssue = z.object({}); 508 + export const AdditionalPropertiesUnknownIssue3 = z.unknown(); 200 509 201 - export const Generic_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({}); 510 + export const AdditionalPropertiesIntegerIssue = z.object({ 511 + value: z.number() 512 + }); 202 513 203 - export const Generic_Schema_Duplicate_Issue_1_System_String_ = z.object({}); 514 + export const OneOfAllOfIssue = z.unknown(); 515 + 516 + export const Generic_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({ 517 + item: z.boolean().optional(), 518 + error: z.unknown().optional(), 519 + hasError: z.boolean().readonly().optional(), 520 + data: z.object({}).optional() 521 + }); 522 + 523 + export const Generic_Schema_Duplicate_Issue_1_System_String_ = z.object({ 524 + item: z.unknown().optional(), 525 + error: z.unknown().optional(), 526 + hasError: z.boolean().readonly().optional() 527 + });
+380 -62
packages/openapi-ts/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts
··· 30 30 31 31 export const SimpleFile = z.string(); 32 32 33 - export const SimpleReference = z.object({}); 33 + export const SimpleReference = z.object({ 34 + prop: z.string().optional() 35 + }); 36 + 37 + export const SimpleStringWithPattern = z.unknown(); 38 + 39 + export const EnumWithStrings = z.enum([ 40 + 'Success', 41 + 'Warning', 42 + 'Error', 43 + "'Single Quote'", 44 + '"Double Quotes"', 45 + 'Non-ascii: øæåôöØÆÅÔÖ字符串' 46 + ]); 47 + 48 + export const EnumWithReplacedCharacters = z.enum([ 49 + "'Single Quote'", 50 + '"Double Quotes"', 51 + 'øæåôöØÆÅÔÖ字符串', 52 + '' 53 + ]); 54 + 55 + export const EnumWithNumbers = z.unknown(); 34 56 35 57 export const EnumFromDescription = z.number(); 58 + 59 + export const EnumWithExtensions = z.unknown(); 60 + 61 + export const EnumWithXEnumNames = z.unknown(); 36 62 37 63 export const ArrayWithNumbers = z.array(z.number()); 38 64 ··· 40 66 41 67 export const ArrayWithStrings = z.array(z.string()); 42 68 43 - export const ArrayWithReferences = z.array(z.object({})); 69 + export const ArrayWithReferences = z.array(z.object({ 70 + prop: z.string().optional() 71 + })); 44 72 45 - export const ArrayWithArray = z.array(z.array(z.object({}))); 73 + export const ArrayWithArray = z.array(z.array(z.object({ 74 + prop: z.string().optional() 75 + }))); 46 76 47 - export const ArrayWithProperties = z.array(z.object({})); 77 + export const ArrayWithProperties = z.array(z.object({ 78 + '16x16': camelCaseCommentWithBreaks.optional(), 79 + bar: z.string().optional() 80 + })); 48 81 49 82 export const ArrayWithAnyOfProperties = z.array(z.unknown()); 50 83 51 - export const AnyOfAnyAndNull = z.object({}); 84 + export const AnyOfAnyAndNull = z.object({ 85 + data: z.unknown().optional() 86 + }); 52 87 53 - export const AnyOfArrays = z.object({}); 88 + export const AnyOfArrays = z.object({ 89 + results: z.array(z.unknown()).optional() 90 + }); 54 91 55 92 export const DictionaryWithString = z.object({}); 56 93 57 - export const DictionaryWithPropertiesAndAdditionalProperties = z.object({}); 94 + export const DictionaryWithPropertiesAndAdditionalProperties = z.object({ 95 + foo: z.number().optional(), 96 + bar: z.boolean().optional() 97 + }); 58 98 59 99 export const DictionaryWithReference = z.object({}); 60 100 ··· 64 104 65 105 export const DictionaryWithProperties = z.object({}); 66 106 67 - export const ModelWithInteger = z.object({}); 107 + export const ModelWithInteger = z.object({ 108 + prop: z.number().optional() 109 + }); 68 110 69 - export const ModelWithBoolean = z.object({}); 111 + export const ModelWithBoolean = z.object({ 112 + prop: z.boolean().optional() 113 + }); 70 114 71 - export const ModelWithString = z.object({}); 115 + export const ModelWithString = z.object({ 116 + prop: z.string().optional() 117 + }); 72 118 73 - export const ModelWithStringError = z.object({}); 119 + export const ModelWithStringError = z.object({ 120 + prop: z.string().optional() 121 + }); 74 122 75 123 export const Model_From_Zendesk = z.string(); 76 124 77 - export const ModelWithNullableString = z.object({}); 125 + export const ModelWithNullableString = z.object({ 126 + nullableProp1: z.unknown().optional(), 127 + nullableRequiredProp1: z.unknown(), 128 + nullableProp2: z.unknown().optional(), 129 + nullableRequiredProp2: z.unknown(), 130 + 'foo_bar-enum': z.enum([ 131 + 'Success', 132 + 'Warning', 133 + 'Error', 134 + 'ØÆÅ字符串' 135 + ]).optional() 136 + }); 137 + 138 + export const ModelWithEnum = z.object({ 139 + 'foo_bar-enum': z.enum([ 140 + 'Success', 141 + 'Warning', 142 + 'Error', 143 + 'ØÆÅ字符串' 144 + ]).optional(), 145 + statusCode: z.enum([ 146 + '100', 147 + '200 FOO', 148 + '300 FOO_BAR', 149 + '400 foo-bar', 150 + '500 foo.bar', 151 + '600 foo&bar' 152 + ]).optional(), 153 + bool: z.unknown().optional() 154 + }); 78 155 79 - export const ModelWithEnum = z.object({}); 156 + export const ModelWithEnumWithHyphen = z.object({ 157 + 'foo-bar-baz-qux': z.enum([ 158 + '3.0' 159 + ]).optional() 160 + }); 80 161 81 - export const ModelWithEnumWithHyphen = z.object({}); 162 + export const ModelWithEnumFromDescription = z.object({ 163 + test: z.number().optional() 164 + }); 82 165 83 - export const ModelWithEnumFromDescription = z.object({}); 166 + export const ModelWithNestedEnums = z.object({ 167 + dictionaryWithEnum: z.object({}).optional(), 168 + dictionaryWithEnumFromDescription: z.object({}).optional(), 169 + arrayWithEnum: z.array(z.enum([ 170 + 'Success', 171 + 'Warning', 172 + 'Error' 173 + ])).optional(), 174 + arrayWithDescription: z.array(z.number()).optional(), 175 + 'foo_bar-enum': z.enum([ 176 + 'Success', 177 + 'Warning', 178 + 'Error', 179 + 'ØÆÅ字符串' 180 + ]).optional() 181 + }); 84 182 85 - export const ModelWithNestedEnums = z.object({}); 183 + export const ModelWithReference = z.object({ 184 + prop: z.object({ 185 + required: z.string(), 186 + requiredAndReadOnly: z.string().readonly(), 187 + requiredAndNullable: z.unknown(), 188 + string: z.string().optional(), 189 + number: z.number().optional(), 190 + boolean: z.boolean().optional(), 191 + reference: ModelWithString.optional(), 192 + 'property with space': z.string().optional(), 193 + default: z.string().optional(), 194 + try: z.string().optional(), 195 + '@namespace.string': z.string().readonly().optional(), 196 + '@namespace.integer': z.number().readonly().optional() 197 + }).optional() 198 + }); 86 199 87 - export const ModelWithReference = z.object({}); 200 + export const ModelWithArrayReadOnlyAndWriteOnly = z.object({ 201 + prop: z.array(z.object({ 202 + foo: z.string(), 203 + bar: z.string().readonly(), 204 + baz: z.string() 205 + })).optional(), 206 + propWithFile: z.array(z.string()).optional(), 207 + propWithNumber: z.array(z.number()).optional() 208 + }); 88 209 89 - export const ModelWithArrayReadOnlyAndWriteOnly = z.object({}); 210 + export const ModelWithArray = z.object({ 211 + prop: z.array(ModelWithString).optional(), 212 + propWithFile: z.array(z.string()).optional(), 213 + propWithNumber: z.array(z.number()).optional() 214 + }); 90 215 91 - export const ModelWithArray = z.object({}); 216 + export const ModelWithDictionary = z.object({ 217 + prop: z.object({}).optional() 218 + }); 92 219 93 - export const ModelWithDictionary = z.object({}); 220 + export const DeprecatedModel = z.object({ 221 + prop: z.string().optional() 222 + }); 94 223 95 - export const DeprecatedModel = z.object({}); 224 + export const CompositionWithOneOf = z.object({ 225 + propA: z.unknown().optional() 226 + }); 96 227 97 - export const ModelWithCircularReference = z.object({}); 228 + export const CompositionWithOneOfAnonymous = z.object({ 229 + propA: z.unknown().optional() 230 + }); 98 231 99 - export const CompositionWithOneOf = z.object({}); 232 + export const ModelCircle = z.object({ 233 + kind: z.string(), 234 + radius: z.number().optional() 235 + }); 100 236 101 - export const CompositionWithOneOfAnonymous = z.object({}); 237 + export const ModelSquare = z.object({ 238 + kind: z.string(), 239 + sideLength: z.number().optional() 240 + }); 102 241 103 - export const ModelCircle = z.object({}); 242 + export const CompositionWithOneOfDiscriminator = z.unknown(); 104 243 105 - export const ModelSquare = z.object({}); 244 + export const CompositionWithAnyOf = z.object({ 245 + propA: z.unknown().optional() 246 + }); 106 247 107 - export const CompositionWithAnyOf = z.object({}); 248 + export const CompositionWithAnyOfAnonymous = z.object({ 249 + propA: z.unknown().optional() 250 + }); 108 251 109 - export const CompositionWithAnyOfAnonymous = z.object({}); 252 + export const CompositionWithNestedAnyAndTypeNull = z.object({ 253 + propA: z.unknown().optional() 254 + }); 110 255 111 - export const CompositionWithNestedAnyAndTypeNull = z.object({}); 256 + export const _3e_num_1Период = z.enum([ 257 + 'Bird', 258 + 'Dog' 259 + ]); 112 260 113 261 export const ConstValue = z.string(); 114 262 115 - export const CompositionWithNestedAnyOfAndNull = z.object({}); 263 + export const CompositionWithNestedAnyOfAndNull = z.object({ 264 + propA: z.unknown().optional() 265 + }); 116 266 117 - export const CompositionWithOneOfAndNullable = z.object({}); 267 + export const CompositionWithOneOfAndNullable = z.object({ 268 + propA: z.unknown().optional() 269 + }); 118 270 119 - export const CompositionWithOneOfAndSimpleDictionary = z.object({}); 271 + export const CompositionWithOneOfAndSimpleDictionary = z.object({ 272 + propA: z.unknown().optional() 273 + }); 120 274 121 - export const CompositionWithOneOfAndSimpleArrayDictionary = z.object({}); 275 + export const CompositionWithOneOfAndSimpleArrayDictionary = z.object({ 276 + propA: z.unknown().optional() 277 + }); 122 278 123 - export const CompositionWithOneOfAndComplexArrayDictionary = z.object({}); 279 + export const CompositionWithOneOfAndComplexArrayDictionary = z.object({ 280 + propA: z.unknown().optional() 281 + }); 124 282 125 - export const CompositionWithAllOfAndNullable = z.object({}); 283 + export const CompositionWithAllOfAndNullable = z.object({ 284 + propA: z.unknown().optional() 285 + }); 126 286 127 - export const CompositionWithAnyOfAndNullable = z.object({}); 287 + export const CompositionWithAnyOfAndNullable = z.object({ 288 + propA: z.unknown().optional() 289 + }); 128 290 129 - export const CompositionBaseModel = z.object({}); 291 + export const CompositionBaseModel = z.object({ 292 + firstName: z.string().optional(), 293 + lastname: z.string().optional() 294 + }); 130 295 131 - export const ModelWithProperties = z.object({}); 296 + export const CompositionExtendedModel = z.unknown(); 132 297 133 - export const ModelWithNestedProperties = z.object({}); 298 + export const ModelWithProperties = z.object({ 299 + required: z.string(), 300 + requiredAndReadOnly: z.string().readonly(), 301 + requiredAndNullable: z.unknown(), 302 + string: z.string().optional(), 303 + number: z.number().optional(), 304 + boolean: z.boolean().optional(), 305 + reference: ModelWithString.optional(), 306 + 'property with space': z.string().optional(), 307 + default: z.string().optional(), 308 + try: z.string().optional(), 309 + '@namespace.string': z.string().readonly().optional(), 310 + '@namespace.integer': z.number().readonly().optional() 311 + }); 134 312 135 - export const ModelWithDuplicateProperties = z.object({}); 313 + export const ModelWithNestedProperties = z.object({ 314 + first: z.unknown().readonly() 315 + }); 136 316 137 - export const ModelWithOrderedProperties = z.object({}); 317 + export const ModelWithDuplicateProperties = z.object({ 318 + prop: ModelWithString.optional() 319 + }); 320 + 321 + export const ModelWithOrderedProperties = z.object({ 322 + zebra: z.string().optional(), 323 + apple: z.string().optional(), 324 + hawaii: z.string().optional() 325 + }); 326 + 327 + export const ModelWithDuplicateImports = z.object({ 328 + propA: ModelWithString.optional(), 329 + propB: ModelWithString.optional(), 330 + propC: ModelWithString.optional() 331 + }); 332 + 333 + export const ModelThatExtends = z.unknown(); 138 334 139 - export const ModelWithDuplicateImports = z.object({}); 335 + export const ModelThatExtendsExtends = z.unknown(); 140 336 141 - export const ModelWithPattern = z.object({}); 337 + export const ModelWithPattern = z.object({ 338 + key: z.string(), 339 + name: z.string(), 340 + enabled: z.boolean().readonly().optional(), 341 + modified: z.string().datetime().readonly().optional(), 342 + id: z.string().optional(), 343 + text: z.string().optional(), 344 + patternWithSingleQuotes: z.string().optional(), 345 + patternWithNewline: z.string().optional(), 346 + patternWithBacktick: z.string().optional() 347 + }); 142 348 143 - export const File = z.object({}); 349 + export const File = z.object({ 350 + id: z.string().readonly().optional(), 351 + updated_at: z.string().datetime().readonly().optional(), 352 + created_at: z.string().datetime().readonly().optional(), 353 + mime: z.string(), 354 + file: z.string().url().readonly().optional() 355 + }); 144 356 145 - export const _default = z.object({}); 357 + export const _default = z.object({ 358 + name: z.string().optional() 359 + }); 146 360 147 - export const Pageable = z.object({}); 361 + export const Pageable = z.object({ 362 + page: z.number().optional(), 363 + size: z.number().optional(), 364 + sort: z.array(z.string()).optional() 365 + }); 148 366 149 367 export const FreeFormObjectWithoutAdditionalProperties = z.object({}); 150 368 ··· 152 370 153 371 export const FreeFormObjectWithAdditionalPropertiesEqEmptyObject = z.object({}); 154 372 155 - export const ModelWithConst = z.object({}); 373 + export const ModelWithConst = z.object({ 374 + String: z.string().optional(), 375 + number: z.number().optional(), 376 + null: z.null().optional(), 377 + withType: z.string().optional() 378 + }); 379 + 380 + export const ModelWithAdditionalPropertiesEqTrue = z.object({ 381 + prop: z.string().optional() 382 + }); 383 + 384 + export const NestedAnyOfArraysNullable = z.object({ 385 + nullableArray: z.unknown().optional() 386 + }); 156 387 157 - export const ModelWithAdditionalPropertiesEqTrue = z.object({}); 388 + export const CompositionWithOneOfAndProperties = z.unknown(); 158 389 159 - export const NestedAnyOfArraysNullable = z.object({}); 390 + export const NullableObject = z.unknown(); 160 391 161 392 export const CharactersInDescription = z.string(); 162 393 163 - export const ModelWithNullableObject = z.object({}); 394 + export const ModelWithNullableObject = z.object({ 395 + data: NullableObject.optional() 396 + }); 397 + 398 + export const ModelWithOneOfEnum = z.unknown(); 399 + 400 + export const ModelWithNestedArrayEnumsDataFoo = z.enum([ 401 + 'foo', 402 + 'bar' 403 + ]); 404 + 405 + export const ModelWithNestedArrayEnumsDataBar = z.enum([ 406 + 'baz', 407 + 'qux' 408 + ]); 409 + 410 + export const ModelWithNestedArrayEnumsData = z.object({ 411 + foo: z.array(ModelWithNestedArrayEnumsDataFoo).optional(), 412 + bar: z.array(ModelWithNestedArrayEnumsDataBar).optional() 413 + }); 414 + 415 + export const ModelWithNestedArrayEnums = z.object({ 416 + array_strings: z.array(z.string()).optional(), 417 + data: ModelWithNestedArrayEnumsData.optional() 418 + }); 419 + 420 + export const ModelWithNestedCompositionEnums = z.object({ 421 + foo: ModelWithNestedArrayEnumsDataFoo.optional() 422 + }); 423 + 424 + export const ModelWithReadOnlyAndWriteOnly = z.object({ 425 + foo: z.string(), 426 + bar: z.string().readonly(), 427 + baz: z.string() 428 + }); 164 429 165 - export const ModelWithNestedArrayEnumsData = z.object({}); 430 + export const ModelWithConstantSizeArray = z.unknown(); 166 431 167 - export const ModelWithNestedArrayEnums = z.object({}); 432 + export const ModelWithAnyOfConstantSizeArray = z.unknown(); 433 + 434 + export const ModelWithPrefixItemsConstantSizeArray = z.unknown(); 435 + 436 + export const ModelWithAnyOfConstantSizeArrayNullable = z.unknown(); 437 + 438 + export const ModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.unknown(); 168 439 169 - export const ModelWithNestedCompositionEnums = z.object({}); 440 + export const ModelWithAnyOfConstantSizeArrayAndIntersect = z.unknown(); 170 441 171 - export const ModelWithReadOnlyAndWriteOnly = z.object({}); 442 + export const ModelWithNumericEnumUnion = z.object({ 443 + value: z.unknown().optional() 444 + }); 172 445 173 - export const ModelWithNumericEnumUnion = z.object({}); 446 + export const ModelWithBackticksInDescription = z.object({ 447 + template: z.string().optional() 448 + }); 174 449 175 - export const ModelWithBackticksInDescription = z.object({}); 450 + export const ModelWithOneOfAndProperties = z.unknown(); 176 451 177 452 export const ParameterSimpleParameterUnused = z.string(); 178 453 ··· 186 461 187 462 export const _import = z.string(); 188 463 189 - export const SchemaWithFormRestrictedKeys = z.object({}); 464 + export const SchemaWithFormRestrictedKeys = z.object({ 465 + description: z.string().optional(), 466 + 'x-enum-descriptions': z.string().optional(), 467 + 'x-enum-varnames': z.string().optional(), 468 + 'x-enumNames': z.string().optional(), 469 + title: z.string().optional(), 470 + object: z.object({ 471 + description: z.string().optional(), 472 + 'x-enum-descriptions': z.string().optional(), 473 + 'x-enum-varnames': z.string().optional(), 474 + 'x-enumNames': z.string().optional(), 475 + title: z.string().optional() 476 + }).optional(), 477 + array: z.array(z.object({ 478 + description: z.string().optional(), 479 + 'x-enum-descriptions': z.string().optional(), 480 + 'x-enum-varnames': z.string().optional(), 481 + 'x-enumNames': z.string().optional(), 482 + title: z.string().optional() 483 + })).optional() 484 + }); 190 485 191 - export const io_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({}); 486 + export const io_k8s_apimachinery_pkg_apis_meta_v1_DeleteOptions = z.object({ 487 + preconditions: z.object({ 488 + resourceVersion: z.string().optional(), 489 + uid: z.string().optional() 490 + }).optional() 491 + }); 192 492 193 - export const io_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({}); 493 + export const io_k8s_apimachinery_pkg_apis_meta_v1_Preconditions = z.object({ 494 + resourceVersion: z.string().optional(), 495 + uid: z.string().optional() 496 + }); 194 497 195 498 export const AdditionalPropertiesUnknownIssue = z.object({}); 196 499 197 500 export const AdditionalPropertiesUnknownIssue2 = z.object({}); 198 501 199 - export const AdditionalPropertiesIntegerIssue = z.object({}); 502 + export const AdditionalPropertiesUnknownIssue3 = z.unknown(); 503 + 504 + export const AdditionalPropertiesIntegerIssue = z.object({ 505 + value: z.number() 506 + }); 507 + 508 + export const OneOfAllOfIssue = z.unknown(); 200 509 201 - export const Generic_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({}); 510 + export const Generic_Schema_Duplicate_Issue_1_System_Boolean_ = z.object({ 511 + item: z.boolean().optional(), 512 + error: z.unknown().optional(), 513 + hasError: z.boolean().readonly().optional(), 514 + data: z.object({}).optional() 515 + }); 202 516 203 - export const Generic_Schema_Duplicate_Issue_1_System_String_ = z.object({}); 517 + export const Generic_Schema_Duplicate_Issue_1_System_String_ = z.object({ 518 + item: z.unknown().optional(), 519 + error: z.unknown().optional(), 520 + hasError: z.boolean().readonly().optional() 521 + });
+8 -3
packages/openapi-ts/test/plugins.spec.ts
··· 22 22 describe(`OpenAPI ${version} ${namespace}`, () => { 23 23 const createConfig = ( 24 24 userConfig: Omit<UserConfig, 'input'> & 25 - Pick<Required<UserConfig>, 'plugins'>, 25 + Pick<Required<UserConfig>, 'plugins'> & 26 + Pick<Partial<UserConfig>, 'input'>, 26 27 ): UserConfig => ({ 27 28 client: '@hey-api/client-fetch', 28 29 experimentalParser: true, 29 - ...userConfig, 30 30 input: path.join(__dirname, 'spec', version, 'full.json'), 31 + ...userConfig, 31 32 output: path.join( 32 33 outputDir, 33 34 typeof userConfig.plugins[0] === 'string' ··· 211 212 }, 212 213 { 213 214 config: createConfig({ 215 + input: { 216 + // TODO: parser - remove `exclude` once recursive references are handled 217 + exclude: '^#/components/schemas/ModelWithCircularReference$', 218 + path: path.join(__dirname, 'spec', version, 'full.json'), 219 + }, 214 220 output: 'default', 215 - // @ts-expect-error 216 221 plugins: ['zod'], 217 222 }), 218 223 description: 'generate Zod schemas with Zod plugin',
+1
packages/openapi-ts/test/sample.cjs
··· 11 11 // debug: true, 12 12 experimentalParser: true, 13 13 input: { 14 + exclude: '^#/components/schemas/ModelWithCircularReference$', 14 15 // include: 15 16 // '^(#/components/schemas/import|#/paths/api/v{api-version}/simple/options)$', 16 17 path: './test/spec/3.1.x/full.json',