From e8b6becb91da11fb67676791ae91926643813597 Mon Sep 17 00:00:00 2001 From: Lubos Date: Tue, 21 Oct 2025 18:45:03 +0800 Subject: [PATCH 1/5] refactor: clean up validator plugins --- .vscode/settings.json | 3 +- .../src/vitest.base.config.ts | 3 +- packages/openapi-ts-tests/main/package.json | 1 + .../main/test/openapi-ts.config.ts | 10 +- .../src/generate/__tests__/renderer.test.ts | 75 + packages/openapi-ts/src/generate/renderer.ts | 12 +- .../src/plugins/@hey-api/typescript/plugin.ts | 2 +- .../openapi-ts/src/plugins/arktype/api.ts | 55 + .../openapi-ts/src/plugins/arktype/config.ts | 271 ++++ .../src/plugins/arktype/constants.ts | 44 + .../openapi-ts/src/plugins/arktype/index.ts | 2 + .../openapi-ts/src/plugins/arktype/plugin.ts | 4 + .../src/plugins/arktype/shared/export.ts | 53 + .../src/plugins/arktype/shared/types.d.ts | 35 + .../openapi-ts/src/plugins/arktype/types.d.ts | 665 ++++++++ .../openapi-ts/src/plugins/arktype/v2/api.ts | 73 + .../src/plugins/arktype/v2/plugin.ts | 454 ++++++ .../src/plugins/arktype/v2/toAst/index.ts | 122 ++ packages/openapi-ts/src/plugins/config.ts | 4 + .../src/plugins/shared/types/schema.d.ts | 8 + packages/openapi-ts/src/plugins/types.d.ts | 2 +- .../openapi-ts/src/plugins/valibot/api.ts | 102 +- .../openapi-ts/src/plugins/valibot/plugin.ts | 1375 +---------------- .../{number-helpers.ts => shared/numbers.ts} | 3 +- .../src/plugins/valibot/shared/types.d.ts | 24 + .../openapi-ts/src/plugins/valibot/v1/api.ts | 85 + .../src/plugins/valibot/{ => v1}/constants.ts | 2 +- .../src/plugins/valibot/{ => v1}/operation.ts | 37 +- .../src/plugins/valibot/v1/pipesToAst.ts | 29 + .../src/plugins/valibot/v1/plugin.ts | 363 +++++ .../src/plugins/valibot/v1/toAst/array.ts | 126 ++ .../src/plugins/valibot/v1/toAst/boolean.ts | 34 + .../src/plugins/valibot/v1/toAst/enum.ts | 71 + .../src/plugins/valibot/v1/toAst/index.ts | 121 ++ .../src/plugins/valibot/v1/toAst/never.ts | 21 + .../src/plugins/valibot/v1/toAst/null.ts | 21 + .../src/plugins/valibot/v1/toAst/number.ts | 254 +++ .../src/plugins/valibot/v1/toAst/object.ts | 118 ++ .../src/plugins/valibot/v1/toAst/string.ts | 143 ++ .../src/plugins/valibot/v1/toAst/tuple.ts | 77 + .../src/plugins/valibot/v1/toAst/undefined.ts | 22 + .../src/plugins/valibot/v1/toAst/unknown.ts | 22 + .../src/plugins/valibot/v1/toAst/void.ts | 22 + .../src/plugins/valibot/{ => v1}/webhook.ts | 25 +- packages/openapi-ts/src/plugins/zod/api.ts | 106 +- .../openapi-ts/src/plugins/zod/constants.ts | 1 + .../openapi-ts/src/plugins/zod/mini/api.ts | 71 + .../openapi-ts/src/plugins/zod/mini/plugin.ts | 1193 ++------------ .../src/plugins/zod/mini/toAst/array.ts | 172 +++ .../src/plugins/zod/mini/toAst/boolean.ts | 34 + .../src/plugins/zod/mini/toAst/enum.ts | 127 ++ .../src/plugins/zod/mini/toAst/index.ts | 85 + .../src/plugins/zod/mini/toAst/never.ts | 20 + .../src/plugins/zod/mini/toAst/null.ts | 20 + .../src/plugins/zod/mini/toAst/number.ts | 118 ++ .../src/plugins/zod/mini/toAst/object.ts | 135 ++ .../src/plugins/zod/mini/toAst/string.ts | 192 +++ .../src/plugins/zod/mini/toAst/tuple.ts | 76 + .../src/plugins/zod/mini/toAst/undefined.ts | 20 + .../src/plugins/zod/mini/toAst/unknown.ts | 20 + .../src/plugins/zod/mini/toAst/void.ts | 20 + packages/openapi-ts/src/plugins/zod/plugin.ts | 3 +- .../src/plugins/zod/{ => shared}/export.ts | 34 +- .../src/plugins/zod/shared/numbers.ts | 28 + .../src/plugins/zod/shared/operation.ts | 46 +- .../src/plugins/zod/shared/types.d.ts | 26 +- .../src/plugins/zod/shared/webhook.ts | 32 +- .../openapi-ts/src/plugins/zod/types.d.ts | 20 +- packages/openapi-ts/src/plugins/zod/v3/api.ts | 71 + .../openapi-ts/src/plugins/zod/v3/plugin.ts | 1117 ++----------- .../src/plugins/zod/v3/toAst/array.ts | 160 ++ .../src/plugins/zod/v3/toAst/boolean.ts | 32 + .../src/plugins/zod/v3/toAst/enum.ts | 125 ++ .../src/plugins/zod/v3/toAst/index.ts | 105 ++ .../src/plugins/zod/v3/toAst/never.ts | 19 + .../src/plugins/zod/v3/toAst/null.ts | 19 + .../src/plugins/zod/v3/toAst/number.ts | 94 ++ .../src/plugins/zod/v3/toAst/object.ts | 111 ++ .../src/plugins/zod/v3/toAst/string.ts | 152 ++ .../src/plugins/zod/v3/toAst/tuple.ts | 83 + .../src/plugins/zod/v3/toAst/undefined.ts | 19 + .../src/plugins/zod/v3/toAst/unknown.ts | 19 + .../src/plugins/zod/v3/toAst/void.ts | 19 + packages/openapi-ts/src/plugins/zod/v4/api.ts | 71 + .../openapi-ts/src/plugins/zod/v4/plugin.ts | 1150 ++------------ .../src/plugins/zod/v4/toAst/array.ts | 154 ++ .../src/plugins/zod/v4/toAst/boolean.ts | 34 + .../src/plugins/zod/v4/toAst/enum.ts | 127 ++ .../src/plugins/zod/v4/toAst/index.ts | 85 + .../src/plugins/zod/v4/toAst/never.ts | 20 + .../src/plugins/zod/v4/toAst/null.ts | 20 + .../src/plugins/zod/v4/toAst/number.ts | 96 ++ .../src/plugins/zod/v4/toAst/object.ts | 152 ++ .../src/plugins/zod/v4/toAst/string.ts | 170 ++ .../src/plugins/zod/v4/toAst/tuple.ts | 76 + .../src/plugins/zod/v4/toAst/undefined.ts | 20 + .../src/plugins/zod/v4/toAst/unknown.ts | 20 + .../src/plugins/zod/v4/toAst/void.ts | 20 + packages/openapi-ts/src/utils/escape.ts | 2 +- pnpm-lock.yaml | 227 +-- 100 files changed, 7293 insertions(+), 4890 deletions(-) create mode 100644 packages/openapi-ts/src/plugins/arktype/api.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/config.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/constants.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/index.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/plugin.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/shared/export.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/shared/types.d.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/types.d.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/v2/api.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/v2/plugin.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts create mode 100644 packages/openapi-ts/src/plugins/shared/types/schema.d.ts rename packages/openapi-ts/src/plugins/valibot/{number-helpers.ts => shared/numbers.ts} (97%) create mode 100644 packages/openapi-ts/src/plugins/valibot/shared/types.d.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/api.ts rename packages/openapi-ts/src/plugins/valibot/{ => v1}/constants.ts (99%) rename packages/openapi-ts/src/plugins/valibot/{ => v1}/operation.ts (84%) create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/plugin.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts create mode 100644 packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts rename packages/openapi-ts/src/plugins/valibot/{ => v1}/webhook.ts (85%) create mode 100644 packages/openapi-ts/src/plugins/zod/mini/api.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts create mode 100644 packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts rename packages/openapi-ts/src/plugins/zod/{ => shared}/export.ts (64%) create mode 100644 packages/openapi-ts/src/plugins/zod/shared/numbers.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/api.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/api.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts create mode 100644 packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 6df8dbfe3..c4595f000 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,11 +3,12 @@ "source.fixAll.eslint": "explicit" }, "editor.quickSuggestions": { - "strings": true + "strings": "on" }, "eslint.format.enable": true, "eslint.nodePath": "./node_modules", "eslint.workingDirectories": [{ "pattern": "./packages/*/" }], "typescript.preferences.autoImportFileExcludePatterns": ["dist/**"], + "typescript.preferences.autoImportSpecifierExcludeRegexes": ["^(node:)?os$"], "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/packages/config-vite-base/src/vitest.base.config.ts b/packages/config-vite-base/src/vitest.base.config.ts index 3d84f7be7..d9167e834 100644 --- a/packages/config-vite-base/src/vitest.base.config.ts +++ b/packages/config-vite-base/src/vitest.base.config.ts @@ -1,4 +1,5 @@ -import { platform } from 'os'; +import { platform } from 'node:os'; + import type { ViteUserConfig } from 'vitest/config'; import { configDefaults, defineConfig, mergeConfig } from 'vitest/config'; diff --git a/packages/openapi-ts-tests/main/package.json b/packages/openapi-ts-tests/main/package.json index df0d540b7..9a76ba135 100644 --- a/packages/openapi-ts-tests/main/package.json +++ b/packages/openapi-ts-tests/main/package.json @@ -37,6 +37,7 @@ "@tanstack/svelte-query": "5.73.3", "@tanstack/vue-query": "5.73.3", "@types/cross-spawn": "6.0.6", + "arktype": "2.1.23", "axios": "1.8.2", "cross-spawn": "7.0.6", "eslint": "9.17.0", diff --git a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts index 99a651ac5..c170531d4 100644 --- a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts +++ b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts @@ -299,13 +299,19 @@ export default defineConfig(() => { }, }, }, + { + name: 'arktype', + types: { + infer: true, + }, + }, { // case: 'SCREAMING_SNAKE_CASE', // comments: false, // definitions: 'z{{name}}Definition', exportFromIndex: true, // metadata: true, - // name: 'valibot', + name: 'valibot', // requests: { // case: 'PascalCase', // name: '{{name}}Data', @@ -347,7 +353,7 @@ export default defineConfig(() => { }, exportFromIndex: true, metadata: true, - // name: 'zod', + name: 'zod', // requests: { // // case: 'SCREAMING_SNAKE_CASE', // // name: 'z{{name}}TestData', diff --git a/packages/openapi-ts/src/generate/__tests__/renderer.test.ts b/packages/openapi-ts/src/generate/__tests__/renderer.test.ts index 1c0cccb32..95e6c373e 100644 --- a/packages/openapi-ts/src/generate/__tests__/renderer.test.ts +++ b/packages/openapi-ts/src/generate/__tests__/renderer.test.ts @@ -2,6 +2,26 @@ import { describe, expect, it } from 'vitest'; import { TypeScriptRenderer } from '../renderer'; +// Minimal local BiMap for tests to avoid importing runtime-only class +class LocalBiMap { + private map = new Map(); + private reverse = new Map(); + get(key: Key) { + return this.map.get(key); + } + getKey(value: Value) { + return this.reverse.get(value); + } + set(key: Key, value: Value) { + this.map.set(key, value); + this.reverse.set(value, key); + return this; + } + hasValue(value: Value) { + return this.reverse.has(value); + } +} + describe('TypeScriptRenderer', () => { describe('default import placeholder replacement', () => { it('should replace placeholders in default imports correctly', () => { @@ -44,4 +64,59 @@ describe('TypeScriptRenderer', () => { expect(importLines).toEqual(["import foo from 'foo';"]); }); }); + + describe('replacer duplicate name handling', () => { + it('allows duplicate names when kinds differ (type vs value)', () => { + const renderer = new TypeScriptRenderer(); + + // Prepare a mock file and project + const file: any = { + resolvedNames: new LocalBiMap(), + symbols: { body: [], exports: [], imports: [] }, + }; + + const project = { + symbolIdToFiles: () => [file], + symbols: new Map(), + } as any; + + // Two symbols with the same name but different kinds + const typeSymbolId = 1; + const valueSymbolId = 2; + + const typeSymbol: any = { + exportFrom: [], + id: typeSymbolId, + meta: { kind: 'type' }, + name: 'Foo', + placeholder: '_heyapi_1_', + }; + const valueSymbol: any = { + exportFrom: [], + id: valueSymbolId, + meta: {}, + name: 'Foo', + placeholder: '_heyapi_2_', + }; + + project.symbols.set(typeSymbolId, typeSymbol); + project.symbols.set(valueSymbolId, valueSymbol); + + // First replacement should register the name 'Foo' + const first = renderer['replacerFn']({ + file, + project, + symbol: typeSymbol, + }); + expect(first).toEqual('Foo'); + + // Second replacement (different kind) should be allowed to also use 'Foo' + const second = renderer['replacerFn']({ + file, + project, + symbol: valueSymbol, + }); + expect(second).toEqual('Foo'); + }); + }); }); diff --git a/packages/openapi-ts/src/generate/renderer.ts b/packages/openapi-ts/src/generate/renderer.ts index f6e107b8f..9a25299f2 100644 --- a/packages/openapi-ts/src/generate/renderer.ts +++ b/packages/openapi-ts/src/generate/renderer.ts @@ -479,8 +479,16 @@ export class TypeScriptRenderer implements Renderer { const [symbolFile] = project.symbolIdToFiles(symbol.id); const symbolFileResolvedName = symbolFile?.resolvedNames.get(symbol.id); let name = ensureValidIdentifier(symbolFileResolvedName ?? symbol.name); - if (file.resolvedNames.hasValue(name)) { - name = this.getUniqueName(name, file.resolvedNames); + const conflictId = file.resolvedNames.getKey(name); + if (conflictId !== undefined) { + const conflictSymbol = project.symbols.get(conflictId); + if ( + (conflictSymbol?.meta?.kind === 'type' && + symbol.meta?.kind === 'type') || + (conflictSymbol?.meta?.kind !== 'type' && symbol.meta?.kind !== 'type') + ) { + name = this.getUniqueName(name, file.resolvedNames); + } } file.resolvedNames.set(symbol.id, name); return name; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts index 78ac210c8..1a2a48b78 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts @@ -7,9 +7,9 @@ import type { Property } from '../../../tsc'; import { tsc } from '../../../tsc'; import { refToName } from '../../../utils/ref'; import { stringCase } from '../../../utils/stringCase'; +import type { SchemaWithType } from '../../shared/types/schema'; import { fieldName } from '../../shared/utils/case'; import { createSchemaComment } from '../../shared/utils/schema'; -import type { SchemaWithType } from '../../zod/shared/types'; import { createClientOptions } from './clientOptions'; import { exportType } from './export'; import { operationToType } from './operation'; diff --git a/packages/openapi-ts/src/plugins/arktype/api.ts b/packages/openapi-ts/src/plugins/arktype/api.ts new file mode 100644 index 000000000..b9b505206 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/api.ts @@ -0,0 +1,55 @@ +import type { Selector } from '@hey-api/codegen-core'; +import type ts from 'typescript'; + +import type { Plugin } from '../types'; +import type { ValidatorArgs } from './shared/types'; +import { createRequestValidatorV2, createResponseValidatorV2 } from './v2/api'; + +type SelectorType = + | 'data' + | 'external' + | 'ref' + | 'responses' + | 'type-infer-data' + | 'type-infer-ref' + | 'type-infer-responses' + | 'type-infer-webhook-request' + | 'webhook-request'; + +export type IApi = { + createRequestValidator: (args: ValidatorArgs) => ts.ArrowFunction | undefined; + createResponseValidator: ( + args: ValidatorArgs, + ) => ts.ArrowFunction | undefined; + /** + * @param type Selector type. + * @param value Depends on `type`: + * - `data`: `operation.id` string + * - `external`: external modules + * - `ref`: `$ref` JSON pointer + * - `responses`: `operation.id` string + * - `type-infer-data`: `operation.id` string + * - `type-infer-ref`: `$ref` JSON pointer + * - `type-infer-responses`: `operation.id` string + * - `type-infer-webhook-request`: `operation.id` string + * - `webhook-request`: `operation.id` string + * @returns Selector array + */ + getSelector: (type: SelectorType, value?: string) => Selector; +}; + +export class Api implements IApi { + constructor(public meta: Plugin.Name<'arktype'>) {} + + createRequestValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { + return createRequestValidatorV2(args); + } + + createResponseValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { + return createResponseValidatorV2(args); + } + + getSelector(...args: ReadonlyArray): Selector { + return [this.meta.name, ...(args as Selector)]; + } +} diff --git a/packages/openapi-ts/src/plugins/arktype/config.ts b/packages/openapi-ts/src/plugins/arktype/config.ts new file mode 100644 index 000000000..60bde7267 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/config.ts @@ -0,0 +1,271 @@ +import { definePluginConfig, mappers } from '../shared/utils/config'; +import { Api } from './api'; +import { handler } from './plugin'; +import type { ArktypePlugin } from './types'; + +export const defaultConfig: ArktypePlugin['Config'] = { + api: new Api({ + name: 'arktype', + }), + config: { + case: 'PascalCase', + comments: true, + exportFromIndex: false, + metadata: false, + }, + handler, + name: 'arktype', + resolveConfig: (plugin, context) => { + plugin.config.types = context.valueToObject({ + defaultValue: { + infer: { + case: 'PascalCase', + enabled: false, + }, + }, + mappers: { + object: (fields, defaultValue) => ({ + ...fields, + infer: context.valueToObject({ + defaultValue: { + ...(defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + >), + enabled: + fields.infer !== undefined + ? Boolean(fields.infer) + : ( + defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + > + ).enabled, + }, + mappers, + value: fields.infer, + }), + }), + }, + value: plugin.config.types, + }); + + plugin.config.definitions = context.valueToObject({ + defaultValue: { + case: plugin.config.case ?? 'PascalCase', + enabled: true, + name: '{{name}}', + types: { + ...plugin.config.types, + infer: { + ...(plugin.config.types.infer as Extract< + typeof plugin.config.types.infer, + Record + >), + name: '{{name}}', + }, + }, + }, + mappers: { + ...mappers, + object: (fields, defaultValue) => ({ + ...fields, + types: context.valueToObject({ + defaultValue: defaultValue.types!, + mappers: { + object: (fields, defaultValue) => ({ + ...fields, + infer: context.valueToObject({ + defaultValue: { + ...(defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + >), + enabled: + fields.infer !== undefined + ? Boolean(fields.infer) + : ( + defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + > + ).enabled, + }, + mappers, + value: fields.infer, + }), + }), + }, + value: fields.types, + }), + }), + }, + value: plugin.config.definitions, + }); + + plugin.config.requests = context.valueToObject({ + defaultValue: { + case: plugin.config.case ?? 'PascalCase', + enabled: true, + name: '{{name}}Data', + types: { + ...plugin.config.types, + infer: { + ...(plugin.config.types.infer as Extract< + typeof plugin.config.types.infer, + Record + >), + name: '{{name}}Data', + }, + }, + }, + mappers: { + ...mappers, + object: (fields, defaultValue) => ({ + ...fields, + types: context.valueToObject({ + defaultValue: defaultValue.types!, + mappers: { + object: (fields, defaultValue) => ({ + ...fields, + infer: context.valueToObject({ + defaultValue: { + ...(defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + >), + enabled: + fields.infer !== undefined + ? Boolean(fields.infer) + : ( + defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + > + ).enabled, + }, + mappers, + value: fields.infer, + }), + }), + }, + value: fields.types, + }), + }), + }, + value: plugin.config.requests, + }); + + plugin.config.responses = context.valueToObject({ + defaultValue: { + case: plugin.config.case ?? 'PascalCase', + enabled: true, + name: '{{name}}Response', + types: { + ...plugin.config.types, + infer: { + ...(plugin.config.types.infer as Extract< + typeof plugin.config.types.infer, + Record + >), + name: '{{name}}Response', + }, + }, + }, + mappers: { + ...mappers, + object: (fields, defaultValue) => ({ + ...fields, + types: context.valueToObject({ + defaultValue: defaultValue.types!, + mappers: { + object: (fields, defaultValue) => ({ + ...fields, + infer: context.valueToObject({ + defaultValue: { + ...(defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + >), + enabled: + fields.infer !== undefined + ? Boolean(fields.infer) + : ( + defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + > + ).enabled, + }, + mappers, + value: fields.infer, + }), + }), + }, + value: fields.types, + }), + }), + }, + value: plugin.config.responses, + }); + + plugin.config.webhooks = context.valueToObject({ + defaultValue: { + case: plugin.config.case ?? 'PascalCase', + enabled: true, + name: '{{name}}WebhookRequest', + types: { + ...plugin.config.types, + infer: { + ...(plugin.config.types.infer as Extract< + typeof plugin.config.types.infer, + Record + >), + name: '{{name}}WebhookRequest', + }, + }, + }, + mappers: { + ...mappers, + object: (fields, defaultValue) => ({ + ...fields, + types: context.valueToObject({ + defaultValue: defaultValue.types!, + mappers: { + object: (fields, defaultValue) => ({ + ...fields, + infer: context.valueToObject({ + defaultValue: { + ...(defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + >), + enabled: + fields.infer !== undefined + ? Boolean(fields.infer) + : ( + defaultValue.infer as Extract< + typeof defaultValue.infer, + Record + > + ).enabled, + }, + mappers, + value: fields.infer, + }), + }), + }, + value: fields.types, + }), + }), + }, + value: plugin.config.webhooks, + }); + }, + tags: ['validator'], +}; + +/** + * Type helper for Arktype plugin, returns {@link Plugin.Config} object + */ +export const defineConfig = definePluginConfig(defaultConfig); diff --git a/packages/openapi-ts/src/plugins/arktype/constants.ts b/packages/openapi-ts/src/plugins/arktype/constants.ts new file mode 100644 index 000000000..03d831365 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/constants.ts @@ -0,0 +1,44 @@ +import { tsc } from '../../tsc'; + +export const identifiers = { + /** + * {@link https://arktype.io/docs/type-api Type API} + */ + type: { + $: tsc.identifier({ text: '$' }), + allows: tsc.identifier({ text: 'allows' }), + and: tsc.identifier({ text: 'and' }), + array: tsc.identifier({ text: 'array' }), + as: tsc.identifier({ text: 'as' }), + assert: tsc.identifier({ text: 'assert' }), + brand: tsc.identifier({ text: 'brand' }), + configure: tsc.identifier({ text: 'configure' }), + default: tsc.identifier({ text: 'default' }), + describe: tsc.identifier({ text: 'describe' }), + description: tsc.identifier({ text: 'description' }), + equals: tsc.identifier({ text: 'equals' }), + exclude: tsc.identifier({ text: 'exclude' }), + expression: tsc.identifier({ text: 'expression' }), + extends: tsc.identifier({ text: 'extends' }), + extract: tsc.identifier({ text: 'extract' }), + filter: tsc.identifier({ text: 'filter' }), + from: tsc.identifier({ text: 'from' }), + ifEquals: tsc.identifier({ text: 'ifEquals' }), + ifExtends: tsc.identifier({ text: 'ifExtends' }), + infer: tsc.identifier({ text: 'infer' }), + inferIn: tsc.identifier({ text: 'inferIn' }), + intersect: tsc.identifier({ text: 'intersect' }), + json: tsc.identifier({ text: 'json' }), + meta: tsc.identifier({ text: 'meta' }), + narrow: tsc.identifier({ text: 'narrow' }), + onDeepUndeclaredKey: tsc.identifier({ text: 'onDeepUndeclaredKey' }), + onUndeclaredKey: tsc.identifier({ text: 'onUndeclaredKey' }), + optional: tsc.identifier({ text: 'optional' }), + or: tsc.identifier({ text: 'or' }), + overlaps: tsc.identifier({ text: 'overlaps' }), + pipe: tsc.identifier({ text: 'pipe' }), + select: tsc.identifier({ text: 'select' }), + to: tsc.identifier({ text: 'to' }), + toJsonSchema: tsc.identifier({ text: 'toJsonSchema' }), + }, +}; diff --git a/packages/openapi-ts/src/plugins/arktype/index.ts b/packages/openapi-ts/src/plugins/arktype/index.ts new file mode 100644 index 000000000..3325c5553 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/index.ts @@ -0,0 +1,2 @@ +export { defaultConfig, defineConfig } from './config'; +export type { ArktypePlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/arktype/plugin.ts b/packages/openapi-ts/src/plugins/arktype/plugin.ts new file mode 100644 index 000000000..205edde7c --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/plugin.ts @@ -0,0 +1,4 @@ +import type { ArktypePlugin } from './types'; +import { handlerV2 } from './v2/plugin'; + +export const handler: ArktypePlugin['Handler'] = (args) => handlerV2(args); diff --git a/packages/openapi-ts/src/plugins/arktype/shared/export.ts b/packages/openapi-ts/src/plugins/arktype/shared/export.ts new file mode 100644 index 000000000..2d2226c4c --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/shared/export.ts @@ -0,0 +1,53 @@ +import type { Symbol } from '@hey-api/codegen-core'; +import ts from 'typescript'; + +import type { IR } from '../../../ir/types'; +import { tsc } from '../../../tsc'; +import { createSchemaComment } from '../../shared/utils/schema'; +import { identifiers } from '../constants'; +import type { ArktypePlugin } from '../types'; +import type { Ast } from './types'; + +export const exportAst = ({ + ast, + plugin, + schema, + symbol, + typeInferSymbol, +}: { + ast: Ast; + plugin: ArktypePlugin['Instance']; + schema: IR.SchemaObject; + symbol: Symbol; + typeInferSymbol: Symbol | undefined; +}): void => { + const statement = tsc.constVariable({ + comment: plugin.config.comments + ? createSchemaComment({ schema }) + : undefined, + exportConst: symbol.exported, + expression: ast.expression, + name: symbol.placeholder, + // typeName: ast.typeName + // ? (tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: ast.typeName, + // }) as unknown as ts.TypeNode) + // : undefined, + }); + plugin.setSymbolValue(symbol, statement); + + if (typeInferSymbol) { + const inferType = tsc.typeAliasDeclaration({ + exportType: typeInferSymbol.exported, + name: typeInferSymbol.placeholder, + type: ts.factory.createTypeQueryNode( + ts.factory.createQualifiedName( + ts.factory.createIdentifier(symbol.placeholder), + identifiers.type.infer, + ), + ), + }); + plugin.setSymbolValue(typeInferSymbol, inferType); + } +}; diff --git a/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts new file mode 100644 index 000000000..95991994e --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts @@ -0,0 +1,35 @@ +import type ts from 'typescript'; + +import type { IR } from '../../../ir/types'; +import type { ArktypePlugin } from '../types'; + +export type Ast = { + expression: ts.Expression; + hasCircularReference?: boolean; + typeName?: string | ts.Identifier; +}; + +export type IrSchemaToAstOptions = { + plugin: ArktypePlugin['Instance']; + state: State; +}; + +export type State = { + /** + * Path to the schema in the intermediary representation. + */ + _path: ReadonlyArray; + circularReferenceTracker: Array; + /** + * Works the same as `circularReferenceTracker`, but it resets whenever we + * walk inside another schema. This can be used to detect if a schema + * directly references itself. + */ + currentReferenceTracker: Array; + hasCircularReference: boolean; +}; + +export type ValidatorArgs = { + operation: IR.OperationObject; + plugin: ArktypePlugin['Instance']; +}; diff --git a/packages/openapi-ts/src/plugins/arktype/types.d.ts b/packages/openapi-ts/src/plugins/arktype/types.d.ts new file mode 100644 index 000000000..e7f940787 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/types.d.ts @@ -0,0 +1,665 @@ +import type { StringCase, StringName } from '../../types/case'; +import type { DefinePlugin, Plugin } from '../types'; +import type { IApi } from './api'; + +export type UserConfig = Plugin.Name<'arktype'> & + Plugin.Hooks & { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Add comments from input to the generated Arktype schemas? + * + * @default true + */ + comments?: boolean; + /** + * Configuration for reusable schema definitions. + * + * Controls generation of shared Arktype schemas that can be referenced + * across requests and responses. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + */ + definitions?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate Arktype schemas for reusable definitions. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated schema names. The name variable + * is obtained from the schema name. + * + * @default '{{name}}' + */ + name?: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types?: { + /** + * Configuration for `infer` types. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default false + */ + infer?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}' + */ + name?: StringName; + }; + }; + }; + /** + * Should the exports from the generated files be re-exported in the index + * barrel file? + * + * @default false + */ + exportFromIndex?: boolean; + /** + * Enable Arktype metadata support? It's often useful to associate a schema + * with some additional metadata for documentation, code generation, AI + * structured outputs, form validation, and other purposes. + * + * @default false + */ + metadata?: boolean; + /** + * Configuration for request-specific Arktype schemas. + * + * Controls generation of Arktype schemas for request bodies, query parameters, path + * parameters, and headers. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default true + */ + requests?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate Arktype schemas for request definitions. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated schema names. The name variable + * is obtained from the operation name. + * + * @default '{{name}}Data' + */ + name?: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types?: { + /** + * Configuration for `infer` types. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default false + */ + infer?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}Data' + */ + name?: StringName; + }; + }; + }; + /** + * Configuration for response-specific Arktype schemas. + * + * Controls generation of Arktype schemas for response bodies, error responses, + * and status codes. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default true + */ + responses?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate Arktype schemas for response definitions. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated schema names. The name variable + * is obtained from the operation name. + * + * @default '{{name}}Response' + */ + name?: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types?: { + /** + * Configuration for `infer` types. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default false + */ + infer?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}Response' + */ + name?: StringName; + }; + }; + }; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types?: { + /** + * Configuration for `infer` types. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default false + */ + infer?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled?: boolean; + }; + }; + /** + * Configuration for webhook-specific Arktype schemas. + * + * Controls generation of Arktype schemas for webhook payloads. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default true + */ + webhooks?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate Arktype schemas for webhook definitions. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated schema names. The name variable + * is obtained from the webhook key. + * + * @default '{{name}}WebhookRequest' + */ + name?: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types?: { + /** + * Configuration for `infer` types. + * + * Can be: + * - `boolean`: Shorthand for `{ enabled: boolean }` + * - `string` or `function`: Shorthand for `{ name: string | function }` + * - `object`: Full configuration object + * + * @default false + */ + infer?: + | boolean + | StringName + | { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case?: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled?: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}WebhookRequest' + */ + name?: StringName; + }; + }; + }; + }; + +export type Config = Plugin.Name<'arktype'> & + Plugin.Hooks & { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Add comments from input to the generated Arktype schemas? + * + * @default true + */ + comments: boolean; + /** + * Configuration for reusable schema definitions. + * + * Controls generation of shared Arktype schemas that can be referenced across + * requests and responses. + */ + definitions: { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate Arktype schemas for reusable definitions. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated schema names. The name variable is + * obtained from the schema name. + * + * @default '{{name}}' + */ + name: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types: { + /** + * Configuration for `infer` types. + */ + infer: { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}' + */ + name: StringName; + }; + }; + }; + /** + * Should the exports from the generated files be re-exported in the index + * barrel file? + * + * @default false + */ + exportFromIndex: boolean; + /** + * Enable Arktype metadata support? It's often useful to associate a schema with + * some additional metadata for documentation, code generation, AI + * structured outputs, form validation, and other purposes. + * + * @default false + */ + metadata: boolean; + /** + * Configuration for request-specific Arktype schemas. + * + * Controls generation of Arktype schemas for request bodies, query parameters, path + * parameters, and headers. + */ + requests: { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate Arktype schemas for request definitions. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated schema names. The name variable is + * obtained from the operation name. + * + * @default '{{name}}Data' + */ + name: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types: { + /** + * Configuration for `infer` types. + */ + infer: { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}Data' + */ + name: StringName; + }; + }; + }; + /** + * Configuration for response-specific Arktype schemas. + * + * Controls generation of Arktype schemas for response bodies, error responses, + * and status codes. + */ + responses: { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate Arktype schemas for response definitions. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated schema names. The name variable is + * obtained from the operation name. + * + * @default '{{name}}Response' + */ + name: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types: { + /** + * Configuration for `infer` types. + */ + infer: { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}Response' + */ + name: StringName; + }; + }; + }; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types: { + /** + * Configuration for `infer` types. + */ + infer: { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled: boolean; + }; + }; + /** + * Configuration for webhook-specific Arktype schemas. + * + * Controls generation of Arktype schemas for webhook payloads. + */ + webhooks: { + /** + * The casing convention to use for generated names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate Arktype schemas for webhook definitions. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated schema names. The name variable is + * is obtained from the webhook key. + * + * @default '{{name}}WebhookRequest' + */ + name: StringName; + /** + * Configuration for TypeScript type generation from Arktype schemas. + * + * Controls generation of TypeScript types based on the generated Arktype schemas. + */ + types: { + /** + * Configuration for `infer` types. + */ + infer: { + /** + * The casing convention to use for generated type names. + * + * @default 'PascalCase' + */ + case: StringCase; + /** + * Whether to generate TypeScript types from Arktype schemas. + * + * @default true + */ + enabled: boolean; + /** + * Custom naming pattern for generated type names. The name variable is + * obtained from the Arktype schema name. + * + * @default '{{name}}WebhookRequest' + */ + name: StringName; + }; + }; + }; + }; + +export type ArktypePlugin = DefinePlugin; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/api.ts b/packages/openapi-ts/src/plugins/arktype/v2/api.ts new file mode 100644 index 000000000..65589ed60 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/v2/api.ts @@ -0,0 +1,73 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../tsc'; +// import { identifiers } from '../constants'; +import type { ValidatorArgs } from '../shared/types'; + +export const createRequestValidatorV2 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: 'parseAsync', + // name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; + +export const createResponseValidatorV2 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol( + plugin.api.getSelector('responses', operation.id), + ); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: 'parseAsync', + // name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts new file mode 100644 index 000000000..492751647 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts @@ -0,0 +1,454 @@ +import ts from 'typescript'; + +import type { IR } from '../../../ir/types'; +import { buildName } from '../../../openApi/shared/utils/name'; +import { refToName } from '../../../utils/ref'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { exportAst } from '../shared/export'; +import type { Ast, IrSchemaToAstOptions } from '../shared/types'; +import type { ArktypePlugin } from '../types'; +import { irSchemaWithTypeToAst } from './toAst'; + +export const irSchemaToAst = ({ + // optional, + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + /** + * Accept `optional` to handle optional object properties. We can't handle + * this inside the object function because `.optional()` must come before + * `.default()` which is handled in this function. + */ + optional?: boolean; + schema: IR.SchemaObject; +}): Ast => { + let ast: Partial = {}; + + // const z = plugin.referenceSymbol( + // plugin.api.getSelector('external', 'zod.z'), + // ); + + if (schema.$ref) { + // const isCircularReference = state.circularReferenceTracker.includes( + // schema.$ref, + // ); + // const isSelfReference = state.currentReferenceTracker.includes(schema.$ref); + // state.circularReferenceTracker.push(schema.$ref); + // state.currentReferenceTracker.push(schema.$ref); + // const selector = plugin.api.getSelector('ref', schema.$ref); + // let symbol = plugin.getSymbol(selector); + // if (isCircularReference) { + // if (!symbol) { + // symbol = plugin.referenceSymbol(selector); + // } + // if (isSelfReference) { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: identifiers.lazy, + // }), + // parameters: [ + // tsc.arrowFunction({ + // returnType: tsc.keywordTypeNode({ keyword: 'any' }), + // statements: [ + // tsc.returnStatement({ + // expression: tsc.identifier({ text: symbol.placeholder }), + // }), + // ], + // }), + // ], + // }); + // } else { + // ast.expression = tsc.identifier({ text: symbol.placeholder }); + // } + // ast.hasCircularReference = schema.circular; + // } else { + // if (!symbol) { + // // if $ref hasn't been processed yet, inline it to avoid the + // // "Block-scoped variable used before its declaration." error + // // this could be (maybe?) fixed by reshuffling the generation order + // const ref = plugin.context.resolveIrRef(schema.$ref); + // handleComponent({ + // id: schema.$ref, + // plugin, + // schema: ref, + // state: { + // ...state, + // _path: jsonPointerToPath(schema.$ref), + // }, + // }); + // } else { + // ast.hasCircularReference = schema.circular; + // } + // const refSymbol = plugin.referenceSymbol(selector); + // ast.expression = tsc.identifier({ text: refSymbol.placeholder }); + // } + // state.circularReferenceTracker.pop(); + // state.currentReferenceTracker.pop(); + } else if (schema.type) { + // const zSchema = irSchemaWithTypeToAst({ + // plugin, + // schema: schema as SchemaWithType, + // state, + // }); + // ast.expression = zSchema.expression; + // ast.hasCircularReference = zSchema.hasCircularReference; + // if (plugin.config.metadata && schema.description) { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: ast.expression, + // name: identifiers.register, + // }), + // parameters: [ + // tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: identifiers.globalRegistry, + // }), + // tsc.objectExpression({ + // obj: [ + // { + // key: 'description', + // value: tsc.stringLiteral({ text: schema.description }), + // }, + // ], + // }), + // ], + // }); + // } + } else if (schema.items) { + // schema = deduplicateSchema({ schema }); + + if (schema.items) { + // const itemSchemas = schema.items.map((item, index) => + // irSchemaToAst({ + // plugin, + // schema: item, + // state: { + // ...state, + // _path: [...state._path, 'items', index], + // }, + // }), + // ); + // if (schema.logicalOperator === 'and') { + // const firstSchema = schema.items[0]!; + // // we want to add an intersection, but not every schema can use the same API. + // // if the first item contains another array or not an object, we cannot use + // // `.merge()` as that does not exist on `.union()` and non-object schemas. + // if ( + // firstSchema.logicalOperator === 'or' || + // (firstSchema.type && firstSchema.type !== 'object') + // ) { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: identifiers.intersection, + // }), + // parameters: itemSchemas.map((schema) => schema.expression), + // }); + // } else { + // ast.expression = itemSchemas[0]!.expression; + // itemSchemas.slice(1).forEach((schema) => { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: ast.expression!, + // name: identifiers.and, + // }), + // parameters: [ + // schema.hasCircularReference + // ? tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: identifiers.lazy, + // }), + // parameters: [ + // tsc.arrowFunction({ + // statements: [ + // tsc.returnStatement({ + // expression: schema.expression, + // }), + // ], + // }), + // ], + // }) + // : schema.expression, + // ], + // }); + // }); + // } + // } else { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: identifiers.union, + // }), + // parameters: [ + // tsc.arrayLiteralExpression({ + // elements: itemSchemas.map((schema) => schema.expression), + // }), + // ], + // }); + // } + } else { + ast = irSchemaToAst({ + plugin, + schema, + state, + }); + } + } else { + // // catch-all fallback for failed schemas + // const zSchema = irSchemaWithTypeToAst({ + // plugin, + // schema: { + // type: 'unknown', + // }, + // state, + // }); + // ast.expression = zSchema.expression; + } + + // TODO: remove later + const zSchema = irSchemaWithTypeToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + ast.expression = zSchema.expression; + // END TODO: remove later + + // if (ast.expression) { + // if (schema.accessScope === 'read') { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: ast.expression, + // name: identifiers.readonly, + // }), + // }); + // } + + // if (optional) { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: identifiers.optional, + // }), + // parameters: [ast.expression], + // }); + // ast.typeName = identifiers.ZodOptional; + // } + + // if (schema.default !== undefined) { + // const isBigInt = schema.type === 'integer' && schema.format === 'int64'; + // const callParameter = numberParameter({ + // isBigInt, + // value: schema.default, + // }); + // if (callParameter) { + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: ast.expression, + // name: identifiers.default, + // }), + // parameters: [callParameter], + // }); + // } + // } + // } + + return ast as Ast; +}; + +const handleComponent = ({ + id, + plugin, + schema, + state: _state, +}: Omit & { + id: string; + schema: IR.SchemaObject; + state?: Partial; +}): void => { + const state: IrSchemaToAstOptions['state'] = { + _path: _state?._path ?? [], + circularReferenceTracker: _state?.circularReferenceTracker ?? [id], + currentReferenceTracker: _state?.currentReferenceTracker ?? [id], + hasCircularReference: _state?.hasCircularReference ?? false, + }; + + const selector = plugin.api.getSelector('ref', id); + let symbol = plugin.getSymbol(selector); + if (symbol && !plugin.getSymbolValue(symbol)) return; + + const ast = irSchemaToAst({ plugin, schema, state }); + const baseName = refToName(id); + const resourceType = pathToSymbolResourceType(state._path); + symbol = plugin.registerSymbol({ + exported: true, + meta: { + resourceType, + }, + name: buildName({ + config: plugin.config.definitions, + name: baseName, + }), + selector, + }); + const typeInferSymbol = plugin.config.definitions.types.infer.enabled + ? plugin.registerSymbol({ + exported: true, + meta: { + kind: 'type', + resourceType, + }, + name: buildName({ + config: plugin.config.definitions.types.infer, + name: baseName, + }), + selector: plugin.api.getSelector('type-infer-ref', id), + }) + : undefined; + exportAst({ + ast, + plugin, + schema, + symbol, + typeInferSymbol, + }); +}; + +export const handlerV2: ArktypePlugin['Handler'] = ({ plugin }) => { + const typeSymbol = plugin.registerSymbol({ + external: 'arktype', + name: 'type', + selector: plugin.api.getSelector('external', 'arktype.type'), + }); + + plugin.forEach( + 'operation', + 'parameter', + 'requestBody', + 'schema', + 'webhook', + (event) => { + if (event.type === 'schema') { + const symbol = plugin.registerSymbol({ + exported: true, + meta: { + resourceType: pathToSymbolResourceType([ + 'components', + 'schemas', + event.name, + ]), + }, + name: event.name, + selector: plugin.api.getSelector('ref', event.$ref), + }); + + const userNode = ts.factory.createVariableStatement( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + symbol.placeholder, + undefined, + undefined, + ts.factory.createCallExpression( + ts.factory.createIdentifier(typeSymbol.placeholder), + undefined, + [ + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + 'name', + ts.factory.createStringLiteral('string'), + ), + ts.factory.createPropertyAssignment( + 'platform', + ts.factory.createStringLiteral("'android' | 'ios'"), + ), + ts.factory.createPropertyAssignment( + ts.factory.createComputedPropertyName( + ts.factory.createStringLiteral('versions?'), + ), + ts.factory.createStringLiteral('(number | string)[]'), + ), + ], + true, + ), + ], + ), + ), + ], + ts.NodeFlags.Const, + ), + ); + + plugin.setSymbolValue(symbol, userNode); + } + + switch (event.type) { + // case 'operation': + // operationToZodSchema({ + // getZodSchema: (schema) => { + // const state: State = { + // circularReferenceTracker: [], + // currentReferenceTracker: [], + // hasCircularReference: false, + // }; + // return schemaToZodSchema({ plugin, schema, state }); + // }, + // operation: event.operation, + // plugin, + // }); + // break; + case 'parameter': + handleComponent({ + id: event.$ref, + plugin, + schema: event.parameter.schema, + state: { + _path: event._path, + }, + }); + break; + case 'requestBody': + handleComponent({ + id: event.$ref, + plugin, + schema: event.requestBody.schema, + state: { + _path: event._path, + }, + }); + break; + case 'schema': + handleComponent({ + id: event.$ref, + plugin, + schema: event.schema, + state: { + _path: event._path, + }, + }); + break; + // case 'webhook': + // webhookToZodSchema({ + // getZodSchema: (schema) => { + // const state: State = { + // circularReferenceTracker: [], + // currentReferenceTracker: [], + // hasCircularReference: false, + // }; + // return schemaToZodSchema({ plugin, schema, state }); + // }, + // operation: event.operation, + // plugin, + // }); + // break; + } + }, + ); +}; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts new file mode 100644 index 000000000..ec06668b0 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts @@ -0,0 +1,122 @@ +import ts from 'typescript'; + +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +// import { arrayToAst } from "./array"; +// import { booleanToAst } from "./boolean"; +// import { enumToAst } from "./enum"; +// import { neverToAst } from "./never"; +// import { nullToAst } from "./null"; +// import { numberToAst } from "./number"; +// import { objectToAst } from "./object"; +// import { stringToAst } from "./string"; +// import { tupleToAst } from "./tuple"; +// import { undefinedToAst } from "./undefined"; +// import { unknownToAst } from "./unknown"; +// import { voidToAst } from "./void"; + +export const irSchemaWithTypeToAst = ({ + schema, + ...args +}: IrSchemaToAstOptions & { + schema: SchemaWithType; +}): Omit => { + const type = args.plugin.referenceSymbol( + args.plugin.api.getSelector('external', 'arktype.type'), + ); + + const expression = ts.factory.createCallExpression( + ts.factory.createIdentifier(type.placeholder), + undefined, + [ + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + 'name', + ts.factory.createStringLiteral('string'), + ), + ts.factory.createPropertyAssignment( + 'platform', + ts.factory.createStringLiteral("'android' | 'ios'"), + ), + ts.factory.createPropertyAssignment( + ts.factory.createComputedPropertyName( + ts.factory.createStringLiteral('versions?'), + ), + ts.factory.createStringLiteral('(number | string)[]'), + ), + ], + true, + ), + ], + ); + + switch (schema.type) { + // case 'array': + // return arrayToAst({ + // ...args, + // schema: schema as SchemaWithType<'array'>, + // }); + // case 'boolean': + // return booleanToAst({ + // ...args, + // schema: schema as SchemaWithType<'boolean'>, + // }); + // case 'enum': + // return enumToAst({ + // ...args, + // schema: schema as SchemaWithType<'enum'>, + // }); + // case 'integer': + // case 'number': + // return numberToAst({ + // ...args, + // schema: schema as SchemaWithType<'integer' | 'number'>, + // }); + // case 'never': + // return neverToAst({ + // ...args, + // schema: schema as SchemaWithType<'never'>, + // }); + // case 'null': + // return nullToAst({ + // ...args, + // schema: schema as SchemaWithType<'null'>, + // }); + // case 'object': + // return objectToAst({ + // ...args, + // schema: schema as SchemaWithType<'object'>, + // }); + // case 'string': + // return stringToAst({ + // ...args, + // schema: schema as SchemaWithType<'string'>, + // }); + // case 'tuple': + // return tupleToAst({ + // ...args, + // schema: schema as SchemaWithType<'tuple'>, + // }); + // case 'undefined': + // return undefinedToAst({ + // ...args, + // schema: schema as SchemaWithType<'undefined'>, + // }); + // case 'unknown': + // return unknownToAst({ + // ...args, + // schema: schema as SchemaWithType<'unknown'>, + // }); + // case 'void': + // return voidToAst({ + // ...args, + // schema: schema as SchemaWithType<'void'>, + // }); + default: + return { + expression, + hasCircularReference: false, + }; + } +}; diff --git a/packages/openapi-ts/src/plugins/config.ts b/packages/openapi-ts/src/plugins/config.ts index d8460ecec..46002cb6e 100644 --- a/packages/openapi-ts/src/plugins/config.ts +++ b/packages/openapi-ts/src/plugins/config.ts @@ -42,6 +42,8 @@ import type { TanStackSvelteQueryPlugin } from './@tanstack/svelte-query'; import { defaultConfig as tanStackSvelteQuery } from './@tanstack/svelte-query'; import type { TanStackVueQueryPlugin } from './@tanstack/vue-query'; import { defaultConfig as tanStackVueQuery } from './@tanstack/vue-query'; +import type { ArktypePlugin } from './arktype'; +import { defaultConfig as arktype } from './arktype'; import type { FastifyPlugin } from './fastify'; import { defaultConfig as fastify } from './fastify'; import type { Plugin, PluginNames } from './types'; @@ -68,6 +70,7 @@ export interface PluginConfigMap { '@tanstack/solid-query': TanStackSolidQueryPlugin['Types']; '@tanstack/svelte-query': TanStackSvelteQueryPlugin['Types']; '@tanstack/vue-query': TanStackVueQueryPlugin['Types']; + arktype: ArktypePlugin['Types']; fastify: FastifyPlugin['Types']; 'legacy/angular': HeyApiClientLegacyAngularPlugin['Types']; 'legacy/axios': HeyApiClientLegacyAxiosPlugin['Types']; @@ -98,6 +101,7 @@ export const defaultPluginConfigs: { '@tanstack/solid-query': tanStackSolidQuery, '@tanstack/svelte-query': tanStackSvelteQuery, '@tanstack/vue-query': tanStackVueQuery, + arktype, fastify, 'legacy/angular': heyApiLegacyAngular, 'legacy/axios': heyApiLegacyAxios, diff --git a/packages/openapi-ts/src/plugins/shared/types/schema.d.ts b/packages/openapi-ts/src/plugins/shared/types/schema.d.ts new file mode 100644 index 000000000..712368428 --- /dev/null +++ b/packages/openapi-ts/src/plugins/shared/types/schema.d.ts @@ -0,0 +1,8 @@ +import type { IR } from '../../../ir/types'; + +export interface SchemaWithType< + T extends + Required['type'] = Required['type'], +> extends Omit { + type: Extract['type'], T>; +} diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 79f92e220..5d3172ab5 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -19,7 +19,7 @@ export type PluginClientNames = | 'legacy/node' | 'legacy/xhr'; -export type PluginValidatorNames = 'valibot' | 'zod'; +export type PluginValidatorNames = 'arktype' | 'valibot' | 'zod'; export type PluginNames = | PluginClientNames diff --git a/packages/openapi-ts/src/plugins/valibot/api.ts b/packages/openapi-ts/src/plugins/valibot/api.ts index be4b955e8..7a1813f69 100644 --- a/packages/openapi-ts/src/plugins/valibot/api.ts +++ b/packages/openapi-ts/src/plugins/valibot/api.ts @@ -1,18 +1,16 @@ import type { Selector } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { IR } from '../../ir/types'; -import { tsc } from '../../tsc'; import type { Plugin } from '../types'; -import { identifiers } from './constants'; -import type { ValibotPlugin } from './types'; +import type { ValidatorArgs } from './shared/types'; +import { createRequestValidatorV1, createResponseValidatorV1 } from './v1/api'; -type SelectorType = 'data' | 'import' | 'ref' | 'responses' | 'webhook-request'; - -type ValidatorArgs = { - operation: IR.OperationObject; - plugin: ValibotPlugin['Instance']; -}; +type SelectorType = + | 'data' + | 'external' + | 'ref' + | 'responses' + | 'webhook-request'; export type IApi = { createRequestValidator: (args: ValidatorArgs) => ts.ArrowFunction | undefined; @@ -23,7 +21,7 @@ export type IApi = { * @param type Selector type. * @param value Depends on `type`: * - `data`: `operation.id` string - * - `import`: headless symbols representing module imports + * - `external`: external modules * - `ref`: `$ref` JSON pointer * - `responses`: `operation.id` string * - `webhook-request`: `operation.id` string @@ -35,86 +33,12 @@ export type IApi = { export class Api implements IApi { constructor(public meta: Plugin.Name<'valibot'>) {} - createRequestValidator({ - operation, - plugin, - }: ValidatorArgs): ts.ArrowFunction | undefined { - const symbol = plugin.getSymbol( - plugin.api.getSelector('data', operation.id), - ); - if (!symbol) return; - - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - const dataParameterName = 'data'; - - return tsc.arrowFunction({ - async: true, - parameters: [ - { - name: dataParameterName, - }, - ], - statements: [ - tsc.returnStatement({ - expression: tsc.awaitExpression({ - expression: tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.async.parseAsync, - }), - parameters: [ - tsc.identifier({ text: symbol.placeholder }), - tsc.identifier({ text: dataParameterName }), - ], - }), - }), - }), - ], - }); + createRequestValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { + return createRequestValidatorV1(args); } - createResponseValidator({ - operation, - plugin, - }: ValidatorArgs): ts.ArrowFunction | undefined { - const symbol = plugin.getSymbol( - plugin.api.getSelector('responses', operation.id), - ); - if (!symbol) return; - - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - const dataParameterName = 'data'; - - return tsc.arrowFunction({ - async: true, - parameters: [ - { - name: dataParameterName, - }, - ], - statements: [ - tsc.returnStatement({ - expression: tsc.awaitExpression({ - expression: tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.async.parseAsync, - }), - parameters: [ - tsc.identifier({ text: symbol.placeholder }), - tsc.identifier({ text: dataParameterName }), - ], - }), - }), - }), - ], - }); + createResponseValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { + return createResponseValidatorV1(args); } getSelector(...args: ReadonlyArray): Selector { diff --git a/packages/openapi-ts/src/plugins/valibot/plugin.ts b/packages/openapi-ts/src/plugins/valibot/plugin.ts index 00cc6814d..4a72ae614 100644 --- a/packages/openapi-ts/src/plugins/valibot/plugin.ts +++ b/packages/openapi-ts/src/plugins/valibot/plugin.ts @@ -1,1375 +1,4 @@ -import type { Symbol } from '@hey-api/codegen-core'; -import ts from 'typescript'; - -import { deduplicateSchema } from '../../ir/schema'; -import type { IR } from '../../ir/types'; -import { buildName } from '../../openApi/shared/utils/name'; -import { tsc } from '../../tsc'; -import type { StringCase, StringName } from '../../types/case'; -import { jsonPointerToPath, refToName } from '../../utils/ref'; -import { numberRegExp } from '../../utils/regexp'; -import { pathToSymbolResourceType } from '../shared/utils/meta'; -import { createSchemaComment } from '../shared/utils/schema'; -import type { SchemaWithType } from '../zod/shared/types'; -import { identifiers } from './constants'; -import { - INTEGER_FORMATS, - isIntegerFormat, - needsBigIntForFormat, - numberParameter, -} from './number-helpers'; -import { operationToValibotSchema } from './operation'; import type { ValibotPlugin } from './types'; -import { webhookToValibotSchema } from './webhook'; - -export interface State { - circularReferenceTracker: Set; - hasCircularReference: boolean; - nameCase: StringCase; - nameTransformer: StringName; -} - -type SchemaToValibotSchemaOptions = { - /** - * Path to the schema in the intermediary representation. - */ - _path: ReadonlyArray; - plugin: ValibotPlugin['Instance']; -}; - -const pipesToExpression = ({ - pipes, - plugin, -}: { - pipes: Array; - plugin: ValibotPlugin['Instance']; -}) => { - if (pipes.length === 1) { - return pipes[0]!; - } - - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.methods.pipe, - }), - parameters: pipes, - }); - return expression; -}; - -const arrayTypeToValibotSchema = ({ - _path, - plugin, - schema, - state, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'array'>; - state: State; -}): ts.Expression => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - const functionName = tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.array, - }); - - const pipes: Array = []; - - if (!schema.items) { - const expression = tsc.callExpression({ - functionName, - parameters: [ - unknownTypeToValibotSchema({ - _path, - plugin, - schema: { - type: 'unknown', - }, - }), - ], - }); - pipes.push(expression); - } else { - schema = deduplicateSchema({ schema }); - - // at least one item is guaranteed - const itemExpressions = schema.items!.map((item, index) => { - const schemaPipes = schemaToValibotSchema({ - _path: [..._path, 'items', index], - plugin, - schema: item, - state, - }); - return pipesToExpression({ pipes: schemaPipes, plugin }); - }); - - if (itemExpressions.length === 1) { - const expression = tsc.callExpression({ - functionName, - parameters: itemExpressions, - }); - pipes.push(expression); - } else { - if (schema.logicalOperator === 'and') { - // TODO: parser - handle intersection - // return tsc.typeArrayNode( - // tsc.typeIntersectionNode({ types: itemExpressions }), - // ); - } - - // TODO: parser - handle union - // return tsc.typeArrayNode(tsc.typeUnionNode({ types: itemExpressions })); - - const expression = tsc.callExpression({ - functionName, - parameters: [ - unknownTypeToValibotSchema({ - _path, - plugin, - schema: { - type: 'unknown', - }, - }), - ], - }); - pipes.push(expression); - } - } - - if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }); - pipes.push(expression); - } else { - if (schema.minItems !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.minLength, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }); - pipes.push(expression); - } - - if (schema.maxItems !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.maxLength, - }), - parameters: [tsc.valueToExpression({ value: schema.maxItems })], - }); - pipes.push(expression); - } - } - - return pipesToExpression({ pipes, plugin }); -}; - -const booleanTypeToValibotSchema = ({ - plugin, - schema, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'boolean'>; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - if (typeof schema.const === 'boolean') { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.literal, - }), - parameters: [tsc.ots.boolean(schema.const)], - }); - return expression; - } - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.boolean, - }), - }); - return expression; -}; - -const enumTypeToValibotSchema = ({ - _path, - plugin, - schema, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'enum'>; -}): ts.CallExpression => { - const enumMembers: Array = []; - - let isNullable = false; - - for (const item of schema.items ?? []) { - // Zod supports only string enums - if (item.type === 'string' && typeof item.const === 'string') { - enumMembers.push( - tsc.stringLiteral({ - text: item.const, - }), - ); - } else if (item.type === 'null' || item.const === null) { - isNullable = true; - } - } - - if (!enumMembers.length) { - return unknownTypeToValibotSchema({ - _path, - plugin, - schema: { - type: 'unknown', - }, - }); - } - - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - let resultExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.picklist, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: enumMembers, - multiLine: false, - }), - ], - }); - - if (isNullable) { - resultExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.nullable, - }), - parameters: [resultExpression], - }); - } - - return resultExpression; -}; - -const neverTypeToValibotSchema = ({ - plugin, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'never'>; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.never, - }), - }); - return expression; -}; - -const nullTypeToValibotSchema = ({ - plugin, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'null'>; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.null, - }), - }); - return expression; -}; - -const numberTypeToValibotSchema = ({ - plugin, - schema, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'integer' | 'number'>; -}) => { - const format = schema.format; - const isInteger = schema.type === 'integer'; - const isBigInt = needsBigIntForFormat(format); - const formatInfo = isIntegerFormat(format) ? INTEGER_FORMATS[format] : null; - - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - // Return early if const is defined since we can create a literal type directly without additional validation - if (schema.const !== undefined && schema.const !== null) { - const constValue = schema.const; - let literalValue; - - // Case 1: Number with no format -> generate literal with the number - if (typeof constValue === 'number' && !format) { - literalValue = tsc.ots.number(constValue); - } - // Case 2: Number with format -> check if format needs BigInt, generate appropriate literal - else if (typeof constValue === 'number' && format) { - if (isBigInt) { - // Format requires BigInt, convert number to BigInt - literalValue = tsc.callExpression({ - functionName: 'BigInt', - parameters: [tsc.ots.string(constValue.toString())], - }); - } else { - // Regular format, use number as-is - literalValue = tsc.ots.number(constValue); - } - } - // Case 3: Format that allows string -> generate BigInt literal (for int64/uint64 formats) - else if (typeof constValue === 'string' && isBigInt) { - // Remove 'n' suffix if present in string - const cleanString = constValue.endsWith('n') - ? constValue.slice(0, -1) - : constValue; - literalValue = tsc.callExpression({ - functionName: 'BigInt', - parameters: [tsc.ots.string(cleanString)], - }); - } - // Case 4: Const is typeof bigint (literal) -> transform from literal to BigInt() - else if (typeof constValue === 'bigint') { - // Convert BigInt to string and remove 'n' suffix that toString() adds - const bigintString = constValue.toString(); - const cleanString = bigintString.endsWith('n') - ? bigintString.slice(0, -1) - : bigintString; - literalValue = tsc.callExpression({ - functionName: 'BigInt', - parameters: [tsc.ots.string(cleanString)], - }); - } - // Default case: use value as-is for other types - else { - literalValue = tsc.valueToExpression({ value: constValue }); - } - - return tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.literal, - }), - parameters: [literalValue], - }); - } - - const pipes: Array = []; - - // For bigint formats (int64, uint64), create union of number, string, and bigint with transform - if (isBigInt) { - const unionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.number, - }), - }), - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.string, - }), - }), - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.bigInt, - }), - }), - ], - multiLine: false, - }), - ], - }); - pipes.push(unionExpression); - - // Add transform to convert to BigInt - const transformExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.transform, - }), - parameters: [ - tsc.arrowFunction({ - parameters: [{ name: 'x' }], - statements: tsc.callExpression({ - functionName: 'BigInt', - parameters: [tsc.identifier({ text: 'x' })], - }), - }), - ], - }); - pipes.push(transformExpression); - } else { - // For regular number formats, use number schema - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.number, - }), - }); - pipes.push(expression); - } - - // Add integer validation for integer types (except when using bigint union) - if (!isBigInt && isInteger) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.integer, - }), - }); - pipes.push(expression); - } - - // Add format-specific range validations - if (formatInfo) { - const minValue = formatInfo.min; - const maxValue = formatInfo.max; - const minErrorMessage = formatInfo.minError; - const maxErrorMessage = formatInfo.maxError; - - // Add minimum value validation - const minExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.minValue, - }), - parameters: [ - isBigInt - ? tsc.callExpression({ - functionName: 'BigInt', - parameters: [tsc.ots.string(minValue.toString())], - }) - : tsc.ots.number(minValue as number), - tsc.ots.string(minErrorMessage), - ], - }); - pipes.push(minExpression); - - // Add maximum value validation - const maxExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.maxValue, - }), - parameters: [ - isBigInt - ? tsc.callExpression({ - functionName: 'BigInt', - parameters: [tsc.ots.string(maxValue.toString())], - }) - : tsc.ots.number(maxValue as number), - tsc.ots.string(maxErrorMessage), - ], - }); - pipes.push(maxExpression); - } - - if (schema.exclusiveMinimum !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.gtValue, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMinimum }), - ], - }); - pipes.push(expression); - } else if (schema.minimum !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.minValue, - }), - parameters: [numberParameter({ isBigInt, value: schema.minimum })], - }); - pipes.push(expression); - } - - if (schema.exclusiveMaximum !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.ltValue, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMaximum }), - ], - }); - pipes.push(expression); - } else if (schema.maximum !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.maxValue, - }), - parameters: [numberParameter({ isBigInt, value: schema.maximum })], - }); - pipes.push(expression); - } - - return pipesToExpression({ pipes, plugin }); -}; - -const objectTypeToValibotSchema = ({ - _path, - plugin, - schema, - state, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'object'>; - state: State; -}): { - anyType: string; - expression: ts.CallExpression; -} => { - // TODO: parser - handle constants - const properties: Array = []; - - const required = schema.required ?? []; - - for (const name in schema.properties) { - const property = schema.properties[name]!; - const isRequired = required.includes(name); - - const schemaPipes = schemaToValibotSchema({ - _path: [..._path, 'properties', name], - optional: !isRequired, - plugin, - schema: property, - state, - }); - - numberRegExp.lastIndex = 0; - let propertyName; - if (numberRegExp.test(name)) { - // For numeric literals, we'll handle negative numbers by using a string literal - // instead of trying to use a PrefixUnaryExpression - propertyName = name.startsWith('-') - ? ts.factory.createStringLiteral(name) - : ts.factory.createNumericLiteral(name); - } else { - propertyName = name; - } - // TODO: parser - abstract safe property name logic - if ( - ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && - !name.startsWith("'") && - !name.endsWith("'") - ) { - propertyName = `'${name}'`; - } - properties.push( - tsc.propertyAssignment({ - initializer: pipesToExpression({ pipes: schemaPipes, plugin }), - name: propertyName, - }), - ); - } - - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - if ( - schema.additionalProperties && - schema.additionalProperties.type === 'object' && - !Object.keys(properties).length - ) { - const pipes = schemaToValibotSchema({ - _path: [..._path, 'additionalProperties'], - plugin, - schema: schema.additionalProperties, - state, - }); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.record, - }), - parameters: [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.string, - }), - parameters: [], - }), - pipesToExpression({ pipes, plugin }), - ], - }); - return { - anyType: 'AnyZodObject', - expression, - }; - } - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.object, - }), - parameters: [ts.factory.createObjectLiteralExpression(properties, true)], - }); - return { - // Zod uses AnyZodObject here, maybe we want to be more specific too - anyType: identifiers.types.GenericSchema.text, - expression, - }; -}; - -const stringTypeToValibotSchema = ({ - plugin, - schema, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'string'>; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - if (typeof schema.const === 'string') { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.literal, - }), - parameters: [tsc.ots.string(schema.const)], - }); - return expression; - } - - const pipes: Array = []; - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.string, - }), - }); - pipes.push(expression); - - if (schema.format) { - switch (schema.format) { - case 'date': - pipes.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.isoDate, - }), - }), - ); - break; - case 'date-time': - pipes.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.isoTimestamp, - }), - }), - ); - break; - case 'ipv4': - case 'ipv6': - pipes.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.ip, - }), - }), - ); - break; - case 'uri': - pipes.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.url, - }), - }), - ); - break; - case 'email': - case 'time': - case 'uuid': - pipes.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: tsc.identifier({ text: schema.format }), - }), - }), - ); - break; - } - } - - if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }); - pipes.push(expression); - } else { - if (schema.minLength !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.minLength, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }); - pipes.push(expression); - } - - if (schema.maxLength !== undefined) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.maxLength, - }), - parameters: [tsc.valueToExpression({ value: schema.maxLength })], - }); - pipes.push(expression); - } - } - - if (schema.pattern) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.regex, - }), - parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], - }); - pipes.push(expression); - } - - return pipesToExpression({ pipes, plugin }); -}; - -const tupleTypeToValibotSchema = ({ - _path, - plugin, - schema, - state, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'tuple'>; - state: State; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - if (schema.const && Array.isArray(schema.const)) { - const tupleElements = schema.const.map((value) => - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.literal, - }), - parameters: [tsc.valueToExpression({ value })], - }), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - return expression; - } - - if (schema.items) { - const tupleElements = schema.items.map((item, index) => { - const schemaPipes = schemaToValibotSchema({ - _path: [..._path, 'items', index], - plugin, - schema: item, - state, - }); - return pipesToExpression({ pipes: schemaPipes, plugin }); - }); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - return expression; - } - - return unknownTypeToValibotSchema({ - _path, - plugin, - schema: { - type: 'unknown', - }, - }); -}; - -const undefinedTypeToValibotSchema = ({ - plugin, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'undefined'>; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.undefined, - }), - }); - return expression; -}; - -const unknownTypeToValibotSchema = ({ - plugin, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'unknown'>; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.unknown, - }), - }); - return expression; -}; - -const voidTypeToValibotSchema = ({ - plugin, -}: SchemaToValibotSchemaOptions & { - schema: SchemaWithType<'void'>; -}) => { - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.void, - }), - }); - return expression; -}; - -const schemaTypeToValibotSchema = ({ - _path, - plugin, - schema, - state, -}: SchemaToValibotSchemaOptions & { - schema: IR.SchemaObject; - state: State; -}): { - anyType?: string; - expression: ts.Expression; -} => { - switch (schema.type as Required['type']) { - case 'array': - return { - expression: arrayTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'array'>, - state, - }), - }; - case 'boolean': - return { - expression: booleanTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'boolean'>, - }), - }; - case 'enum': - return { - expression: enumTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'enum'>, - }), - }; - case 'integer': - case 'number': - return { - expression: numberTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'integer' | 'number'>, - }), - }; - case 'never': - return { - expression: neverTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'never'>, - }), - }; - case 'null': - return { - expression: nullTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'null'>, - }), - }; - case 'object': - return objectTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'object'>, - state, - }); - case 'string': - // For string schemas with int64/uint64 formats, use number handler to generate union with transform - if (schema.format === 'int64' || schema.format === 'uint64') { - return { - expression: numberTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'integer' | 'number'>, - }), - }; - } - return { - expression: stringTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'string'>, - }), - }; - case 'tuple': - return { - expression: tupleTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'tuple'>, - state, - }), - }; - case 'undefined': - return { - expression: undefinedTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'undefined'>, - }), - }; - case 'unknown': - return { - expression: unknownTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'unknown'>, - }), - }; - case 'void': - return { - expression: voidTypeToValibotSchema({ - _path, - plugin, - schema: schema as SchemaWithType<'void'>, - }), - }; - } -}; - -export const schemaToValibotSchema = ({ - $ref, - _path = [], - optional, - plugin, - schema, - state, - symbol, -}: SchemaToValibotSchemaOptions & { - /** - * When $ref is supplied, a node will be emitted to the file. - */ - $ref?: string; - /** - * Accept `optional` to handle optional object properties. We can't handle - * this inside the object function because `.optional()` must come before - * `.default()` which is handled in this function. - */ - optional?: boolean; - schema: IR.SchemaObject; - state: State; - symbol?: Symbol; -}): Array => { - let anyType: string | undefined; - let pipes: Array = []; - - if ($ref) { - state.circularReferenceTracker.add($ref); - - if (!symbol) { - const selector = plugin.api.getSelector('ref', $ref); - if (!plugin.getSymbol(selector)) { - symbol = plugin.referenceSymbol(selector); - } - } - } - - const vSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'valibot'), - ); - - if (schema.$ref) { - const isCircularReference = state.circularReferenceTracker.has(schema.$ref); - - // if $ref hasn't been processed yet, inline it to avoid the - // "Block-scoped variable used before its declaration." error - // this could be (maybe?) fixed by reshuffling the generation order - const selector = plugin.api.getSelector('ref', schema.$ref); - let refSymbol = plugin.getSymbol(selector); - if (!refSymbol) { - const ref = plugin.context.resolveIrRef(schema.$ref); - const schemaPipes = schemaToValibotSchema({ - $ref: schema.$ref, - _path: jsonPointerToPath(schema.$ref), - plugin, - schema: ref, - state, - }); - pipes.push(...schemaPipes); - - refSymbol = plugin.getSymbol(selector); - } - - if (refSymbol) { - const refIdentifier = tsc.identifier({ text: refSymbol.placeholder }); - if (isCircularReference) { - const lazyExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.lazy, - }), - parameters: [ - tsc.arrowFunction({ - statements: [ - tsc.returnStatement({ - expression: refIdentifier, - }), - ], - }), - ], - }); - pipes.push(lazyExpression); - state.hasCircularReference = true; - } else { - pipes.push(refIdentifier); - } - } - } else if (schema.type) { - const valibotSchema = schemaTypeToValibotSchema({ - _path, - plugin, - schema, - state, - }); - anyType = valibotSchema.anyType; - pipes.push(valibotSchema.expression); - - if (plugin.config.metadata && schema.description) { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.metadata, - }), - parameters: [ - tsc.objectExpression({ - obj: [ - { - key: 'description', - value: tsc.stringLiteral({ text: schema.description }), - }, - ], - }), - ], - }); - pipes.push(expression); - } - } else if (schema.items) { - schema = deduplicateSchema({ schema }); - - if (schema.items) { - const itemTypes = schema.items.map((item, index) => { - const schemaPipes = schemaToValibotSchema({ - _path: [..._path, 'items', index], - plugin, - schema: item, - state, - }); - return pipesToExpression({ pipes: schemaPipes, plugin }); - }); - - if (schema.logicalOperator === 'and') { - const intersectExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.intersect, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: itemTypes, - }), - ], - }); - pipes.push(intersectExpression); - } else { - const unionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: itemTypes, - }), - ], - }); - pipes.push(unionExpression); - } - } else { - const schemaPipes = schemaToValibotSchema({ - _path, - plugin, - schema, - state, - }); - pipes.push(...schemaPipes); - } - } else { - // catch-all fallback for failed schemas - const valibotSchema = schemaTypeToValibotSchema({ - _path, - plugin, - schema: { - type: 'unknown', - }, - state, - }); - anyType = valibotSchema.anyType; - pipes.push(valibotSchema.expression); - } - - if ($ref) { - state.circularReferenceTracker.delete($ref); - } - - if (pipes.length) { - if (schema.accessScope === 'read') { - const readonlyExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.actions.readonly, - }), - }); - pipes.push(readonlyExpression); - } - } - - if (pipes.length) { - let callParameter: ts.Expression | undefined; - - if (schema.default !== undefined) { - const isBigInt = schema.type === 'integer' && schema.format === 'int64'; - callParameter = numberParameter({ isBigInt, value: schema.default }); - if (callParameter) { - pipes = [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.optional, - }), - parameters: [pipesToExpression({ pipes, plugin }), callParameter], - }), - ]; - } - } - - if (optional && !callParameter) { - pipes = [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: identifiers.schemas.optional, - }), - parameters: [pipesToExpression({ pipes, plugin })], - }), - ]; - } - } - - if (symbol) { - if ($ref) { - symbol = plugin.registerSymbol({ - exported: true, - meta: { - resourceType: pathToSymbolResourceType(_path), - }, - name: buildName({ - config: { - case: state.nameCase, - name: state.nameTransformer, - }, - name: refToName($ref), - }), - selector: plugin.api.getSelector('ref', $ref), - }); - } - const statement = tsc.constVariable({ - comment: plugin.config.comments - ? createSchemaComment({ schema }) - : undefined, - exportConst: symbol.exported, - expression: pipesToExpression({ pipes, plugin }), - name: symbol.placeholder, - typeName: state.hasCircularReference - ? (tsc.propertyAccessExpression({ - expression: vSymbol.placeholder, - name: anyType || identifiers.types.GenericSchema.text, - }) as unknown as ts.TypeNode) - : undefined, - }); - plugin.setSymbolValue(symbol, statement); - return []; - } - - return pipes; -}; - -export const handler: ValibotPlugin['Handler'] = ({ plugin }) => { - plugin.registerSymbol({ - external: 'valibot', - meta: { importKind: 'namespace' }, - name: 'v', - selector: plugin.api.getSelector('import', 'valibot'), - }); - - plugin.forEach( - 'operation', - 'parameter', - 'requestBody', - 'schema', - 'webhook', - (event) => { - const state: State = { - circularReferenceTracker: new Set(), - hasCircularReference: false, - nameCase: plugin.config.definitions.case, - nameTransformer: plugin.config.definitions.name, - }; +import { handlerV1 } from './v1/plugin'; - switch (event.type) { - case 'operation': - operationToValibotSchema({ - _path: event._path, - operation: event.operation, - plugin, - state, - }); - break; - case 'parameter': - schemaToValibotSchema({ - $ref: event.$ref, - _path: event._path, - plugin, - schema: event.parameter.schema, - state, - }); - break; - case 'requestBody': - schemaToValibotSchema({ - $ref: event.$ref, - _path: event._path, - plugin, - schema: event.requestBody.schema, - state, - }); - break; - case 'schema': - schemaToValibotSchema({ - $ref: event.$ref, - _path: event._path, - plugin, - schema: event.schema, - state, - }); - break; - case 'webhook': - webhookToValibotSchema({ - _path: event._path, - operation: event.operation, - plugin, - state, - }); - break; - } - }, - ); -}; +export const handler: ValibotPlugin['Handler'] = (args) => handlerV1(args); diff --git a/packages/openapi-ts/src/plugins/valibot/number-helpers.ts b/packages/openapi-ts/src/plugins/valibot/shared/numbers.ts similarity index 97% rename from packages/openapi-ts/src/plugins/valibot/number-helpers.ts rename to packages/openapi-ts/src/plugins/valibot/shared/numbers.ts index 8fa532a72..2606f1ada 100644 --- a/packages/openapi-ts/src/plugins/valibot/number-helpers.ts +++ b/packages/openapi-ts/src/plugins/valibot/shared/numbers.ts @@ -1,6 +1,5 @@ -import { tsc } from '../../tsc'; +import { tsc } from '../../../tsc'; -// Integer format ranges and properties export const INTEGER_FORMATS = { int16: { max: 32767, diff --git a/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts new file mode 100644 index 000000000..d7e5be114 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts @@ -0,0 +1,24 @@ +import type { IR } from '../../../ir/types'; +import type { StringCase, StringName } from '../../../types/case'; +import type { ValibotPlugin } from '../types'; + +export type IrSchemaToAstOptions = { + plugin: ValibotPlugin['Instance']; + state: State; +}; + +export type State = { + /** + * Path to the schema in the intermediary representation. + */ + _path: ReadonlyArray; + circularReferenceTracker: Set; + hasCircularReference: boolean; + nameCase: StringCase; + nameTransformer: StringName; +}; + +export type ValidatorArgs = { + operation: IR.OperationObject; + plugin: ValibotPlugin['Instance']; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/api.ts b/packages/openapi-ts/src/plugins/valibot/v1/api.ts new file mode 100644 index 000000000..cfd6952b8 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/api.ts @@ -0,0 +1,85 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../tsc'; +import type { ValidatorArgs } from '../shared/types'; +import { identifiers } from './constants'; + +export const createRequestValidatorV1 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + if (!symbol) return; + + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.async.parseAsync, + }), + parameters: [ + tsc.identifier({ text: symbol.placeholder }), + tsc.identifier({ text: dataParameterName }), + ], + }), + }), + }), + ], + }); +}; + +export const createResponseValidatorV1 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol( + plugin.api.getSelector('responses', operation.id), + ); + if (!symbol) return; + + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.async.parseAsync, + }), + parameters: [ + tsc.identifier({ text: symbol.placeholder }), + tsc.identifier({ text: dataParameterName }), + ], + }), + }), + }), + ], + }); +}; diff --git a/packages/openapi-ts/src/plugins/valibot/constants.ts b/packages/openapi-ts/src/plugins/valibot/v1/constants.ts similarity index 99% rename from packages/openapi-ts/src/plugins/valibot/constants.ts rename to packages/openapi-ts/src/plugins/valibot/v1/constants.ts index 5f1790715..b3a085d68 100644 --- a/packages/openapi-ts/src/plugins/valibot/constants.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/constants.ts @@ -1,4 +1,4 @@ -import { tsc } from '../../tsc'; +import { tsc } from '../../../tsc'; export const identifiers = { /** diff --git a/packages/openapi-ts/src/plugins/valibot/operation.ts b/packages/openapi-ts/src/plugins/valibot/v1/operation.ts similarity index 84% rename from packages/openapi-ts/src/plugins/valibot/operation.ts rename to packages/openapi-ts/src/plugins/valibot/v1/operation.ts index b3e9cfd2e..acd106937 100644 --- a/packages/openapi-ts/src/plugins/valibot/operation.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/operation.ts @@ -1,20 +1,16 @@ -import { operationResponsesMap } from '../../ir/operation'; -import type { IR } from '../../ir/types'; -import { buildName } from '../../openApi/shared/utils/name'; -import { pathToSymbolResourceType } from '../shared/utils/meta'; -import { schemaToValibotSchema, type State } from './plugin'; -import type { ValibotPlugin } from './types'; - -export const operationToValibotSchema = ({ - _path, +import { operationResponsesMap } from '../../../ir/operation'; +import type { IR } from '../../../ir/types'; +import { buildName } from '../../../openApi/shared/utils/name'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import type { IrSchemaToAstOptions } from '../shared/types'; +import { irSchemaToAst } from './plugin'; + +export const irOperationToAst = ({ operation, plugin, state, -}: { - _path: ReadonlyArray; +}: IrSchemaToAstOptions & { operation: IR.OperationObject; - plugin: ValibotPlugin['Instance']; - state: State; }) => { if (plugin.config.requests.enabled) { const requiredProperties = new Set(); @@ -117,7 +113,7 @@ export const operationToValibotSchema = ({ const symbol = plugin.registerSymbol({ exported: true, meta: { - resourceType: pathToSymbolResourceType(_path), + resourceType: pathToSymbolResourceType(state._path), }, name: buildName({ config: plugin.config.requests, @@ -125,8 +121,7 @@ export const operationToValibotSchema = ({ }), selector: plugin.api.getSelector('data', operation.id), }); - schemaToValibotSchema({ - _path, + irSchemaToAst({ plugin, schema: schemaData, state, @@ -139,7 +134,7 @@ export const operationToValibotSchema = ({ const { response } = operationResponsesMap(operation); if (response) { - const path = [..._path, 'responses']; + const path = [...state._path, 'responses']; const symbol = plugin.registerSymbol({ exported: true, meta: { @@ -151,11 +146,13 @@ export const operationToValibotSchema = ({ }), selector: plugin.api.getSelector('responses', operation.id), }); - schemaToValibotSchema({ - _path: path, + irSchemaToAst({ plugin, schema: response, - state, + state: { + ...state, + _path: path, + }, symbol, }); } diff --git a/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts b/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts new file mode 100644 index 000000000..df5660e9c --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts @@ -0,0 +1,29 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../tsc'; +import type { ValibotPlugin } from '../types'; +import { identifiers } from './constants'; + +export const pipesToAst = ({ + pipes, + plugin, +}: { + pipes: Array; + plugin: ValibotPlugin['Instance']; +}): ts.Expression => { + if (pipes.length === 1) { + return pipes[0]!; + } + + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.methods.pipe, + }), + parameters: pipes, + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts new file mode 100644 index 000000000..fa3860fcf --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts @@ -0,0 +1,363 @@ +import type { Symbol } from '@hey-api/codegen-core'; +import type ts from 'typescript'; + +import { deduplicateSchema } from '../../../ir/schema'; +import type { IR } from '../../../ir/types'; +import { buildName } from '../../../openApi/shared/utils/name'; +import { tsc } from '../../../tsc'; +import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import type { SchemaWithType } from '../../shared/types/schema'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { createSchemaComment } from '../../shared/utils/schema'; +import { numberParameter } from '../shared/numbers'; +import type { IrSchemaToAstOptions } from '../shared/types'; +import type { ValibotPlugin } from '../types'; +import { identifiers } from './constants'; +import { irOperationToAst } from './operation'; +import { pipesToAst } from './pipesToAst'; +import { irSchemaWithTypeToAst } from './toAst'; +import { irWebhookToAst } from './webhook'; + +export const irSchemaToAst = ({ + $ref, + optional, + plugin, + schema, + state, + symbol, +}: IrSchemaToAstOptions & { + /** + * When $ref is supplied, a node will be emitted to the file. + */ + $ref?: string; + /** + * Accept `optional` to handle optional object properties. We can't handle + * this inside the object function because `.optional()` must come before + * `.default()` which is handled in this function. + */ + optional?: boolean; + schema: IR.SchemaObject; + symbol?: Symbol; +}): Array => { + let anyType: string | undefined; + let pipes: Array = []; + + if ($ref) { + state.circularReferenceTracker.add($ref); + + if (!symbol) { + const selector = plugin.api.getSelector('ref', $ref); + if (!plugin.getSymbol(selector)) { + symbol = plugin.referenceSymbol(selector); + } + } + } + + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + if (schema.$ref) { + const isCircularReference = state.circularReferenceTracker.has(schema.$ref); + + // if $ref hasn't been processed yet, inline it to avoid the + // "Block-scoped variable used before its declaration." error + // this could be (maybe?) fixed by reshuffling the generation order + const selector = plugin.api.getSelector('ref', schema.$ref); + let refSymbol = plugin.getSymbol(selector); + if (!refSymbol) { + const ref = plugin.context.resolveIrRef(schema.$ref); + const schemaPipes = irSchemaToAst({ + $ref: schema.$ref, + plugin, + schema: ref, + state: { + ...state, + _path: jsonPointerToPath(schema.$ref), + }, + }); + pipes.push(...schemaPipes); + + refSymbol = plugin.getSymbol(selector); + } + + if (refSymbol) { + const refIdentifier = tsc.identifier({ text: refSymbol.placeholder }); + if (isCircularReference) { + const lazyExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.lazy, + }), + parameters: [ + tsc.arrowFunction({ + statements: [ + tsc.returnStatement({ + expression: refIdentifier, + }), + ], + }), + ], + }); + pipes.push(lazyExpression); + state.hasCircularReference = true; + } else { + pipes.push(refIdentifier); + } + } + } else if (schema.type) { + const valibotSchema = irSchemaWithTypeToAst({ + plugin, + schema: schema as SchemaWithType, + state, + }); + anyType = valibotSchema.anyType; + pipes.push(valibotSchema.expression); + + if (plugin.config.metadata && schema.description) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.metadata, + }), + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'description', + value: tsc.stringLiteral({ text: schema.description }), + }, + ], + }), + ], + }); + pipes.push(expression); + } + } else if (schema.items) { + schema = deduplicateSchema({ schema }); + + if (schema.items) { + const itemTypes = schema.items.map((item, index) => { + const schemaPipes = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + return pipesToAst({ pipes: schemaPipes, plugin }); + }); + + if (schema.logicalOperator === 'and') { + const intersectExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.intersect, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: itemTypes, + }), + ], + }); + pipes.push(intersectExpression); + } else { + const unionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: itemTypes, + }), + ], + }); + pipes.push(unionExpression); + } + } else { + const schemaPipes = irSchemaToAst({ + plugin, + schema, + state, + }); + pipes.push(...schemaPipes); + } + } else { + // catch-all fallback for failed schemas + const valibotSchema = irSchemaWithTypeToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + anyType = valibotSchema.anyType; + pipes.push(valibotSchema.expression); + } + + if ($ref) { + state.circularReferenceTracker.delete($ref); + } + + if (pipes.length) { + if (schema.accessScope === 'read') { + const readonlyExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.readonly, + }), + }); + pipes.push(readonlyExpression); + } + + let callParameter: ts.Expression | undefined; + + if (schema.default !== undefined) { + const isBigInt = schema.type === 'integer' && schema.format === 'int64'; + callParameter = numberParameter({ isBigInt, value: schema.default }); + if (callParameter) { + pipes = [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.optional, + }), + parameters: [pipesToAst({ pipes, plugin }), callParameter], + }), + ]; + } + } + + if (optional && !callParameter) { + pipes = [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.optional, + }), + parameters: [pipesToAst({ pipes, plugin })], + }), + ]; + } + } + + if (symbol) { + if ($ref) { + symbol = plugin.registerSymbol({ + exported: true, + meta: { + resourceType: pathToSymbolResourceType(state._path), + }, + name: buildName({ + config: { + case: state.nameCase, + name: state.nameTransformer, + }, + name: refToName($ref), + }), + selector: plugin.api.getSelector('ref', $ref), + }); + } + const statement = tsc.constVariable({ + comment: plugin.config.comments + ? createSchemaComment({ schema }) + : undefined, + exportConst: symbol.exported, + expression: pipesToAst({ pipes, plugin }), + name: symbol.placeholder, + typeName: state.hasCircularReference + ? (tsc.propertyAccessExpression({ + expression: v.placeholder, + name: anyType || identifiers.types.GenericSchema.text, + }) as unknown as ts.TypeNode) + : undefined, + }); + plugin.setSymbolValue(symbol, statement); + return []; + } + + return pipes; +}; + +export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { + plugin.registerSymbol({ + external: 'valibot', + meta: { importKind: 'namespace' }, + name: 'v', + selector: plugin.api.getSelector('external', 'valibot.v'), + }); + + plugin.forEach( + 'operation', + 'parameter', + 'requestBody', + 'schema', + 'webhook', + (event) => { + const state: Omit = { + circularReferenceTracker: new Set(), + hasCircularReference: false, + nameCase: plugin.config.definitions.case, + nameTransformer: plugin.config.definitions.name, + }; + + switch (event.type) { + case 'operation': + irOperationToAst({ + operation: event.operation, + plugin, + state: { + ...state, + _path: event._path, + }, + }); + break; + case 'parameter': + irSchemaToAst({ + $ref: event.$ref, + plugin, + schema: event.parameter.schema, + state: { + ...state, + _path: event._path, + }, + }); + break; + case 'requestBody': + irSchemaToAst({ + $ref: event.$ref, + plugin, + schema: event.requestBody.schema, + state: { + ...state, + _path: event._path, + }, + }); + break; + case 'schema': + irSchemaToAst({ + $ref: event.$ref, + plugin, + schema: event.schema, + state: { + ...state, + _path: event._path, + }, + }); + break; + case 'webhook': + irWebhookToAst({ + operation: event.operation, + plugin, + state: { + ...state, + _path: event._path, + }, + }); + break; + } + }, + ); +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts new file mode 100644 index 000000000..796f6fb2d --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts @@ -0,0 +1,126 @@ +import type ts from 'typescript'; + +import { deduplicateSchema } from '../../../../ir/schema'; +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; +import { pipesToAst } from '../pipesToAst'; +import { irSchemaToAst } from '../plugin'; +import { unknownToAst } from './unknown'; + +export const arrayToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'array'>; +}): ts.Expression => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + const functionName = tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.array, + }); + + const pipes: Array = []; + + if (!schema.items) { + const expression = tsc.callExpression({ + functionName, + parameters: [ + unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }), + ], + }); + pipes.push(expression); + } else { + schema = deduplicateSchema({ schema }); + + // at least one item is guaranteed + const itemExpressions = schema.items!.map((item, index) => { + const schemaPipes = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + return pipesToAst({ pipes: schemaPipes, plugin }); + }); + + if (itemExpressions.length === 1) { + const expression = tsc.callExpression({ + functionName, + parameters: itemExpressions, + }); + pipes.push(expression); + } else { + if (schema.logicalOperator === 'and') { + // TODO: parser - handle intersection + // return tsc.typeArrayNode( + // tsc.typeIntersectionNode({ types: itemExpressions }), + // ); + } + + // TODO: parser - handle union + // return tsc.typeArrayNode(tsc.typeUnionNode({ types: itemExpressions })); + + const expression = tsc.callExpression({ + functionName, + parameters: [ + unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }), + ], + }); + pipes.push(expression); + } + } + + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }); + pipes.push(expression); + } else { + if (schema.minItems !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.minLength, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }); + pipes.push(expression); + } + + if (schema.maxItems !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.maxLength, + }), + parameters: [tsc.valueToExpression({ value: schema.maxItems })], + }); + pipes.push(expression); + } + } + + return pipesToAst({ pipes, plugin }); +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts new file mode 100644 index 000000000..b48cbf0c8 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts @@ -0,0 +1,34 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; + +export const booleanToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'boolean'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + if (typeof schema.const === 'boolean') { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.literal, + }), + parameters: [tsc.ots.boolean(schema.const)], + }); + return expression; + } + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.boolean, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts new file mode 100644 index 000000000..bcf4bcb40 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts @@ -0,0 +1,71 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; +import { unknownToAst } from './unknown'; + +export const enumToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'enum'>; +}): ts.CallExpression => { + const enumMembers: Array = []; + + let isNullable = false; + + for (const item of schema.items ?? []) { + // Zod supports only string enums + if (item.type === 'string' && typeof item.const === 'string') { + enumMembers.push( + tsc.stringLiteral({ + text: item.const, + }), + ); + } else if (item.type === 'null' || item.const === null) { + isNullable = true; + } + } + + if (!enumMembers.length) { + return unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + } + + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + let resultExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.picklist, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: enumMembers, + multiLine: false, + }), + ], + }); + + if (isNullable) { + resultExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.nullable, + }), + parameters: [resultExpression], + }); + } + + return resultExpression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts new file mode 100644 index 000000000..4750ebcc6 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts @@ -0,0 +1,121 @@ +import type ts from 'typescript'; + +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { arrayToAst } from './array'; +import { booleanToAst } from './boolean'; +import { enumToAst } from './enum'; +import { neverToAst } from './never'; +import { nullToAst } from './null'; +import { numberToAst } from './number'; +import { objectToAst } from './object'; +import { stringToAst } from './string'; +import { tupleToAst } from './tuple'; +import { undefinedToAst } from './undefined'; +import { unknownToAst } from './unknown'; +import { voidToAst } from './void'; + +export const irSchemaWithTypeToAst = ({ + schema, + ...args +}: IrSchemaToAstOptions & { + schema: SchemaWithType; +}): { + anyType?: string; + expression: ts.Expression; +} => { + switch (schema.type) { + case 'array': + return { + expression: arrayToAst({ + ...args, + schema: schema as SchemaWithType<'array'>, + }), + }; + case 'boolean': + return { + expression: booleanToAst({ + ...args, + schema: schema as SchemaWithType<'boolean'>, + }), + }; + case 'enum': + return { + expression: enumToAst({ + ...args, + schema: schema as SchemaWithType<'enum'>, + }), + }; + case 'integer': + case 'number': + return { + expression: numberToAst({ + ...args, + schema: schema as SchemaWithType<'integer' | 'number'>, + }), + }; + case 'never': + return { + expression: neverToAst({ + ...args, + schema: schema as SchemaWithType<'never'>, + }), + }; + case 'null': + return { + expression: nullToAst({ + ...args, + schema: schema as SchemaWithType<'null'>, + }), + }; + case 'object': + return objectToAst({ + ...args, + schema: schema as SchemaWithType<'object'>, + }); + case 'string': + // For string schemas with int64/uint64 formats, use number handler to generate union with transform + if (schema.format === 'int64' || schema.format === 'uint64') { + return { + expression: numberToAst({ + ...args, + schema: schema as SchemaWithType<'integer' | 'number'>, + }), + }; + } + return { + expression: stringToAst({ + ...args, + schema: schema as SchemaWithType<'string'>, + }), + }; + case 'tuple': + return { + expression: tupleToAst({ + ...args, + schema: schema as SchemaWithType<'tuple'>, + }), + }; + case 'undefined': + return { + expression: undefinedToAst({ + ...args, + schema: schema as SchemaWithType<'undefined'>, + }), + }; + case 'unknown': + return { + expression: unknownToAst({ + ...args, + schema: schema as SchemaWithType<'unknown'>, + }), + }; + case 'void': + return { + expression: voidToAst({ + ...args, + schema: schema as SchemaWithType<'void'>, + }), + }; + } +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts new file mode 100644 index 000000000..ae24ae353 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts @@ -0,0 +1,21 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; + +export const neverToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'never'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.never, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts new file mode 100644 index 000000000..79dcc70bc --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts @@ -0,0 +1,21 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; + +export const nullToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'null'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.null, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts new file mode 100644 index 000000000..f6d78a0d8 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts @@ -0,0 +1,254 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { + INTEGER_FORMATS, + isIntegerFormat, + needsBigIntForFormat, + numberParameter, +} from '../../shared/numbers'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; +import { pipesToAst } from '../pipesToAst'; + +export const numberToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'integer' | 'number'>; +}) => { + const format = schema.format; + const isInteger = schema.type === 'integer'; + const isBigInt = needsBigIntForFormat(format); + const formatInfo = isIntegerFormat(format) ? INTEGER_FORMATS[format] : null; + + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + // Return early if const is defined since we can create a literal type directly without additional validation + if (schema.const !== undefined && schema.const !== null) { + const constValue = schema.const; + let literalValue; + + // Case 1: Number with no format -> generate literal with the number + if (typeof constValue === 'number' && !format) { + literalValue = tsc.ots.number(constValue); + } + // Case 2: Number with format -> check if format needs BigInt, generate appropriate literal + else if (typeof constValue === 'number' && format) { + if (isBigInt) { + // Format requires BigInt, convert number to BigInt + literalValue = tsc.callExpression({ + functionName: 'BigInt', + parameters: [tsc.ots.string(constValue.toString())], + }); + } else { + // Regular format, use number as-is + literalValue = tsc.ots.number(constValue); + } + } + // Case 3: Format that allows string -> generate BigInt literal (for int64/uint64 formats) + else if (typeof constValue === 'string' && isBigInt) { + // Remove 'n' suffix if present in string + const cleanString = constValue.endsWith('n') + ? constValue.slice(0, -1) + : constValue; + literalValue = tsc.callExpression({ + functionName: 'BigInt', + parameters: [tsc.ots.string(cleanString)], + }); + } + // Case 4: Const is typeof bigint (literal) -> transform from literal to BigInt() + else if (typeof constValue === 'bigint') { + // Convert BigInt to string and remove 'n' suffix that toString() adds + const bigintString = constValue.toString(); + const cleanString = bigintString.endsWith('n') + ? bigintString.slice(0, -1) + : bigintString; + literalValue = tsc.callExpression({ + functionName: 'BigInt', + parameters: [tsc.ots.string(cleanString)], + }); + } + // Default case: use value as-is for other types + else { + literalValue = tsc.valueToExpression({ value: constValue }); + } + + return tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.literal, + }), + parameters: [literalValue], + }); + } + + const pipes: Array = []; + + // For bigint formats (int64, uint64), create union of number, string, and bigint with transform + if (isBigInt) { + const unionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.number, + }), + }), + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.string, + }), + }), + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.bigInt, + }), + }), + ], + multiLine: false, + }), + ], + }); + pipes.push(unionExpression); + + // Add transform to convert to BigInt + const transformExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.transform, + }), + parameters: [ + tsc.arrowFunction({ + parameters: [{ name: 'x' }], + statements: tsc.callExpression({ + functionName: 'BigInt', + parameters: [tsc.identifier({ text: 'x' })], + }), + }), + ], + }); + pipes.push(transformExpression); + } else { + // For regular number formats, use number schema + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.number, + }), + }); + pipes.push(expression); + } + + // Add integer validation for integer types (except when using bigint union) + if (!isBigInt && isInteger) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.integer, + }), + }); + pipes.push(expression); + } + + // Add format-specific range validations + if (formatInfo) { + const minValue = formatInfo.min; + const maxValue = formatInfo.max; + const minErrorMessage = formatInfo.minError; + const maxErrorMessage = formatInfo.maxError; + + // Add minimum value validation + const minExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.minValue, + }), + parameters: [ + isBigInt + ? tsc.callExpression({ + functionName: 'BigInt', + parameters: [tsc.ots.string(minValue.toString())], + }) + : tsc.ots.number(minValue as number), + tsc.ots.string(minErrorMessage), + ], + }); + pipes.push(minExpression); + + // Add maximum value validation + const maxExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.maxValue, + }), + parameters: [ + isBigInt + ? tsc.callExpression({ + functionName: 'BigInt', + parameters: [tsc.ots.string(maxValue.toString())], + }) + : tsc.ots.number(maxValue as number), + tsc.ots.string(maxErrorMessage), + ], + }); + pipes.push(maxExpression); + } + + if (schema.exclusiveMinimum !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.gtValue, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMinimum }), + ], + }); + pipes.push(expression); + } else if (schema.minimum !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.minValue, + }), + parameters: [numberParameter({ isBigInt, value: schema.minimum })], + }); + pipes.push(expression); + } + + if (schema.exclusiveMaximum !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.ltValue, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMaximum }), + ], + }); + pipes.push(expression); + } else if (schema.maximum !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.maxValue, + }), + parameters: [numberParameter({ isBigInt, value: schema.maximum })], + }); + pipes.push(expression); + } + + return pipesToAst({ pipes, plugin }); +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts new file mode 100644 index 000000000..4ab215de5 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts @@ -0,0 +1,118 @@ +import ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import { numberRegExp } from '../../../../utils/regexp'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; +import { pipesToAst } from '../pipesToAst'; +import { irSchemaToAst } from '../plugin'; + +export const objectToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'object'>; +}): { + anyType: string; + expression: ts.CallExpression; +} => { + // TODO: parser - handle constants + const properties: Array = []; + + const required = schema.required ?? []; + + for (const name in schema.properties) { + const property = schema.properties[name]!; + const isRequired = required.includes(name); + + const schemaPipes = irSchemaToAst({ + optional: !isRequired, + plugin, + schema: property, + state: { + ...state, + _path: [...state._path, 'properties', name], + }, + }); + + numberRegExp.lastIndex = 0; + let propertyName; + if (numberRegExp.test(name)) { + // For numeric literals, we'll handle negative numbers by using a string literal + // instead of trying to use a PrefixUnaryExpression + propertyName = name.startsWith('-') + ? ts.factory.createStringLiteral(name) + : ts.factory.createNumericLiteral(name); + } else { + propertyName = name; + } + // TODO: parser - abstract safe property name logic + if ( + ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && + !name.startsWith("'") && + !name.endsWith("'") + ) { + propertyName = `'${name}'`; + } + properties.push( + tsc.propertyAssignment({ + initializer: pipesToAst({ pipes: schemaPipes, plugin }), + name: propertyName, + }), + ); + } + + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + if ( + schema.additionalProperties && + schema.additionalProperties.type === 'object' && + !Object.keys(properties).length + ) { + const pipes = irSchemaToAst({ + plugin, + schema: schema.additionalProperties, + state: { + ...state, + _path: [...state._path, 'additionalProperties'], + }, + }); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.record, + }), + parameters: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.string, + }), + parameters: [], + }), + pipesToAst({ pipes, plugin }), + ], + }); + return { + anyType: 'AnyZodObject', + expression, + }; + } + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.object, + }), + parameters: [ts.factory.createObjectLiteralExpression(properties, true)], + }); + return { + // Zod uses AnyZodObject here, maybe we want to be more specific too + anyType: identifiers.types.GenericSchema.text, + expression, + }; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts new file mode 100644 index 000000000..ebeba4679 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts @@ -0,0 +1,143 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; +import { pipesToAst } from '../pipesToAst'; + +export const stringToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'string'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + if (typeof schema.const === 'string') { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.literal, + }), + parameters: [tsc.ots.string(schema.const)], + }); + return expression; + } + + const pipes: Array = []; + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.string, + }), + }); + pipes.push(expression); + + if (schema.format) { + switch (schema.format) { + case 'date': + pipes.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.isoDate, + }), + }), + ); + break; + case 'date-time': + pipes.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.isoTimestamp, + }), + }), + ); + break; + case 'ipv4': + case 'ipv6': + pipes.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.ip, + }), + }), + ); + break; + case 'uri': + pipes.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.url, + }), + }), + ); + break; + case 'email': + case 'time': + case 'uuid': + pipes.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: tsc.identifier({ text: schema.format }), + }), + }), + ); + break; + } + } + + if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }); + pipes.push(expression); + } else { + if (schema.minLength !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.minLength, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }); + pipes.push(expression); + } + + if (schema.maxLength !== undefined) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.maxLength, + }), + parameters: [tsc.valueToExpression({ value: schema.maxLength })], + }); + pipes.push(expression); + } + } + + if (schema.pattern) { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.actions.regex, + }), + parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], + }); + pipes.push(expression); + } + + return pipesToAst({ pipes, plugin }); +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts new file mode 100644 index 000000000..039bd6fd5 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts @@ -0,0 +1,77 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; +import { pipesToAst } from '../pipesToAst'; +import { irSchemaToAst } from '../plugin'; +import { unknownToAst } from './unknown'; + +export const tupleToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'tuple'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + if (schema.const && Array.isArray(schema.const)) { + const tupleElements = schema.const.map((value) => + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.literal, + }), + parameters: [tsc.valueToExpression({ value })], + }), + ); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + return expression; + } + + if (schema.items) { + const tupleElements = schema.items.map((item, index) => { + const schemaPipes = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + return pipesToAst({ pipes: schemaPipes, plugin }); + }); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + return expression; + } + + return unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts new file mode 100644 index 000000000..8be4e6f33 --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts @@ -0,0 +1,22 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; + +export const undefinedToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'undefined'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.undefined, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts new file mode 100644 index 000000000..72065fc7e --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts @@ -0,0 +1,22 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; + +export const unknownToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'unknown'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.unknown, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts new file mode 100644 index 000000000..6f199222c --- /dev/null +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts @@ -0,0 +1,22 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { identifiers } from '../constants'; + +export const voidToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'void'>; +}) => { + const v = plugin.referenceSymbol( + plugin.api.getSelector('external', 'valibot.v'), + ); + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.void, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/webhook.ts b/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts similarity index 85% rename from packages/openapi-ts/src/plugins/valibot/webhook.ts rename to packages/openapi-ts/src/plugins/valibot/v1/webhook.ts index 6400d46b7..37ba6144e 100644 --- a/packages/openapi-ts/src/plugins/valibot/webhook.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts @@ -1,19 +1,15 @@ -import type { IR } from '../../ir/types'; -import { buildName } from '../../openApi/shared/utils/name'; -import { pathToSymbolResourceType } from '../shared/utils/meta'; -import { schemaToValibotSchema, type State } from './plugin'; -import type { ValibotPlugin } from './types'; - -export const webhookToValibotSchema = ({ - _path, +import type { IR } from '../../../ir/types'; +import { buildName } from '../../../openApi/shared/utils/name'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import type { IrSchemaToAstOptions } from '../shared/types'; +import { irSchemaToAst } from './plugin'; + +export const irWebhookToAst = ({ operation, plugin, state, -}: { - _path: ReadonlyArray; +}: IrSchemaToAstOptions & { operation: IR.OperationObject; - plugin: ValibotPlugin['Instance']; - state: State; }) => { if (plugin.config.webhooks.enabled) { const requiredProperties = new Set(); @@ -116,7 +112,7 @@ export const webhookToValibotSchema = ({ const symbol = plugin.registerSymbol({ exported: true, meta: { - resourceType: pathToSymbolResourceType(_path), + resourceType: pathToSymbolResourceType(state._path), }, name: buildName({ config: plugin.config.webhooks, @@ -124,8 +120,7 @@ export const webhookToValibotSchema = ({ }), selector: plugin.api.getSelector('webhook-request', operation.id), }); - schemaToValibotSchema({ - _path, + irSchemaToAst({ plugin, schema: schemaData, state, diff --git a/packages/openapi-ts/src/plugins/zod/api.ts b/packages/openapi-ts/src/plugins/zod/api.ts index 0c78dc943..0d4eb8810 100644 --- a/packages/openapi-ts/src/plugins/zod/api.ts +++ b/packages/openapi-ts/src/plugins/zod/api.ts @@ -1,15 +1,18 @@ import type { Selector } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { IR } from '../../ir/types'; -import { tsc } from '../../tsc'; import type { Plugin } from '../types'; -import { identifiers } from './constants'; -import type { ZodPlugin } from './types'; +import { + createRequestValidatorMini, + createResponseValidatorMini, +} from './mini/api'; +import type { ValidatorArgs } from './shared/types'; +import { createRequestValidatorV3, createResponseValidatorV3 } from './v3/api'; +import { createRequestValidatorV4, createResponseValidatorV4 } from './v4/api'; type SelectorType = | 'data' - | 'import' + | 'external' | 'ref' | 'responses' | 'type-infer-data' @@ -18,11 +21,6 @@ type SelectorType = | 'type-infer-webhook-request' | 'webhook-request'; -type ValidatorArgs = { - operation: IR.OperationObject; - plugin: ZodPlugin['Instance']; -}; - export type IApi = { createRequestValidator: (args: ValidatorArgs) => ts.ArrowFunction | undefined; createResponseValidator: ( @@ -32,7 +30,7 @@ export type IApi = { * @param type Selector type. * @param value Depends on `type`: * - `data`: `operation.id` string - * - `import`: headless symbols representing module imports + * - `external`: external modules * - `ref`: `$ref` JSON pointer * - `responses`: `operation.id` string * - `type-infer-data`: `operation.id` string @@ -48,72 +46,30 @@ export type IApi = { export class Api implements IApi { constructor(public meta: Plugin.Name<'zod'>) {} - createRequestValidator({ - operation, - plugin, - }: ValidatorArgs): ts.ArrowFunction | undefined { - const symbol = plugin.getSymbol( - plugin.api.getSelector('data', operation.id), - ); - if (!symbol) return; - - const dataParameterName = 'data'; - - return tsc.arrowFunction({ - async: true, - parameters: [ - { - name: dataParameterName, - }, - ], - statements: [ - tsc.returnStatement({ - expression: tsc.awaitExpression({ - expression: tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: symbol.placeholder, - name: identifiers.parseAsync, - }), - parameters: [tsc.identifier({ text: dataParameterName })], - }), - }), - }), - ], - }); + createRequestValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { + const { plugin } = args; + switch (plugin.config.compatibilityVersion) { + case 3: + return createRequestValidatorV3(args); + case 'mini': + return createRequestValidatorMini(args); + case 4: + default: + return createRequestValidatorV4(args); + } } - createResponseValidator({ - operation, - plugin, - }: ValidatorArgs): ts.ArrowFunction | undefined { - const symbol = plugin.getSymbol( - plugin.api.getSelector('responses', operation.id), - ); - if (!symbol) return; - - const dataParameterName = 'data'; - - return tsc.arrowFunction({ - async: true, - parameters: [ - { - name: dataParameterName, - }, - ], - statements: [ - tsc.returnStatement({ - expression: tsc.awaitExpression({ - expression: tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: symbol.placeholder, - name: identifiers.parseAsync, - }), - parameters: [tsc.identifier({ text: dataParameterName })], - }), - }), - }), - ], - }); + createResponseValidator(args: ValidatorArgs): ts.ArrowFunction | undefined { + const { plugin } = args; + switch (plugin.config.compatibilityVersion) { + case 3: + return createResponseValidatorV3(args); + case 'mini': + return createResponseValidatorMini(args); + case 4: + default: + return createResponseValidatorV4(args); + } } getSelector(...args: ReadonlyArray): Selector { diff --git a/packages/openapi-ts/src/plugins/zod/constants.ts b/packages/openapi-ts/src/plugins/zod/constants.ts index 5fbf81c8e..d0df73c8d 100644 --- a/packages/openapi-ts/src/plugins/zod/constants.ts +++ b/packages/openapi-ts/src/plugins/zod/constants.ts @@ -1,5 +1,6 @@ import { tsc } from '../../tsc'; +// TODO: this is inaccurate, it combines identifiers for all supported versions export const identifiers = { ZodMiniOptional: tsc.identifier({ text: 'ZodMiniOptional' }), ZodOptional: tsc.identifier({ text: 'ZodOptional' }), diff --git a/packages/openapi-ts/src/plugins/zod/mini/api.ts b/packages/openapi-ts/src/plugins/zod/mini/api.ts new file mode 100644 index 000000000..f6efe671d --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/api.ts @@ -0,0 +1,71 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../tsc'; +import { identifiers } from '../constants'; +import type { ValidatorArgs } from '../shared/types'; + +export const createRequestValidatorMini = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; + +export const createResponseValidatorMini = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol( + plugin.api.getSelector('responses', operation.id), + ); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts index 4c03374eb..4a8b1129d 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts @@ -1,1051 +1,37 @@ -import ts from 'typescript'; - import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import { numberRegExp } from '../../../utils/regexp'; +import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import type { SchemaWithType } from '../../shared/types/schema'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; import { identifiers } from '../constants'; -import { exportZodSchema } from '../export'; +import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; -import { operationToZodSchema } from '../shared/operation'; -import type { SchemaWithType, State, ZodSchema } from '../shared/types'; -import { webhookToZodSchema } from '../shared/webhook'; +import { numberParameter } from '../shared/numbers'; +import { irOperationToAst } from '../shared/operation'; +import type { Ast, IrSchemaToAstOptions } from '../shared/types'; +import { irWebhookToAst } from '../shared/webhook'; import type { ZodPlugin } from '../types'; +import { irSchemaWithTypeToAst } from './toAst'; -const arrayTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'array'>; - state: State; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const result: Partial> = {}; - - const functionName = tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.array, - }); - - if (!schema.items) { - result.expression = tsc.callExpression({ - functionName, - parameters: [ - unknownTypeToZodSchema({ - plugin, - schema: { - type: 'unknown', - }, - }).expression, - ], - }); - } else { - schema = deduplicateSchema({ schema }); - - // at least one item is guaranteed - const itemExpressions = schema.items!.map((item) => { - const zodSchema = schemaToZodSchema({ - plugin, - schema: item, - state, - }); - if (zodSchema.hasCircularReference) { - result.hasCircularReference = true; - } - return zodSchema.expression; - }); - - if (itemExpressions.length === 1) { - result.expression = tsc.callExpression({ - functionName, - parameters: itemExpressions, - }); - } else { - if (schema.logicalOperator === 'and') { - const firstSchema = schema.items![0]!; - // we want to add an intersection, but not every schema can use the same API. - // if the first item contains another array or not an object, we cannot use - // `.intersection()` as that does not exist on `.union()` and non-object schemas. - let intersectionExpression: ts.Expression; - if ( - firstSchema.logicalOperator === 'or' || - (firstSchema.type && firstSchema.type !== 'object') - ) { - intersectionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.intersection, - }), - parameters: itemExpressions, - }); - } else { - intersectionExpression = itemExpressions[0]!; - for (let i = 1; i < itemExpressions.length; i++) { - intersectionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.intersection, - }), - parameters: [intersectionExpression, itemExpressions[i]!], - }); - } - } - - result.expression = tsc.callExpression({ - functionName, - parameters: [intersectionExpression], - }); - } else { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.array, - }), - parameters: [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: itemExpressions, - }), - ], - }), - ], - }); - } - } - } - - const checks: Array = []; - - if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }), - ); - } else { - if (schema.minItems !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.minLength, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }), - ); - } - - if (schema.maxItems !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.maxLength, - }), - parameters: [tsc.valueToExpression({ value: schema.maxItems })], - }), - ); - } - } - - if (checks.length) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.check, - }), - parameters: checks, - }); - } - - return result as Omit; -}; - -const booleanTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'boolean'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const result: Partial> = {}; - - if (typeof schema.const === 'boolean') { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.boolean(schema.const)], - }); - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.boolean, - }), - }); - return result as Omit; -}; - -const enumTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'enum'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const result: Partial> = {}; - - const enumMembers: Array = []; - const literalMembers: Array = []; - - let isNullable = false; - let allStrings = true; - - for (const item of schema.items ?? []) { - // Zod supports string, number, and boolean enums - if (item.type === 'string' && typeof item.const === 'string') { - const stringLiteral = tsc.stringLiteral({ - text: item.const, - }); - enumMembers.push(stringLiteral); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [stringLiteral], - }), - ); - } else if ( - (item.type === 'number' || item.type === 'integer') && - typeof item.const === 'number' - ) { - allStrings = false; - const numberLiteral = tsc.ots.number(item.const); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [numberLiteral], - }), - ); - } else if (item.type === 'boolean' && typeof item.const === 'boolean') { - allStrings = false; - const booleanLiteral = tsc.ots.boolean(item.const); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [booleanLiteral], - }), - ); - } else if (item.type === 'null' || item.const === null) { - isNullable = true; - } - } - - if (!literalMembers.length) { - return unknownTypeToZodSchema({ - plugin, - schema: { - type: 'unknown', - }, - }); - } - - // Use z.enum() for pure string enums, z.union() for mixed or non-string types - if (allStrings && enumMembers.length > 0) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.enum, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: enumMembers, - multiLine: false, - }), - ], - }); - } else if (literalMembers.length === 1) { - // For single-member unions, use the member directly instead of wrapping in z.union() - result.expression = literalMembers[0]; - } else { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: literalMembers, - multiLine: literalMembers.length > 3, - }), - ], - }); - } - - if (isNullable) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.nullable, - }), - parameters: [result.expression], - }); - } - - return result as Omit; -}; - -const neverTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'never'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const result: Partial> = {}; - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.never, - }), - }); - return result as Omit; -}; - -const nullTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'null'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const result: Partial> = {}; - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.null, - }), - }); - return result as Omit; -}; - -const numberParameter = ({ - isBigInt, - value, -}: { - isBigInt: boolean; - value: unknown; -}): ts.Expression | undefined => { - const expression = tsc.valueToExpression({ value }); - - if ( - isBigInt && - (typeof value === 'bigint' || - typeof value === 'number' || - typeof value === 'string' || - typeof value === 'boolean') - ) { - return tsc.callExpression({ - functionName: 'BigInt', - parameters: [expression], - }); - } - - return expression; -}; - -const numberTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'integer' | 'number'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const result: Partial> = {}; - - const isBigInt = schema.type === 'integer' && schema.format === 'int64'; - - if (typeof schema.const === 'number') { - // TODO: parser - handle bigint constants - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.number(schema.const)], - }); - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: isBigInt - ? tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.coerce, - }), - name: identifiers.bigint, - }) - : tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.number, - }), - }); - - if (!isBigInt && schema.type === 'integer') { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.int, - }), - }); - } - - const checks: Array = []; - - if (schema.exclusiveMinimum !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.gt, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMinimum }), - ], - }), - ); - } else if (schema.minimum !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.gte, - }), - parameters: [numberParameter({ isBigInt, value: schema.minimum })], - }), - ); - } - - if (schema.exclusiveMaximum !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.lt, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMaximum }), - ], - }), - ); - } else if (schema.maximum !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.lte, - }), - parameters: [numberParameter({ isBigInt, value: schema.maximum })], - }), - ); - } - - if (checks.length) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.check, - }), - parameters: checks, - }); - } - - return result as Omit; -}; - -const objectTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'object'>; - state: State; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const result: Partial> = {}; - - // TODO: parser - handle constants - const properties: Array = - []; - - const required = schema.required ?? []; - - for (const name in schema.properties) { - const property = schema.properties[name]!; - const isRequired = required.includes(name); - - const propertySchema = schemaToZodSchema({ - optional: !isRequired, - plugin, - schema: property, - state, - }); - if (propertySchema.hasCircularReference) { - result.hasCircularReference = true; - } - - numberRegExp.lastIndex = 0; - let propertyName; - if (numberRegExp.test(name)) { - // For numeric literals, we'll handle negative numbers by using a string literal - // instead of trying to use a PrefixUnaryExpression - propertyName = name.startsWith('-') - ? ts.factory.createStringLiteral(name) - : ts.factory.createNumericLiteral(name); - } else { - propertyName = name; - } - // TODO: parser - abstract safe property name logic - if ( - ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && - !name.startsWith("'") && - !name.endsWith("'") - ) { - propertyName = `'${name}'`; - } - - if (propertySchema.hasCircularReference) { - properties.push( - tsc.getAccessorDeclaration({ - name: propertyName, - // @ts-expect-error - returnType: propertySchema.typeName - ? tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: propertySchema.typeName, - }) - : undefined, - statements: [ - tsc.returnStatement({ - expression: propertySchema.expression, - }), - ], - }), - ); - } else { - properties.push( - tsc.propertyAssignment({ - initializer: propertySchema.expression, - name: propertyName, - }), - ); - } - } - - if ( - schema.additionalProperties && - (!schema.properties || !Object.keys(schema.properties).length) - ) { - const zodSchema = schemaToZodSchema({ - plugin, - schema: schema.additionalProperties, - state, - }); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.record, - }), - parameters: [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.string, - }), - parameters: [], - }), - zodSchema.expression, - ], - }); - if (zodSchema.hasCircularReference) { - result.hasCircularReference = true; - } - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.object, - }), - parameters: [ts.factory.createObjectLiteralExpression(properties, true)], - }); - - return result as Omit; -}; - -const stringTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'string'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const result: Partial> = {}; - - if (typeof schema.const === 'string') { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.string(schema.const)], - }); - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.string, - }), - }); - - const dateTimeOptions: { key: string; value: boolean }[] = []; - - if (plugin.config.dates.offset) { - dateTimeOptions.push({ key: 'offset', value: true }); - } - if (plugin.config.dates.local) { - dateTimeOptions.push({ key: 'local', value: true }); - } - - if (schema.format) { - switch (schema.format) { - case 'date': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.iso, - }), - name: identifiers.date, - }), - }); - break; - case 'date-time': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.iso, - }), - name: identifiers.datetime, - }), - parameters: - dateTimeOptions.length > 0 - ? [ - tsc.objectExpression({ - obj: dateTimeOptions, - }), - ] - : [], - }); - break; - case 'email': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.email, - }), - }); - break; - case 'ipv4': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.ipv4, - }), - }); - break; - case 'ipv6': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.ipv6, - }), - }); - break; - case 'time': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.iso, - }), - name: identifiers.time, - }), - }); - break; - case 'uri': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.url, - }), - }); - break; - case 'uuid': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.uuid, - }), - }); - break; - } - } - - const checks: Array = []; - - if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }), - ); - } else { - if (schema.minLength !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.minLength, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }), - ); - } - - if (schema.maxLength !== undefined) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.maxLength, - }), - parameters: [tsc.valueToExpression({ value: schema.maxLength })], - }), - ); - } - } - - if (schema.pattern) { - checks.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.regex, - }), - parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], - }), - ); - } - - if (checks.length) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.check, - }), - parameters: checks, - }); - } - - return result as Omit; -}; - -const tupleTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'tuple'>; - state: State; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const result: Partial> = {}; - - if (schema.const && Array.isArray(schema.const)) { - const tupleElements = schema.const.map((value) => - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.valueToExpression({ value })], - }), - ); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - return result as Omit; - } - - const tupleElements: Array = []; - - for (const item of schema.items ?? []) { - const itemSchema = schemaToZodSchema({ - plugin, - schema: item, - state, - }); - tupleElements.push(itemSchema.expression); - - if (itemSchema.hasCircularReference) { - result.hasCircularReference = true; - } - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - - return result as Omit; -}; - -const undefinedTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'undefined'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const result: Partial> = {}; - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.undefined, - }), - }); - return result as Omit; -}; - -const unknownTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'unknown'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const result: Partial> = {}; - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.unknown, - }), - }); - return result as Omit; -}; - -const voidTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'void'>; -}): Omit => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const result: Partial> = {}; - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.void, - }), - }); - return result as Omit; -}; - -const schemaTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: IR.SchemaObject; - state: State; -}): Omit => { - switch (schema.type as Required['type']) { - case 'array': - return arrayTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'array'>, - state, - }); - case 'boolean': - return booleanTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'boolean'>, - }); - case 'enum': - return enumTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'enum'>, - }); - case 'integer': - case 'number': - return numberTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'integer' | 'number'>, - }); - case 'never': - return neverTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'never'>, - }); - case 'null': - return nullTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'null'>, - }); - case 'object': - return objectTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'object'>, - state, - }); - case 'string': - return stringTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'string'>, - }); - case 'tuple': - return tupleTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'tuple'>, - state, - }); - case 'undefined': - return undefinedTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'undefined'>, - }); - case 'unknown': - return unknownTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'unknown'>, - }); - case 'void': - return voidTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'void'>, - }); - } -}; - -const schemaToZodSchema = ({ +export const irSchemaToAst = ({ optional, plugin, schema, state, -}: { +}: IrSchemaToAstOptions & { /** * Accept `optional` to handle optional object properties. We can't handle * this inside the object function because `.optional()` must come before * `.default()` which is handled in this function. */ optional?: boolean; - plugin: ZodPlugin['Instance']; schema: IR.SchemaObject; - state: State; -}): ZodSchema => { - let zodSchema: Partial = {}; +}): Ast => { + let ast: Partial = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); if (schema.$ref) { const isCircularReference = state.circularReferenceTracker.includes( @@ -1064,9 +50,9 @@ const schemaToZodSchema = ({ } if (isSelfReference) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.lazy, }), parameters: [ @@ -1081,9 +67,9 @@ const schemaToZodSchema = ({ ], }); } else { - zodSchema.expression = tsc.identifier({ text: symbol.placeholder }); + ast.expression = tsc.identifier({ text: symbol.placeholder }); } - zodSchema.hasCircularReference = schema.circular; + ast.hasCircularReference = schema.circular; } else { if (!symbol) { // if $ref hasn't been processed yet, inline it to avoid the @@ -1094,32 +80,39 @@ const schemaToZodSchema = ({ id: schema.$ref, plugin, schema: ref, - state, + state: { + ...state, + _path: jsonPointerToPath(schema.$ref), + }, }); } else { - zodSchema.hasCircularReference = schema.circular; + ast.hasCircularReference = schema.circular; } const refSymbol = plugin.referenceSymbol(selector); - zodSchema.expression = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = tsc.identifier({ text: refSymbol.placeholder }); } state.circularReferenceTracker.pop(); state.currentReferenceTracker.pop(); } else if (schema.type) { - const zSchema = schemaTypeToZodSchema({ plugin, schema, state }); - zodSchema.expression = zSchema.expression; - zodSchema.hasCircularReference = zSchema.hasCircularReference; + const zSchema = irSchemaWithTypeToAst({ + plugin, + schema: schema as SchemaWithType, + state, + }); + ast.expression = zSchema.expression; + ast.hasCircularReference = zSchema.hasCircularReference; if (plugin.config.metadata && schema.description) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.register, }), parameters: [ tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.globalRegistry, }), tsc.objectExpression({ @@ -1137,11 +130,14 @@ const schemaToZodSchema = ({ schema = deduplicateSchema({ schema }); if (schema.items) { - const itemSchemas = schema.items.map((item) => - schemaToZodSchema({ + const itemSchemas = schema.items.map((item, index) => + irSchemaToAst({ plugin, schema: item, - state, + state: { + ...state, + _path: [...state._path, 'items', index], + }, }), ); @@ -1154,27 +150,27 @@ const schemaToZodSchema = ({ firstSchema.logicalOperator === 'or' || (firstSchema.type && firstSchema.type !== 'object') ) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.intersection, }), parameters: itemSchemas.map((schema) => schema.expression), }); } else { - zodSchema.expression = itemSchemas[0]!.expression; + ast.expression = itemSchemas[0]!.expression; itemSchemas.slice(1).forEach((schema) => { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.intersection, }), parameters: [ - zodSchema.expression, + ast.expression, schema.hasCircularReference ? tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.lazy, }), parameters: [ @@ -1193,9 +189,9 @@ const schemaToZodSchema = ({ }); } } else { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.union, }), parameters: [ @@ -1206,40 +202,40 @@ const schemaToZodSchema = ({ }); } } else { - zodSchema = schemaToZodSchema({ plugin, schema, state }); + ast = irSchemaToAst({ plugin, schema, state }); } } else { // catch-all fallback for failed schemas - const zSchema = schemaTypeToZodSchema({ + const zSchema = irSchemaWithTypeToAst({ plugin, schema: { type: 'unknown', }, state, }); - zodSchema.expression = zSchema.expression; + ast.expression = zSchema.expression; } - if (zodSchema.expression) { + if (ast.expression) { if (schema.accessScope === 'read') { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.readonly, }), - parameters: [zodSchema.expression], + parameters: [ast.expression], }); } if (optional) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.optional, }), - parameters: [zodSchema.expression], + parameters: [ast.expression], }); - zodSchema.typeName = identifiers.ZodMiniOptional; + ast.typeName = identifiers.ZodMiniOptional; } if (schema.default !== undefined) { @@ -1249,18 +245,18 @@ const schemaToZodSchema = ({ value: schema.default, }); if (callParameter) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers._default, }), - parameters: [zodSchema.expression, callParameter], + parameters: [ast.expression, callParameter], }); } } } - return zodSchema as ZodSchema; + return ast as Ast; }; const handleComponent = ({ @@ -1268,27 +264,30 @@ const handleComponent = ({ plugin, schema, state: _state, -}: { +}: Omit & { id: string; - plugin: ZodPlugin['Instance']; schema: IR.SchemaObject; - state?: Omit; + state?: Partial; }): void => { - const state: State = { - circularReferenceTracker: [id], - hasCircularReference: false, - ..._state, - currentReferenceTracker: [id], + const state: IrSchemaToAstOptions['state'] = { + _path: _state?._path ?? [], + circularReferenceTracker: _state?.circularReferenceTracker ?? [id], + currentReferenceTracker: _state?.currentReferenceTracker ?? [id], + hasCircularReference: _state?.hasCircularReference ?? false, }; const selector = plugin.api.getSelector('ref', id); let symbol = plugin.getSymbol(selector); if (symbol && !plugin.getSymbolValue(symbol)) return; - const zodSchema = schemaToZodSchema({ plugin, schema, state }); + const ast = irSchemaToAst({ plugin, schema, state }); const baseName = refToName(id); + const resourceType = pathToSymbolResourceType(state._path); symbol = plugin.registerSymbol({ exported: true, + meta: { + resourceType, + }, name: buildName({ config: plugin.config.definitions, name: baseName, @@ -1300,6 +299,7 @@ const handleComponent = ({ exported: true, meta: { kind: 'type', + resourceType, }, name: buildName({ config: plugin.config.definitions.types.infer, @@ -1308,12 +308,12 @@ const handleComponent = ({ selector: plugin.api.getSelector('type-infer-ref', id), }) : undefined; - exportZodSchema({ + exportAst({ + ast, plugin, schema, symbol, typeInferSymbol, - zodSchema, }); }; @@ -1322,7 +322,7 @@ export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { external: getZodModule({ plugin }), meta: { importKind: 'namespace' }, name: 'z', - selector: plugin.api.getSelector('import', 'zod'), + selector: plugin.api.getSelector('external', 'zod.z'), }); plugin.forEach( @@ -1334,17 +334,21 @@ export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { (event) => { switch (event.type) { case 'operation': - operationToZodSchema({ - getZodSchema: (schema) => { - const state: State = { + irOperationToAst({ + getAst: (schema, path) => { + const state: IrSchemaToAstOptions['state'] = { + _path: path, circularReferenceTracker: [], currentReferenceTracker: [], hasCircularReference: false, }; - return schemaToZodSchema({ plugin, schema, state }); + return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, + state: { + _path: event._path, + }, }); break; case 'parameter': @@ -1352,6 +356,9 @@ export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.parameter.schema, + state: { + _path: event._path, + }, }); break; case 'requestBody': @@ -1359,6 +366,9 @@ export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.requestBody.schema, + state: { + _path: event._path, + }, }); break; case 'schema': @@ -1366,20 +376,27 @@ export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.schema, + state: { + _path: event._path, + }, }); break; case 'webhook': - webhookToZodSchema({ - getZodSchema: (schema) => { - const state: State = { + irWebhookToAst({ + getAst: (schema, path) => { + const state: IrSchemaToAstOptions['state'] = { + _path: path, circularReferenceTracker: [], currentReferenceTracker: [], hasCircularReference: false, }; - return schemaToZodSchema({ plugin, schema, state }); + return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, + state: { + _path: event._path, + }, }); break; } diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts new file mode 100644 index 000000000..da7bb61ed --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts @@ -0,0 +1,172 @@ +import type ts from 'typescript'; + +import { deduplicateSchema } from '../../../../ir/schema'; +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; +import { unknownToAst } from './unknown'; + +export const arrayToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'array'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const result: Partial> = {}; + + const functionName = tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.array, + }); + + if (!schema.items) { + result.expression = tsc.callExpression({ + functionName, + parameters: [ + unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }).expression, + ], + }); + } else { + schema = deduplicateSchema({ schema }); + + // at least one item is guaranteed + const itemExpressions = schema.items!.map((item, index) => { + const zodSchema = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + if (zodSchema.hasCircularReference) { + result.hasCircularReference = true; + } + return zodSchema.expression; + }); + + if (itemExpressions.length === 1) { + result.expression = tsc.callExpression({ + functionName, + parameters: itemExpressions, + }); + } else { + if (schema.logicalOperator === 'and') { + const firstSchema = schema.items![0]!; + // we want to add an intersection, but not every schema can use the same API. + // if the first item contains another array or not an object, we cannot use + // `.intersection()` as that does not exist on `.union()` and non-object schemas. + let intersectionExpression: ts.Expression; + if ( + firstSchema.logicalOperator === 'or' || + (firstSchema.type && firstSchema.type !== 'object') + ) { + intersectionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.intersection, + }), + parameters: itemExpressions, + }); + } else { + intersectionExpression = itemExpressions[0]!; + for (let i = 1; i < itemExpressions.length; i++) { + intersectionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.intersection, + }), + parameters: [intersectionExpression, itemExpressions[i]!], + }); + } + } + + result.expression = tsc.callExpression({ + functionName, + parameters: [intersectionExpression], + }); + } else { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.array, + }), + parameters: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: itemExpressions, + }), + ], + }), + ], + }); + } + } + } + + const checks: Array = []; + + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }), + ); + } else { + if (schema.minItems !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.minLength, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }), + ); + } + + if (schema.maxItems !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.maxLength, + }), + parameters: [tsc.valueToExpression({ value: schema.maxItems })], + }), + ); + } + } + + if (checks.length) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.check, + }), + parameters: checks, + }); + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts new file mode 100644 index 000000000..8f47ccca4 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts @@ -0,0 +1,34 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const booleanToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'boolean'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const result: Partial> = {}; + + if (typeof schema.const === 'boolean') { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.boolean(schema.const)], + }); + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.boolean, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts new file mode 100644 index 000000000..766e5db62 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts @@ -0,0 +1,127 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { unknownToAst } from './unknown'; + +export const enumToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'enum'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const result: Partial> = {}; + + const enumMembers: Array = []; + const literalMembers: Array = []; + + let isNullable = false; + let allStrings = true; + + for (const item of schema.items ?? []) { + // Zod supports string, number, and boolean enums + if (item.type === 'string' && typeof item.const === 'string') { + const stringLiteral = tsc.stringLiteral({ + text: item.const, + }); + enumMembers.push(stringLiteral); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [stringLiteral], + }), + ); + } else if ( + (item.type === 'number' || item.type === 'integer') && + typeof item.const === 'number' + ) { + allStrings = false; + const numberLiteral = tsc.ots.number(item.const); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [numberLiteral], + }), + ); + } else if (item.type === 'boolean' && typeof item.const === 'boolean') { + allStrings = false; + const booleanLiteral = tsc.ots.boolean(item.const); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [booleanLiteral], + }), + ); + } else if (item.type === 'null' || item.const === null) { + isNullable = true; + } + } + + if (!literalMembers.length) { + return unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + } + + // Use z.enum() for pure string enums, z.union() for mixed or non-string types + if (allStrings && enumMembers.length > 0) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.enum, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: enumMembers, + multiLine: false, + }), + ], + }); + } else if (literalMembers.length === 1) { + // For single-member unions, use the member directly instead of wrapping in z.union() + result.expression = literalMembers[0]; + } else { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: literalMembers, + multiLine: literalMembers.length > 3, + }), + ], + }); + } + + if (isNullable) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.nullable, + }), + parameters: [result.expression], + }); + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts new file mode 100644 index 000000000..62c481bfd --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts @@ -0,0 +1,85 @@ +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { arrayToAst } from './array'; +import { booleanToAst } from './boolean'; +import { enumToAst } from './enum'; +import { neverToAst } from './never'; +import { nullToAst } from './null'; +import { numberToAst } from './number'; +import { objectToAst } from './object'; +import { stringToAst } from './string'; +import { tupleToAst } from './tuple'; +import { undefinedToAst } from './undefined'; +import { unknownToAst } from './unknown'; +import { voidToAst } from './void'; + +export const irSchemaWithTypeToAst = ({ + schema, + ...args +}: IrSchemaToAstOptions & { + schema: SchemaWithType; +}): Omit => { + switch (schema.type) { + case 'array': + return arrayToAst({ + ...args, + schema: schema as SchemaWithType<'array'>, + }); + case 'boolean': + return booleanToAst({ + ...args, + schema: schema as SchemaWithType<'boolean'>, + }); + case 'enum': + return enumToAst({ + ...args, + schema: schema as SchemaWithType<'enum'>, + }); + case 'integer': + case 'number': + return numberToAst({ + ...args, + schema: schema as SchemaWithType<'integer' | 'number'>, + }); + case 'never': + return neverToAst({ + ...args, + schema: schema as SchemaWithType<'never'>, + }); + case 'null': + return nullToAst({ + ...args, + schema: schema as SchemaWithType<'null'>, + }); + case 'object': + return objectToAst({ + ...args, + schema: schema as SchemaWithType<'object'>, + }); + case 'string': + return stringToAst({ + ...args, + schema: schema as SchemaWithType<'string'>, + }); + case 'tuple': + return tupleToAst({ + ...args, + schema: schema as SchemaWithType<'tuple'>, + }); + case 'undefined': + return undefinedToAst({ + ...args, + schema: schema as SchemaWithType<'undefined'>, + }); + case 'unknown': + return unknownToAst({ + ...args, + schema: schema as SchemaWithType<'unknown'>, + }); + case 'void': + return voidToAst({ + ...args, + schema: schema as SchemaWithType<'void'>, + }); + } +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts new file mode 100644 index 000000000..b1e3aa880 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const neverToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'never'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const result: Partial> = {}; + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.never, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts new file mode 100644 index 000000000..cc9b51168 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const nullToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'null'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const result: Partial> = {}; + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.null, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts new file mode 100644 index 000000000..58c3d81e3 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts @@ -0,0 +1,118 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import { numberParameter } from '../../shared/numbers'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const numberToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'integer' | 'number'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const result: Partial> = {}; + + const isBigInt = schema.type === 'integer' && schema.format === 'int64'; + + if (typeof schema.const === 'number') { + // TODO: parser - handle bigint constants + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.number(schema.const)], + }); + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: isBigInt + ? tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.coerce, + }), + name: identifiers.bigint, + }) + : tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.number, + }), + }); + + if (!isBigInt && schema.type === 'integer') { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.int, + }), + }); + } + + const checks: Array = []; + + if (schema.exclusiveMinimum !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.gt, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMinimum }), + ], + }), + ); + } else if (schema.minimum !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.gte, + }), + parameters: [numberParameter({ isBigInt, value: schema.minimum })], + }), + ); + } + + if (schema.exclusiveMaximum !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.lt, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMaximum }), + ], + }), + ); + } else if (schema.maximum !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.lte, + }), + parameters: [numberParameter({ isBigInt, value: schema.maximum })], + }), + ); + } + + if (checks.length) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.check, + }), + parameters: checks, + }); + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts new file mode 100644 index 000000000..32b92190a --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts @@ -0,0 +1,135 @@ +import ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import { numberRegExp } from '../../../../utils/regexp'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; + +export const objectToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'object'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const result: Partial> = {}; + + // TODO: parser - handle constants + const properties: Array = + []; + + const required = schema.required ?? []; + + for (const name in schema.properties) { + const property = schema.properties[name]!; + const isRequired = required.includes(name); + + const propertySchema = irSchemaToAst({ + optional: !isRequired, + plugin, + schema: property, + state: { + ...state, + _path: [...state._path, 'properties', name], + }, + }); + if (propertySchema.hasCircularReference) { + result.hasCircularReference = true; + } + + numberRegExp.lastIndex = 0; + let propertyName; + if (numberRegExp.test(name)) { + // For numeric literals, we'll handle negative numbers by using a string literal + // instead of trying to use a PrefixUnaryExpression + propertyName = name.startsWith('-') + ? ts.factory.createStringLiteral(name) + : ts.factory.createNumericLiteral(name); + } else { + propertyName = name; + } + // TODO: parser - abstract safe property name logic + if ( + ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && + !name.startsWith("'") && + !name.endsWith("'") + ) { + propertyName = `'${name}'`; + } + + if (propertySchema.hasCircularReference) { + properties.push( + tsc.getAccessorDeclaration({ + name: propertyName, + // @ts-expect-error + returnType: propertySchema.typeName + ? tsc.propertyAccessExpression({ + expression: z.placeholder, + name: propertySchema.typeName, + }) + : undefined, + statements: [ + tsc.returnStatement({ + expression: propertySchema.expression, + }), + ], + }), + ); + } else { + properties.push( + tsc.propertyAssignment({ + initializer: propertySchema.expression, + name: propertyName, + }), + ); + } + } + + if ( + schema.additionalProperties && + (!schema.properties || !Object.keys(schema.properties).length) + ) { + const zodSchema = irSchemaToAst({ + plugin, + schema: schema.additionalProperties, + state: { + ...state, + _path: [...state._path, 'additionalProperties'], + }, + }); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.record, + }), + parameters: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.string, + }), + parameters: [], + }), + zodSchema.expression, + ], + }); + if (zodSchema.hasCircularReference) { + result.hasCircularReference = true; + } + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.object, + }), + parameters: [ts.factory.createObjectLiteralExpression(properties, true)], + }); + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts new file mode 100644 index 000000000..6aa88db4e --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts @@ -0,0 +1,192 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const stringToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'string'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const result: Partial> = {}; + + if (typeof schema.const === 'string') { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.string(schema.const)], + }); + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.string, + }), + }); + + const dateTimeOptions: { key: string; value: boolean }[] = []; + + if (plugin.config.dates.offset) { + dateTimeOptions.push({ key: 'offset', value: true }); + } + if (plugin.config.dates.local) { + dateTimeOptions.push({ key: 'local', value: true }); + } + + if (schema.format) { + switch (schema.format) { + case 'date': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.iso, + }), + name: identifiers.date, + }), + }); + break; + case 'date-time': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.iso, + }), + name: identifiers.datetime, + }), + parameters: + dateTimeOptions.length > 0 + ? [ + tsc.objectExpression({ + obj: dateTimeOptions, + }), + ] + : [], + }); + break; + case 'email': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.email, + }), + }); + break; + case 'ipv4': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.ipv4, + }), + }); + break; + case 'ipv6': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.ipv6, + }), + }); + break; + case 'time': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.iso, + }), + name: identifiers.time, + }), + }); + break; + case 'uri': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.url, + }), + }); + break; + case 'uuid': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.uuid, + }), + }); + break; + } + } + + const checks: Array = []; + + if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }), + ); + } else { + if (schema.minLength !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.minLength, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }), + ); + } + + if (schema.maxLength !== undefined) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.maxLength, + }), + parameters: [tsc.valueToExpression({ value: schema.maxLength })], + }), + ); + } + } + + if (schema.pattern) { + checks.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.regex, + }), + parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], + }), + ); + } + + if (checks.length) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.check, + }), + parameters: checks, + }); + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts new file mode 100644 index 000000000..c66779036 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts @@ -0,0 +1,76 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; + +export const tupleToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'tuple'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const result: Partial> = {}; + + if (schema.const && Array.isArray(schema.const)) { + const tupleElements = schema.const.map((value) => + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.valueToExpression({ value })], + }), + ); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + return result as Omit; + } + + const tupleElements: Array = []; + + if (schema.items) { + schema.items.forEach((item, index) => { + const itemSchema = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + tupleElements.push(itemSchema.expression); + if (itemSchema.hasCircularReference) { + result.hasCircularReference = true; + } + }); + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts new file mode 100644 index 000000000..f76718e3c --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const undefinedToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'undefined'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const result: Partial> = {}; + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.undefined, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts new file mode 100644 index 000000000..32512858a --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const unknownToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'unknown'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const result: Partial> = {}; + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.unknown, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts new file mode 100644 index 000000000..5c2c0e344 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const voidToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'void'>; +}): Omit => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const result: Partial> = {}; + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.void, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/plugin.ts b/packages/openapi-ts/src/plugins/zod/plugin.ts index fbd86a5a3..78e159140 100644 --- a/packages/openapi-ts/src/plugins/zod/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/plugin.ts @@ -8,10 +8,9 @@ export const handler: ZodPlugin['Handler'] = (args) => { switch (plugin.config.compatibilityVersion) { case 3: return handlerV3(args); - case 4: - return handlerV4(args); case 'mini': return handlerMini(args); + case 4: default: return handlerV4(args); } diff --git a/packages/openapi-ts/src/plugins/zod/export.ts b/packages/openapi-ts/src/plugins/zod/shared/export.ts similarity index 64% rename from packages/openapi-ts/src/plugins/zod/export.ts rename to packages/openapi-ts/src/plugins/zod/shared/export.ts index 21c836849..16ae40bde 100644 --- a/packages/openapi-ts/src/plugins/zod/export.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/export.ts @@ -1,41 +1,39 @@ import type { Symbol } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { IR } from '../../ir/types'; -import { tsc } from '../../tsc'; -import { createSchemaComment } from '../shared/utils/schema'; -import { identifiers } from './constants'; -import type { ZodSchema } from './shared/types'; -import type { ZodPlugin } from './types'; +import type { IR } from '../../../ir/types'; +import { tsc } from '../../../tsc'; +import { createSchemaComment } from '../../shared/utils/schema'; +import { identifiers } from '../constants'; +import type { ZodPlugin } from '../types'; +import type { Ast } from './types'; -export const exportZodSchema = ({ +export const exportAst = ({ + ast, plugin, schema, symbol, typeInferSymbol, - zodSchema, }: { + ast: Ast; plugin: ZodPlugin['Instance']; schema: IR.SchemaObject; symbol: Symbol; typeInferSymbol: Symbol | undefined; - zodSchema: ZodSchema; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); +}): void => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); const statement = tsc.constVariable({ comment: plugin.config.comments ? createSchemaComment({ schema }) : undefined, exportConst: symbol.exported, - expression: zodSchema.expression, + expression: ast.expression, name: symbol.placeholder, - typeName: zodSchema.typeName + typeName: ast.typeName ? (tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: zodSchema.typeName, + expression: z.placeholder, + name: ast.typeName, }) as unknown as ts.TypeNode) : undefined, }); @@ -52,7 +50,7 @@ export const exportZodSchema = ({ }) as unknown as ts.TypeNode, ], typeName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.infer, }) as unknown as string, }), diff --git a/packages/openapi-ts/src/plugins/zod/shared/numbers.ts b/packages/openapi-ts/src/plugins/zod/shared/numbers.ts new file mode 100644 index 000000000..63e44916d --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/shared/numbers.ts @@ -0,0 +1,28 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../tsc'; + +export const numberParameter = ({ + isBigInt, + value, +}: { + isBigInt: boolean; + value: unknown; +}): ts.Expression | undefined => { + const expression = tsc.valueToExpression({ value }); + + if ( + isBigInt && + (typeof value === 'bigint' || + typeof value === 'number' || + typeof value === 'string' || + typeof value === 'boolean') + ) { + return tsc.callExpression({ + functionName: 'BigInt', + parameters: [expression], + }); + } + + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/shared/operation.ts b/packages/openapi-ts/src/plugins/zod/shared/operation.ts index b4b4f7dd1..bb929618f 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/operation.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/operation.ts @@ -1,19 +1,23 @@ import { operationResponsesMap } from '../../../ir/operation'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; -import { exportZodSchema } from '../export'; -import type { ZodPlugin } from '../types'; -import type { ZodSchema } from './types'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { exportAst } from './export'; +import type { Ast, IrSchemaToAstOptions } from './types'; -export const operationToZodSchema = ({ - getZodSchema, +export const irOperationToAst = ({ + getAst, operation, plugin, -}: { - getZodSchema: (schema: IR.SchemaObject) => ZodSchema; + state, +}: Omit & { + getAst: ( + schema: IR.SchemaObject, + path: ReadonlyArray, + ) => Ast; operation: IR.OperationObject; - plugin: ZodPlugin['Instance']; -}) => { + state: Partial; +}): void => { if (plugin.config.requests.enabled) { const requiredProperties = new Set(); @@ -112,9 +116,14 @@ export const operationToZodSchema = ({ schemaData.required = [...requiredProperties]; - const zodSchema = getZodSchema(schemaData); + const path = state._path || []; + const ast = getAst(schemaData, path); + const resourceType = pathToSymbolResourceType(path); const symbol = plugin.registerSymbol({ exported: true, + meta: { + resourceType, + }, name: buildName({ config: plugin.config.requests, name: operation.id, @@ -126,6 +135,7 @@ export const operationToZodSchema = ({ exported: true, meta: { kind: 'type', + resourceType, }, name: buildName({ config: plugin.config.requests.types.infer, @@ -134,12 +144,12 @@ export const operationToZodSchema = ({ selector: plugin.api.getSelector('type-infer-data', operation.id), }) : undefined; - exportZodSchema({ + exportAst({ + ast, plugin, schema: schemaData, symbol, typeInferSymbol, - zodSchema, }); } @@ -148,9 +158,14 @@ export const operationToZodSchema = ({ const { response } = operationResponsesMap(operation); if (response) { - const zodSchema = getZodSchema(response); + const path = [...(state._path || []), 'responses']; + const ast = getAst(response, path); + const resourceType = pathToSymbolResourceType(path); const symbol = plugin.registerSymbol({ exported: true, + meta: { + resourceType, + }, name: buildName({ config: plugin.config.responses, name: operation.id, @@ -162,6 +177,7 @@ export const operationToZodSchema = ({ exported: true, meta: { kind: 'type', + resourceType, }, name: buildName({ config: plugin.config.responses.types.infer, @@ -173,12 +189,12 @@ export const operationToZodSchema = ({ ), }) : undefined; - exportZodSchema({ + exportAst({ + ast, plugin, schema: response, symbol, typeInferSymbol, - zodSchema, }); } } diff --git a/packages/openapi-ts/src/plugins/zod/shared/types.d.ts b/packages/openapi-ts/src/plugins/zod/shared/types.d.ts index 6f352346f..474d9965e 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/types.d.ts @@ -1,13 +1,24 @@ import type ts from 'typescript'; import type { IR } from '../../../ir/types'; +import type { ZodPlugin } from '../types'; -export interface SchemaWithType['type']> - extends Omit { - type: Extract['type'], T>; -} +export type Ast = { + expression: ts.Expression; + hasCircularReference?: boolean; + typeName?: string | ts.Identifier; +}; + +export type IrSchemaToAstOptions = { + plugin: ZodPlugin['Instance']; + state: State; +}; export type State = { + /** + * Path to the schema in the intermediary representation. + */ + _path: ReadonlyArray; circularReferenceTracker: Array; /** * Works the same as `circularReferenceTracker`, but it resets whenever we @@ -18,8 +29,7 @@ export type State = { hasCircularReference: boolean; }; -export type ZodSchema = { - expression: ts.Expression; - hasCircularReference?: boolean; - typeName?: string | ts.Identifier; +export type ValidatorArgs = { + operation: IR.OperationObject; + plugin: ZodPlugin['Instance']; }; diff --git a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts index fc753cffb..9ebdddbb0 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts @@ -1,17 +1,21 @@ import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; -import { exportZodSchema } from '../export'; -import type { ZodPlugin } from '../types'; -import type { ZodSchema } from './types'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { exportAst } from './export'; +import type { Ast, IrSchemaToAstOptions } from './types'; -export const webhookToZodSchema = ({ - getZodSchema, +export const irWebhookToAst = ({ + getAst, operation, plugin, -}: { - getZodSchema: (schema: IR.SchemaObject) => ZodSchema; + state, +}: Omit & { + getAst: ( + schema: IR.SchemaObject, + path: ReadonlyArray, + ) => Ast; operation: IR.OperationObject; - plugin: ZodPlugin['Instance']; + state: Partial; }) => { if (plugin.config.webhooks.enabled) { const requiredProperties = new Set(); @@ -111,9 +115,14 @@ export const webhookToZodSchema = ({ schemaData.required = [...requiredProperties]; - const zodSchema = getZodSchema(schemaData); + const path = state._path || []; + const ast = getAst(schemaData, path); + const resourceType = pathToSymbolResourceType(path); const symbol = plugin.registerSymbol({ exported: true, + meta: { + resourceType, + }, name: buildName({ config: plugin.config.webhooks, name: operation.id, @@ -125,6 +134,7 @@ export const webhookToZodSchema = ({ exported: true, meta: { kind: 'type', + resourceType, }, name: buildName({ config: plugin.config.webhooks.types.infer, @@ -136,12 +146,12 @@ export const webhookToZodSchema = ({ ), }) : undefined; - exportZodSchema({ + exportAst({ + ast, plugin, schema: schemaData, symbol, typeInferSymbol, - zodSchema, }); } }; diff --git a/packages/openapi-ts/src/plugins/zod/types.d.ts b/packages/openapi-ts/src/plugins/zod/types.d.ts index e5bc03a25..ab95797aa 100644 --- a/packages/openapi-ts/src/plugins/zod/types.d.ts +++ b/packages/openapi-ts/src/plugins/zod/types.d.ts @@ -96,7 +96,7 @@ export type UserConfig = Plugin.Name<'zod'> & */ types?: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. * * Can be: * - `boolean`: Shorthand for `{ enabled: boolean }` @@ -189,7 +189,7 @@ export type UserConfig = Plugin.Name<'zod'> & */ types?: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. * * Can be: * - `boolean`: Shorthand for `{ enabled: boolean }` @@ -267,7 +267,7 @@ export type UserConfig = Plugin.Name<'zod'> & */ types?: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. * * Can be: * - `boolean`: Shorthand for `{ enabled: boolean }` @@ -309,7 +309,7 @@ export type UserConfig = Plugin.Name<'zod'> & */ types?: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. * * Can be: * - `boolean`: Shorthand for `{ enabled: boolean }` @@ -378,7 +378,7 @@ export type UserConfig = Plugin.Name<'zod'> & */ types?: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. * * Can be: * - `boolean`: Shorthand for `{ enabled: boolean }` @@ -499,7 +499,7 @@ export type Config = Plugin.Name<'zod'> & */ types: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. */ infer: { /** @@ -572,7 +572,7 @@ export type Config = Plugin.Name<'zod'> & */ types: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. */ infer: { /** @@ -630,7 +630,7 @@ export type Config = Plugin.Name<'zod'> & */ types: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. */ infer: { /** @@ -662,7 +662,7 @@ export type Config = Plugin.Name<'zod'> & */ types: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. */ infer: { /** @@ -711,7 +711,7 @@ export type Config = Plugin.Name<'zod'> & */ types: { /** - * Configuration for `z.infer` types. + * Configuration for `infer` types. */ infer: { /** diff --git a/packages/openapi-ts/src/plugins/zod/v3/api.ts b/packages/openapi-ts/src/plugins/zod/v3/api.ts new file mode 100644 index 000000000..3be0a8c54 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/api.ts @@ -0,0 +1,71 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../tsc'; +import { identifiers } from '../constants'; +import type { ValidatorArgs } from '../shared/types'; + +export const createRequestValidatorV3 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; + +export const createResponseValidatorV3 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol( + plugin.api.getSelector('responses', operation.id), + ); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts index ab7fc5535..6d1cb6dcc 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts @@ -1,972 +1,37 @@ -import ts from 'typescript'; - import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import { numberRegExp } from '../../../utils/regexp'; +import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import type { SchemaWithType } from '../../shared/types/schema'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; import { identifiers } from '../constants'; -import { exportZodSchema } from '../export'; +import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; -import { operationToZodSchema } from '../shared/operation'; -import type { SchemaWithType, State, ZodSchema } from '../shared/types'; -import { webhookToZodSchema } from '../shared/webhook'; +import { numberParameter } from '../shared/numbers'; +import { irOperationToAst } from '../shared/operation'; +import type { Ast, IrSchemaToAstOptions } from '../shared/types'; +import { irWebhookToAst } from '../shared/webhook'; import type { ZodPlugin } from '../types'; +import { irSchemaWithTypeToAst } from './toAst'; -const arrayTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'array'>; - state: State; -}): Omit & { - anyType?: string; -} => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const functionName = tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.array, - }); - - let arrayExpression: ts.CallExpression | undefined; - let hasCircularReference = false; - - if (!schema.items) { - arrayExpression = tsc.callExpression({ - functionName, - parameters: [ - unknownTypeToZodSchema({ - plugin, - schema: { - type: 'unknown', - }, - }), - ], - }); - } else { - schema = deduplicateSchema({ schema }); - - // at least one item is guaranteed - const itemExpressions = schema.items!.map((item) => { - const zodSchema = schemaToZodSchema({ - plugin, - schema: item, - state, - }); - if (zodSchema.hasCircularReference) { - hasCircularReference = true; - } - return zodSchema.expression; - }); - - if (itemExpressions.length === 1) { - arrayExpression = tsc.callExpression({ - functionName, - parameters: itemExpressions, - }); - } else { - if (schema.logicalOperator === 'and') { - const firstSchema = schema.items![0]!; - // we want to add an intersection, but not every schema can use the same API. - // if the first item contains another array or not an object, we cannot use - // `.and()` as that does not exist on `.union()` and non-object schemas. - let intersectionExpression: ts.Expression; - if ( - firstSchema.logicalOperator === 'or' || - (firstSchema.type && firstSchema.type !== 'object') - ) { - intersectionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.intersection, - }), - parameters: itemExpressions, - }); - } else { - intersectionExpression = itemExpressions[0]!; - for (let i = 1; i < itemExpressions.length; i++) { - intersectionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: intersectionExpression, - name: identifiers.and, - }), - parameters: [itemExpressions[i]!], - }); - } - } - - arrayExpression = tsc.callExpression({ - functionName, - parameters: [intersectionExpression], - }); - } else { - arrayExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.array, - }), - parameters: [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: itemExpressions, - }), - ], - }), - ], - }); - } - } - } - - if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { - arrayExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: arrayExpression, - name: identifiers.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }); - } else { - if (schema.minItems !== undefined) { - arrayExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: arrayExpression, - name: identifiers.min, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }); - } - - if (schema.maxItems !== undefined) { - arrayExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: arrayExpression, - name: identifiers.max, - }), - parameters: [tsc.valueToExpression({ value: schema.maxItems })], - }); - } - } - - return { - expression: arrayExpression, - hasCircularReference, - }; -}; - -const booleanTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'boolean'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - if (typeof schema.const === 'boolean') { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.boolean(schema.const)], - }); - return expression; - } - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.boolean, - }), - }); - return expression; -}; - -const enumTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'enum'>; -}): ts.CallExpression => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const enumMembers: Array = []; - const literalMembers: Array = []; - - let isNullable = false; - let allStrings = true; - - for (const item of schema.items ?? []) { - // Zod supports string, number, and boolean enums - if (item.type === 'string' && typeof item.const === 'string') { - const stringLiteral = tsc.stringLiteral({ - text: item.const, - }); - enumMembers.push(stringLiteral); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [stringLiteral], - }), - ); - } else if ( - (item.type === 'number' || item.type === 'integer') && - typeof item.const === 'number' - ) { - allStrings = false; - const numberLiteral = tsc.ots.number(item.const); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [numberLiteral], - }), - ); - } else if (item.type === 'boolean' && typeof item.const === 'boolean') { - allStrings = false; - const booleanLiteral = tsc.ots.boolean(item.const); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [booleanLiteral], - }), - ); - } else if (item.type === 'null' || item.const === null) { - isNullable = true; - } - } - - if (!literalMembers.length) { - return unknownTypeToZodSchema({ - plugin, - schema: { - type: 'unknown', - }, - }); - } - - // Use z.enum() for pure string enums, z.union() for mixed or non-string types - let enumExpression: ts.CallExpression; - if (allStrings && enumMembers.length > 0) { - enumExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.enum, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: enumMembers, - multiLine: false, - }), - ], - }); - } else if (literalMembers.length === 1) { - // For single-member unions, use the member directly instead of wrapping in z.union() - enumExpression = literalMembers[0]!; - } else { - enumExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: literalMembers, - multiLine: literalMembers.length > 3, - }), - ], - }); - } - - if (isNullable) { - enumExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: enumExpression, - name: identifiers.nullable, - }), - }); - } - - return enumExpression; -}; - -const neverTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'never'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.never, - }), - }); - return expression; -}; - -const nullTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'null'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.null, - }), - }); - return expression; -}; - -const numberParameter = ({ - isBigInt, - value, -}: { - isBigInt: boolean; - value: unknown; -}) => { - const expression = tsc.valueToExpression({ value }); - - if ( - isBigInt && - (typeof value === 'bigint' || - typeof value === 'number' || - typeof value === 'string' || - typeof value === 'boolean') - ) { - return tsc.callExpression({ - functionName: 'BigInt', - parameters: [expression], - }); - } - - return expression; -}; - -const numberTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'integer' | 'number'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const isBigInt = schema.type === 'integer' && schema.format === 'int64'; - - if (typeof schema.const === 'number') { - // TODO: parser - handle bigint constants - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.number(schema.const)], - }); - return expression; - } - - let numberExpression = tsc.callExpression({ - functionName: isBigInt - ? tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.coerce, - }), - name: identifiers.bigint, - }) - : tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.number, - }), - }); - - if (!isBigInt && schema.type === 'integer') { - numberExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: numberExpression, - name: identifiers.int, - }), - }); - } - - if (schema.exclusiveMinimum !== undefined) { - numberExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: numberExpression, - name: identifiers.gt, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMinimum }), - ], - }); - } else if (schema.minimum !== undefined) { - numberExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: numberExpression, - name: identifiers.gte, - }), - parameters: [numberParameter({ isBigInt, value: schema.minimum })], - }); - } - - if (schema.exclusiveMaximum !== undefined) { - numberExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: numberExpression, - name: identifiers.lt, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMaximum }), - ], - }); - } else if (schema.maximum !== undefined) { - numberExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: numberExpression, - name: identifiers.lte, - }), - parameters: [numberParameter({ isBigInt, value: schema.maximum })], - }); - } - - return numberExpression; -}; - -const objectTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'object'>; - state: State; -}): Omit & { - anyType?: string; -} => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - let hasCircularReference = false; - - // TODO: parser - handle constants - const properties: Array = []; - - const required = schema.required ?? []; - - for (const name in schema.properties) { - const property = schema.properties[name]!; - const isRequired = required.includes(name); - - const propertyExpression = schemaToZodSchema({ - optional: !isRequired, - plugin, - schema: property, - state, - }); - - if (propertyExpression.hasCircularReference) { - hasCircularReference = true; - } - - numberRegExp.lastIndex = 0; - let propertyName; - if (numberRegExp.test(name)) { - // For numeric literals, we'll handle negative numbers by using a string literal - // instead of trying to use a PrefixUnaryExpression - propertyName = name.startsWith('-') - ? ts.factory.createStringLiteral(name) - : ts.factory.createNumericLiteral(name); - } else { - propertyName = name; - } - // TODO: parser - abstract safe property name logic - if ( - ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && - !name.startsWith("'") && - !name.endsWith("'") - ) { - propertyName = `'${name}'`; - } - properties.push( - tsc.propertyAssignment({ - initializer: propertyExpression.expression, - name: propertyName, - }), - ); - } - - if ( - schema.additionalProperties && - (!schema.properties || !Object.keys(schema.properties).length) - ) { - const zodSchema = schemaToZodSchema({ - plugin, - schema: schema.additionalProperties, - state, - }); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.record, - }), - parameters: [zodSchema.expression], - }); - return { - anyType: 'AnyZodObject', - expression, - hasCircularReference: zodSchema.hasCircularReference, - }; - } - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.object, - }), - parameters: [ts.factory.createObjectLiteralExpression(properties, true)], - }); - return { - anyType: 'AnyZodObject', - expression, - hasCircularReference, - }; -}; - -const stringTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'string'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - if (typeof schema.const === 'string') { - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.string(schema.const)], - }); - return expression; - } - - let stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.string, - }), - }); - - const dateTimeOptions: { key: string; value: boolean }[] = []; - - if (plugin.config.dates.offset) { - dateTimeOptions.push({ key: 'offset', value: true }); - } - if (plugin.config.dates.local) { - dateTimeOptions.push({ key: 'local', value: true }); - } - - if (schema.format) { - switch (schema.format) { - case 'date': - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.date, - }), - }); - break; - case 'date-time': - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.datetime, - }), - parameters: - dateTimeOptions.length > 0 - ? [ - tsc.objectExpression({ - obj: dateTimeOptions, - }), - ] - : [], - }); - break; - case 'email': - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.email, - }), - }); - break; - case 'ipv4': - case 'ipv6': - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.ip, - }), - }); - break; - case 'time': - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.time, - }), - }); - break; - case 'uri': - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.url, - }), - }); - break; - case 'uuid': - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.uuid, - }), - }); - break; - } - } - - if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }); - } else { - if (schema.minLength !== undefined) { - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.min, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }); - } - - if (schema.maxLength !== undefined) { - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.max, - }), - parameters: [tsc.valueToExpression({ value: schema.maxLength })], - }); - } - } - - if (schema.pattern) { - stringExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: stringExpression, - name: identifiers.regex, - }), - parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], - }); - } - - return stringExpression; -}; - -const tupleTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'tuple'>; - state: State; -}): Omit & { - anyType?: string; -} => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - let hasCircularReference = false; - - if (schema.const && Array.isArray(schema.const)) { - const tupleElements = schema.const.map((value) => - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.valueToExpression({ value })], - }), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - return { - expression, - hasCircularReference, - }; - } - - const tupleElements: Array = []; - - for (const item of schema.items ?? []) { - const zodSchema = schemaToZodSchema({ - plugin, - schema: item, - state, - }); - if (zodSchema.hasCircularReference) { - hasCircularReference = true; - } - tupleElements.push(zodSchema.expression); - } - - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - return { - expression, - hasCircularReference, - }; -}; - -const undefinedTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'undefined'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.undefined, - }), - }); - return expression; -}; - -const unknownTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'unknown'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.unknown, - }), - }); - return expression; -}; - -const voidTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'void'>; -}) => { - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - const expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.void, - }), - }); - return expression; -}; - -const schemaTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: IR.SchemaObject; - state: State; -}): Omit & { - anyType?: string; -} => { - switch (schema.type as Required['type']) { - case 'array': - return arrayTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'array'>, - state, - }); - case 'boolean': - return { - expression: booleanTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'boolean'>, - }), - }; - case 'enum': - return { - expression: enumTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'enum'>, - }), - }; - case 'integer': - case 'number': - return { - expression: numberTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'integer' | 'number'>, - }), - }; - case 'never': - return { - expression: neverTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'never'>, - }), - }; - case 'null': - return { - expression: nullTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'null'>, - }), - }; - case 'object': - return objectTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'object'>, - state, - }); - case 'string': - return { - expression: stringTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'string'>, - }), - }; - case 'tuple': - return tupleTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'tuple'>, - state, - }); - case 'undefined': - return { - expression: undefinedTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'undefined'>, - }), - }; - case 'unknown': - return { - expression: unknownTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'unknown'>, - }), - }; - case 'void': - return { - expression: voidTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'void'>, - }), - }; - } -}; - -const schemaToZodSchema = ({ +export const irSchemaToAst = ({ optional, plugin, schema, state, -}: { +}: IrSchemaToAstOptions & { /** * Accept `optional` to handle optional object properties. We can't handle * this inside the object function because `.optional()` must come before * `.default()` which is handled in this function. */ optional?: boolean; - plugin: ZodPlugin['Instance']; schema: IR.SchemaObject; - state: State; -}): ZodSchema => { - let zodSchema: Partial = {}; +}): Ast => { + let ast: Partial = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); if (schema.$ref) { const isCircularReference = state.circularReferenceTracker.includes( @@ -983,9 +48,9 @@ const schemaToZodSchema = ({ symbol = plugin.referenceSymbol(selector); } - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.lazy, }), parameters: [ @@ -998,7 +63,7 @@ const schemaToZodSchema = ({ }), ], }); - zodSchema.hasCircularReference = schema.circular; + ast.hasCircularReference = schema.circular; } else { if (!symbol) { // if $ref hasn't been processed yet, inline it to avoid the @@ -1011,29 +76,34 @@ const schemaToZodSchema = ({ schema: ref, state: { ...state, + _path: jsonPointerToPath(schema.$ref), currentReferenceTracker: [schema.$ref], }, }); } else { - zodSchema.hasCircularReference = schema.circular; + ast.hasCircularReference = schema.circular; } const refSymbol = plugin.referenceSymbol(selector); - zodSchema.expression = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = tsc.identifier({ text: refSymbol.placeholder }); } state.circularReferenceTracker.pop(); state.currentReferenceTracker.pop(); } else if (schema.type) { - const zSchema = schemaTypeToZodSchema({ plugin, schema, state }); - zodSchema.expression = zSchema.expression; - zodSchema.hasCircularReference = zSchema.hasCircularReference; - zodSchema.typeName = zSchema.anyType; + const zSchema = irSchemaWithTypeToAst({ + plugin, + schema: schema as SchemaWithType, + state, + }); + ast.expression = zSchema.expression; + ast.hasCircularReference = zSchema.hasCircularReference; + ast.typeName = zSchema.anyType; if (plugin.config.metadata && schema.description) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.describe, }), parameters: [tsc.stringLiteral({ text: schema.description })], @@ -1043,14 +113,17 @@ const schemaToZodSchema = ({ schema = deduplicateSchema({ schema }); if (schema.items) { - const itemTypes = schema.items.map((item) => { - const zSchema = schemaToZodSchema({ + const itemTypes = schema.items.map((item, index) => { + const zSchema = irSchemaToAst({ plugin, schema: item, - state, + state: { + ...state, + _path: [...state._path, 'items', index], + }, }); if (zSchema.hasCircularReference) { - zodSchema.hasCircularReference = true; + ast.hasCircularReference = true; } return zSchema.expression; }); @@ -1064,19 +137,19 @@ const schemaToZodSchema = ({ firstSchema.logicalOperator === 'or' || (firstSchema.type && firstSchema.type !== 'object') ) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.intersection, }), parameters: itemTypes, }); } else { - zodSchema.expression = itemTypes[0]; + ast.expression = itemTypes[0]; itemTypes.slice(1).forEach((item) => { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression!, + expression: ast.expression!, name: identifiers.and, }), parameters: [item], @@ -1084,9 +157,9 @@ const schemaToZodSchema = ({ }); } } else { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.union, }), parameters: [ @@ -1097,36 +170,36 @@ const schemaToZodSchema = ({ }); } } else { - zodSchema = schemaToZodSchema({ plugin, schema, state }); + ast = irSchemaToAst({ plugin, schema, state }); } } else { // catch-all fallback for failed schemas - const zSchema = schemaTypeToZodSchema({ + const zSchema = irSchemaWithTypeToAst({ plugin, schema: { type: 'unknown', }, state, }); - zodSchema.expression = zSchema.expression; - zodSchema.hasCircularReference = zSchema.hasCircularReference; - zodSchema.typeName = zSchema.anyType; + ast.expression = zSchema.expression; + ast.hasCircularReference = zSchema.hasCircularReference; + ast.typeName = zSchema.anyType; } - if (zodSchema.expression) { + if (ast.expression) { if (schema.accessScope === 'read') { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.readonly, }), }); } if (optional) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.optional, }), }); @@ -1139,9 +212,9 @@ const schemaToZodSchema = ({ value: schema.default, }); if (callParameter) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.default, }), parameters: [callParameter], @@ -1150,44 +223,46 @@ const schemaToZodSchema = ({ } } - if (zodSchema.hasCircularReference) { - if (!zodSchema.typeName) { - zodSchema.typeName = 'ZodTypeAny'; + if (ast.hasCircularReference) { + if (!ast.typeName) { + ast.typeName = 'ZodTypeAny'; } - } else if (zodSchema.typeName) { - zodSchema.typeName = undefined; + } else if (ast.typeName) { + ast.typeName = undefined; } - return zodSchema as ZodSchema; + return ast as Ast; }; const handleComponent = ({ id, plugin, schema, - state, -}: { + state: _state, +}: Omit & { id: string; - plugin: ZodPlugin['Instance']; schema: IR.SchemaObject; - state?: State; + state?: Partial; }): void => { - if (!state) { - state = { - circularReferenceTracker: [id], - currentReferenceTracker: [id], - hasCircularReference: false, - }; - } + const state: IrSchemaToAstOptions['state'] = { + _path: _state?._path ?? [], + circularReferenceTracker: _state?.circularReferenceTracker ?? [id], + currentReferenceTracker: _state?.currentReferenceTracker ?? [id], + hasCircularReference: _state?.hasCircularReference ?? false, + }; const selector = plugin.api.getSelector('ref', id); let symbol = plugin.getSymbol(selector); if (symbol) return; - const zodSchema = schemaToZodSchema({ plugin, schema, state }); + const ast = irSchemaToAst({ plugin, schema, state }); const baseName = refToName(id); + const resourceType = pathToSymbolResourceType(state._path); symbol = plugin.registerSymbol({ exported: true, + meta: { + resourceType, + }, name: buildName({ config: plugin.config.definitions, name: baseName, @@ -1199,6 +274,7 @@ const handleComponent = ({ exported: true, meta: { kind: 'type', + resourceType, }, name: buildName({ config: plugin.config.definitions.types.infer, @@ -1207,12 +283,12 @@ const handleComponent = ({ selector: plugin.api.getSelector('type-infer-ref', id), }) : undefined; - exportZodSchema({ + exportAst({ + ast, plugin, schema, symbol, typeInferSymbol, - zodSchema, }); }; @@ -1220,7 +296,7 @@ export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { plugin.registerSymbol({ external: getZodModule({ plugin }), name: 'z', - selector: plugin.api.getSelector('import', 'zod'), + selector: plugin.api.getSelector('external', 'zod.z'), }); plugin.forEach( @@ -1232,17 +308,21 @@ export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { (event) => { switch (event.type) { case 'operation': - operationToZodSchema({ - getZodSchema: (schema) => { - const state: State = { + irOperationToAst({ + getAst: (schema, path) => { + const state: IrSchemaToAstOptions['state'] = { + _path: path, circularReferenceTracker: [], currentReferenceTracker: [], hasCircularReference: false, }; - return schemaToZodSchema({ plugin, schema, state }); + return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, + state: { + _path: event._path, + }, }); break; case 'parameter': @@ -1250,6 +330,9 @@ export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.parameter.schema, + state: { + _path: event._path, + }, }); break; case 'requestBody': @@ -1257,6 +340,9 @@ export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.requestBody.schema, + state: { + _path: event._path, + }, }); break; case 'schema': @@ -1264,20 +350,27 @@ export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.schema, + state: { + _path: event._path, + }, }); break; case 'webhook': - webhookToZodSchema({ - getZodSchema: (schema) => { - const state: State = { + irWebhookToAst({ + getAst: (schema, path) => { + const state: IrSchemaToAstOptions['state'] = { + _path: path, circularReferenceTracker: [], currentReferenceTracker: [], hasCircularReference: false, }; - return schemaToZodSchema({ plugin, schema, state }); + return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, + state: { + _path: event._path, + }, }); break; } diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts new file mode 100644 index 000000000..2d8b4e1d1 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts @@ -0,0 +1,160 @@ +import type ts from 'typescript'; + +import { deduplicateSchema } from '../../../../ir/schema'; +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; +import { unknownToAst } from './unknown'; + +export const arrayToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'array'>; +}): Omit & { + anyType?: string; +} => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const functionName = tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.array, + }); + + let arrayExpression: ts.CallExpression | undefined; + let hasCircularReference = false; + + if (!schema.items) { + arrayExpression = tsc.callExpression({ + functionName, + parameters: [ + unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }), + ], + }); + } else { + schema = deduplicateSchema({ schema }); + + // at least one item is guaranteed + const itemExpressions = schema.items!.map((item, index) => { + const zodSchema = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + if (zodSchema.hasCircularReference) { + hasCircularReference = true; + } + return zodSchema.expression; + }); + + if (itemExpressions.length === 1) { + arrayExpression = tsc.callExpression({ + functionName, + parameters: itemExpressions, + }); + } else { + if (schema.logicalOperator === 'and') { + const firstSchema = schema.items![0]!; + // we want to add an intersection, but not every schema can use the same API. + // if the first item contains another array or not an object, we cannot use + // `.and()` as that does not exist on `.union()` and non-object schemas. + let intersectionExpression: ts.Expression; + if ( + firstSchema.logicalOperator === 'or' || + (firstSchema.type && firstSchema.type !== 'object') + ) { + intersectionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.intersection, + }), + parameters: itemExpressions, + }); + } else { + intersectionExpression = itemExpressions[0]!; + for (let i = 1; i < itemExpressions.length; i++) { + intersectionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: intersectionExpression, + name: identifiers.and, + }), + parameters: [itemExpressions[i]!], + }); + } + } + + arrayExpression = tsc.callExpression({ + functionName, + parameters: [intersectionExpression], + }); + } else { + arrayExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.array, + }), + parameters: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: itemExpressions, + }), + ], + }), + ], + }); + } + } + } + + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { + arrayExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: arrayExpression, + name: identifiers.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }); + } else { + if (schema.minItems !== undefined) { + arrayExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: arrayExpression, + name: identifiers.min, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }); + } + + if (schema.maxItems !== undefined) { + arrayExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: arrayExpression, + name: identifiers.max, + }), + parameters: [tsc.valueToExpression({ value: schema.maxItems })], + }); + } + } + + return { + expression: arrayExpression, + hasCircularReference, + }; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts new file mode 100644 index 000000000..5285daab1 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts @@ -0,0 +1,32 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const booleanToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'boolean'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + if (typeof schema.const === 'boolean') { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.boolean(schema.const)], + }); + return expression; + } + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.boolean, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts new file mode 100644 index 000000000..c2c5ba649 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts @@ -0,0 +1,125 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; +import { unknownToAst } from './unknown'; + +export const enumToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'enum'>; +}): ts.CallExpression => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const enumMembers: Array = []; + const literalMembers: Array = []; + + let isNullable = false; + let allStrings = true; + + for (const item of schema.items ?? []) { + // Zod supports string, number, and boolean enums + if (item.type === 'string' && typeof item.const === 'string') { + const stringLiteral = tsc.stringLiteral({ + text: item.const, + }); + enumMembers.push(stringLiteral); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [stringLiteral], + }), + ); + } else if ( + (item.type === 'number' || item.type === 'integer') && + typeof item.const === 'number' + ) { + allStrings = false; + const numberLiteral = tsc.ots.number(item.const); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [numberLiteral], + }), + ); + } else if (item.type === 'boolean' && typeof item.const === 'boolean') { + allStrings = false; + const booleanLiteral = tsc.ots.boolean(item.const); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [booleanLiteral], + }), + ); + } else if (item.type === 'null' || item.const === null) { + isNullable = true; + } + } + + if (!literalMembers.length) { + return unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + } + + // Use z.enum() for pure string enums, z.union() for mixed or non-string types + let enumExpression: ts.CallExpression; + if (allStrings && enumMembers.length > 0) { + enumExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.enum, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: enumMembers, + multiLine: false, + }), + ], + }); + } else if (literalMembers.length === 1) { + // For single-member unions, use the member directly instead of wrapping in z.union() + enumExpression = literalMembers[0]!; + } else { + enumExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: literalMembers, + multiLine: literalMembers.length > 3, + }), + ], + }); + } + + if (isNullable) { + enumExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: enumExpression, + name: identifiers.nullable, + }), + }); + } + + return enumExpression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts new file mode 100644 index 000000000..50790d8c7 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts @@ -0,0 +1,105 @@ +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { arrayToAst } from './array'; +import { booleanToAst } from './boolean'; +import { enumToAst } from './enum'; +import { neverToAst } from './never'; +import { nullToAst } from './null'; +import { numberToAst } from './number'; +import { objectToAst } from './object'; +import { stringToAst } from './string'; +import { tupleToAst } from './tuple'; +import { undefinedToAst } from './undefined'; +import { unknownToAst } from './unknown'; +import { voidToAst } from './void'; + +export const irSchemaWithTypeToAst = ({ + schema, + ...args +}: IrSchemaToAstOptions & { + schema: SchemaWithType; +}): Omit & { + anyType?: string; +} => { + switch (schema.type) { + case 'array': + return arrayToAst({ + ...args, + schema: schema as SchemaWithType<'array'>, + }); + case 'boolean': + return { + expression: booleanToAst({ + ...args, + schema: schema as SchemaWithType<'boolean'>, + }), + }; + case 'enum': + return { + expression: enumToAst({ + ...args, + schema: schema as SchemaWithType<'enum'>, + }), + }; + case 'integer': + case 'number': + return { + expression: numberToAst({ + ...args, + schema: schema as SchemaWithType<'integer' | 'number'>, + }), + }; + case 'never': + return { + expression: neverToAst({ + ...args, + schema: schema as SchemaWithType<'never'>, + }), + }; + case 'null': + return { + expression: nullToAst({ + ...args, + schema: schema as SchemaWithType<'null'>, + }), + }; + case 'object': + return objectToAst({ + ...args, + schema: schema as SchemaWithType<'object'>, + }); + case 'string': + return { + expression: stringToAst({ + ...args, + schema: schema as SchemaWithType<'string'>, + }), + }; + case 'tuple': + return tupleToAst({ + ...args, + schema: schema as SchemaWithType<'tuple'>, + }); + case 'undefined': + return { + expression: undefinedToAst({ + ...args, + schema: schema as SchemaWithType<'undefined'>, + }), + }; + case 'unknown': + return { + expression: unknownToAst({ + ...args, + schema: schema as SchemaWithType<'unknown'>, + }), + }; + case 'void': + return { + expression: voidToAst({ + ...args, + schema: schema as SchemaWithType<'void'>, + }), + }; + } +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts new file mode 100644 index 000000000..1394dbfc5 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts @@ -0,0 +1,19 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const neverToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'never'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.never, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts new file mode 100644 index 000000000..26d1ca5df --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts @@ -0,0 +1,19 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const nullToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'null'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.null, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts new file mode 100644 index 000000000..125a0b2ed --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts @@ -0,0 +1,94 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import { numberParameter } from '../../shared/numbers'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const numberToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'integer' | 'number'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const isBigInt = schema.type === 'integer' && schema.format === 'int64'; + + if (typeof schema.const === 'number') { + // TODO: parser - handle bigint constants + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.number(schema.const)], + }); + return expression; + } + + let numberExpression = tsc.callExpression({ + functionName: isBigInt + ? tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.coerce, + }), + name: identifiers.bigint, + }) + : tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.number, + }), + }); + + if (!isBigInt && schema.type === 'integer') { + numberExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: numberExpression, + name: identifiers.int, + }), + }); + } + + if (schema.exclusiveMinimum !== undefined) { + numberExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: numberExpression, + name: identifiers.gt, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMinimum }), + ], + }); + } else if (schema.minimum !== undefined) { + numberExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: numberExpression, + name: identifiers.gte, + }), + parameters: [numberParameter({ isBigInt, value: schema.minimum })], + }); + } + + if (schema.exclusiveMaximum !== undefined) { + numberExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: numberExpression, + name: identifiers.lt, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMaximum }), + ], + }); + } else if (schema.maximum !== undefined) { + numberExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: numberExpression, + name: identifiers.lte, + }), + parameters: [numberParameter({ isBigInt, value: schema.maximum })], + }); + } + + return numberExpression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts new file mode 100644 index 000000000..d25b92d24 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts @@ -0,0 +1,111 @@ +import ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import { numberRegExp } from '../../../../utils/regexp'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; + +export const objectToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'object'>; +}): Omit & { + anyType?: string; +} => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + let hasCircularReference = false; + + // TODO: parser - handle constants + const properties: Array = []; + + const required = schema.required ?? []; + + for (const name in schema.properties) { + const property = schema.properties[name]!; + const isRequired = required.includes(name); + + const propertyExpression = irSchemaToAst({ + optional: !isRequired, + plugin, + schema: property, + state: { + ...state, + _path: [...state._path, 'properties', name], + }, + }); + + if (propertyExpression.hasCircularReference) { + hasCircularReference = true; + } + + numberRegExp.lastIndex = 0; + let propertyName; + if (numberRegExp.test(name)) { + // For numeric literals, we'll handle negative numbers by using a string literal + // instead of trying to use a PrefixUnaryExpression + propertyName = name.startsWith('-') + ? ts.factory.createStringLiteral(name) + : ts.factory.createNumericLiteral(name); + } else { + propertyName = name; + } + // TODO: parser - abstract safe property name logic + if ( + ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && + !name.startsWith("'") && + !name.endsWith("'") + ) { + propertyName = `'${name}'`; + } + properties.push( + tsc.propertyAssignment({ + initializer: propertyExpression.expression, + name: propertyName, + }), + ); + } + + if ( + schema.additionalProperties && + (!schema.properties || !Object.keys(schema.properties).length) + ) { + const zodSchema = irSchemaToAst({ + plugin, + schema: schema.additionalProperties, + state: { + ...state, + _path: [...state._path, 'additionalProperties'], + }, + }); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.record, + }), + parameters: [zodSchema.expression], + }); + return { + anyType: 'AnyZodObject', + expression, + hasCircularReference: zodSchema.hasCircularReference, + }; + } + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.object, + }), + parameters: [ts.factory.createObjectLiteralExpression(properties, true)], + }); + return { + anyType: 'AnyZodObject', + expression, + hasCircularReference, + }; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts new file mode 100644 index 000000000..ca375b9c1 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts @@ -0,0 +1,152 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const stringToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'string'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + if (typeof schema.const === 'string') { + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.string(schema.const)], + }); + return expression; + } + + let stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.string, + }), + }); + + const dateTimeOptions: { key: string; value: boolean }[] = []; + + if (plugin.config.dates.offset) { + dateTimeOptions.push({ key: 'offset', value: true }); + } + if (plugin.config.dates.local) { + dateTimeOptions.push({ key: 'local', value: true }); + } + + if (schema.format) { + switch (schema.format) { + case 'date': + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.date, + }), + }); + break; + case 'date-time': + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.datetime, + }), + parameters: + dateTimeOptions.length > 0 + ? [ + tsc.objectExpression({ + obj: dateTimeOptions, + }), + ] + : [], + }); + break; + case 'email': + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.email, + }), + }); + break; + case 'ipv4': + case 'ipv6': + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.ip, + }), + }); + break; + case 'time': + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.time, + }), + }); + break; + case 'uri': + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.url, + }), + }); + break; + case 'uuid': + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.uuid, + }), + }); + break; + } + } + + if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }); + } else { + if (schema.minLength !== undefined) { + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.min, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }); + } + + if (schema.maxLength !== undefined) { + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.max, + }), + parameters: [tsc.valueToExpression({ value: schema.maxLength })], + }); + } + } + + if (schema.pattern) { + stringExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: stringExpression, + name: identifiers.regex, + }), + parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], + }); + } + + return stringExpression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts new file mode 100644 index 000000000..a8edb0b4b --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts @@ -0,0 +1,83 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; + +export const tupleToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'tuple'>; +}): Omit & { + anyType?: string; +} => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + let hasCircularReference = false; + + if (schema.const && Array.isArray(schema.const)) { + const tupleElements = schema.const.map((value) => + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.valueToExpression({ value })], + }), + ); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + return { + expression, + hasCircularReference, + }; + } + + const tupleElements: Array = []; + + if (schema.items) { + schema.items.forEach((item, index) => { + const itemSchema = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + tupleElements.push(itemSchema.expression); + if (itemSchema.hasCircularReference) { + hasCircularReference = true; + } + }); + } + + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + return { + expression, + hasCircularReference, + }; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts new file mode 100644 index 000000000..1c7a7d19c --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts @@ -0,0 +1,19 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const undefinedToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'undefined'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.undefined, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts new file mode 100644 index 000000000..dc1ab5da3 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts @@ -0,0 +1,19 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const unknownToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'unknown'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.unknown, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts new file mode 100644 index 000000000..7da3f191f --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts @@ -0,0 +1,19 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { IrSchemaToAstOptions } from '../../shared/types'; + +export const voidToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'void'>; +}) => { + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.void, + }), + }); + return expression; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/api.ts b/packages/openapi-ts/src/plugins/zod/v4/api.ts new file mode 100644 index 000000000..cb59b1b06 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/api.ts @@ -0,0 +1,71 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../tsc'; +import { identifiers } from '../constants'; +import type { ValidatorArgs } from '../shared/types'; + +export const createRequestValidatorV4 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; + +export const createResponseValidatorV4 = ({ + operation, + plugin, +}: ValidatorArgs): ts.ArrowFunction | undefined => { + const symbol = plugin.getSymbol( + plugin.api.getSelector('responses', operation.id), + ); + if (!symbol) return; + + const dataParameterName = 'data'; + + return tsc.arrowFunction({ + async: true, + parameters: [ + { + name: dataParameterName, + }, + ], + statements: [ + tsc.returnStatement({ + expression: tsc.awaitExpression({ + expression: tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: symbol.placeholder, + name: identifiers.parseAsync, + }), + parameters: [tsc.identifier({ text: dataParameterName })], + }), + }), + }), + ], + }); +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts index 27ce9fe2b..6fb967bed 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts @@ -1,1010 +1,37 @@ -import ts from 'typescript'; - import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import { numberRegExp } from '../../../utils/regexp'; +import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import type { SchemaWithType } from '../../shared/types/schema'; +import { pathToSymbolResourceType } from '../../shared/utils/meta'; import { identifiers } from '../constants'; -import { exportZodSchema } from '../export'; +import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; -import { operationToZodSchema } from '../shared/operation'; -import type { SchemaWithType, State, ZodSchema } from '../shared/types'; -import { webhookToZodSchema } from '../shared/webhook'; +import { numberParameter } from '../shared/numbers'; +import { irOperationToAst } from '../shared/operation'; +import type { Ast, IrSchemaToAstOptions } from '../shared/types'; +import { irWebhookToAst } from '../shared/webhook'; import type { ZodPlugin } from '../types'; +import { irSchemaWithTypeToAst } from './toAst'; -const arrayTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'array'>; - state: State; -}): Omit => { - const result: Partial> = {}; - - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const functionName = tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.array, - }); - - if (!schema.items) { - result.expression = tsc.callExpression({ - functionName, - parameters: [ - unknownTypeToZodSchema({ - plugin, - schema: { - type: 'unknown', - }, - }).expression, - ], - }); - } else { - schema = deduplicateSchema({ schema }); - - // at least one item is guaranteed - const itemExpressions = schema.items!.map((item) => { - const zodSchema = schemaToZodSchema({ - plugin, - schema: item, - state, - }); - if (zodSchema.hasCircularReference) { - result.hasCircularReference = true; - } - return zodSchema.expression; - }); - - if (itemExpressions.length === 1) { - result.expression = tsc.callExpression({ - functionName, - parameters: itemExpressions, - }); - } else { - if (schema.logicalOperator === 'and') { - const firstSchema = schema.items![0]!; - // we want to add an intersection, but not every schema can use the same API. - // if the first item contains another array or not an object, we cannot use - // `.and()` as that does not exist on `.union()` and non-object schemas. - let intersectionExpression: ts.Expression; - if ( - firstSchema.logicalOperator === 'or' || - (firstSchema.type && firstSchema.type !== 'object') - ) { - intersectionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.intersection, - }), - parameters: itemExpressions, - }); - } else { - intersectionExpression = itemExpressions[0]!; - for (let i = 1; i < itemExpressions.length; i++) { - intersectionExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: intersectionExpression, - name: identifiers.and, - }), - parameters: [itemExpressions[i]!], - }); - } - } - - result.expression = tsc.callExpression({ - functionName, - parameters: [intersectionExpression], - }); - } else { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.array, - }), - parameters: [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: itemExpressions, - }), - ], - }), - ], - }); - } - } - } - - if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }); - } else { - if (schema.minItems !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.min, - }), - parameters: [tsc.valueToExpression({ value: schema.minItems })], - }); - } - - if (schema.maxItems !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.max, - }), - parameters: [tsc.valueToExpression({ value: schema.maxItems })], - }); - } - } - - return result as Omit; -}; - -const booleanTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'boolean'>; -}): Omit => { - const result: Partial> = {}; - - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - if (typeof schema.const === 'boolean') { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.boolean(schema.const)], - }); - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.boolean, - }), - }); - return result as Omit; -}; - -const enumTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'enum'>; -}): Omit => { - const result: Partial> = {}; - - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - const enumMembers: Array = []; - const literalMembers: Array = []; - - let isNullable = false; - let allStrings = true; - - for (const item of schema.items ?? []) { - // Zod supports string, number, and boolean enums - if (item.type === 'string' && typeof item.const === 'string') { - const stringLiteral = tsc.stringLiteral({ - text: item.const, - }); - enumMembers.push(stringLiteral); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [stringLiteral], - }), - ); - } else if ( - (item.type === 'number' || item.type === 'integer') && - typeof item.const === 'number' - ) { - allStrings = false; - const numberLiteral = tsc.ots.number(item.const); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [numberLiteral], - }), - ); - } else if (item.type === 'boolean' && typeof item.const === 'boolean') { - allStrings = false; - const booleanLiteral = tsc.ots.boolean(item.const); - literalMembers.push( - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [booleanLiteral], - }), - ); - } else if (item.type === 'null' || item.const === null) { - isNullable = true; - } - } - - if (!literalMembers.length) { - return unknownTypeToZodSchema({ - plugin, - schema: { - type: 'unknown', - }, - }); - } - - // Use z.enum() for pure string enums, z.union() for mixed or non-string types - if (allStrings && enumMembers.length > 0) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.enum, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: enumMembers, - multiLine: false, - }), - ], - }); - } else if (literalMembers.length === 1) { - // For single-member unions, use the member directly instead of wrapping in z.union() - result.expression = literalMembers[0]; - } else { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.union, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: literalMembers, - multiLine: literalMembers.length > 3, - }), - ], - }); - } - - if (isNullable) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.nullable, - }), - parameters: [result.expression], - }); - } - - return result as Omit; -}; - -const neverTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'never'>; -}): Omit => { - const result: Partial> = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.never, - }), - }); - return result as Omit; -}; - -const nullTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'null'>; -}): Omit => { - const result: Partial> = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.null, - }), - }); - return result as Omit; -}; - -const numberParameter = ({ - isBigInt, - value, -}: { - isBigInt: boolean; - value: unknown; -}): ts.Expression | undefined => { - const expression = tsc.valueToExpression({ value }); - - if ( - isBigInt && - (typeof value === 'bigint' || - typeof value === 'number' || - typeof value === 'string' || - typeof value === 'boolean') - ) { - return tsc.callExpression({ - functionName: 'BigInt', - parameters: [expression], - }); - } - - return expression; -}; - -const numberTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'integer' | 'number'>; -}): Omit => { - const result: Partial> = {}; - - const isBigInt = schema.type === 'integer' && schema.format === 'int64'; - - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - if (typeof schema.const === 'number') { - // TODO: parser - handle bigint constants - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.number(schema.const)], - }); - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: isBigInt - ? tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.coerce, - }), - name: identifiers.bigint, - }) - : tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.number, - }), - }); - - if (!isBigInt && schema.type === 'integer') { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.int, - }), - }); - } - - if (schema.exclusiveMinimum !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.gt, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMinimum }), - ], - }); - } else if (schema.minimum !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.gte, - }), - parameters: [numberParameter({ isBigInt, value: schema.minimum })], - }); - } - - if (schema.exclusiveMaximum !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.lt, - }), - parameters: [ - numberParameter({ isBigInt, value: schema.exclusiveMaximum }), - ], - }); - } else if (schema.maximum !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.lte, - }), - parameters: [numberParameter({ isBigInt, value: schema.maximum })], - }); - } - - return result as Omit; -}; - -const objectTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'object'>; - state: State; -}): Omit => { - const result: Partial> = {}; - - // TODO: parser - handle constants - const properties: Array = - []; - - const required = schema.required ?? []; - - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - for (const name in schema.properties) { - const property = schema.properties[name]!; - const isRequired = required.includes(name); - - const propertySchema = schemaToZodSchema({ - optional: !isRequired, - plugin, - schema: property, - state, - }); - if (propertySchema.hasCircularReference) { - result.hasCircularReference = true; - } - - numberRegExp.lastIndex = 0; - let propertyName; - if (numberRegExp.test(name)) { - // For numeric literals, we'll handle negative numbers by using a string literal - // instead of trying to use a PrefixUnaryExpression - propertyName = name.startsWith('-') - ? ts.factory.createStringLiteral(name) - : ts.factory.createNumericLiteral(name); - } else { - propertyName = name; - } - // TODO: parser - abstract safe property name logic - if ( - ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && - !name.startsWith("'") && - !name.endsWith("'") - ) { - propertyName = `'${name}'`; - } - - if (propertySchema.hasCircularReference) { - properties.push( - tsc.getAccessorDeclaration({ - name: propertyName, - // @ts-expect-error - returnType: propertySchema.typeName - ? tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: propertySchema.typeName, - }) - : undefined, - statements: [ - tsc.returnStatement({ - expression: propertySchema.expression, - }), - ], - }), - ); - } else { - properties.push( - tsc.propertyAssignment({ - initializer: propertySchema.expression, - name: propertyName, - }), - ); - } - } - - if ( - schema.additionalProperties && - (!schema.properties || !Object.keys(schema.properties).length) - ) { - const zodSchema = schemaToZodSchema({ - plugin, - schema: schema.additionalProperties, - state, - }); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.record, - }), - parameters: [ - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.string, - }), - parameters: [], - }), - zodSchema.expression, - ], - }); - if (zodSchema.hasCircularReference) { - result.hasCircularReference = true; - } - - // Return with typeName for circular references - if (result.hasCircularReference) { - return { - ...result, - typeName: 'ZodType', - } as ZodSchema; - } - - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.object, - }), - parameters: [ts.factory.createObjectLiteralExpression(properties, true)], - }); - - // Return with typeName for circular references (AnyZodObject doesn't exist in Zod v4, use ZodType) - if (result.hasCircularReference) { - return { - ...result, - typeName: 'ZodType', - } as ZodSchema; - } - - return result as Omit; -}; - -const stringTypeToZodSchema = ({ - plugin, - schema, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'string'>; -}): Omit => { - const result: Partial> = {}; - - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - if (typeof schema.const === 'string') { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.ots.string(schema.const)], - }); - return result as Omit; - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.string, - }), - }); - - const dateTimeOptions: { key: string; value: boolean }[] = []; - - if (plugin.config.dates.offset) { - dateTimeOptions.push({ key: 'offset', value: true }); - } - if (plugin.config.dates.local) { - dateTimeOptions.push({ key: 'local', value: true }); - } - - if (schema.format) { - switch (schema.format) { - case 'date': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.iso, - }), - name: identifiers.date, - }), - }); - break; - case 'date-time': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.iso, - }), - name: identifiers.datetime, - }), - parameters: - dateTimeOptions.length > 0 - ? [ - tsc.objectExpression({ - obj: dateTimeOptions, - }), - ] - : [], - }); - break; - case 'email': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.email, - }), - }); - break; - case 'ipv4': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.ipv4, - }), - }); - break; - case 'ipv6': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.ipv6, - }), - }); - break; - case 'time': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.iso, - }), - name: identifiers.time, - }), - }); - break; - case 'uri': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.url, - }), - }); - break; - case 'uuid': - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.uuid, - }), - }); - break; - } - } - - if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.length, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }); - } else { - if (schema.minLength !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.min, - }), - parameters: [tsc.valueToExpression({ value: schema.minLength })], - }); - } - - if (schema.maxLength !== undefined) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.max, - }), - parameters: [tsc.valueToExpression({ value: schema.maxLength })], - }); - } - } - - if (schema.pattern) { - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: result.expression, - name: identifiers.regex, - }), - parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], - }); - } - - return result as Omit; -}; - -const tupleTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'tuple'>; - state: State; -}): Omit => { - const result: Partial> = {}; - - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - - if (schema.const && Array.isArray(schema.const)) { - const tupleElements = schema.const.map((value) => - tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.literal, - }), - parameters: [tsc.valueToExpression({ value })], - }), - ); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - return result as Omit; - } - - const tupleElements: Array = []; - - for (const item of schema.items ?? []) { - const itemSchema = schemaToZodSchema({ - plugin, - schema: item, - state, - }); - tupleElements.push(itemSchema.expression); - - if (itemSchema.hasCircularReference) { - result.hasCircularReference = true; - } - } - - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.tuple, - }), - parameters: [ - tsc.arrayLiteralExpression({ - elements: tupleElements, - }), - ], - }); - - return result as Omit; -}; - -const undefinedTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'undefined'>; -}): Omit => { - const result: Partial> = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.undefined, - }), - }); - return result as Omit; -}; - -const unknownTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'unknown'>; -}): Omit => { - const result: Partial> = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.unknown, - }), - }); - return result as Omit; -}; - -const voidTypeToZodSchema = ({ - plugin, -}: { - plugin: ZodPlugin['Instance']; - schema: SchemaWithType<'void'>; -}): Omit => { - const result: Partial> = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); - result.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, - name: identifiers.void, - }), - }); - return result as Omit; -}; - -const schemaTypeToZodSchema = ({ - plugin, - schema, - state, -}: { - plugin: ZodPlugin['Instance']; - schema: IR.SchemaObject; - state: State; -}): Omit => { - switch (schema.type as Required['type']) { - case 'array': - return arrayTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'array'>, - state, - }); - case 'boolean': - return booleanTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'boolean'>, - }); - case 'enum': - return enumTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'enum'>, - }); - case 'integer': - case 'number': - return numberTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'integer' | 'number'>, - }); - case 'never': - return neverTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'never'>, - }); - case 'null': - return nullTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'null'>, - }); - case 'object': - return objectTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'object'>, - state, - }); - case 'string': - return stringTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'string'>, - }); - case 'tuple': - return tupleTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'tuple'>, - state, - }); - case 'undefined': - return undefinedTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'undefined'>, - }); - case 'unknown': - return unknownTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'unknown'>, - }); - case 'void': - return voidTypeToZodSchema({ - plugin, - schema: schema as SchemaWithType<'void'>, - }); - } -}; - -const schemaToZodSchema = ({ +export const irSchemaToAst = ({ optional, plugin, schema, state, -}: { +}: IrSchemaToAstOptions & { /** * Accept `optional` to handle optional object properties. We can't handle * this inside the object function because `.optional()` must come before * `.default()` which is handled in this function. */ optional?: boolean; - plugin: ZodPlugin['Instance']; schema: IR.SchemaObject; - state: State; -}): ZodSchema => { - let zodSchema: Partial = {}; +}): Ast => { + let ast: Partial = {}; - const zSymbol = plugin.referenceSymbol( - plugin.api.getSelector('import', 'zod'), - ); + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); if (schema.$ref) { const isCircularReference = state.circularReferenceTracker.includes( @@ -1023,9 +50,9 @@ const schemaToZodSchema = ({ } if (isSelfReference) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.lazy, }), parameters: [ @@ -1040,9 +67,9 @@ const schemaToZodSchema = ({ ], }); } else { - zodSchema.expression = tsc.identifier({ text: symbol.placeholder }); + ast.expression = tsc.identifier({ text: symbol.placeholder }); } - zodSchema.hasCircularReference = schema.circular; + ast.hasCircularReference = schema.circular; } else { if (!symbol) { // if $ref hasn't been processed yet, inline it to avoid the @@ -1053,32 +80,39 @@ const schemaToZodSchema = ({ id: schema.$ref, plugin, schema: ref, - state, + state: { + ...state, + _path: jsonPointerToPath(schema.$ref), + }, }); } else { - zodSchema.hasCircularReference = schema.circular; + ast.hasCircularReference = schema.circular; } const refSymbol = plugin.referenceSymbol(selector); - zodSchema.expression = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = tsc.identifier({ text: refSymbol.placeholder }); } state.circularReferenceTracker.pop(); state.currentReferenceTracker.pop(); } else if (schema.type) { - const zSchema = schemaTypeToZodSchema({ plugin, schema, state }); - zodSchema.expression = zSchema.expression; - zodSchema.hasCircularReference = zSchema.hasCircularReference; + const zSchema = irSchemaWithTypeToAst({ + plugin, + schema: schema as SchemaWithType, + state, + }); + ast.expression = zSchema.expression; + ast.hasCircularReference = zSchema.hasCircularReference; if (plugin.config.metadata && schema.description) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.register, }), parameters: [ tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.globalRegistry, }), tsc.objectExpression({ @@ -1096,11 +130,14 @@ const schemaToZodSchema = ({ schema = deduplicateSchema({ schema }); if (schema.items) { - const itemSchemas = schema.items.map((item) => - schemaToZodSchema({ + const itemSchemas = schema.items.map((item, index) => + irSchemaToAst({ plugin, schema: item, - state, + state: { + ...state, + _path: [...state._path, 'items', index], + }, }), ); @@ -1113,26 +150,26 @@ const schemaToZodSchema = ({ firstSchema.logicalOperator === 'or' || (firstSchema.type && firstSchema.type !== 'object') ) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.intersection, }), parameters: itemSchemas.map((schema) => schema.expression), }); } else { - zodSchema.expression = itemSchemas[0]!.expression; + ast.expression = itemSchemas[0]!.expression; itemSchemas.slice(1).forEach((schema) => { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression!, + expression: ast.expression!, name: identifiers.and, }), parameters: [ schema.hasCircularReference ? tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.lazy, }), parameters: [ @@ -1151,9 +188,9 @@ const schemaToZodSchema = ({ }); } } else { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.union, }), parameters: [ @@ -1164,39 +201,43 @@ const schemaToZodSchema = ({ }); } } else { - zodSchema = schemaToZodSchema({ plugin, schema, state }); + ast = irSchemaToAst({ + plugin, + schema, + state, + }); } } else { // catch-all fallback for failed schemas - const zSchema = schemaTypeToZodSchema({ + const zSchema = irSchemaWithTypeToAst({ plugin, schema: { type: 'unknown', }, state, }); - zodSchema.expression = zSchema.expression; + ast.expression = zSchema.expression; } - if (zodSchema.expression) { + if (ast.expression) { if (schema.accessScope === 'read') { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.readonly, }), }); } if (optional) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zSymbol.placeholder, + expression: z.placeholder, name: identifiers.optional, }), - parameters: [zodSchema.expression], + parameters: [ast.expression], }); - zodSchema.typeName = identifiers.ZodOptional; + ast.typeName = identifiers.ZodOptional; } if (schema.default !== undefined) { @@ -1206,9 +247,9 @@ const schemaToZodSchema = ({ value: schema.default, }); if (callParameter) { - zodSchema.expression = tsc.callExpression({ + ast.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ - expression: zodSchema.expression, + expression: ast.expression, name: identifiers.default, }), parameters: [callParameter], @@ -1217,7 +258,7 @@ const schemaToZodSchema = ({ } } - return zodSchema as ZodSchema; + return ast as Ast; }; const handleComponent = ({ @@ -1225,27 +266,30 @@ const handleComponent = ({ plugin, schema, state: _state, -}: { +}: Omit & { id: string; - plugin: ZodPlugin['Instance']; schema: IR.SchemaObject; - state?: Omit; + state?: Partial; }): void => { - const state: State = { - circularReferenceTracker: [id], - hasCircularReference: false, - ..._state, - currentReferenceTracker: [id], + const state: IrSchemaToAstOptions['state'] = { + _path: _state?._path ?? [], + circularReferenceTracker: _state?.circularReferenceTracker ?? [id], + currentReferenceTracker: _state?.currentReferenceTracker ?? [id], + hasCircularReference: _state?.hasCircularReference ?? false, }; const selector = plugin.api.getSelector('ref', id); let symbol = plugin.getSymbol(selector); if (symbol && !plugin.getSymbolValue(symbol)) return; - const zodSchema = schemaToZodSchema({ plugin, schema, state }); + const ast = irSchemaToAst({ plugin, schema, state }); const baseName = refToName(id); + const resourceType = pathToSymbolResourceType(state._path); symbol = plugin.registerSymbol({ exported: true, + meta: { + resourceType, + }, name: buildName({ config: plugin.config.definitions, name: baseName, @@ -1257,6 +301,7 @@ const handleComponent = ({ exported: true, meta: { kind: 'type', + resourceType, }, name: buildName({ config: plugin.config.definitions.types.infer, @@ -1265,12 +310,12 @@ const handleComponent = ({ selector: plugin.api.getSelector('type-infer-ref', id), }) : undefined; - exportZodSchema({ + exportAst({ + ast, plugin, schema, symbol, typeInferSymbol, - zodSchema, }); }; @@ -1278,7 +323,7 @@ export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { plugin.registerSymbol({ external: getZodModule({ plugin }), name: 'z', - selector: plugin.api.getSelector('import', 'zod'), + selector: plugin.api.getSelector('external', 'zod.z'), }); plugin.forEach( @@ -1290,17 +335,21 @@ export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { (event) => { switch (event.type) { case 'operation': - operationToZodSchema({ - getZodSchema: (schema) => { - const state: State = { + irOperationToAst({ + getAst: (schema, path) => { + const state: IrSchemaToAstOptions['state'] = { + _path: path, circularReferenceTracker: [], currentReferenceTracker: [], hasCircularReference: false, }; - return schemaToZodSchema({ plugin, schema, state }); + return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, + state: { + _path: event._path, + }, }); break; case 'parameter': @@ -1308,6 +357,9 @@ export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.parameter.schema, + state: { + _path: event._path, + }, }); break; case 'requestBody': @@ -1315,6 +367,9 @@ export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.requestBody.schema, + state: { + _path: event._path, + }, }); break; case 'schema': @@ -1322,20 +377,27 @@ export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { id: event.$ref, plugin, schema: event.schema, + state: { + _path: event._path, + }, }); break; case 'webhook': - webhookToZodSchema({ - getZodSchema: (schema) => { - const state: State = { + irWebhookToAst({ + getAst: (schema, path) => { + const state: IrSchemaToAstOptions['state'] = { + _path: path, circularReferenceTracker: [], currentReferenceTracker: [], hasCircularReference: false, }; - return schemaToZodSchema({ plugin, schema, state }); + return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, + state: { + _path: event._path, + }, }); break; } diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts new file mode 100644 index 000000000..a10d9f76a --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts @@ -0,0 +1,154 @@ +import type ts from 'typescript'; + +import { deduplicateSchema } from '../../../../ir/schema'; +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; +import { unknownToAst } from './unknown'; + +export const arrayToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'array'>; +}): Omit => { + const result: Partial> = {}; + + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const functionName = tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.array, + }); + + if (!schema.items) { + result.expression = tsc.callExpression({ + functionName, + parameters: [ + unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }).expression, + ], + }); + } else { + schema = deduplicateSchema({ schema }); + + // at least one item is guaranteed + const itemExpressions = schema.items!.map((item, index) => { + const zodSchema = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + if (zodSchema.hasCircularReference) { + result.hasCircularReference = true; + } + return zodSchema.expression; + }); + + if (itemExpressions.length === 1) { + result.expression = tsc.callExpression({ + functionName, + parameters: itemExpressions, + }); + } else { + if (schema.logicalOperator === 'and') { + const firstSchema = schema.items![0]!; + // we want to add an intersection, but not every schema can use the same API. + // if the first item contains another array or not an object, we cannot use + // `.and()` as that does not exist on `.union()` and non-object schemas. + let intersectionExpression: ts.Expression; + if ( + firstSchema.logicalOperator === 'or' || + (firstSchema.type && firstSchema.type !== 'object') + ) { + intersectionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.intersection, + }), + parameters: itemExpressions, + }); + } else { + intersectionExpression = itemExpressions[0]!; + for (let i = 1; i < itemExpressions.length; i++) { + intersectionExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: intersectionExpression, + name: identifiers.and, + }), + parameters: [itemExpressions[i]!], + }); + } + } + + result.expression = tsc.callExpression({ + functionName, + parameters: [intersectionExpression], + }); + } else { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.array, + }), + parameters: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: itemExpressions, + }), + ], + }), + ], + }); + } + } + } + + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }); + } else { + if (schema.minItems !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.min, + }), + parameters: [tsc.valueToExpression({ value: schema.minItems })], + }); + } + + if (schema.maxItems !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.max, + }), + parameters: [tsc.valueToExpression({ value: schema.maxItems })], + }); + } + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts new file mode 100644 index 000000000..88fc167e3 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts @@ -0,0 +1,34 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const booleanToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'boolean'>; +}): Omit => { + const result: Partial> = {}; + + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + if (typeof schema.const === 'boolean') { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.boolean(schema.const)], + }); + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.boolean, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts new file mode 100644 index 000000000..8ac9fd311 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts @@ -0,0 +1,127 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { unknownToAst } from './unknown'; + +export const enumToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'enum'>; +}): Omit => { + const result: Partial> = {}; + + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + const enumMembers: Array = []; + const literalMembers: Array = []; + + let isNullable = false; + let allStrings = true; + + for (const item of schema.items ?? []) { + // Zod supports string, number, and boolean enums + if (item.type === 'string' && typeof item.const === 'string') { + const stringLiteral = tsc.stringLiteral({ + text: item.const, + }); + enumMembers.push(stringLiteral); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [stringLiteral], + }), + ); + } else if ( + (item.type === 'number' || item.type === 'integer') && + typeof item.const === 'number' + ) { + allStrings = false; + const numberLiteral = tsc.ots.number(item.const); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [numberLiteral], + }), + ); + } else if (item.type === 'boolean' && typeof item.const === 'boolean') { + allStrings = false; + const booleanLiteral = tsc.ots.boolean(item.const); + literalMembers.push( + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [booleanLiteral], + }), + ); + } else if (item.type === 'null' || item.const === null) { + isNullable = true; + } + } + + if (!literalMembers.length) { + return unknownToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + } + + // Use z.enum() for pure string enums, z.union() for mixed or non-string types + if (allStrings && enumMembers.length > 0) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.enum, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: enumMembers, + multiLine: false, + }), + ], + }); + } else if (literalMembers.length === 1) { + // For single-member unions, use the member directly instead of wrapping in z.union() + result.expression = literalMembers[0]; + } else { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.union, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: literalMembers, + multiLine: literalMembers.length > 3, + }), + ], + }); + } + + if (isNullable) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.nullable, + }), + parameters: [result.expression], + }); + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts new file mode 100644 index 000000000..62c481bfd --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts @@ -0,0 +1,85 @@ +import type { SchemaWithType } from '../../../shared/types/schema'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { arrayToAst } from './array'; +import { booleanToAst } from './boolean'; +import { enumToAst } from './enum'; +import { neverToAst } from './never'; +import { nullToAst } from './null'; +import { numberToAst } from './number'; +import { objectToAst } from './object'; +import { stringToAst } from './string'; +import { tupleToAst } from './tuple'; +import { undefinedToAst } from './undefined'; +import { unknownToAst } from './unknown'; +import { voidToAst } from './void'; + +export const irSchemaWithTypeToAst = ({ + schema, + ...args +}: IrSchemaToAstOptions & { + schema: SchemaWithType; +}): Omit => { + switch (schema.type) { + case 'array': + return arrayToAst({ + ...args, + schema: schema as SchemaWithType<'array'>, + }); + case 'boolean': + return booleanToAst({ + ...args, + schema: schema as SchemaWithType<'boolean'>, + }); + case 'enum': + return enumToAst({ + ...args, + schema: schema as SchemaWithType<'enum'>, + }); + case 'integer': + case 'number': + return numberToAst({ + ...args, + schema: schema as SchemaWithType<'integer' | 'number'>, + }); + case 'never': + return neverToAst({ + ...args, + schema: schema as SchemaWithType<'never'>, + }); + case 'null': + return nullToAst({ + ...args, + schema: schema as SchemaWithType<'null'>, + }); + case 'object': + return objectToAst({ + ...args, + schema: schema as SchemaWithType<'object'>, + }); + case 'string': + return stringToAst({ + ...args, + schema: schema as SchemaWithType<'string'>, + }); + case 'tuple': + return tupleToAst({ + ...args, + schema: schema as SchemaWithType<'tuple'>, + }); + case 'undefined': + return undefinedToAst({ + ...args, + schema: schema as SchemaWithType<'undefined'>, + }); + case 'unknown': + return unknownToAst({ + ...args, + schema: schema as SchemaWithType<'unknown'>, + }); + case 'void': + return voidToAst({ + ...args, + schema: schema as SchemaWithType<'void'>, + }); + } +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts new file mode 100644 index 000000000..971cea475 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const neverToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'never'>; +}): Omit => { + const result: Partial> = {}; + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.never, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts new file mode 100644 index 000000000..ea1ef65db --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const nullToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'null'>; +}): Omit => { + const result: Partial> = {}; + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.null, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts new file mode 100644 index 000000000..f8ff11779 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts @@ -0,0 +1,96 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import { numberParameter } from '../../shared/numbers'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const numberToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'integer' | 'number'>; +}): Omit => { + const result: Partial> = {}; + + const isBigInt = schema.type === 'integer' && schema.format === 'int64'; + + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + if (typeof schema.const === 'number') { + // TODO: parser - handle bigint constants + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.number(schema.const)], + }); + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: isBigInt + ? tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.coerce, + }), + name: identifiers.bigint, + }) + : tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.number, + }), + }); + + if (!isBigInt && schema.type === 'integer') { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.int, + }), + }); + } + + if (schema.exclusiveMinimum !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.gt, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMinimum }), + ], + }); + } else if (schema.minimum !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.gte, + }), + parameters: [numberParameter({ isBigInt, value: schema.minimum })], + }); + } + + if (schema.exclusiveMaximum !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.lt, + }), + parameters: [ + numberParameter({ isBigInt, value: schema.exclusiveMaximum }), + ], + }); + } else if (schema.maximum !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.lte, + }), + parameters: [numberParameter({ isBigInt, value: schema.maximum })], + }); + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts new file mode 100644 index 000000000..954398c4f --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts @@ -0,0 +1,152 @@ +import ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import { numberRegExp } from '../../../../utils/regexp'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; + +export const objectToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'object'>; +}): Omit => { + const result: Partial> = {}; + + // TODO: parser - handle constants + const properties: Array = + []; + + const required = schema.required ?? []; + + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + for (const name in schema.properties) { + const property = schema.properties[name]!; + const isRequired = required.includes(name); + + const propertySchema = irSchemaToAst({ + optional: !isRequired, + plugin, + schema: property, + state: { + ...state, + _path: [...state._path, 'properties', name], + }, + }); + if (propertySchema.hasCircularReference) { + result.hasCircularReference = true; + } + + numberRegExp.lastIndex = 0; + let propertyName; + if (numberRegExp.test(name)) { + // For numeric literals, we'll handle negative numbers by using a string literal + // instead of trying to use a PrefixUnaryExpression + propertyName = name.startsWith('-') + ? ts.factory.createStringLiteral(name) + : ts.factory.createNumericLiteral(name); + } else { + propertyName = name; + } + // TODO: parser - abstract safe property name logic + if ( + ((name.match(/^[0-9]/) && name.match(/\D+/g)) || name.match(/\W/g)) && + !name.startsWith("'") && + !name.endsWith("'") + ) { + propertyName = `'${name}'`; + } + + if (propertySchema.hasCircularReference) { + properties.push( + tsc.getAccessorDeclaration({ + name: propertyName, + // @ts-expect-error + returnType: propertySchema.typeName + ? tsc.propertyAccessExpression({ + expression: z.placeholder, + name: propertySchema.typeName, + }) + : undefined, + statements: [ + tsc.returnStatement({ + expression: propertySchema.expression, + }), + ], + }), + ); + } else { + properties.push( + tsc.propertyAssignment({ + initializer: propertySchema.expression, + name: propertyName, + }), + ); + } + } + + if ( + schema.additionalProperties && + (!schema.properties || !Object.keys(schema.properties).length) + ) { + const zodSchema = irSchemaToAst({ + plugin, + schema: schema.additionalProperties, + state: { + ...state, + _path: [...state._path, 'additionalProperties'], + }, + }); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.record, + }), + parameters: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.string, + }), + parameters: [], + }), + zodSchema.expression, + ], + }); + if (zodSchema.hasCircularReference) { + result.hasCircularReference = true; + } + + // Return with typeName for circular references + if (result.hasCircularReference) { + return { + ...result, + typeName: 'ZodType', + } as Ast; + } + + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.object, + }), + parameters: [ts.factory.createObjectLiteralExpression(properties, true)], + }); + + // Return with typeName for circular references (AnyZodObject doesn't exist in Zod v4, use ZodType) + if (result.hasCircularReference) { + return { + ...result, + typeName: 'ZodType', + } as Ast; + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts new file mode 100644 index 000000000..1075f100f --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts @@ -0,0 +1,170 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const stringToAst = ({ + plugin, + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'string'>; +}): Omit => { + const result: Partial> = {}; + + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + if (typeof schema.const === 'string') { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.ots.string(schema.const)], + }); + return result as Omit; + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.string, + }), + }); + + const dateTimeOptions: { key: string; value: boolean }[] = []; + + if (plugin.config.dates.offset) { + dateTimeOptions.push({ key: 'offset', value: true }); + } + if (plugin.config.dates.local) { + dateTimeOptions.push({ key: 'local', value: true }); + } + + if (schema.format) { + switch (schema.format) { + case 'date': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.iso, + }), + name: identifiers.date, + }), + }); + break; + case 'date-time': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.iso, + }), + name: identifiers.datetime, + }), + parameters: + dateTimeOptions.length > 0 + ? [ + tsc.objectExpression({ + obj: dateTimeOptions, + }), + ] + : [], + }); + break; + case 'email': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.email, + }), + }); + break; + case 'ipv4': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.ipv4, + }), + }); + break; + case 'ipv6': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.ipv6, + }), + }); + break; + case 'time': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.iso, + }), + name: identifiers.time, + }), + }); + break; + case 'uri': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.url, + }), + }); + break; + case 'uuid': + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.uuid, + }), + }); + break; + } + } + + if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.length, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }); + } else { + if (schema.minLength !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.min, + }), + parameters: [tsc.valueToExpression({ value: schema.minLength })], + }); + } + + if (schema.maxLength !== undefined) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.max, + }), + parameters: [tsc.valueToExpression({ value: schema.maxLength })], + }); + } + } + + if (schema.pattern) { + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: result.expression, + name: identifiers.regex, + }), + parameters: [tsc.regularExpressionLiteral({ text: schema.pattern })], + }); + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts new file mode 100644 index 000000000..f0cf2acec --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts @@ -0,0 +1,76 @@ +import type ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; + +export const tupleToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'tuple'>; +}): Omit => { + const result: Partial> = {}; + + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + + if (schema.const && Array.isArray(schema.const)) { + const tupleElements = schema.const.map((value) => + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.literal, + }), + parameters: [tsc.valueToExpression({ value })], + }), + ); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + return result as Omit; + } + + const tupleElements: Array = []; + + if (schema.items) { + schema.items.forEach((item, index) => { + const itemSchema = irSchemaToAst({ + plugin, + schema: item, + state: { + ...state, + _path: [...state._path, 'items', index], + }, + }); + tupleElements.push(itemSchema.expression); + if (itemSchema.hasCircularReference) { + result.hasCircularReference = true; + } + }); + } + + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.tuple, + }), + parameters: [ + tsc.arrayLiteralExpression({ + elements: tupleElements, + }), + ], + }); + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts new file mode 100644 index 000000000..076bae8fb --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const undefinedToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'undefined'>; +}): Omit => { + const result: Partial> = {}; + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.undefined, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts new file mode 100644 index 000000000..7a824e351 --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const unknownToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'unknown'>; +}): Omit => { + const result: Partial> = {}; + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.unknown, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts new file mode 100644 index 000000000..596b2fe8f --- /dev/null +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts @@ -0,0 +1,20 @@ +import { tsc } from '../../../../tsc'; +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const voidToAst = ({ + plugin, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'void'>; +}): Omit => { + const result: Partial> = {}; + const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.void, + }), + }); + return result as Omit; +}; diff --git a/packages/openapi-ts/src/utils/escape.ts b/packages/openapi-ts/src/utils/escape.ts index 35728b37c..cf0aa9392 100644 --- a/packages/openapi-ts/src/utils/escape.ts +++ b/packages/openapi-ts/src/utils/escape.ts @@ -1,4 +1,4 @@ -import { EOL } from 'os'; +import { EOL } from 'node:os'; import { validTypescriptIdentifierRegExp } from './regexp'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96f0c9abe..54d9aa69e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1336,7 +1336,7 @@ importers: version: 10.4.3 nuxt: specifier: 3.14.1592 - version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) ofetch: specifier: 1.4.1 version: 1.4.1 @@ -1427,6 +1427,9 @@ importers: '@types/cross-spawn': specifier: 6.0.6 version: 6.0.6 + arktype: + specifier: 2.1.23 + version: 2.1.23 axios: specifier: 1.8.2 version: 1.8.2 @@ -2124,6 +2127,15 @@ packages: resolution: {integrity: sha512-Izvir8iIoU+X4SKtDAa5kpb+9cpifclzsbA8x/AZY0k0gIfXYQ1fa1B6Epfe6vNA2YfDX8VtrZFgvnXB6aPEoQ==} engines: {node: '>=18'} + '@ark/regex@0.0.0': + resolution: {integrity: sha512-p4vsWnd/LRGOdGQglbwOguIVhPmCAf5UzquvnDoxqhhPWTP84wWgi1INea8MgJ4SnI2gp37f13oA4Waz9vwNYg==} + + '@ark/schema@0.50.0': + resolution: {integrity: sha512-hfmP82GltBZDadIOeR3argKNlYYyB2wyzHp0eeAqAOFBQguglMV/S7Ip2q007bRtKxIMLDqFY6tfPie1dtssaQ==} + + '@ark/util@0.50.0': + resolution: {integrity: sha512-tIkgIMVRpkfXRQIEf0G2CJryZVtHVrqcWHMDa5QKo0OEEBu0tHkRSIMm4Ln8cd8Bn9TPZtvc/kE2Gma8RESPSg==} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -7268,6 +7280,9 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} + arktype@2.1.23: + resolution: {integrity: sha512-tyxNWX6xJVMb2EPJJ3OjgQS1G/vIeQRrZuY4DeBNQmh8n7geS+czgbauQWB6Pr+RXiOO8ChEey44XdmxsqGmfQ==} + array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -14220,7 +14235,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0)) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.0) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14234,11 +14249,11 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) browserslist: 4.25.4 copy-webpack-plugin: 12.0.2(webpack@5.98.0) css-loader: 7.1.2(webpack@5.98.0) @@ -14258,7 +14273,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14308,7 +14323,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0)) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.0) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14322,11 +14337,11 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) browserslist: 4.25.4 copy-webpack-plugin: 12.0.2(webpack@5.98.0) css-loader: 7.1.2(webpack@5.98.0) @@ -14346,7 +14361,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14434,7 +14449,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14484,7 +14499,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.18(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0) + '@angular-devkit/build-webpack': 0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4)) '@angular-devkit/core': 19.2.18(chokidar@4.0.3) '@angular/build': 19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(@angular/compiler@19.2.15)(@angular/platform-server@19.2.0(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/compiler@19.2.15)(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/animations@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1)))(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.9.3)))(terser@5.39.0)(typescript@5.9.3)(yaml@2.8.0) '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3) @@ -14498,7 +14513,7 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.10) '@babel/runtime': 7.26.10 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0) + '@ngtools/webpack': 19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) @@ -14522,7 +14537,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14567,11 +14582,11 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0))': + '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0)': dependencies: '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server: 5.2.0(webpack@5.98.0) transitivePeerDependencies: - chokidar @@ -14580,16 +14595,16 @@ snapshots: dependencies: '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar - '@angular-devkit/build-webpack@0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0)': + '@angular-devkit/build-webpack@0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4))': dependencies: '@angular-devkit/architect': 0.1902.18(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar @@ -15182,6 +15197,16 @@ snapshots: typescript: 5.6.1-rc validate-npm-package-name: 5.0.1 + '@ark/regex@0.0.0': + dependencies: + '@ark/util': 0.50.0 + + '@ark/schema@0.50.0': + dependencies: + '@ark/util': 0.50.0 + + '@ark/util@0.50.0': {} + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -17975,23 +18000,23 @@ snapshots: '@next/swc-win32-x64-msvc@15.2.4': optional: true - '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0))': + '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) '@ngtools/webpack@19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) - '@ngtools/webpack@19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0)': + '@ngtools/webpack@19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4))': dependencies: '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3) typescript: 5.9.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -18068,32 +18093,32 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - magicast - supports-color - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - magicast - supports-color - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) transitivePeerDependencies: - magicast - supports-color @@ -18158,13 +18183,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18193,9 +18218,9 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 unimport: 3.14.6(rollup@4.50.0) - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-plugin-vue-inspector: 5.3.2(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -18205,13 +18230,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18240,9 +18265,9 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 unimport: 3.14.6(rollup@4.50.0) - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -18252,13 +18277,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18287,9 +18312,9 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 unimport: 3.14.6(rollup@4.50.0) - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -21042,38 +21067,38 @@ snapshots: dependencies: '@vue/devtools-kit': 8.0.2 - '@vue/devtools-core@7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-hot-client: 0.2.4(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) vue: 3.5.13(typescript@5.9.3) transitivePeerDependencies: - vite - '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + vite-hot-client: 0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) vue: 3.5.13(typescript@5.9.3) transitivePeerDependencies: - vite - '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) vue: 3.5.13(typescript@5.9.3) transitivePeerDependencies: - vite @@ -21525,6 +21550,12 @@ snapshots: aria-query@5.3.2: {} + arktype@2.1.23: + dependencies: + '@ark/regex': 0.0.0 + '@ark/schema': 0.50.0 + '@ark/util': 0.50.0 + array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 @@ -21695,14 +21726,14 @@ snapshots: '@babel/core': 7.26.10 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) - babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)): + babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0): dependencies: '@babel/core': 7.26.9 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.26.10): dependencies: @@ -22286,7 +22317,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) core-js-compat@3.45.1: dependencies: @@ -22365,7 +22396,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) css-select@5.2.2: dependencies: @@ -23129,7 +23160,7 @@ snapshots: '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.17.0(jiti@2.6.1)) @@ -23157,7 +23188,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -23172,14 +23203,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -23194,7 +23225,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -24902,7 +24933,7 @@ snapshots: dependencies: less: 4.2.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) less@4.2.2: dependencies: @@ -24927,7 +24958,7 @@ snapshots: dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) light-my-request@6.6.0: dependencies: @@ -25468,7 +25499,7 @@ snapshots: dependencies: schema-utils: 4.3.2 tapable: 2.2.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) minimalistic-assert@1.0.1: {} @@ -26180,10 +26211,10 @@ snapshots: - vue-tsc - xml2js - nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/telemetry': 2.6.6(magicast@0.3.5) @@ -26301,10 +26332,10 @@ snapshots: - vue-tsc - xml2js - nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/telemetry': 2.6.6(magicast@0.3.5) @@ -26944,25 +26975,25 @@ snapshots: ts-node: 10.9.2(@types/node@22.10.5)(typescript@5.9.3) optional: true - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0): dependencies: cosmiconfig: 9.0.0(typescript@5.8.3) jiti: 1.21.7 postcss: 8.5.2 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - typescript - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 1.21.7 postcss: 8.5.2 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - typescript @@ -27787,7 +27818,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass: 1.85.0 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) sass@1.85.0: dependencies: @@ -28154,7 +28185,7 @@ snapshots: dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) source-map-support@0.5.21: dependencies: @@ -28587,7 +28618,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) optionalDependencies: esbuild: 0.25.0 @@ -28598,7 +28629,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) optionalDependencies: esbuild: 0.25.4 @@ -29400,6 +29431,10 @@ snapshots: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) vite-hot-client: 2.1.0(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-hot-client@0.2.4(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + dependencies: + vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite-hot-client@0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) @@ -29408,10 +29443,6 @@ snapshots: dependencies: vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) - vite-hot-client@0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) - vite-hot-client@2.1.0(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) @@ -29556,7 +29587,7 @@ snapshots: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29567,14 +29598,14 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.1 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29585,14 +29616,14 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.1 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29603,7 +29634,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.1 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: @@ -29640,7 +29671,7 @@ snapshots: - supports-color - vue - vite-plugin-vue-inspector@5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-vue-inspector@5.3.2(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29651,11 +29682,11 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29666,11 +29697,11 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29681,7 +29712,7 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -30149,7 +30180,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server@5.2.0(webpack@5.98.0): dependencies: @@ -30181,7 +30212,7 @@ snapshots: webpack-dev-middleware: 7.4.2(webpack@5.98.0) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - bufferutil - debug @@ -30219,7 +30250,7 @@ snapshots: webpack-dev-middleware: 7.4.2(webpack@5.98.0) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - bufferutil - debug @@ -30237,7 +30268,7 @@ snapshots: webpack-subresource-integrity@5.1.0(webpack@5.98.0): dependencies: typed-assert: 1.0.9 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-virtual-modules@0.6.2: {} -- 2.51.2 From 523038e82c949a436a467486a26333d50ce0e7cd Mon Sep 17 00:00:00 2001 From: Lubos Date: Wed, 22 Oct 2025 17:44:52 +0800 Subject: [PATCH 2/5] refactor: use refs in plugin state --- .changeset/fast-views-attack.md | 5 + packages/custom-client/src/plugin.ts | 4 +- .../my-client/base-url-false/client/plugin.ts | 4 +- .../base-url-number/client/plugin.ts | 4 +- .../base-url-strict/client/plugin.ts | 4 +- .../base-url-string/client/plugin.ts | 4 +- .../clients/my-client/bundle/client/plugin.ts | 4 +- .../my-client/default/client/plugin.ts | 4 +- .../sdk-client-optional/client/plugin.ts | 4 +- .../sdk-client-required/client/plugin.ts | 4 +- .../main/test/custom/client/plugin.ts | 4 +- .../main/test/openapi-ts.config.ts | 26 +- .../src/generate/__tests__/class.test.ts | 6 +- .../src/generate/__tests__/core.test.ts | 18 +- .../generate/legacy/__tests__/index.test.ts | 6 +- .../generate/legacy/__tests__/output.test.ts | 6 +- .../common/parser/__tests__/type.test.ts | 2 +- .../v3/parser/__tests__/getModel.test.ts | 2 +- .../src/plugins/@angular/common/api.ts | 4 +- .../plugins/@angular/common/httpRequests.ts | 22 +- .../plugins/@angular/common/httpResources.ts | 20 +- .../src/plugins/@angular/common/plugin.ts | 8 +- .../plugins/@hey-api/client-angular/api.ts | 4 +- .../src/plugins/@hey-api/client-axios/api.ts | 4 +- .../plugins/@hey-api/client-core/client.ts | 4 +- .../client-core/createClientConfig.ts | 2 +- .../src/plugins/@hey-api/client-fetch/api.ts | 4 +- .../src/plugins/@hey-api/client-next/api.ts | 4 +- .../src/plugins/@hey-api/client-nuxt/api.ts | 4 +- .../src/plugins/@hey-api/client-ofetch/api.ts | 4 +- .../schemas/__tests__/schemas.test.ts | 12 +- .../src/plugins/@hey-api/schemas/api.ts | 4 +- .../src/plugins/@hey-api/schemas/plugin.ts | 6 +- .../@hey-api/sdk/__tests__/plugin.test.ts | 24 +- .../src/plugins/@hey-api/sdk/api.ts | 4 +- .../src/plugins/@hey-api/sdk/operation.ts | 24 +- .../src/plugins/@hey-api/sdk/plugin.ts | 34 +- .../src/plugins/@hey-api/sdk/typeOptions.ts | 6 +- .../src/plugins/@hey-api/transformers/api.ts | 4 +- .../plugins/@hey-api/transformers/plugin.ts | 6 +- .../typescript/__tests__/plugin.test.ts | 6 +- .../src/plugins/@hey-api/typescript/api.ts | 6 +- .../plugins/@hey-api/typescript/operation.ts | 10 +- .../src/plugins/@hey-api/typescript/plugin.ts | 12 +- .../plugins/@hey-api/typescript/webhook.ts | 6 +- .../src/plugins/@pinia/colada/api.ts | 4 +- .../plugins/@pinia/colada/mutationOptions.ts | 2 +- .../src/plugins/@pinia/colada/plugin.ts | 12 +- .../src/plugins/@pinia/colada/queryKey.ts | 20 +- .../src/plugins/@pinia/colada/queryOptions.ts | 8 +- .../src/plugins/@pinia/colada/useType.ts | 6 +- .../angular-query-experimental/api.ts | 4 +- .../query-core/infiniteQueryOptions.ts | 14 +- .../@tanstack/query-core/mutationOptions.ts | 2 +- .../plugins/@tanstack/query-core/plugin.ts | 18 +- .../plugins/@tanstack/query-core/queryKey.ts | 16 +- .../@tanstack/query-core/queryOptions.ts | 6 +- .../plugins/@tanstack/query-core/useQuery.ts | 4 +- .../plugins/@tanstack/query-core/useType.ts | 10 +- .../src/plugins/@tanstack/react-query/api.ts | 4 +- .../src/plugins/@tanstack/solid-query/api.ts | 4 +- .../src/plugins/@tanstack/svelte-query/api.ts | 4 +- .../src/plugins/@tanstack/vue-query/api.ts | 4 +- .../openapi-ts/src/plugins/arktype/api.ts | 4 +- .../src/plugins/arktype/constants.ts | 71 +++++ .../src/plugins/arktype/shared/export.ts | 11 +- .../src/plugins/arktype/shared/types.d.ts | 1 + .../openapi-ts/src/plugins/arktype/v2/api.ts | 18 +- .../src/plugins/arktype/v2/plugin.ts | 293 ++++++++---------- .../src/plugins/arktype/v2/toAst/index.ts | 107 +++---- .../src/plugins/arktype/v2/toAst/null.ts | 14 + .../src/plugins/arktype/v2/toAst/object.ts | 170 ++++++++++ .../src/plugins/arktype/v2/toAst/string.ts | 65 ++++ .../openapi-ts/src/plugins/fastify/api.ts | 4 +- .../openapi-ts/src/plugins/fastify/plugin.ts | 10 +- .../src/plugins/shared/types/refs.d.ts | 59 ++++ .../shared/utils/__tests__/refs.test.ts | 85 +++++ .../src/plugins/shared/utils/refs.ts | 66 ++++ .../openapi-ts/src/plugins/valibot/api.ts | 4 +- .../src/plugins/valibot/shared/types.d.ts | 5 +- .../openapi-ts/src/plugins/valibot/v1/api.ts | 8 +- .../src/plugins/valibot/v1/operation.ts | 11 +- .../src/plugins/valibot/v1/pipesToAst.ts | 2 +- .../src/plugins/valibot/v1/plugin.ts | 47 +-- .../src/plugins/valibot/v1/toAst/array.ts | 5 +- .../src/plugins/valibot/v1/toAst/boolean.ts | 2 +- .../src/plugins/valibot/v1/toAst/enum.ts | 2 +- .../src/plugins/valibot/v1/toAst/never.ts | 2 +- .../src/plugins/valibot/v1/toAst/null.ts | 2 +- .../src/plugins/valibot/v1/toAst/number.ts | 2 +- .../src/plugins/valibot/v1/toAst/object.ts | 7 +- .../src/plugins/valibot/v1/toAst/string.ts | 2 +- .../src/plugins/valibot/v1/toAst/tuple.ts | 5 +- .../src/plugins/valibot/v1/toAst/undefined.ts | 2 +- .../src/plugins/valibot/v1/toAst/unknown.ts | 2 +- .../src/plugins/valibot/v1/toAst/void.ts | 2 +- .../src/plugins/valibot/v1/webhook.ts | 4 +- packages/openapi-ts/src/plugins/zod/api.ts | 4 +- .../openapi-ts/src/plugins/zod/mini/api.ts | 4 +- .../openapi-ts/src/plugins/zod/mini/plugin.ts | 20 +- .../src/plugins/zod/mini/toAst/array.ts | 8 +- .../src/plugins/zod/mini/toAst/boolean.ts | 2 +- .../src/plugins/zod/mini/toAst/enum.ts | 2 +- .../src/plugins/zod/mini/toAst/never.ts | 2 +- .../src/plugins/zod/mini/toAst/null.ts | 2 +- .../src/plugins/zod/mini/toAst/number.ts | 2 +- .../src/plugins/zod/mini/toAst/object.ts | 22 +- .../src/plugins/zod/mini/toAst/string.ts | 2 +- .../src/plugins/zod/mini/toAst/tuple.ts | 2 +- .../src/plugins/zod/mini/toAst/undefined.ts | 2 +- .../src/plugins/zod/mini/toAst/unknown.ts | 2 +- .../src/plugins/zod/mini/toAst/void.ts | 2 +- .../src/plugins/zod/shared/export.ts | 2 +- .../src/plugins/zod/shared/operation.ts | 8 +- .../src/plugins/zod/shared/webhook.ts | 4 +- packages/openapi-ts/src/plugins/zod/v3/api.ts | 4 +- .../openapi-ts/src/plugins/zod/v3/plugin.ts | 32 +- .../src/plugins/zod/v3/toAst/array.ts | 8 +- .../src/plugins/zod/v3/toAst/boolean.ts | 2 +- .../src/plugins/zod/v3/toAst/enum.ts | 2 +- .../src/plugins/zod/v3/toAst/never.ts | 2 +- .../src/plugins/zod/v3/toAst/null.ts | 2 +- .../src/plugins/zod/v3/toAst/number.ts | 2 +- .../src/plugins/zod/v3/toAst/object.ts | 8 +- .../src/plugins/zod/v3/toAst/string.ts | 2 +- .../src/plugins/zod/v3/toAst/tuple.ts | 2 +- .../src/plugins/zod/v3/toAst/undefined.ts | 2 +- .../src/plugins/zod/v3/toAst/unknown.ts | 2 +- .../src/plugins/zod/v3/toAst/void.ts | 2 +- packages/openapi-ts/src/plugins/zod/v4/api.ts | 4 +- .../openapi-ts/src/plugins/zod/v4/plugin.ts | 20 +- .../src/plugins/zod/v4/toAst/array.ts | 8 +- .../src/plugins/zod/v4/toAst/boolean.ts | 2 +- .../src/plugins/zod/v4/toAst/enum.ts | 2 +- .../src/plugins/zod/v4/toAst/never.ts | 2 +- .../src/plugins/zod/v4/toAst/null.ts | 2 +- .../src/plugins/zod/v4/toAst/number.ts | 2 +- .../src/plugins/zod/v4/toAst/object.ts | 22 +- .../src/plugins/zod/v4/toAst/string.ts | 2 +- .../src/plugins/zod/v4/toAst/tuple.ts | 2 +- .../src/plugins/zod/v4/toAst/undefined.ts | 2 +- .../src/plugins/zod/v4/toAst/unknown.ts | 2 +- .../src/plugins/zod/v4/toAst/void.ts | 2 +- .../src/utils/__tests__/handlebars.test.ts | 12 +- .../src/utils/__tests__/parse.test.ts | 14 +- 145 files changed, 1187 insertions(+), 672 deletions(-) create mode 100644 .changeset/fast-views-attack.md create mode 100644 packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts create mode 100644 packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts create mode 100644 packages/openapi-ts/src/plugins/shared/types/refs.d.ts create mode 100644 packages/openapi-ts/src/plugins/shared/utils/__tests__/refs.test.ts create mode 100644 packages/openapi-ts/src/plugins/shared/utils/refs.ts diff --git a/.changeset/fast-views-attack.md b/.changeset/fast-views-attack.md new file mode 100644 index 000000000..765069493 --- /dev/null +++ b/.changeset/fast-views-attack.md @@ -0,0 +1,5 @@ +--- +'@hey-api/openapi-ts': patch +--- + +fix(renderer): allow duplicate names when one symbol is a type diff --git a/packages/custom-client/src/plugin.ts b/packages/custom-client/src/plugin.ts index 7d4ddc8d7..a69653309 100644 --- a/packages/custom-client/src/plugin.ts +++ b/packages/custom-client/src/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-false/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-false/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-false/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-false/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-number/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-number/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-number/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-number/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-strict/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-strict/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-strict/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-strict/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-string/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-string/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-string/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/base-url-string/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/bundle/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/bundle/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/bundle/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/bundle/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/default/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/default/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/default/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/default/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-optional/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-optional/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-optional/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-optional/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-required/client/plugin.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-required/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-required/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/my-client/sdk-client-required/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/custom/client/plugin.ts b/packages/openapi-ts-tests/main/test/custom/client/plugin.ts index 83305b41f..c11a1606a 100644 --- a/packages/openapi-ts-tests/main/test/custom/client/plugin.ts +++ b/packages/openapi-ts-tests/main/test/custom/client/plugin.ts @@ -17,13 +17,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Pick) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts index c170531d4..1411badb7 100644 --- a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts +++ b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts @@ -34,9 +34,10 @@ export default defineConfig(() => { // }, path: path.resolve( getSpecsPath(), - '3.0.x', + // '3.0.x', + '3.1.x', // 'circular.yaml', - 'dutchie.json', + // 'dutchie.json', // 'invalid', // 'openai.yaml', // 'full.yaml', @@ -46,6 +47,7 @@ export default defineConfig(() => { // 'transformers.json', // 'type-format.yaml', // 'validators.yaml', + 'validators-circular-ref.json', // 'validators-circular-ref-2.yaml', // 'zoom-video-sdk.json', ), @@ -213,7 +215,7 @@ export default defineConfig(() => { // }, }, { - asClass: true, + // asClass: true, // auth: false, // classNameBuilder: '{{name}}', // classNameBuilder: '{{name}}Service', @@ -234,10 +236,10 @@ export default defineConfig(() => { // signature: 'object', // transformer: '@hey-api/transformers', // transformer: true, - // validator: { - // request: 'valibot', - // response: 'zod', - // }, + validator: { + request: 'arktype', + response: 'arktype', + }, '~hooks': { symbols: { // getFilePath: (symbol) => { @@ -300,10 +302,10 @@ export default defineConfig(() => { }, }, { - name: 'arktype', - types: { - infer: true, - }, + // name: 'arktype', + // types: { + // infer: true, + // }, }, { // case: 'SCREAMING_SNAKE_CASE', @@ -353,7 +355,7 @@ export default defineConfig(() => { }, exportFromIndex: true, metadata: true, - name: 'zod', + // name: 'zod', // requests: { // // case: 'SCREAMING_SNAKE_CASE', // // name: 'z{{name}}TestData', diff --git a/packages/openapi-ts/src/generate/__tests__/class.test.ts b/packages/openapi-ts/src/generate/__tests__/class.test.ts index 6060d05ed..4d56bc0b2 100644 --- a/packages/openapi-ts/src/generate/__tests__/class.test.ts +++ b/packages/openapi-ts/src/generate/__tests__/class.test.ts @@ -85,7 +85,7 @@ describe('generateLegacyClientClass', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -97,7 +97,7 @@ describe('generateLegacyClientClass', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -108,8 +108,8 @@ describe('generateLegacyClientClass', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', diff --git a/packages/openapi-ts/src/generate/__tests__/core.test.ts b/packages/openapi-ts/src/generate/__tests__/core.test.ts index 6d81edfa6..89b02629d 100644 --- a/packages/openapi-ts/src/generate/__tests__/core.test.ts +++ b/packages/openapi-ts/src/generate/__tests__/core.test.ts @@ -100,7 +100,7 @@ describe('generateLegacyCore', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -112,7 +112,7 @@ describe('generateLegacyCore', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -123,8 +123,8 @@ describe('generateLegacyCore', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', @@ -258,7 +258,7 @@ describe('generateLegacyCore', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -270,7 +270,7 @@ describe('generateLegacyCore', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -281,8 +281,8 @@ describe('generateLegacyCore', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', @@ -399,7 +399,7 @@ describe('generateLegacyCore', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -411,7 +411,7 @@ describe('generateLegacyCore', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -422,8 +422,8 @@ describe('generateLegacyCore', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', diff --git a/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts b/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts index f7350e438..03c272773 100644 --- a/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts +++ b/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts @@ -84,7 +84,7 @@ describe('generateIndexFile', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -96,7 +96,7 @@ describe('generateIndexFile', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -107,8 +107,8 @@ describe('generateIndexFile', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', diff --git a/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts b/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts index adc032164..6148cb2eb 100644 --- a/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts +++ b/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts @@ -97,7 +97,7 @@ describe('generateLegacyOutput', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -109,7 +109,7 @@ describe('generateLegacyOutput', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -120,8 +120,8 @@ describe('generateLegacyOutput', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', diff --git a/packages/openapi-ts/src/openApi/common/parser/__tests__/type.test.ts b/packages/openapi-ts/src/openApi/common/parser/__tests__/type.test.ts index bc2d9426d..62de82c5e 100644 --- a/packages/openapi-ts/src/openApi/common/parser/__tests__/type.test.ts +++ b/packages/openapi-ts/src/openApi/common/parser/__tests__/type.test.ts @@ -9,8 +9,8 @@ vi.mock('../../../../utils/config', () => { plugins: { '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { name: '@hey-api/typescript', diff --git a/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts b/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts index dbc8979b3..53461cd76 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts @@ -11,8 +11,8 @@ vi.mock('../../../../utils/config', () => { plugins: { '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { name: '@hey-api/typescript', diff --git a/packages/openapi-ts/src/plugins/@angular/common/api.ts b/packages/openapi-ts/src/plugins/@angular/common/api.ts index 3cac30850..00e28a062 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/api.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/api.ts @@ -22,13 +22,13 @@ export type IApi = { * - `Injectable`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@angular/common'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts b/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts index 3c60b7f48..bb4ad9d69 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts @@ -127,7 +127,7 @@ const generateAngularClassRequests = ({ } const symbolInjectable = plugin.referenceSymbol( - plugin.api.getSelector('Injectable'), + plugin.api.selector('Injectable'), ); const symbolClass = plugin.registerSymbol({ exported: true, @@ -138,7 +138,7 @@ const generateAngularClassRequests = ({ }, name: currentClass.className, }), - selector: plugin.api.getSelector('class', currentClass.className), + selector: plugin.api.selector('class', currentClass.className), }); const node = tsc.classDeclaration({ decorator: currentClass.root @@ -175,7 +175,7 @@ const generateAngularFunctionRequests = ({ const symbol = plugin.registerSymbol({ exported: true, name: plugin.config.httpRequests.methodNameBuilder(operation), - selector: plugin.api.getSelector('httpRequest', operation.id), + selector: plugin.api.selector('httpRequest', operation.id), }); const node = generateAngularRequestFunction({ isRequiredOptions, @@ -196,10 +196,10 @@ const generateRequestCallExpression = ({ }) => { const client = getClientPlugin(plugin.context.config); const symbolClient = - client.api && 'getSelector' in client.api + client.api && 'selector' in client.api ? plugin.getSymbol( // @ts-expect-error - client.api.getSelector('client'), + client.api.selector('client'), ) : undefined; @@ -265,16 +265,16 @@ const generateAngularRequestMethod = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolHttpRequest = plugin.referenceSymbol( - plugin.api.getSelector('HttpRequest'), + plugin.api.selector('HttpRequest'), ); const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const symbolDataType = plugin.getSymbol( - pluginTypeScript.api.getSelector('data', operation.id), + pluginTypeScript.api.selector('data', operation.id), ); const dataType = symbolDataType?.placeholder || 'unknown'; @@ -322,16 +322,16 @@ const generateAngularRequestFunction = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolHttpRequest = plugin.referenceSymbol( - plugin.api.getSelector('HttpRequest'), + plugin.api.selector('HttpRequest'), ); const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const symbolDataType = plugin.getSymbol( - pluginTypeScript.api.getSelector('data', operation.id), + pluginTypeScript.api.selector('data', operation.id), ); const dataType = symbolDataType?.placeholder || 'unknown'; diff --git a/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts b/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts index 4e0c0b017..4ce8d0d14 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts @@ -126,7 +126,7 @@ const generateAngularClassServices = ({ } const symbolInjectable = plugin.referenceSymbol( - plugin.api.getSelector('Injectable'), + plugin.api.selector('Injectable'), ); const symbolClass = plugin.registerSymbol({ exported: true, @@ -195,11 +195,11 @@ const generateResourceCallExpression = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolHttpResource = plugin.referenceSymbol( - plugin.api.getSelector('httpResource'), + plugin.api.selector('httpResource'), ); const symbolResponseType = plugin.getSymbol( - pluginTypeScript.api.getSelector('response', operation.id), + pluginTypeScript.api.selector('response', operation.id), ); const responseType = symbolResponseType?.placeholder || 'unknown'; @@ -216,12 +216,12 @@ const generateResourceCallExpression = ({ // Import the root class from HTTP requests const rootClassName = firstEntry.path[0]!; const symbolClass = plugin.referenceSymbol( - plugin.api.getSelector('class', rootClassName), + plugin.api.selector('class', rootClassName), ); // Build the method access path using inject const symbolInject = plugin.referenceSymbol( - plugin.api.getSelector('inject'), + plugin.api.selector('inject'), ); let methodAccess: ts.Expression = tsc.callExpression({ functionName: symbolInject.placeholder, @@ -282,7 +282,7 @@ const generateResourceCallExpression = ({ } } else { const symbolHttpRequest = plugin.referenceSymbol( - plugin.api.getSelector('httpRequest', operation.id), + plugin.api.selector('httpRequest', operation.id), ); return tsc.callExpression({ @@ -351,11 +351,11 @@ const generateAngularResourceMethod = ({ const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const symbolDataType = plugin.getSymbol( - pluginTypeScript.api.getSelector('data', operation.id), + pluginTypeScript.api.selector('data', operation.id), ); const dataType = symbolDataType?.placeholder || 'unknown'; @@ -404,11 +404,11 @@ const generateAngularResourceFunction = ({ const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const symbolDataType = plugin.getSymbol( - pluginTypeScript.api.getSelector('data', operation.id), + pluginTypeScript.api.selector('data', operation.id), ); const dataType = symbolDataType?.placeholder || 'unknown'; diff --git a/packages/openapi-ts/src/plugins/@angular/common/plugin.ts b/packages/openapi-ts/src/plugins/@angular/common/plugin.ts index 289723764..da20d8c6a 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/plugin.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/plugin.ts @@ -9,22 +9,22 @@ export const handler: AngularCommonPlugin['Handler'] = ({ plugin }) => { kind: 'type', }, name: 'HttpRequest', - selector: plugin.api.getSelector('HttpRequest'), + selector: plugin.api.selector('HttpRequest'), }); plugin.registerSymbol({ external: '@angular/core', name: 'inject', - selector: plugin.api.getSelector('inject'), + selector: plugin.api.selector('inject'), }); plugin.registerSymbol({ external: '@angular/core', name: 'Injectable', - selector: plugin.api.getSelector('Injectable'), + selector: plugin.api.selector('Injectable'), }); plugin.registerSymbol({ external: '@angular/common/http', name: 'httpResource', - selector: plugin.api.getSelector('httpResource'), + selector: plugin.api.selector('httpResource'), }); if (plugin.config.httpRequests.enabled) { diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts index 5af4687d0..421e8e03e 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/client-angular'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts index 9414a28fe..08ee36a65 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/client-axios'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts index 35c4ee0e9..b7180fc35 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts @@ -38,7 +38,7 @@ export const createClient: PluginHandler = ({ plugin }) => { }); const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolClientOptions = plugin.referenceSymbol( - pluginTypeScript.api.getSelector('ClientOptions'), + pluginTypeScript.api.selector('ClientOptions'), ); const { runtimeConfigPath } = plugin.config; @@ -93,7 +93,7 @@ export const createClient: PluginHandler = ({ plugin }) => { const symbolClient = plugin.registerSymbol({ name: 'client', - selector: plugin.api.getSelector('client'), + selector: plugin.api.selector('client'), }); const statement = tsc.constVariable({ exportConst: true, diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts index 972f9b696..cb39f82ff 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts @@ -8,7 +8,7 @@ export const createClientConfigType = ({ const clientModule = clientFolderAbsolutePath(plugin.context.config); const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolClientOptions = plugin.referenceSymbol( - pluginTypeScript.api.getSelector('ClientOptions'), + pluginTypeScript.api.selector('ClientOptions'), ); const symbolConfig = plugin.registerSymbol({ external: clientModule, diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts index 1a4e9f742..d72153064 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/client-fetch'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts index 0aa1c5e88..6db16fbf8 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/client-next'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts index ddb217aa1..b5965c69b 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/client-nuxt'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts index 11a238387..479523b23 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `client`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/client-ofetch'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts index a7e827630..073916160 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts @@ -88,7 +88,7 @@ describe('generateLegacySchemas', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -100,7 +100,7 @@ describe('generateLegacySchemas', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -111,8 +111,8 @@ describe('generateLegacySchemas', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', @@ -251,7 +251,7 @@ describe('generateLegacySchemas', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -264,7 +264,7 @@ describe('generateLegacySchemas', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -275,8 +275,8 @@ describe('generateLegacySchemas', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts index 0deea2b62..71d0200ed 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `ref`: `$ref` JSON pointer * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/schemas'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts index c902cbb6d..999f5a693 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts @@ -370,7 +370,7 @@ const schemasV2_0_X = ({ const symbol = plugin.registerSymbol({ exported: true, name: schemaName({ name, plugin, schema }), - selector: plugin.api.getSelector('ref', name), + selector: plugin.api.selector('ref', name), }); const obj = schemaToJsonSchemaDraft_04({ context, @@ -403,7 +403,7 @@ const schemasV3_0_X = ({ const symbol = plugin.registerSymbol({ exported: true, name: schemaName({ name, plugin, schema }), - selector: plugin.api.getSelector('ref', name), + selector: plugin.api.selector('ref', name), }); const obj = schemaToJsonSchemaDraft_05({ context, @@ -436,7 +436,7 @@ const schemasV3_1_X = ({ const symbol = plugin.registerSymbol({ exported: true, name: schemaName({ name, plugin, schema }), - selector: plugin.api.getSelector('ref', name), + selector: plugin.api.selector('ref', name), }); const obj = schemaToJsonSchema2020_12({ context, diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts index 2c32ca954..d1e9a4ae5 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts @@ -90,7 +90,7 @@ describe('handlerLegacy', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -102,7 +102,7 @@ describe('handlerLegacy', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { asClass: true, @@ -114,8 +114,8 @@ describe('handlerLegacy', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { name: '@hey-api/typescript', @@ -326,7 +326,7 @@ describe('methodNameBuilder', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -338,7 +338,7 @@ describe('methodNameBuilder', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { asClass: true, @@ -350,8 +350,8 @@ describe('methodNameBuilder', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { name: '@hey-api/typescript', @@ -484,7 +484,7 @@ describe('methodNameBuilder', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -496,7 +496,7 @@ describe('methodNameBuilder', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { asClass: true, @@ -509,8 +509,8 @@ describe('methodNameBuilder', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { name: '@hey-api/typescript', @@ -645,7 +645,7 @@ describe('methodNameBuilder', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -657,7 +657,7 @@ describe('methodNameBuilder', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { asClass: false, @@ -670,8 +670,8 @@ describe('methodNameBuilder', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { name: '@hey-api/typescript', diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts index 0737a347f..313c8a5e4 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts @@ -30,7 +30,7 @@ export type IApi = { * - `urlSearchParamsBodySerializer`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { @@ -42,7 +42,7 @@ export class Api implements IApi { return createOperationComment(...args); } - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts index 51a7c224c..2c2bacce3 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts @@ -160,17 +160,15 @@ export const operationOptionsType = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolDataType = plugin.getSymbol( - pluginTypeScript.api.getSelector('data', operation.id), + pluginTypeScript.api.selector('data', operation.id), ); const dataType = symbolDataType?.placeholder || 'unknown'; - const symbolOptions = plugin.referenceSymbol( - plugin.api.getSelector('Options'), - ); + const symbolOptions = plugin.referenceSymbol(plugin.api.selector('Options')); if (isNuxtClient) { const symbolResponseType = plugin.getSymbol( - pluginTypeScript.api.getSelector('response', operation.id), + pluginTypeScript.api.selector('response', operation.id), ); const responseType = symbolResponseType?.placeholder || 'unknown'; return `${symbolOptions.placeholder}<${nuxtTypeComposable}, ${dataType}, ${responseType}, ${nuxtTypeDefault}>`; @@ -358,7 +356,7 @@ export const operationStatements = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolResponseType = plugin.getSymbol( - pluginTypeScript.api.getSelector( + pluginTypeScript.api.selector( isNuxtClient ? 'response' : 'responses', operation.id, ), @@ -366,7 +364,7 @@ export const operationStatements = ({ const responseType = symbolResponseType?.placeholder || 'unknown'; const symbolErrorType = plugin.getSymbol( - pluginTypeScript.api.getSelector( + pluginTypeScript.api.selector( isNuxtClient ? 'error' : 'errors', operation.id, ), @@ -395,7 +393,7 @@ export const operationStatements = ({ switch (operation.body.type) { case 'form-data': { const symbol = plugin.referenceSymbol( - plugin.api.getSelector('formDataBodySerializer'), + plugin.api.selector('formDataBodySerializer'), ); requestOptions.push({ spread: symbol.placeholder }); break; @@ -413,7 +411,7 @@ export const operationStatements = ({ break; case 'url-search-params': { const symbol = plugin.referenceSymbol( - plugin.api.getSelector('urlSearchParamsBodySerializer'), + plugin.api.selector('urlSearchParamsBodySerializer'), ); requestOptions.push({ spread: symbol.placeholder }); break; @@ -468,7 +466,7 @@ export const operationStatements = ({ plugin.config.transformer, ); const symbolResponseTransformer = plugin.getSymbol( - pluginTransformers.api.getSelector('response', operation.id), + pluginTransformers.api.selector('response', operation.id), ); if ( symbolResponseTransformer && @@ -571,7 +569,7 @@ export const operationStatements = ({ config.push(tsc.objectExpression({ obj })); } const symbol = plugin.referenceSymbol( - plugin.api.getSelector('buildClientParams'), + plugin.api.selector('buildClientParams'), ); statements.push( tsc.constVariable({ @@ -626,10 +624,10 @@ export const operationStatements = ({ } const symbolClient = - plugin.config.client && client.api && 'getSelector' in client.api + plugin.config.client && client.api && 'selector' in client.api ? plugin.getSymbol( // @ts-expect-error - client.api.getSelector('client'), + client.api.selector('client'), ) : undefined; diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts index f799a89e1..a7736dcaf 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts @@ -34,13 +34,13 @@ const createClientClassNodes = ({ }), }); - const symbolClient = plugin.referenceSymbol(plugin.api.getSelector('Client')); + const symbolClient = plugin.referenceSymbol(plugin.api.selector('Client')); const client = getClientPlugin(plugin.context.config); const symClient = - client.api && 'getSelector' in client.api + client.api && 'selector' in client.api ? plugin.getSymbol( // @ts-expect-error - client.api.getSelector('client'), + client.api.selector('client'), ) : undefined; @@ -144,7 +144,7 @@ const generateClassSdk = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolResponse = isNuxtClient ? plugin.getSymbol( - pluginTypeScript.api.getSelector('response', operation.id), + pluginTypeScript.api.selector('response', operation.id), ) : undefined; @@ -157,7 +157,7 @@ const generateClassSdk = ({ for (const entry of classes.values()) { entry.path.forEach((currentClassName, index) => { const symbolCurrentClass = plugin.referenceSymbol( - plugin.api.getSelector('class', currentClassName), + plugin.api.selector('class', currentClassName), ); if (!sdkClasses.has(symbolCurrentClass.id)) { sdkClasses.set(symbolCurrentClass.id, { @@ -173,7 +173,7 @@ const generateClassSdk = ({ const parentClassName = entry.path[index - 1]; if (parentClassName) { const symbolParentClass = plugin.referenceSymbol( - plugin.api.getSelector('class', parentClassName), + plugin.api.selector('class', parentClassName), ); if ( symbolParentClass.placeholder !== symbolCurrentClass.placeholder @@ -221,7 +221,7 @@ const generateClassSdk = ({ { default: tsc.ots.string('$fetch'), extends: tsc.typeNode( - plugin.referenceSymbol(plugin.api.getSelector('Composable')) + plugin.referenceSymbol(plugin.api.selector('Composable')) .placeholder, ), name: nuxtTypeComposable, @@ -324,7 +324,7 @@ const generateClassSdk = ({ const symbol = plugin.registerSymbol({ exported: true, name: currentClass.className, - selector: plugin.api.getSelector('class', currentClass.className), + selector: plugin.api.selector('class', currentClass.className), }); const node = tsc.classDeclaration({ decorator: @@ -335,7 +335,7 @@ const generateClassSdk = ({ providedIn: 'root', }, ], - name: plugin.referenceSymbol(plugin.api.getSelector('Injectable')) + name: plugin.referenceSymbol(plugin.api.selector('Injectable')) .placeholder, } : undefined, @@ -380,7 +380,7 @@ const generateFlatSdk = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolResponse = isNuxtClient ? plugin.getSymbol( - pluginTypeScript.api.getSelector('response', operation.id), + pluginTypeScript.api.selector('response', operation.id), ) : undefined; const opParameters = operationParameters({ @@ -401,7 +401,7 @@ const generateFlatSdk = ({ id: operation.id, operation, }), - selector: plugin.api.getSelector('function', operation.id), + selector: plugin.api.selector('function', operation.id), }); const node = tsc.constVariable({ comment: plugin.api.createOperationComment({ operation }), @@ -415,7 +415,7 @@ const generateFlatSdk = ({ { default: tsc.ots.string('$fetch'), extends: tsc.typeNode( - plugin.referenceSymbol(plugin.api.getSelector('Composable')) + plugin.referenceSymbol(plugin.api.selector('Composable')) .placeholder, ), name: nuxtTypeComposable, @@ -456,17 +456,17 @@ export const handler: HeyApiSdkPlugin['Handler'] = ({ plugin }) => { plugin.registerSymbol({ external: clientModule, name: 'formDataBodySerializer', - selector: plugin.api.getSelector('formDataBodySerializer'), + selector: plugin.api.selector('formDataBodySerializer'), }); plugin.registerSymbol({ external: clientModule, name: 'urlSearchParamsBodySerializer', - selector: plugin.api.getSelector('urlSearchParamsBodySerializer'), + selector: plugin.api.selector('urlSearchParamsBodySerializer'), }); plugin.registerSymbol({ external: clientModule, name: 'buildClientParams', - selector: plugin.api.getSelector('buildClientParams'), + selector: plugin.api.selector('buildClientParams'), }); const client = getClientPlugin(plugin.context.config); @@ -479,7 +479,7 @@ export const handler: HeyApiSdkPlugin['Handler'] = ({ plugin }) => { kind: 'type', }, name: 'Composable', - selector: plugin.api.getSelector('Composable'), + selector: plugin.api.selector('Composable'), }); } @@ -487,7 +487,7 @@ export const handler: HeyApiSdkPlugin['Handler'] = ({ plugin }) => { plugin.registerSymbol({ external: '@angular/core', name: 'Injectable', - selector: plugin.api.getSelector('Injectable'), + selector: plugin.api.selector('Injectable'), }); } diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts index 15ddccb17..52e5d0bc8 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts @@ -26,7 +26,7 @@ export const createTypeOptions = ({ kind: 'type', }, name: 'Client', - selector: plugin.api.getSelector('Client'), + selector: plugin.api.selector('Client'), }); const symbolClientOptions = plugin.registerSymbol({ external: clientModule, @@ -41,7 +41,7 @@ export const createTypeOptions = ({ kind: 'type', }, name: 'Options', - selector: plugin.api.getSelector('Options'), + selector: plugin.api.selector('Options'), }); const typeOptions = tsc.typeAliasDeclaration({ @@ -102,7 +102,7 @@ export const createTypeOptions = ({ tsc.typeParameterDeclaration({ constraint: tsc.typeReferenceNode({ typeName: plugin.referenceSymbol( - plugin.api.getSelector('Composable'), + plugin.api.selector('Composable'), ).placeholder, }), defaultType: tsc.typeNode("'$fetch'"), diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts index 920c22e80..3a60212ba 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts @@ -12,13 +12,13 @@ export type IApi = { * - `response-ref`: `$ref` JSON pointer * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/transformers'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts index aa203c16f..c67ca4c05 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts @@ -60,7 +60,7 @@ const processSchemaType = ({ schema: IR.SchemaObject; }): Array => { if (schema.$ref) { - const selector = plugin.api.getSelector('response-ref', schema.$ref); + const selector = plugin.api.selector('response-ref', schema.$ref); if (!plugin.getSymbol(selector)) { const symbol = plugin.registerSymbol({ @@ -337,7 +337,7 @@ export const handler: HeyApiTransformersPlugin['Handler'] = ({ plugin }) => { const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolResponse = plugin.getSymbol( - pluginTypeScript.api.getSelector('response', operation.id), + pluginTypeScript.api.selector('response', operation.id), ); if (!symbolResponse) return; @@ -350,7 +350,7 @@ export const handler: HeyApiTransformersPlugin['Handler'] = ({ plugin }) => { }, name: operation.id, }), - selector: plugin.api.getSelector('response', operation.id), + selector: plugin.api.selector('response', operation.id), }); // TODO: parser - consider handling simple string response which is also a date diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts index 2d0e38fc8..3a59329d1 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts @@ -88,7 +88,7 @@ describe('generateLegacyTypes', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -100,7 +100,7 @@ describe('generateLegacyTypes', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -111,8 +111,8 @@ describe('generateLegacyTypes', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts index 9ddaef171..aacaaf16a 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts @@ -18,6 +18,7 @@ type SelectorType = | 'Webhooks'; export type IApi = { + schemaToType: (args: Parameters[0]) => ts.TypeNode; /** * @param type Selector type. * @param value Depends on `type`: @@ -34,14 +35,13 @@ export type IApi = { * - `Webhooks`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; - schemaToType: (args: Parameters[0]) => ts.TypeNode; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@hey-api/typescript'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts index b2be1fa9a..1584241f8 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts @@ -128,7 +128,7 @@ const operationToDataType = ({ config: plugin.config.requests, name: operation.id, }), - selector: plugin.api.getSelector('data', operation.id), + selector: plugin.api.selector('data', operation.id), }); const type = schemaToType({ plugin, @@ -164,7 +164,7 @@ export const operationToType = ({ config: plugin.config.errors, name: operation.id, }), - selector: plugin.api.getSelector('errors', operation.id), + selector: plugin.api.selector('errors', operation.id), }); const type = schemaToType({ plugin, @@ -190,7 +190,7 @@ export const operationToType = ({ }, name: operation.id, }), - selector: plugin.api.getSelector('error', operation.id), + selector: plugin.api.selector('error', operation.id), }); const type = tsc.indexedAccessTypeNode({ indexType: tsc.typeOperatorNode({ @@ -220,7 +220,7 @@ export const operationToType = ({ config: plugin.config.responses, name: operation.id, }), - selector: plugin.api.getSelector('responses', operation.id), + selector: plugin.api.selector('responses', operation.id), }); const type = schemaToType({ plugin, @@ -246,7 +246,7 @@ export const operationToType = ({ }, name: operation.id, }), - selector: plugin.api.getSelector('response', operation.id), + selector: plugin.api.selector('response', operation.id), }); const type = tsc.indexedAccessTypeNode({ indexType: tsc.typeOperatorNode({ diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts index 1a2a48b78..cb1a1249c 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts @@ -262,9 +262,9 @@ const stringTypeToIdentifier = ({ parts.pop(); // remove the ID part const type = parts.join('_'); - const selector = plugin.api.getSelector('TypeID', type); + const selector = plugin.api.selector('TypeID', type); if (!plugin.getSymbol(selector)) { - const selectorTypeId = plugin.api.getSelector('TypeID'); + const selectorTypeId = plugin.api.selector('TypeID'); if (!plugin.getSymbol(selectorTypeId)) { const symbolTypeId = plugin.registerSymbol({ @@ -447,7 +447,7 @@ export const schemaToType = ({ }): ts.TypeNode => { if (schema.$ref) { const symbol = plugin.referenceSymbol( - plugin.api.getSelector('ref', schema.$ref), + plugin.api.selector('ref', schema.$ref), ); return tsc.typeReferenceNode({ typeName: symbol.placeholder }); } @@ -506,7 +506,7 @@ const handleComponent = ({ config: plugin.config.definitions, name: refToName(id), }), - selector: plugin.api.getSelector('ref', id), + selector: plugin.api.selector('ref', id), }); exportType({ plugin, @@ -529,7 +529,7 @@ export const handler: HeyApiTypeScriptPlugin['Handler'] = ({ plugin }) => { }, name: 'ClientOptions', }), - selector: plugin.api.getSelector('ClientOptions'), + selector: plugin.api.selector('ClientOptions'), }); // reserve identifier for Webhooks const symbolWebhooks = plugin.registerSymbol({ @@ -543,7 +543,7 @@ export const handler: HeyApiTypeScriptPlugin['Handler'] = ({ plugin }) => { }, name: 'Webhooks', }), - selector: plugin.api.getSelector('Webhooks'), + selector: plugin.api.selector('Webhooks'), }); const servers: Array = []; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts index 003f6238d..7c306aca4 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts @@ -34,7 +34,7 @@ const operationToDataType = ({ }, name: operation.id, }), - selector: plugin.api.getSelector('webhook-payload', operation.id), + selector: plugin.api.selector('webhook-payload', operation.id), }); const type = schemaToType({ plugin, @@ -55,7 +55,7 @@ const operationToDataType = ({ }, name: symbolWebhookPayload.name, placeholder: symbolWebhookPayload.placeholder, - selector: plugin.api.getSelector('ref', symbolWebhookPayload.placeholder), + selector: plugin.api.selector('ref', symbolWebhookPayload.placeholder), }); data.properties.body = { $ref: symbolWebhookPayload.placeholder }; dataRequired.push('body'); @@ -83,7 +83,7 @@ const operationToDataType = ({ config: plugin.config.webhooks, name: operation.id, }), - selector: plugin.api.getSelector('webhook-request', operation.id), + selector: plugin.api.selector('webhook-request', operation.id), }); const type = schemaToType({ plugin, diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/api.ts b/packages/openapi-ts/src/plugins/@pinia/colada/api.ts index 171c8310c..be883d4c4 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/api.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/api.ts @@ -28,13 +28,13 @@ export type IApi = { * - `UseQueryOptions`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@pinia/colada'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts b/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts index 59ce43f54..8d587d2fc 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts @@ -18,7 +18,7 @@ export const createMutationOptions = ({ queryFn: string; }): void => { const symbolMutationOptionsType = plugin.referenceSymbol( - plugin.api.getSelector('UseMutationOptions'), + plugin.api.selector('UseMutationOptions'), ); const typeData = useTypeData({ operation, plugin }); diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts index 4de223285..963791266 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts @@ -11,7 +11,7 @@ export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { kind: 'type', }, name: 'UseMutationOptions', - selector: plugin.api.getSelector('UseMutationOptions'), + selector: plugin.api.selector('UseMutationOptions'), }); plugin.registerSymbol({ external: plugin.name, @@ -19,7 +19,7 @@ export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { kind: 'type', }, name: 'UseQueryOptions', - selector: plugin.api.getSelector('UseQueryOptions'), + selector: plugin.api.selector('UseQueryOptions'), }); plugin.registerSymbol({ external: plugin.name, @@ -27,7 +27,7 @@ export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { kind: 'type', }, name: '_JSONValue', - selector: plugin.api.getSelector('_JSONValue'), + selector: plugin.api.selector('_JSONValue'), }); plugin.registerSymbol({ external: 'axios', @@ -35,7 +35,7 @@ export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { kind: 'type', }, name: 'AxiosError', - selector: plugin.api.getSelector('AxiosError'), + selector: plugin.api.selector('AxiosError'), }); const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); @@ -55,7 +55,7 @@ export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { entry ? [ plugin.referenceSymbol( - sdkPlugin.api.getSelector('class', entry.path[0]), + sdkPlugin.api.selector('class', entry.path[0]), ).placeholder, ...entry.path.slice(1).map((className: string) => stringCase({ @@ -68,7 +68,7 @@ export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { .filter(Boolean) .join('.') : plugin.referenceSymbol( - sdkPlugin.api.getSelector('function', operation.id), + sdkPlugin.api.selector('function', operation.id), ).placeholder; if (plugin.hooks.operation.isQuery(operation)) { diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts b/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts index ea619179e..b53b75f6a 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts @@ -30,13 +30,13 @@ export const createQueryKeyFunction = ({ }, name: 'createQueryKey', }), - selector: plugin.api.getSelector('createQueryKey'), + selector: plugin.api.selector('createQueryKey'), }); const symbolQueryKeyType = plugin.referenceSymbol( - plugin.api.getSelector('QueryKey'), + plugin.api.selector('QueryKey'), ); const symbolJsonValue = plugin.referenceSymbol( - plugin.api.getSelector('_JSONValue'), + plugin.api.selector('_JSONValue'), ); const returnType = tsc.indexedAccessTypeNode({ @@ -53,14 +53,14 @@ export const createQueryKeyFunction = ({ const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const client = getClientPlugin(plugin.context.config); const symbolClient = - client.api && 'getSelector' in client.api + client.api && 'selector' in client.api ? plugin.getSymbol( // @ts-expect-error - client.api.getSelector('client'), + client.api.selector('client'), ) : undefined; @@ -289,7 +289,7 @@ const createQueryKeyLiteral = ({ } const symbolCreateQueryKey = plugin.referenceSymbol( - plugin.api.getSelector('createQueryKey'), + plugin.api.selector('createQueryKey'), ); const createQueryKeyCallExpression = tsc.callExpression({ functionName: symbolCreateQueryKey.placeholder, @@ -304,7 +304,7 @@ export const createQueryKeyType = ({ plugin: PiniaColadaPlugin['Instance']; }) => { const symbolJsonValue = plugin.referenceSymbol( - plugin.api.getSelector('_JSONValue'), + plugin.api.selector('_JSONValue'), ); const properties: Array = [ @@ -333,13 +333,13 @@ export const createQueryKeyType = ({ const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const symbolQueryKeyType = plugin.registerSymbol({ exported: true, meta: { kind: 'type' }, name: 'QueryKey', - selector: plugin.api.getSelector('QueryKey'), + selector: plugin.api.selector('QueryKey'), }); const queryKeyType = tsc.typeAliasDeclaration({ exportType: symbolQueryKeyType.exported, diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts b/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts index 33a696d28..b26ed22c2 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts @@ -37,7 +37,7 @@ export const createQueryOptions = ({ context: plugin.context, operation, }); - if (!plugin.getSymbol(plugin.api.getSelector('createQueryKey'))) { + if (!plugin.getSymbol(plugin.api.selector('createQueryKey'))) { createQueryKeyType({ plugin }); createQueryKeyFunction({ plugin }); } @@ -63,7 +63,7 @@ export const createQueryOptions = ({ }); } else { const symbolCreateQueryKey = plugin.referenceSymbol( - plugin.api.getSelector('createQueryKey'), + plugin.api.selector('createQueryKey'), ); // Optionally include tags when configured let tagsExpr: ts.Expression | undefined; @@ -155,12 +155,12 @@ export const createQueryOptions = ({ config: plugin.config.queryOptions, name: operation.id, }), - selector: plugin.api.getSelector('queryOptionsFn', operation.id), + selector: plugin.api.selector('queryOptionsFn', operation.id), }); const symbolDefineQueryOptions = plugin.registerSymbol({ external: plugin.name, name: 'defineQueryOptions', - selector: plugin.api.getSelector('defineQueryOptions'), + selector: plugin.api.selector('defineQueryOptions'), }); const statement = tsc.constVariable({ comment: plugin.config.comments diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts b/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts index 245051b8a..9bbd47396 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts @@ -26,7 +26,7 @@ export const useTypeError = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolErrorType = plugin.getSymbol( - pluginTypeScript.api.getSelector('error', operation.id), + pluginTypeScript.api.selector('error', operation.id), ); let typeErrorName: string | undefined = symbolErrorType?.placeholder; @@ -34,7 +34,7 @@ export const useTypeError = ({ typeErrorName = 'Error'; } if (client.name === '@hey-api/client-axios') { - const symbol = plugin.referenceSymbol(plugin.api.getSelector('AxiosError')); + const symbol = plugin.referenceSymbol(plugin.api.selector('AxiosError')); typeErrorName = `${symbol.placeholder}<${typeErrorName}>`; } return typeErrorName; @@ -49,7 +49,7 @@ export const useTypeResponse = ({ }): string => { const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolResponseType = plugin.getSymbol( - pluginTypeScript.api.getSelector('response', operation.id), + pluginTypeScript.api.selector('response', operation.id), ); return symbolResponseType?.placeholder || 'unknown'; }; diff --git a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts index 9086df45f..89b5fd940 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts @@ -32,7 +32,7 @@ export type IApi = { * - `useQuery`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { @@ -40,7 +40,7 @@ export class Api implements IApi { public meta: Plugin.Name<'@tanstack/angular-query-experimental'>, ) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts index 66d566b4a..adabe1c92 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts @@ -27,7 +27,7 @@ const createInfiniteParamsFunction = ({ }, name: 'createInfiniteParams', }), - selector: plugin.api.getSelector('createInfiniteParams'), + selector: plugin.api.selector('createInfiniteParams'), }); const fn = tsc.constVariable({ @@ -238,20 +238,20 @@ export const createInfiniteQueryOptions = ({ operation, }); - if (!plugin.getSymbol(plugin.api.getSelector('createQueryKey'))) { + if (!plugin.getSymbol(plugin.api.selector('createQueryKey'))) { createQueryKeyType({ plugin }); createQueryKeyFunction({ plugin }); } - if (!plugin.getSymbol(plugin.api.getSelector('createInfiniteParams'))) { + if (!plugin.getSymbol(plugin.api.selector('createInfiniteParams'))) { createInfiniteParamsFunction({ plugin }); } const symbolInfiniteQueryOptions = plugin.referenceSymbol( - plugin.api.getSelector('infiniteQueryOptions'), + plugin.api.selector('infiniteQueryOptions'), ); const symbolInfiniteDataType = plugin.referenceSymbol( - plugin.api.getSelector('InfiniteData'), + plugin.api.selector('InfiniteData'), ); const typeData = useTypeData({ operation, plugin }); @@ -259,7 +259,7 @@ export const createInfiniteQueryOptions = ({ const typeResponse = useTypeResponse({ operation, plugin }); const symbolQueryKeyType = plugin.referenceSymbol( - plugin.api.getSelector('QueryKey'), + plugin.api.selector('QueryKey'), ); const typeQueryKey = `${symbolQueryKeyType.placeholder}<${typeData}>`; const typePageObjectParam = `Pick<${typeQueryKey}[0], 'body' | 'headers' | 'path' | 'query'>`; @@ -322,7 +322,7 @@ export const createInfiniteQueryOptions = ({ }); const symbolCreateInfiniteParams = plugin.referenceSymbol( - plugin.api.getSelector('createInfiniteParams'), + plugin.api.selector('createInfiniteParams'), ); const statements: Array = [ diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts index 6084b516c..26d6f2a02 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts @@ -17,7 +17,7 @@ export const createMutationOptions = ({ queryFn: string; }): void => { const symbolMutationOptionsType = plugin.referenceSymbol( - plugin.api.getSelector('MutationOptions'), + plugin.api.selector('MutationOptions'), ); const typeData = useTypeData({ operation, plugin }); diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts index 73baee276..4e762c4b3 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts @@ -13,7 +13,7 @@ export const handler: PluginHandler = ({ plugin }) => { kind: 'type', }, name: 'DefaultError', - selector: plugin.api.getSelector('DefaultError'), + selector: plugin.api.selector('DefaultError'), }); plugin.registerSymbol({ external: plugin.name, @@ -21,7 +21,7 @@ export const handler: PluginHandler = ({ plugin }) => { kind: 'type', }, name: 'InfiniteData', - selector: plugin.api.getSelector('InfiniteData'), + selector: plugin.api.selector('InfiniteData'), }); const mutationsType = plugin.name === '@tanstack/angular-query-experimental' || @@ -35,22 +35,22 @@ export const handler: PluginHandler = ({ plugin }) => { kind: 'type', }, name: mutationsType, - selector: plugin.api.getSelector('MutationOptions'), + selector: plugin.api.selector('MutationOptions'), }); plugin.registerSymbol({ external: plugin.name, name: 'infiniteQueryOptions', - selector: plugin.api.getSelector('infiniteQueryOptions'), + selector: plugin.api.selector('infiniteQueryOptions'), }); plugin.registerSymbol({ external: plugin.name, name: 'queryOptions', - selector: plugin.api.getSelector('queryOptions'), + selector: plugin.api.selector('queryOptions'), }); plugin.registerSymbol({ external: plugin.name, name: 'useQuery', - selector: plugin.api.getSelector('useQuery'), + selector: plugin.api.selector('useQuery'), }); plugin.registerSymbol({ external: 'axios', @@ -58,7 +58,7 @@ export const handler: PluginHandler = ({ plugin }) => { kind: 'type', }, name: 'AxiosError', - selector: plugin.api.getSelector('AxiosError'), + selector: plugin.api.selector('AxiosError'), }); const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); @@ -78,7 +78,7 @@ export const handler: PluginHandler = ({ plugin }) => { entry ? [ plugin.referenceSymbol( - sdkPlugin.api.getSelector('class', entry.path[0]), + sdkPlugin.api.selector('class', entry.path[0]), ).placeholder, ...entry.path.slice(1).map((className) => stringCase({ @@ -91,7 +91,7 @@ export const handler: PluginHandler = ({ plugin }) => { .filter(Boolean) .join('.') : plugin.referenceSymbol( - sdkPlugin.api.getSelector('function', operation.id), + sdkPlugin.api.selector('function', operation.id), ).placeholder; if (plugin.hooks.operation.isQuery(operation)) { diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts index 08a80d370..1adf95747 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts @@ -29,10 +29,10 @@ export const createQueryKeyFunction = ({ }, name: 'createQueryKey', }), - selector: plugin.api.getSelector('createQueryKey'), + selector: plugin.api.selector('createQueryKey'), }); const symbolQueryKeyType = plugin.referenceSymbol( - plugin.api.getSelector('QueryKey'), + plugin.api.selector('QueryKey'), ); const returnType = tsc.indexedAccessTypeNode({ @@ -49,16 +49,16 @@ export const createQueryKeyFunction = ({ const client = getClientPlugin(plugin.context.config); const symbolClient = - client.api && 'getSelector' in client.api + client.api && 'selector' in client.api ? plugin.getSymbol( // @ts-expect-error - client.api.getSelector('client'), + client.api.selector('client'), ) : undefined; const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const fn = tsc.constVariable({ @@ -275,7 +275,7 @@ const createQueryKeyLiteral = ({ } const symbolCreateQueryKey = plugin.referenceSymbol( - plugin.api.getSelector('createQueryKey'), + plugin.api.selector('createQueryKey'), ); const createQueryKeyCallExpression = tsc.callExpression({ functionName: symbolCreateQueryKey.placeholder, @@ -311,7 +311,7 @@ export const createQueryKeyType = ({ plugin }: { plugin: PluginInstance }) => { const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); const symbolOptions = plugin.referenceSymbol( - sdkPlugin.api.getSelector('Options'), + sdkPlugin.api.selector('Options'), ); const symbolQueryKeyType = plugin.registerSymbol({ exported: true, @@ -319,7 +319,7 @@ export const createQueryKeyType = ({ plugin }: { plugin: PluginInstance }) => { kind: 'type', }, name: 'QueryKey', - selector: plugin.api.getSelector('QueryKey'), + selector: plugin.api.selector('QueryKey'), }); const queryKeyType = tsc.typeAliasDeclaration({ exportType: symbolQueryKeyType.exported, diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts index de68661af..25b52f9e4 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts @@ -36,13 +36,13 @@ export const createQueryOptions = ({ operation, }); - if (!plugin.getSymbol(plugin.api.getSelector('createQueryKey'))) { + if (!plugin.getSymbol(plugin.api.selector('createQueryKey'))) { createQueryKeyType({ plugin }); createQueryKeyFunction({ plugin }); } const symbolQueryOptions = plugin.referenceSymbol( - plugin.api.getSelector('queryOptions'), + plugin.api.selector('queryOptions'), ); const symbolQueryKey = plugin.registerSymbol({ @@ -160,7 +160,7 @@ export const createQueryOptions = ({ config: plugin.config.queryOptions, name: operation.id, }), - selector: plugin.api.getSelector('queryOptionsFn', operation.id), + selector: plugin.api.selector('queryOptionsFn', operation.id), }); const statement = tsc.constVariable({ comment: plugin.config.comments diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts index 085e72052..03c88edca 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts @@ -36,7 +36,7 @@ export const createUseQuery = ({ }); const symbolUseQuery = plugin.referenceSymbol( - plugin.api.getSelector('useQuery'), + plugin.api.selector('useQuery'), ); const isRequiredOptions = isOperationOptionsRequired({ @@ -46,7 +46,7 @@ export const createUseQuery = ({ const typeData = useTypeData({ operation, plugin }); const symbolQueryOptionsFn = plugin.referenceSymbol( - plugin.api.getSelector('queryOptionsFn', operation.id), + plugin.api.selector('queryOptionsFn', operation.id), ); const statement = tsc.constVariable({ comment: plugin.config.comments diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts index 41f0b6624..18e85cef0 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts @@ -26,18 +26,16 @@ export const useTypeError = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolErrorType = plugin.getSymbol( - pluginTypeScript.api.getSelector('error', operation.id), + pluginTypeScript.api.selector('error', operation.id), ); let typeErrorName: string | undefined = symbolErrorType?.placeholder; if (!typeErrorName) { - const symbol = plugin.referenceSymbol( - plugin.api.getSelector('DefaultError'), - ); + const symbol = plugin.referenceSymbol(plugin.api.selector('DefaultError')); typeErrorName = symbol.placeholder; } if (client.name === '@hey-api/client-axios') { - const symbol = plugin.referenceSymbol(plugin.api.getSelector('AxiosError')); + const symbol = plugin.referenceSymbol(plugin.api.selector('AxiosError')); typeErrorName = `${symbol.placeholder}<${typeErrorName}>`; } return typeErrorName; @@ -52,7 +50,7 @@ export const useTypeResponse = ({ }): string => { const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolResponseType = plugin.getSymbol( - pluginTypeScript.api.getSelector('response', operation.id), + pluginTypeScript.api.selector('response', operation.id), ); return symbolResponseType?.placeholder || 'unknown'; }; diff --git a/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts index 33357cb95..1023a72a1 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts @@ -32,13 +32,13 @@ export type IApi = { * - `useQuery`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@tanstack/react-query'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts index b21c391db..b6f51efa1 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts @@ -32,13 +32,13 @@ export type IApi = { * - `useQuery`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@tanstack/solid-query'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts index db6b292e6..50880f8d9 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts @@ -32,13 +32,13 @@ export type IApi = { * - `useQuery`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@tanstack/svelte-query'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts index 72a6c77b0..334f0efed 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts @@ -32,13 +32,13 @@ export type IApi = { * - `useQuery`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'@tanstack/vue-query'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/arktype/api.ts b/packages/openapi-ts/src/plugins/arktype/api.ts index b9b505206..610106a3b 100644 --- a/packages/openapi-ts/src/plugins/arktype/api.ts +++ b/packages/openapi-ts/src/plugins/arktype/api.ts @@ -35,7 +35,7 @@ export type IApi = { * - `webhook-request`: `operation.id` string * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { @@ -49,7 +49,7 @@ export class Api implements IApi { return createResponseValidatorV2(args); } - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/arktype/constants.ts b/packages/openapi-ts/src/plugins/arktype/constants.ts index 03d831365..7744ee056 100644 --- a/packages/openapi-ts/src/plugins/arktype/constants.ts +++ b/packages/openapi-ts/src/plugins/arktype/constants.ts @@ -1,6 +1,77 @@ import { tsc } from '../../tsc'; export const identifiers = { + keywords: { + false: tsc.identifier({ text: 'false' }), + true: tsc.identifier({ text: 'true' }), + }, + /** + * {@link https://arktype.io/docs/primitives#number Number} + */ + number: { + Infinity: tsc.identifier({ text: 'Infinity' }), + NaN: tsc.identifier({ text: 'NaN' }), + NegativeInfinity: tsc.identifier({ text: 'NegativeInfinity' }), + epoch: tsc.identifier({ text: 'epoch' }), + integer: tsc.identifier({ text: 'integer' }), + safe: tsc.identifier({ text: 'safe' }), + }, + /** + * {@link https://arktype.io/docs/primitives Primitives} + */ + primitives: { + bigint: tsc.identifier({ text: 'bigint' }), + boolean: tsc.identifier({ text: 'boolean' }), + keywords: tsc.identifier({ text: 'keywords' }), + null: 'null', + number: tsc.identifier({ text: 'number' }), + string: 'string', + symbol: tsc.identifier({ text: 'symbol' }), + undefined: tsc.identifier({ text: 'undefined' }), + unit: tsc.identifier({ text: 'unit' }), + }, + /** + * {@link https://arktype.io/docs/primitives#string String} + */ + string: { + NFC: tsc.identifier({ text: 'NFC' }), + NFD: tsc.identifier({ text: 'NFD' }), + NFKC: tsc.identifier({ text: 'NFKC' }), + NFKD: tsc.identifier({ text: 'NFKD' }), + alpha: tsc.identifier({ text: 'alpha' }), + alphanumeric: tsc.identifier({ text: 'alphanumeric' }), + base64: tsc.identifier({ text: 'base64' }), + capitalize: tsc.identifier({ text: 'capitalize' }), + creditCard: tsc.identifier({ text: 'creditCard' }), + date: 'date', + digits: tsc.identifier({ text: 'digits' }), + email: 'email', + epoch: tsc.identifier({ text: 'epoch' }), + hex: tsc.identifier({ text: 'hex' }), + integer: tsc.identifier({ text: 'integer' }), + ip: 'ip', + iso: 'iso', + json: tsc.identifier({ text: 'json' }), + lower: tsc.identifier({ text: 'lower' }), + normalize: tsc.identifier({ text: 'normalize' }), + numeric: tsc.identifier({ text: 'numeric' }), + parse: tsc.identifier({ text: 'parse' }), + preformatted: tsc.identifier({ text: 'preformatted' }), + regex: tsc.identifier({ text: 'regex' }), + semver: tsc.identifier({ text: 'semver' }), + trim: tsc.identifier({ text: 'trim' }), + upper: tsc.identifier({ text: 'upper' }), + url: 'url', + uuid: 'uuid', + v1: 'v1', + v2: 'v2', + v3: 'v3', + v4: 'v4', + v5: 'v5', + v6: 'v6', + v7: 'v7', + v8: 'v8', + }, /** * {@link https://arktype.io/docs/type-api Type API} */ diff --git a/packages/openapi-ts/src/plugins/arktype/shared/export.ts b/packages/openapi-ts/src/plugins/arktype/shared/export.ts index 2d2226c4c..c08731bd8 100644 --- a/packages/openapi-ts/src/plugins/arktype/shared/export.ts +++ b/packages/openapi-ts/src/plugins/arktype/shared/export.ts @@ -21,12 +21,21 @@ export const exportAst = ({ symbol: Symbol; typeInferSymbol: Symbol | undefined; }): void => { + const type = plugin.referenceSymbol( + plugin.api.selector('external', 'arktype.type'), + ); + const statement = tsc.constVariable({ comment: plugin.config.comments ? createSchemaComment({ schema }) : undefined, exportConst: symbol.exported, - expression: ast.expression, + expression: tsc.callExpression({ + functionName: type.placeholder, + parameters: [ + ast.def ? tsc.stringLiteral({ text: ast.def }) : ast.expression, + ], + }), name: symbol.placeholder, // typeName: ast.typeName // ? (tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts index 95991994e..9910ba1b6 100644 --- a/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts @@ -4,6 +4,7 @@ import type { IR } from '../../../ir/types'; import type { ArktypePlugin } from '../types'; export type Ast = { + def: string; expression: ts.Expression; hasCircularReference?: boolean; typeName?: string | ts.Identifier; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/api.ts b/packages/openapi-ts/src/plugins/arktype/v2/api.ts index 65589ed60..9dfeb7bcb 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/api.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/api.ts @@ -8,9 +8,23 @@ export const createRequestValidatorV2 = ({ operation, plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { - const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + const symbol = plugin.getSymbol(plugin.api.selector('data', operation.id)); if (!symbol) return; + // const out = User({ + // name: "Alan Turing", + // device: { + // platform: "enigma", + // versions: [0, "1", 0n] + // } + // }) + // if (out instanceof type.errors) { + // // hover out.summary to see validation errors + // console.error(out.summary) + // } else { + // // hover out to see your validated data + // console.log(`Hello, ${out.name}`) + // } const dataParameterName = 'data'; return tsc.arrowFunction({ @@ -42,7 +56,7 @@ export const createResponseValidatorV2 = ({ plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { const symbol = plugin.getSymbol( - plugin.api.getSelector('responses', operation.id), + plugin.api.selector('responses', operation.id), ); if (!symbol) return; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts index 492751647..3ad82dc76 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts @@ -1,8 +1,9 @@ -import ts from 'typescript'; - +import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; -import { refToName } from '../../../utils/ref'; +import { tsc } from '../../../tsc'; +import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import type { SchemaWithType } from '../../shared/types/schema'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; import { exportAst } from '../shared/export'; import type { Ast, IrSchemaToAstOptions } from '../shared/types'; @@ -26,98 +27,103 @@ export const irSchemaToAst = ({ let ast: Partial = {}; // const z = plugin.referenceSymbol( - // plugin.api.getSelector('external', 'zod.z'), + // plugin.api.selector('external', 'zod.z'), // ); if (schema.$ref) { - // const isCircularReference = state.circularReferenceTracker.includes( - // schema.$ref, - // ); - // const isSelfReference = state.currentReferenceTracker.includes(schema.$ref); - // state.circularReferenceTracker.push(schema.$ref); - // state.currentReferenceTracker.push(schema.$ref); - // const selector = plugin.api.getSelector('ref', schema.$ref); - // let symbol = plugin.getSymbol(selector); - // if (isCircularReference) { - // if (!symbol) { - // symbol = plugin.referenceSymbol(selector); - // } - // if (isSelfReference) { - // ast.expression = tsc.callExpression({ - // functionName: tsc.propertyAccessExpression({ - // expression: z.placeholder, - // name: identifiers.lazy, - // }), - // parameters: [ - // tsc.arrowFunction({ - // returnType: tsc.keywordTypeNode({ keyword: 'any' }), - // statements: [ - // tsc.returnStatement({ - // expression: tsc.identifier({ text: symbol.placeholder }), - // }), - // ], - // }), - // ], - // }); - // } else { - // ast.expression = tsc.identifier({ text: symbol.placeholder }); - // } - // ast.hasCircularReference = schema.circular; - // } else { - // if (!symbol) { - // // if $ref hasn't been processed yet, inline it to avoid the - // // "Block-scoped variable used before its declaration." error - // // this could be (maybe?) fixed by reshuffling the generation order - // const ref = plugin.context.resolveIrRef(schema.$ref); - // handleComponent({ - // id: schema.$ref, - // plugin, - // schema: ref, - // state: { - // ...state, - // _path: jsonPointerToPath(schema.$ref), - // }, - // }); - // } else { - // ast.hasCircularReference = schema.circular; - // } - // const refSymbol = plugin.referenceSymbol(selector); - // ast.expression = tsc.identifier({ text: refSymbol.placeholder }); - // } - // state.circularReferenceTracker.pop(); - // state.currentReferenceTracker.pop(); + const isCircularReference = state.circularReferenceTracker.includes( + schema.$ref, + ); + const isSelfReference = state.currentReferenceTracker.includes(schema.$ref); + state.circularReferenceTracker.push(schema.$ref); + state.currentReferenceTracker.push(schema.$ref); + const selector = plugin.api.selector('ref', schema.$ref); + let symbol = plugin.getSymbol(selector); + if (isCircularReference) { + if (!symbol) { + symbol = plugin.referenceSymbol(selector); + } + if (isSelfReference) { + ast.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + // expression: z.placeholder, + expression: 'TODO', + name: 'TODO', + // name: identifiers.lazy, + }), + parameters: [ + tsc.arrowFunction({ + returnType: tsc.keywordTypeNode({ keyword: 'any' }), + statements: [ + tsc.returnStatement({ + expression: tsc.identifier({ text: symbol.placeholder }), + }), + ], + }), + ], + }); + } else { + ast.expression = tsc.identifier({ text: symbol.placeholder }); + } + ast.hasCircularReference = schema.circular; + } else { + if (!symbol) { + // if $ref hasn't been processed yet, inline it to avoid the + // "Block-scoped variable used before its declaration." error + // this could be (maybe?) fixed by reshuffling the generation order + const ref = plugin.context.resolveIrRef(schema.$ref); + handleComponent({ + id: schema.$ref, + plugin, + schema: ref, + state: { + ...state, + _path: jsonPointerToPath(schema.$ref), + }, + }); + } else { + ast.hasCircularReference = schema.circular; + } + const refSymbol = plugin.referenceSymbol(selector); + ast.expression = tsc.identifier({ text: refSymbol.placeholder }); + } + state.circularReferenceTracker.pop(); + state.currentReferenceTracker.pop(); } else if (schema.type) { - // const zSchema = irSchemaWithTypeToAst({ - // plugin, - // schema: schema as SchemaWithType, - // state, - // }); - // ast.expression = zSchema.expression; - // ast.hasCircularReference = zSchema.hasCircularReference; - // if (plugin.config.metadata && schema.description) { - // ast.expression = tsc.callExpression({ - // functionName: tsc.propertyAccessExpression({ - // expression: ast.expression, - // name: identifiers.register, - // }), - // parameters: [ - // tsc.propertyAccessExpression({ - // expression: z.placeholder, - // name: identifiers.globalRegistry, - // }), - // tsc.objectExpression({ - // obj: [ - // { - // key: 'description', - // value: tsc.stringLiteral({ text: schema.description }), - // }, - // ], - // }), - // ], - // }); - // } + const typeAst = irSchemaWithTypeToAst({ + plugin, + schema: schema as SchemaWithType, + state, + }); + ast.def = typeAst.def; + ast.expression = typeAst.expression; + ast.hasCircularReference = typeAst.hasCircularReference; + + if (plugin.config.metadata && schema.description) { + // TODO: add description + // ast.expression = tsc.callExpression({ + // functionName: tsc.propertyAccessExpression({ + // expression: ast.expression, + // name: identifiers.register, + // }), + // parameters: [ + // tsc.propertyAccessExpression({ + // expression: z.placeholder, + // name: identifiers.globalRegistry, + // }), + // tsc.objectExpression({ + // obj: [ + // { + // key: 'description', + // value: tsc.stringLiteral({ text: schema.description }), + // }, + // ], + // }), + // ], + // }); + } } else if (schema.items) { - // schema = deduplicateSchema({ schema }); + schema = deduplicateSchema({ schema }); if (schema.items) { // const itemSchemas = schema.items.map((item, index) => @@ -190,33 +196,32 @@ export const irSchemaToAst = ({ // }); // } } else { - ast = irSchemaToAst({ - plugin, - schema, - state, - }); + ast = irSchemaToAst({ plugin, schema, state }); } } else { - // // catch-all fallback for failed schemas - // const zSchema = irSchemaWithTypeToAst({ - // plugin, - // schema: { - // type: 'unknown', - // }, - // state, - // }); - // ast.expression = zSchema.expression; + // catch-all fallback for failed schemas + const typeAst = irSchemaWithTypeToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + ast.def = typeAst.def; + ast.expression = typeAst.expression; } // TODO: remove later - const zSchema = irSchemaWithTypeToAst({ - plugin, - schema: { - type: 'unknown', - }, - state, - }); - ast.expression = zSchema.expression; + if (!ast.expression) { + const typeAst = irSchemaWithTypeToAst({ + plugin, + schema: { + type: 'unknown', + }, + state, + }); + ast.expression = typeAst.expression; + } // END TODO: remove later // if (ast.expression) { @@ -278,7 +283,7 @@ const handleComponent = ({ hasCircularReference: _state?.hasCircularReference ?? false, }; - const selector = plugin.api.getSelector('ref', id); + const selector = plugin.api.selector('ref', id); let symbol = plugin.getSymbol(selector); if (symbol && !plugin.getSymbolValue(symbol)) return; @@ -307,7 +312,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.getSelector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', id), }) : undefined; exportAst({ @@ -320,10 +325,10 @@ const handleComponent = ({ }; export const handlerV2: ArktypePlugin['Handler'] = ({ plugin }) => { - const typeSymbol = plugin.registerSymbol({ + plugin.registerSymbol({ external: 'arktype', name: 'type', - selector: plugin.api.getSelector('external', 'arktype.type'), + selector: plugin.api.selector('external', 'arktype.type'), }); plugin.forEach( @@ -333,62 +338,6 @@ export const handlerV2: ArktypePlugin['Handler'] = ({ plugin }) => { 'schema', 'webhook', (event) => { - if (event.type === 'schema') { - const symbol = plugin.registerSymbol({ - exported: true, - meta: { - resourceType: pathToSymbolResourceType([ - 'components', - 'schemas', - event.name, - ]), - }, - name: event.name, - selector: plugin.api.getSelector('ref', event.$ref), - }); - - const userNode = ts.factory.createVariableStatement( - [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - symbol.placeholder, - undefined, - undefined, - ts.factory.createCallExpression( - ts.factory.createIdentifier(typeSymbol.placeholder), - undefined, - [ - ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment( - 'name', - ts.factory.createStringLiteral('string'), - ), - ts.factory.createPropertyAssignment( - 'platform', - ts.factory.createStringLiteral("'android' | 'ios'"), - ), - ts.factory.createPropertyAssignment( - ts.factory.createComputedPropertyName( - ts.factory.createStringLiteral('versions?'), - ), - ts.factory.createStringLiteral('(number | string)[]'), - ), - ], - true, - ), - ], - ), - ), - ], - ts.NodeFlags.Const, - ), - ); - - plugin.setSymbolValue(symbol, userNode); - } - switch (event.type) { // case 'operation': // operationToZodSchema({ diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts index ec06668b0..dc37a0879 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts @@ -2,14 +2,14 @@ import ts from 'typescript'; import type { SchemaWithType } from '../../../shared/types/schema'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { nullToAst } from './null'; +import { objectToAst } from './object'; +import { stringToAst } from './string'; // import { arrayToAst } from "./array"; // import { booleanToAst } from "./boolean"; // import { enumToAst } from "./enum"; // import { neverToAst } from "./never"; -// import { nullToAst } from "./null"; // import { numberToAst } from "./number"; -// import { objectToAst } from "./object"; -// import { stringToAst } from "./string"; // import { tupleToAst } from "./tuple"; // import { undefinedToAst } from "./undefined"; // import { unknownToAst } from "./unknown"; @@ -21,36 +21,6 @@ export const irSchemaWithTypeToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType; }): Omit => { - const type = args.plugin.referenceSymbol( - args.plugin.api.getSelector('external', 'arktype.type'), - ); - - const expression = ts.factory.createCallExpression( - ts.factory.createIdentifier(type.placeholder), - undefined, - [ - ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment( - 'name', - ts.factory.createStringLiteral('string'), - ), - ts.factory.createPropertyAssignment( - 'platform', - ts.factory.createStringLiteral("'android' | 'ios'"), - ), - ts.factory.createPropertyAssignment( - ts.factory.createComputedPropertyName( - ts.factory.createStringLiteral('versions?'), - ), - ts.factory.createStringLiteral('(number | string)[]'), - ), - ], - true, - ), - ], - ); - switch (schema.type) { // case 'array': // return arrayToAst({ @@ -78,21 +48,21 @@ export const irSchemaWithTypeToAst = ({ // ...args, // schema: schema as SchemaWithType<'never'>, // }); - // case 'null': - // return nullToAst({ - // ...args, - // schema: schema as SchemaWithType<'null'>, - // }); - // case 'object': - // return objectToAst({ - // ...args, - // schema: schema as SchemaWithType<'object'>, - // }); - // case 'string': - // return stringToAst({ - // ...args, - // schema: schema as SchemaWithType<'string'>, - // }); + case 'null': + return nullToAst({ + ...args, + schema: schema as SchemaWithType<'null'>, + }); + case 'object': + return objectToAst({ + ...args, + schema: schema as SchemaWithType<'object'>, + }); + case 'string': + return stringToAst({ + ...args, + schema: schema as SchemaWithType<'string'>, + }); // case 'tuple': // return tupleToAst({ // ...args, @@ -113,10 +83,41 @@ export const irSchemaWithTypeToAst = ({ // ...args, // schema: schema as SchemaWithType<'void'>, // }); - default: - return { - expression, - hasCircularReference: false, - }; } + + const type = args.plugin.referenceSymbol( + args.plugin.api.selector('external', 'arktype.type'), + ); + + const expression = ts.factory.createCallExpression( + ts.factory.createIdentifier(type.placeholder), + undefined, + [ + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + 'name', + ts.factory.createStringLiteral('string'), + ), + ts.factory.createPropertyAssignment( + 'platform', + ts.factory.createStringLiteral("'android' | 'ios'"), + ), + ts.factory.createPropertyAssignment( + ts.factory.createComputedPropertyName( + ts.factory.createStringLiteral('versions?'), + ), + ts.factory.createStringLiteral('(number | string)[]'), + ), + ], + true, + ), + ], + ); + + return { + def: '', + expression, + hasCircularReference: false, + }; }; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts new file mode 100644 index 000000000..ba1ce6731 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts @@ -0,0 +1,14 @@ +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const nullToAst = ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _args: IrSchemaToAstOptions & { + schema: SchemaWithType<'null'>; + }, +): Omit => { + const result: Partial> = {}; + result.def = identifiers.primitives.null; + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts new file mode 100644 index 000000000..d17157b1a --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts @@ -0,0 +1,170 @@ +import ts from 'typescript'; + +import { tsc } from '../../../../tsc'; +import { numberRegExp } from '../../../../utils/regexp'; +import type { SchemaWithType } from '../../../shared/types/schema'; +// import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; +import { irSchemaToAst } from '../plugin'; + +export const objectToAst = ({ + plugin, + schema, + state, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'object'>; +}): Omit => { + const result: Partial> = {}; + + // TODO: parser - handle constants + const properties: Array = + []; + + const required = schema.required ?? []; + + for (const name in schema.properties) { + const property = schema.properties[name]!; + const isRequired = required.includes(name); + + const propertyAst = irSchemaToAst({ + optional: !isRequired, + plugin, + schema: property, + state: { + ...state, + _path: [...state._path, 'properties', name], + }, + }); + if (propertyAst.hasCircularReference) { + result.hasCircularReference = true; + } + + let propertyName: + | ts.ComputedPropertyName + | ts.StringLiteral + | ts.NumericLiteral + | string = isRequired ? name : `${name}?`; + + // if (propertyAst.hasCircularReference) { + // properties.push( + // tsc.getAccessorDeclaration({ + // name: propertyName, + // // @ts-expect-error + // returnType: propertyAst.typeName + // ? tsc.propertyAccessExpression({ + // expression: 'TODO', + // name: propertyAst.typeName, + // }) + // : undefined, + // statements: [ + // tsc.returnStatement({ + // expression: propertyAst.expression, + // }), + // ], + // }), + // ); + // } else { + // properties.push( + // tsc.propertyAssignment({ + // initializer: propertyAst.expression, + // name: ts.factory.createComputedPropertyName( + // ts.factory.createStringLiteral(`${propertyName}?`), + // ), + // }), + // ); + // } + + if (propertyName.endsWith('?')) { + propertyName = ts.factory.createComputedPropertyName( + tsc.stringLiteral({ text: propertyName }), + ); + } else { + // TODO: parser - abstract safe property name logic + if ( + ((propertyName.match(/^[0-9]/) && propertyName.match(/\D+/g)) || + propertyName.match(/\W/g)) && + !propertyName.startsWith("'") && + !propertyName.endsWith("'") + ) { + propertyName = `'${propertyName}'`; + } + + numberRegExp.lastIndex = 0; + if (numberRegExp.test(propertyName)) { + // For numeric literals, we'll handle negative numbers by using a string literal + // instead of trying to use a PrefixUnaryExpression + propertyName = propertyName.startsWith('-') + ? tsc.stringLiteral({ text: name }) + : ts.factory.createNumericLiteral(name); + } else { + propertyName = name; + } + } + properties.push( + tsc.propertyAssignment({ + initializer: propertyAst.expression, + name: propertyName, + }), + ); + } + + if ( + schema.additionalProperties && + (!schema.properties || !Object.keys(schema.properties).length) + ) { + const additionalAst = irSchemaToAst({ + plugin, + schema: schema.additionalProperties, + state: { + ...state, + _path: [...state._path, 'additionalProperties'], + }, + }); + result.expression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: 'TODO', + name: 'record', + // name: identifiers.record, + }), + parameters: [ + tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: 'TODO', + name: 'string', + // name: identifiers.string, + }), + parameters: [], + }), + additionalAst.expression, + ], + }); + if (additionalAst.hasCircularReference) { + result.hasCircularReference = true; + } + + // Return with typeName for circular references + if (result.hasCircularReference) { + return { + ...result, + typeName: 'TODO', + } as Ast; + } + + return result as Omit; + } + + result.expression = ts.factory.createObjectLiteralExpression( + properties, + true, + ); + + // return with typeName for circular references + if (result.hasCircularReference) { + return { + ...result, + typeName: 'TODO', + } as Ast; + } + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts new file mode 100644 index 000000000..9395de267 --- /dev/null +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts @@ -0,0 +1,65 @@ +import type { SchemaWithType } from '../../../shared/types/schema'; +import { identifiers } from '../../constants'; +import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; + +export const stringToAst = ({ + schema, +}: IrSchemaToAstOptions & { + schema: SchemaWithType<'string'>; +}): Omit => { + const result: Partial> = {}; + + if (typeof schema.const === 'string') { + result.def = schema.const; + return result as Omit; + } + + let def = identifiers.primitives.string; + + if (schema.format) { + switch (schema.format) { + case 'date': + case 'date-time': + case 'time': + def = `${def}.${identifiers.string.date}.${identifiers.string.iso}`; + break; + case 'email': + def = `${def}.${identifiers.string.email}`; + break; + case 'ipv4': + def = `${def}.${identifiers.string.ip}.${identifiers.string.v4}`; + break; + case 'ipv6': + def = `${def}.${identifiers.string.ip}.${identifiers.string.v6}`; + break; + case 'uri': + def = `${def}.${identifiers.string.url}`; + break; + case 'uuid': + def = `${def}.${identifiers.string.uuid}`; + break; + } + } + + if (schema.minLength === schema.maxLength && schema.minLength !== undefined) { + def = `${schema.minLength} <= ${def} <= ${schema.maxLength}`; + } else { + if (schema.maxLength !== undefined) { + def = `${def} <= ${schema.maxLength}`; + + if (schema.minLength !== undefined) { + def = `${schema.minLength} <= ${def}`; + } + } else if (schema.minLength !== undefined) { + def = `${def} >= ${schema.minLength}`; + } + } + + if (schema.pattern) { + def = `/${schema.pattern}/`; + } + + result.def = def; + + return result as Omit; +}; diff --git a/packages/openapi-ts/src/plugins/fastify/api.ts b/packages/openapi-ts/src/plugins/fastify/api.ts index b8b2cd8f7..eb1e5f9c1 100644 --- a/packages/openapi-ts/src/plugins/fastify/api.ts +++ b/packages/openapi-ts/src/plugins/fastify/api.ts @@ -11,13 +11,13 @@ export type IApi = { * - `RouteHandler`: never * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { constructor(public meta: Plugin.Name<'fastify'>) {} - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/fastify/plugin.ts b/packages/openapi-ts/src/plugins/fastify/plugin.ts index 7c79bcbdc..914ac43a5 100644 --- a/packages/openapi-ts/src/plugins/fastify/plugin.ts +++ b/packages/openapi-ts/src/plugins/fastify/plugin.ts @@ -17,7 +17,7 @@ const operationToRouteHandler = ({ const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); const symbolDataType = plugin.getSymbol( - pluginTypeScript.api.getSelector('data', operation.id), + pluginTypeScript.api.selector('data', operation.id), ); if (symbolDataType) { if (operation.body) { @@ -65,7 +65,7 @@ const operationToRouteHandler = ({ let errorsTypeReference: ts.TypeReferenceNode | undefined = undefined; const symbolErrorType = plugin.getSymbol( - pluginTypeScript.api.getSelector('errors', operation.id), + pluginTypeScript.api.selector('errors', operation.id), ); if (symbolErrorType && errors && errors.properties) { const keys = Object.keys(errors.properties); @@ -92,7 +92,7 @@ const operationToRouteHandler = ({ let responsesTypeReference: ts.TypeReferenceNode | undefined = undefined; const symbolResponseType = plugin.getSymbol( - pluginTypeScript.api.getSelector('responses', operation.id), + pluginTypeScript.api.selector('responses', operation.id), ); if (symbolResponseType && responses && responses.properties) { const keys = Object.keys(responses.properties); @@ -134,7 +134,7 @@ const operationToRouteHandler = ({ } const symbolRouteHandler = plugin.referenceSymbol( - plugin.api.getSelector('RouteHandler'), + plugin.api.selector('RouteHandler'), ); const routeHandler: Property = { name: operation.id, @@ -158,7 +158,7 @@ export const handler: FastifyPlugin['Handler'] = ({ plugin }) => { kind: 'type', }, name: 'RouteHandler', - selector: plugin.api.getSelector('RouteHandler'), + selector: plugin.api.selector('RouteHandler'), }); const symbolRouteHandlers = plugin.registerSymbol({ diff --git a/packages/openapi-ts/src/plugins/shared/types/refs.d.ts b/packages/openapi-ts/src/plugins/shared/types/refs.d.ts new file mode 100644 index 000000000..79c839311 --- /dev/null +++ b/packages/openapi-ts/src/plugins/shared/types/refs.d.ts @@ -0,0 +1,59 @@ +/** + * Ref wrapper which ensures a stable reference for a value. + * + * @example + * ```ts + * type NumRef = Ref; // { value: number } + * const num: NumRef = { value: 42 }; + * console.log(num.value); // 42 + * ``` + */ +type Ref = { value: T }; + +/** + * Utility type: wraps a value in a Ref. + * + * @example + * ```ts + * type R = ToRef; // { value: number } + * ``` + */ +export type ToRef = Ref; + +/** + * Utility type: unwraps a Ref to its value type. + * @example + * ```ts + * type N = FromRef<{ value: number }>; // number + * ``` + */ +export type FromRef = T extends Ref ? V : T; + +/** + * Maps every property of a Ref-wrapped object back to its plain value. + * + * @example + * ```ts + * type Foo = { a: number; b: string }; + * type Refs = ToRefs; // { a: Ref; b: Ref } + * type Foo2 = FromRefs; // { a: number; b: string } + * ``` + */ +export type FromRefs = { + [K in keyof T]: T[K] extends Ref ? V : T[K]; +}; + +/** + * Maps every property of `T` to a `Ref` of that property. + * + * @example + * ```ts + * type Foo = { a: number; b: string }; + * type Refs = ToRefs; // { a: Ref; b: Ref } + * const refs: Refs = { a: { value: 1 }, b: { value: 'x' } }; + * console.log(refs.a.value, refs.b.value); // 1 'x' + * ``` + */ +export type ToRefs = { + [K in keyof T]: Ref; +}; diff --git a/packages/openapi-ts/src/plugins/shared/utils/__tests__/refs.test.ts b/packages/openapi-ts/src/plugins/shared/utils/__tests__/refs.test.ts new file mode 100644 index 000000000..afc221d63 --- /dev/null +++ b/packages/openapi-ts/src/plugins/shared/utils/__tests__/refs.test.ts @@ -0,0 +1,85 @@ +import { describe, expect, it } from 'vitest'; + +import { fromRef, fromRefs, toRef, toRefs } from '../refs'; + +describe('toRef', () => { + it('wraps a primitive value', () => { + expect(toRef(42)).toEqual({ value: 42 }); + expect(toRef('x')).toEqual({ value: 'x' }); + }); + + it('wraps an object', () => { + const obj = { foo: 1 }; + expect(toRef(obj)).toEqual({ value: obj }); + }); +}); + +describe('fromRef', () => { + it('unwraps a primitive value', () => { + expect(fromRef({ value: 42 })).toBe(42); + expect(fromRef({ value: 'x' })).toBe('x'); + }); + + it('unwraps an object', () => { + const obj = { foo: 1 }; + expect(fromRef({ value: obj })).toBe(obj); + }); +}); + +describe('toRef <-> fromRef roundtrip', () => { + it('roundtrips value -> ref -> value', () => { + expect(fromRef(toRef(123))).toBe(123); + const obj = { foo: 'bar' }; + expect(fromRef(toRef(obj))).toBe(obj); + }); +}); + +describe('toRefs', () => { + it('wraps primitives', () => { + expect(toRefs({ a: 1, b: 'x' })).toEqual({ + a: { value: 1 }, + b: { value: 'x' }, + }); + }); + + it('wraps empty object', () => { + expect(toRefs({})).toEqual({}); + }); + + it('wraps nested objects shallowly', () => { + const input = { a: { foo: 1 }, b: [1, 2] }; + const refs = toRefs(input); + expect(refs.a.value).toEqual({ foo: 1 }); + expect(refs.b.value).toEqual([1, 2]); + }); +}); + +describe('fromRefs', () => { + it('unwraps primitives', () => { + expect(fromRefs({ a: { value: 1 }, b: { value: 'x' } })).toEqual({ + a: 1, + b: 'x', + }); + }); + + it('unwraps empty object', () => { + expect(fromRefs({})).toEqual({}); + }); + + it('unwraps nested objects shallowly', () => { + const input = { a: { value: { foo: 1 } }, b: { value: [1, 2] } }; + expect(fromRefs(input)).toEqual({ a: { foo: 1 }, b: [1, 2] }); + }); +}); + +describe('toRefs <-> fromRefs roundtrip', () => { + it('roundtrips plain -> refs -> plain', () => { + const obj = { a: 1, b: 'x', c: [1, 2], d: { foo: 2 } }; + expect(fromRefs(toRefs(obj))).toEqual(obj); + }); + + it('roundtrips refs -> plain -> refs', () => { + const refs = { a: { value: 1 }, b: { value: 'x' }, c: { value: [1, 2] } }; + expect(toRefs(fromRefs(refs))).toEqual(refs); + }); +}); diff --git a/packages/openapi-ts/src/plugins/shared/utils/refs.ts b/packages/openapi-ts/src/plugins/shared/utils/refs.ts new file mode 100644 index 000000000..616136906 --- /dev/null +++ b/packages/openapi-ts/src/plugins/shared/utils/refs.ts @@ -0,0 +1,66 @@ +import type { FromRefs, ToRefs } from '../types/refs'; + +/** + * Wraps a single value in a Ref object. + * + * @example + * ```ts + * const r = toRef(123); // { value: 123 } + * console.log(r.value); // 123 + * ``` + */ +export const toRef = (value: T): { value: T } => ({ value }); + +/** + * Unwraps a single Ref object to its value. + * + * @example + * ```ts + * const r = { value: 42 }; + * const n = fromRef(r); // 42 + * console.log(n); // 42 + * ``` + */ +export const fromRef = (ref: { value: T }): T => ref.value; + +/** + * Converts an object of Refs back to a plain object (unwraps all refs). + * + * @example + * ```ts + * const refs = { a: { value: 1 }, b: { value: "x" } }; + * const plain = fromRefs(refs); // { a: 1, b: "x" } + * ``` + */ +export const fromRefs = >>( + obj: T, +): FromRefs => { + const result = {} as FromRefs; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + result[key] = fromRef(obj[key]!) as (typeof result)[typeof key]; + } + } + return result; +}; + +/** + * Converts a plain object to an object of Refs (deep, per property). + * + * @example + * ```ts + * const obj = { a: 1, b: "x" }; + * const refs = toRefs(obj); // { a: { value: 1 }, b: { value: "x" } } + * ``` + */ +export const toRefs = >( + obj: T, +): ToRefs => { + const result = {} as ToRefs; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + result[key] = toRef(obj[key]); + } + } + return result; +}; diff --git a/packages/openapi-ts/src/plugins/valibot/api.ts b/packages/openapi-ts/src/plugins/valibot/api.ts index 7a1813f69..f59ae2b24 100644 --- a/packages/openapi-ts/src/plugins/valibot/api.ts +++ b/packages/openapi-ts/src/plugins/valibot/api.ts @@ -27,7 +27,7 @@ export type IApi = { * - `webhook-request`: `operation.id` string * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { @@ -41,7 +41,7 @@ export class Api implements IApi { return createResponseValidatorV1(args); } - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts index d7e5be114..67986f1de 100644 --- a/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts @@ -1,13 +1,14 @@ import type { IR } from '../../../ir/types'; import type { StringCase, StringName } from '../../../types/case'; +import type { ToRefs } from '../../shared/types/refs'; import type { ValibotPlugin } from '../types'; export type IrSchemaToAstOptions = { plugin: ValibotPlugin['Instance']; - state: State; + state: ToRefs; }; -export type State = { +export type PluginState = { /** * Path to the schema in the intermediary representation. */ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/api.ts b/packages/openapi-ts/src/plugins/valibot/v1/api.ts index cfd6952b8..790cfbc0b 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/api.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/api.ts @@ -8,11 +8,11 @@ export const createRequestValidatorV1 = ({ operation, plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { - const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + const symbol = plugin.getSymbol(plugin.api.selector('data', operation.id)); if (!symbol) return; const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const dataParameterName = 'data'; @@ -48,12 +48,12 @@ export const createResponseValidatorV1 = ({ plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { const symbol = plugin.getSymbol( - plugin.api.getSelector('responses', operation.id), + plugin.api.selector('responses', operation.id), ); if (!symbol) return; const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const dataParameterName = 'data'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/operation.ts b/packages/openapi-ts/src/plugins/valibot/v1/operation.ts index acd106937..6e2ddea5b 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/operation.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/operation.ts @@ -2,6 +2,7 @@ import { operationResponsesMap } from '../../../ir/operation'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { toRef } from '../../shared/utils/refs'; import type { IrSchemaToAstOptions } from '../shared/types'; import { irSchemaToAst } from './plugin'; @@ -113,13 +114,13 @@ export const irOperationToAst = ({ const symbol = plugin.registerSymbol({ exported: true, meta: { - resourceType: pathToSymbolResourceType(state._path), + resourceType: pathToSymbolResourceType(state._path.value), }, name: buildName({ config: plugin.config.requests, name: operation.id, }), - selector: plugin.api.getSelector('data', operation.id), + selector: plugin.api.selector('data', operation.id), }); irSchemaToAst({ plugin, @@ -134,7 +135,7 @@ export const irOperationToAst = ({ const { response } = operationResponsesMap(operation); if (response) { - const path = [...state._path, 'responses']; + const path = [...state._path.value, 'responses']; const symbol = plugin.registerSymbol({ exported: true, meta: { @@ -144,14 +145,14 @@ export const irOperationToAst = ({ config: plugin.config.responses, name: operation.id, }), - selector: plugin.api.getSelector('responses', operation.id), + selector: plugin.api.selector('responses', operation.id), }); irSchemaToAst({ plugin, schema: response, state: { ...state, - _path: path, + _path: toRef(path), }, symbol, }); diff --git a/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts b/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts index df5660e9c..053e8df07 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts @@ -16,7 +16,7 @@ export const pipesToAst = ({ } const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts index fa3860fcf..a47dc507e 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts @@ -8,9 +8,10 @@ import { tsc } from '../../../tsc'; import { jsonPointerToPath, refToName } from '../../../utils/ref'; import type { SchemaWithType } from '../../shared/types/schema'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { toRef, toRefs } from '../../shared/utils/refs'; import { createSchemaComment } from '../../shared/utils/schema'; import { numberParameter } from '../shared/numbers'; -import type { IrSchemaToAstOptions } from '../shared/types'; +import type { IrSchemaToAstOptions, PluginState } from '../shared/types'; import type { ValibotPlugin } from '../types'; import { identifiers } from './constants'; import { irOperationToAst } from './operation'; @@ -43,10 +44,10 @@ export const irSchemaToAst = ({ let pipes: Array = []; if ($ref) { - state.circularReferenceTracker.add($ref); + state.circularReferenceTracker.value.add($ref); if (!symbol) { - const selector = plugin.api.getSelector('ref', $ref); + const selector = plugin.api.selector('ref', $ref); if (!plugin.getSymbol(selector)) { symbol = plugin.referenceSymbol(selector); } @@ -54,16 +55,18 @@ export const irSchemaToAst = ({ } const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); if (schema.$ref) { - const isCircularReference = state.circularReferenceTracker.has(schema.$ref); + const isCircularReference = state.circularReferenceTracker.value.has( + schema.$ref, + ); // if $ref hasn't been processed yet, inline it to avoid the // "Block-scoped variable used before its declaration." error // this could be (maybe?) fixed by reshuffling the generation order - const selector = plugin.api.getSelector('ref', schema.$ref); + const selector = plugin.api.selector('ref', schema.$ref); let refSymbol = plugin.getSymbol(selector); if (!refSymbol) { const ref = plugin.context.resolveIrRef(schema.$ref); @@ -73,7 +76,7 @@ export const irSchemaToAst = ({ schema: ref, state: { ...state, - _path: jsonPointerToPath(schema.$ref), + _path: toRef(jsonPointerToPath(schema.$ref)), }, }); pipes.push(...schemaPipes); @@ -100,7 +103,7 @@ export const irSchemaToAst = ({ ], }); pipes.push(lazyExpression); - state.hasCircularReference = true; + state.hasCircularReference.value = true; } else { pipes.push(refIdentifier); } @@ -143,7 +146,7 @@ export const irSchemaToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); return pipesToAst({ pipes: schemaPipes, plugin }); @@ -198,7 +201,7 @@ export const irSchemaToAst = ({ } if ($ref) { - state.circularReferenceTracker.delete($ref); + state.circularReferenceTracker.value.delete($ref); } if (pipes.length) { @@ -248,16 +251,16 @@ export const irSchemaToAst = ({ symbol = plugin.registerSymbol({ exported: true, meta: { - resourceType: pathToSymbolResourceType(state._path), + resourceType: pathToSymbolResourceType(state._path.value), }, name: buildName({ config: { - case: state.nameCase, - name: state.nameTransformer, + case: state.nameCase.value, + name: state.nameTransformer.value, }, name: refToName($ref), }), - selector: plugin.api.getSelector('ref', $ref), + selector: plugin.api.selector('ref', $ref), }); } const statement = tsc.constVariable({ @@ -286,7 +289,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { external: 'valibot', meta: { importKind: 'namespace' }, name: 'v', - selector: plugin.api.getSelector('external', 'valibot.v'), + selector: plugin.api.selector('external', 'valibot.v'), }); plugin.forEach( @@ -296,12 +299,12 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { 'schema', 'webhook', (event) => { - const state: Omit = { + const state = toRefs>({ circularReferenceTracker: new Set(), hasCircularReference: false, nameCase: plugin.config.definitions.case, nameTransformer: plugin.config.definitions.name, - }; + }); switch (event.type) { case 'operation': @@ -310,7 +313,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { plugin, state: { ...state, - _path: event._path, + _path: toRef(event._path), }, }); break; @@ -321,7 +324,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { schema: event.parameter.schema, state: { ...state, - _path: event._path, + _path: toRef(event._path), }, }); break; @@ -332,7 +335,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { schema: event.requestBody.schema, state: { ...state, - _path: event._path, + _path: toRef(event._path), }, }); break; @@ -343,7 +346,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { schema: event.schema, state: { ...state, - _path: event._path, + _path: toRef(event._path), }, }); break; @@ -353,7 +356,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { plugin, state: { ...state, - _path: event._path, + _path: toRef(event._path), }, }); break; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts index 796f6fb2d..9bd2fb206 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts @@ -3,6 +3,7 @@ import type ts from 'typescript'; import { deduplicateSchema } from '../../../../ir/schema'; import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { pipesToAst } from '../pipesToAst'; @@ -17,7 +18,7 @@ export const arrayToAst = ({ schema: SchemaWithType<'array'>; }): ts.Expression => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const functionName = tsc.propertyAccessExpression({ expression: v.placeholder, @@ -50,7 +51,7 @@ export const arrayToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); return pipesToAst({ pipes: schemaPipes, plugin }); diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts index b48cbf0c8..149f11be9 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts @@ -10,7 +10,7 @@ export const booleanToAst = ({ schema: SchemaWithType<'boolean'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); if (typeof schema.const === 'boolean') { diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts index bcf4bcb40..dd139d990 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts @@ -41,7 +41,7 @@ export const enumToAst = ({ } const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); let resultExpression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts index ae24ae353..06613a637 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts @@ -9,7 +9,7 @@ export const neverToAst = ({ schema: SchemaWithType<'never'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts index 79dcc70bc..281f3e9ae 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts @@ -9,7 +9,7 @@ export const nullToAst = ({ schema: SchemaWithType<'null'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts index f6d78a0d8..e6f0c66e6 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts @@ -24,7 +24,7 @@ export const numberToAst = ({ const formatInfo = isIntegerFormat(format) ? INTEGER_FORMATS[format] : null; const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); // Return early if const is defined since we can create a literal type directly without additional validation diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts index 4ab215de5..b85bec76a 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts @@ -3,6 +3,7 @@ import ts from 'typescript'; import { tsc } from '../../../../tsc'; import { numberRegExp } from '../../../../utils/regexp'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { pipesToAst } from '../pipesToAst'; @@ -33,7 +34,7 @@ export const objectToAst = ({ schema: property, state: { ...state, - _path: [...state._path, 'properties', name], + _path: toRef([...state._path.value, 'properties', name]), }, }); @@ -65,7 +66,7 @@ export const objectToAst = ({ } const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); if ( @@ -78,7 +79,7 @@ export const objectToAst = ({ schema: schema.additionalProperties, state: { ...state, - _path: [...state._path, 'additionalProperties'], + _path: toRef([...state._path.value, 'additionalProperties']), }, }); const expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts index ebeba4679..e54c6808c 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts @@ -13,7 +13,7 @@ export const stringToAst = ({ schema: SchemaWithType<'string'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); if (typeof schema.const === 'string') { diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts index 039bd6fd5..776103b48 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts @@ -1,5 +1,6 @@ import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { pipesToAst } from '../pipesToAst'; @@ -14,7 +15,7 @@ export const tupleToAst = ({ schema: SchemaWithType<'tuple'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); if (schema.const && Array.isArray(schema.const)) { @@ -48,7 +49,7 @@ export const tupleToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); return pipesToAst({ pipes: schemaPipes, plugin }); diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts index 8be4e6f33..d4c1167e5 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts @@ -9,7 +9,7 @@ export const undefinedToAst = ({ schema: SchemaWithType<'undefined'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts index 72065fc7e..705515414 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts @@ -9,7 +9,7 @@ export const unknownToAst = ({ schema: SchemaWithType<'unknown'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts index 6f199222c..b92cad06a 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts @@ -9,7 +9,7 @@ export const voidToAst = ({ schema: SchemaWithType<'void'>; }) => { const v = plugin.referenceSymbol( - plugin.api.getSelector('external', 'valibot.v'), + plugin.api.selector('external', 'valibot.v'), ); const expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts b/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts index 37ba6144e..e4f4c3042 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts @@ -112,13 +112,13 @@ export const irWebhookToAst = ({ const symbol = plugin.registerSymbol({ exported: true, meta: { - resourceType: pathToSymbolResourceType(state._path), + resourceType: pathToSymbolResourceType(state._path.value), }, name: buildName({ config: plugin.config.webhooks, name: operation.id, }), - selector: plugin.api.getSelector('webhook-request', operation.id), + selector: plugin.api.selector('webhook-request', operation.id), }); irSchemaToAst({ plugin, diff --git a/packages/openapi-ts/src/plugins/zod/api.ts b/packages/openapi-ts/src/plugins/zod/api.ts index 0d4eb8810..1fe8d147e 100644 --- a/packages/openapi-ts/src/plugins/zod/api.ts +++ b/packages/openapi-ts/src/plugins/zod/api.ts @@ -40,7 +40,7 @@ export type IApi = { * - `webhook-request`: `operation.id` string * @returns Selector array */ - getSelector: (type: SelectorType, value?: string) => Selector; + selector: (type: SelectorType, value?: string) => Selector; }; export class Api implements IApi { @@ -72,7 +72,7 @@ export class Api implements IApi { } } - getSelector(...args: ReadonlyArray): Selector { + selector(...args: ReadonlyArray): Selector { return [this.meta.name, ...(args as Selector)]; } } diff --git a/packages/openapi-ts/src/plugins/zod/mini/api.ts b/packages/openapi-ts/src/plugins/zod/mini/api.ts index f6efe671d..f360d67ed 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/api.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/api.ts @@ -8,7 +8,7 @@ export const createRequestValidatorMini = ({ operation, plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { - const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + const symbol = plugin.getSymbol(plugin.api.selector('data', operation.id)); if (!symbol) return; const dataParameterName = 'data'; @@ -41,7 +41,7 @@ export const createResponseValidatorMini = ({ plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { const symbol = plugin.getSymbol( - plugin.api.getSelector('responses', operation.id), + plugin.api.selector('responses', operation.id), ); if (!symbol) return; diff --git a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts index 4a8b1129d..2f88abb08 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts @@ -31,7 +31,7 @@ export const irSchemaToAst = ({ }): Ast => { let ast: Partial = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.$ref) { const isCircularReference = state.circularReferenceTracker.includes( @@ -41,7 +41,7 @@ export const irSchemaToAst = ({ state.circularReferenceTracker.push(schema.$ref); state.currentReferenceTracker.push(schema.$ref); - const selector = plugin.api.getSelector('ref', schema.$ref); + const selector = plugin.api.selector('ref', schema.$ref); let symbol = plugin.getSymbol(selector); if (isCircularReference) { @@ -96,13 +96,13 @@ export const irSchemaToAst = ({ state.circularReferenceTracker.pop(); state.currentReferenceTracker.pop(); } else if (schema.type) { - const zSchema = irSchemaWithTypeToAst({ + const typeAst = irSchemaWithTypeToAst({ plugin, schema: schema as SchemaWithType, state, }); - ast.expression = zSchema.expression; - ast.hasCircularReference = zSchema.hasCircularReference; + ast.expression = typeAst.expression; + ast.hasCircularReference = typeAst.hasCircularReference; if (plugin.config.metadata && schema.description) { ast.expression = tsc.callExpression({ @@ -206,14 +206,14 @@ export const irSchemaToAst = ({ } } else { // catch-all fallback for failed schemas - const zSchema = irSchemaWithTypeToAst({ + const typeAst = irSchemaWithTypeToAst({ plugin, schema: { type: 'unknown', }, state, }); - ast.expression = zSchema.expression; + ast.expression = typeAst.expression; } if (ast.expression) { @@ -276,7 +276,7 @@ const handleComponent = ({ hasCircularReference: _state?.hasCircularReference ?? false, }; - const selector = plugin.api.getSelector('ref', id); + const selector = plugin.api.selector('ref', id); let symbol = plugin.getSymbol(selector); if (symbol && !plugin.getSymbolValue(symbol)) return; @@ -305,7 +305,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.getSelector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', id), }) : undefined; exportAst({ @@ -322,7 +322,7 @@ export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { external: getZodModule({ plugin }), meta: { importKind: 'namespace' }, name: 'z', - selector: plugin.api.getSelector('external', 'zod.z'), + selector: plugin.api.selector('external', 'zod.z'), }); plugin.forEach( diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts index da7bb61ed..7afc02633 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts @@ -15,7 +15,7 @@ export const arrayToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'array'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; @@ -42,7 +42,7 @@ export const arrayToAst = ({ // at least one item is guaranteed const itemExpressions = schema.items!.map((item, index) => { - const zodSchema = irSchemaToAst({ + const itemAst = irSchemaToAst({ plugin, schema: item, state: { @@ -50,10 +50,10 @@ export const arrayToAst = ({ _path: [...state._path, 'items', index], }, }); - if (zodSchema.hasCircularReference) { + if (itemAst.hasCircularReference) { result.hasCircularReference = true; } - return zodSchema.expression; + return itemAst.expression; }); if (itemExpressions.length === 1) { diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts index 8f47ccca4..657848a07 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts @@ -9,7 +9,7 @@ export const booleanToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'boolean'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts index 766e5db62..2b43769ba 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts @@ -13,7 +13,7 @@ export const enumToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'enum'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts index b1e3aa880..57e20c23a 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts @@ -8,7 +8,7 @@ export const neverToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'never'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts index cc9b51168..b3ff566dd 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts @@ -8,7 +8,7 @@ export const nullToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'null'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts index 58c3d81e3..e04617c61 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts @@ -12,7 +12,7 @@ export const numberToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'integer' | 'number'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts index 32b92190a..d172666c0 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts @@ -14,7 +14,7 @@ export const objectToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'object'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; @@ -28,7 +28,7 @@ export const objectToAst = ({ const property = schema.properties[name]!; const isRequired = required.includes(name); - const propertySchema = irSchemaToAst({ + const propertyAst = irSchemaToAst({ optional: !isRequired, plugin, schema: property, @@ -37,7 +37,7 @@ export const objectToAst = ({ _path: [...state._path, 'properties', name], }, }); - if (propertySchema.hasCircularReference) { + if (propertyAst.hasCircularReference) { result.hasCircularReference = true; } @@ -61,20 +61,20 @@ export const objectToAst = ({ propertyName = `'${name}'`; } - if (propertySchema.hasCircularReference) { + if (propertyAst.hasCircularReference) { properties.push( tsc.getAccessorDeclaration({ name: propertyName, // @ts-expect-error - returnType: propertySchema.typeName + returnType: propertyAst.typeName ? tsc.propertyAccessExpression({ expression: z.placeholder, - name: propertySchema.typeName, + name: propertyAst.typeName, }) : undefined, statements: [ tsc.returnStatement({ - expression: propertySchema.expression, + expression: propertyAst.expression, }), ], }), @@ -82,7 +82,7 @@ export const objectToAst = ({ } else { properties.push( tsc.propertyAssignment({ - initializer: propertySchema.expression, + initializer: propertyAst.expression, name: propertyName, }), ); @@ -93,7 +93,7 @@ export const objectToAst = ({ schema.additionalProperties && (!schema.properties || !Object.keys(schema.properties).length) ) { - const zodSchema = irSchemaToAst({ + const additionalAst = irSchemaToAst({ plugin, schema: schema.additionalProperties, state: { @@ -114,10 +114,10 @@ export const objectToAst = ({ }), parameters: [], }), - zodSchema.expression, + additionalAst.expression, ], }); - if (zodSchema.hasCircularReference) { + if (additionalAst.hasCircularReference) { result.hasCircularReference = true; } return result as Omit; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts index 6aa88db4e..4a4d98ce5 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts @@ -11,7 +11,7 @@ export const stringToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'string'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts index c66779036..6022e00b3 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts @@ -13,7 +13,7 @@ export const tupleToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'tuple'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts index f76718e3c..13cddcfa1 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts @@ -8,7 +8,7 @@ export const undefinedToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'undefined'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts index 32512858a..2ada823cb 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts @@ -8,7 +8,7 @@ export const unknownToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'unknown'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts index 5c2c0e344..b15a42845 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts @@ -8,7 +8,7 @@ export const voidToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'void'>; }): Omit => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const result: Partial> = {}; result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/shared/export.ts b/packages/openapi-ts/src/plugins/zod/shared/export.ts index 16ae40bde..9fa2762eb 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/export.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/export.ts @@ -21,7 +21,7 @@ export const exportAst = ({ symbol: Symbol; typeInferSymbol: Symbol | undefined; }): void => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const statement = tsc.constVariable({ comment: plugin.config.comments diff --git a/packages/openapi-ts/src/plugins/zod/shared/operation.ts b/packages/openapi-ts/src/plugins/zod/shared/operation.ts index bb929618f..e8362f9cb 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/operation.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/operation.ts @@ -128,7 +128,7 @@ export const irOperationToAst = ({ config: plugin.config.requests, name: operation.id, }), - selector: plugin.api.getSelector('data', operation.id), + selector: plugin.api.selector('data', operation.id), }); const typeInferSymbol = plugin.config.requests.types.infer.enabled ? plugin.registerSymbol({ @@ -141,7 +141,7 @@ export const irOperationToAst = ({ config: plugin.config.requests.types.infer, name: operation.id, }), - selector: plugin.api.getSelector('type-infer-data', operation.id), + selector: plugin.api.selector('type-infer-data', operation.id), }) : undefined; exportAst({ @@ -170,7 +170,7 @@ export const irOperationToAst = ({ config: plugin.config.responses, name: operation.id, }), - selector: plugin.api.getSelector('responses', operation.id), + selector: plugin.api.selector('responses', operation.id), }); const typeInferSymbol = plugin.config.responses.types.infer.enabled ? plugin.registerSymbol({ @@ -183,7 +183,7 @@ export const irOperationToAst = ({ config: plugin.config.responses.types.infer, name: operation.id, }), - selector: plugin.api.getSelector( + selector: plugin.api.selector( 'type-infer-responses', operation.id, ), diff --git a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts index 9ebdddbb0..e8e47a0f6 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts @@ -127,7 +127,7 @@ export const irWebhookToAst = ({ config: plugin.config.webhooks, name: operation.id, }), - selector: plugin.api.getSelector('webhook-request', operation.id), + selector: plugin.api.selector('webhook-request', operation.id), }); const typeInferSymbol = plugin.config.webhooks.types.infer.enabled ? plugin.registerSymbol({ @@ -140,7 +140,7 @@ export const irWebhookToAst = ({ config: plugin.config.webhooks.types.infer, name: operation.id, }), - selector: plugin.api.getSelector( + selector: plugin.api.selector( 'type-infer-webhook-request', operation.id, ), diff --git a/packages/openapi-ts/src/plugins/zod/v3/api.ts b/packages/openapi-ts/src/plugins/zod/v3/api.ts index 3be0a8c54..3328950cd 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/api.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/api.ts @@ -8,7 +8,7 @@ export const createRequestValidatorV3 = ({ operation, plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { - const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + const symbol = plugin.getSymbol(plugin.api.selector('data', operation.id)); if (!symbol) return; const dataParameterName = 'data'; @@ -41,7 +41,7 @@ export const createResponseValidatorV3 = ({ plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { const symbol = plugin.getSymbol( - plugin.api.getSelector('responses', operation.id), + plugin.api.selector('responses', operation.id), ); if (!symbol) return; diff --git a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts index 6d1cb6dcc..9100d83a9 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts @@ -31,7 +31,7 @@ export const irSchemaToAst = ({ }): Ast => { let ast: Partial = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.$ref) { const isCircularReference = state.circularReferenceTracker.includes( @@ -40,7 +40,7 @@ export const irSchemaToAst = ({ state.circularReferenceTracker.push(schema.$ref); state.currentReferenceTracker.push(schema.$ref); - const selector = plugin.api.getSelector('ref', schema.$ref); + const selector = plugin.api.selector('ref', schema.$ref); let symbol = plugin.getSymbol(selector); if (isCircularReference) { @@ -91,14 +91,14 @@ export const irSchemaToAst = ({ state.circularReferenceTracker.pop(); state.currentReferenceTracker.pop(); } else if (schema.type) { - const zSchema = irSchemaWithTypeToAst({ + const typeAst = irSchemaWithTypeToAst({ plugin, schema: schema as SchemaWithType, state, }); - ast.expression = zSchema.expression; - ast.hasCircularReference = zSchema.hasCircularReference; - ast.typeName = zSchema.anyType; + ast.expression = typeAst.expression; + ast.hasCircularReference = typeAst.hasCircularReference; + ast.typeName = typeAst.anyType; if (plugin.config.metadata && schema.description) { ast.expression = tsc.callExpression({ @@ -114,7 +114,7 @@ export const irSchemaToAst = ({ if (schema.items) { const itemTypes = schema.items.map((item, index) => { - const zSchema = irSchemaToAst({ + const typeAst = irSchemaToAst({ plugin, schema: item, state: { @@ -122,10 +122,10 @@ export const irSchemaToAst = ({ _path: [...state._path, 'items', index], }, }); - if (zSchema.hasCircularReference) { + if (typeAst.hasCircularReference) { ast.hasCircularReference = true; } - return zSchema.expression; + return typeAst.expression; }); if (schema.logicalOperator === 'and') { @@ -174,16 +174,16 @@ export const irSchemaToAst = ({ } } else { // catch-all fallback for failed schemas - const zSchema = irSchemaWithTypeToAst({ + const typeAst = irSchemaWithTypeToAst({ plugin, schema: { type: 'unknown', }, state, }); - ast.expression = zSchema.expression; - ast.hasCircularReference = zSchema.hasCircularReference; - ast.typeName = zSchema.anyType; + ast.expression = typeAst.expression; + ast.hasCircularReference = typeAst.hasCircularReference; + ast.typeName = typeAst.anyType; } if (ast.expression) { @@ -251,7 +251,7 @@ const handleComponent = ({ hasCircularReference: _state?.hasCircularReference ?? false, }; - const selector = plugin.api.getSelector('ref', id); + const selector = plugin.api.selector('ref', id); let symbol = plugin.getSymbol(selector); if (symbol) return; @@ -280,7 +280,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.getSelector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', id), }) : undefined; exportAst({ @@ -296,7 +296,7 @@ export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { plugin.registerSymbol({ external: getZodModule({ plugin }), name: 'z', - selector: plugin.api.getSelector('external', 'zod.z'), + selector: plugin.api.selector('external', 'zod.z'), }); plugin.forEach( diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts index 2d8b4e1d1..72508ba2c 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts @@ -17,7 +17,7 @@ export const arrayToAst = ({ }): Omit & { anyType?: string; } => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const functionName = tsc.propertyAccessExpression({ expression: z.placeholder, @@ -45,7 +45,7 @@ export const arrayToAst = ({ // at least one item is guaranteed const itemExpressions = schema.items!.map((item, index) => { - const zodSchema = irSchemaToAst({ + const itemAst = irSchemaToAst({ plugin, schema: item, state: { @@ -53,10 +53,10 @@ export const arrayToAst = ({ _path: [...state._path, 'items', index], }, }); - if (zodSchema.hasCircularReference) { + if (itemAst.hasCircularReference) { hasCircularReference = true; } - return zodSchema.expression; + return itemAst.expression; }); if (itemExpressions.length === 1) { diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts index 5285daab1..7562823d5 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts @@ -9,7 +9,7 @@ export const booleanToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'boolean'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (typeof schema.const === 'boolean') { const expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts index c2c5ba649..d735f09fb 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts @@ -13,7 +13,7 @@ export const enumToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'enum'>; }): ts.CallExpression => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const enumMembers: Array = []; const literalMembers: Array = []; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts index 1394dbfc5..3dbddf974 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts @@ -8,7 +8,7 @@ export const neverToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'never'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts index 26d1ca5df..fdb2a5875 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts @@ -8,7 +8,7 @@ export const nullToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'null'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts index 125a0b2ed..8754becc9 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts @@ -10,7 +10,7 @@ export const numberToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'integer' | 'number'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const isBigInt = schema.type === 'integer' && schema.format === 'int64'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts index d25b92d24..f16bbcae9 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts @@ -16,7 +16,7 @@ export const objectToAst = ({ }): Omit & { anyType?: string; } => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); let hasCircularReference = false; @@ -74,7 +74,7 @@ export const objectToAst = ({ schema.additionalProperties && (!schema.properties || !Object.keys(schema.properties).length) ) { - const zodSchema = irSchemaToAst({ + const additionalAst = irSchemaToAst({ plugin, schema: schema.additionalProperties, state: { @@ -87,12 +87,12 @@ export const objectToAst = ({ expression: z.placeholder, name: identifiers.record, }), - parameters: [zodSchema.expression], + parameters: [additionalAst.expression], }); return { anyType: 'AnyZodObject', expression, - hasCircularReference: zodSchema.hasCircularReference, + hasCircularReference: additionalAst.hasCircularReference, }; } diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts index ca375b9c1..1512b1ed2 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts @@ -9,7 +9,7 @@ export const stringToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'string'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (typeof schema.const === 'string') { const expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts index a8edb0b4b..8fcb4ba83 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts @@ -15,7 +15,7 @@ export const tupleToAst = ({ }): Omit & { anyType?: string; } => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); let hasCircularReference = false; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts index 1c7a7d19c..f6d75fa79 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts @@ -8,7 +8,7 @@ export const undefinedToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'undefined'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts index dc1ab5da3..4aa9a4274 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts @@ -8,7 +8,7 @@ export const unknownToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'unknown'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts index 7da3f191f..fe0cb9e51 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts @@ -8,7 +8,7 @@ export const voidToAst = ({ }: IrSchemaToAstOptions & { schema: SchemaWithType<'void'>; }) => { - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v4/api.ts b/packages/openapi-ts/src/plugins/zod/v4/api.ts index cb59b1b06..9de07623f 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/api.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/api.ts @@ -8,7 +8,7 @@ export const createRequestValidatorV4 = ({ operation, plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { - const symbol = plugin.getSymbol(plugin.api.getSelector('data', operation.id)); + const symbol = plugin.getSymbol(plugin.api.selector('data', operation.id)); if (!symbol) return; const dataParameterName = 'data'; @@ -41,7 +41,7 @@ export const createResponseValidatorV4 = ({ plugin, }: ValidatorArgs): ts.ArrowFunction | undefined => { const symbol = plugin.getSymbol( - plugin.api.getSelector('responses', operation.id), + plugin.api.selector('responses', operation.id), ); if (!symbol) return; diff --git a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts index 6fb967bed..562d8ce57 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts @@ -31,7 +31,7 @@ export const irSchemaToAst = ({ }): Ast => { let ast: Partial = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.$ref) { const isCircularReference = state.circularReferenceTracker.includes( @@ -41,7 +41,7 @@ export const irSchemaToAst = ({ state.circularReferenceTracker.push(schema.$ref); state.currentReferenceTracker.push(schema.$ref); - const selector = plugin.api.getSelector('ref', schema.$ref); + const selector = plugin.api.selector('ref', schema.$ref); let symbol = plugin.getSymbol(selector); if (isCircularReference) { @@ -96,13 +96,13 @@ export const irSchemaToAst = ({ state.circularReferenceTracker.pop(); state.currentReferenceTracker.pop(); } else if (schema.type) { - const zSchema = irSchemaWithTypeToAst({ + const typeAst = irSchemaWithTypeToAst({ plugin, schema: schema as SchemaWithType, state, }); - ast.expression = zSchema.expression; - ast.hasCircularReference = zSchema.hasCircularReference; + ast.expression = typeAst.expression; + ast.hasCircularReference = typeAst.hasCircularReference; if (plugin.config.metadata && schema.description) { ast.expression = tsc.callExpression({ @@ -209,14 +209,14 @@ export const irSchemaToAst = ({ } } else { // catch-all fallback for failed schemas - const zSchema = irSchemaWithTypeToAst({ + const typeAst = irSchemaWithTypeToAst({ plugin, schema: { type: 'unknown', }, state, }); - ast.expression = zSchema.expression; + ast.expression = typeAst.expression; } if (ast.expression) { @@ -278,7 +278,7 @@ const handleComponent = ({ hasCircularReference: _state?.hasCircularReference ?? false, }; - const selector = plugin.api.getSelector('ref', id); + const selector = plugin.api.selector('ref', id); let symbol = plugin.getSymbol(selector); if (symbol && !plugin.getSymbolValue(symbol)) return; @@ -307,7 +307,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.getSelector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', id), }) : undefined; exportAst({ @@ -323,7 +323,7 @@ export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { plugin.registerSymbol({ external: getZodModule({ plugin }), name: 'z', - selector: plugin.api.getSelector('external', 'zod.z'), + selector: plugin.api.selector('external', 'zod.z'), }); plugin.forEach( diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts index a10d9f76a..75491b388 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts @@ -17,7 +17,7 @@ export const arrayToAst = ({ }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const functionName = tsc.propertyAccessExpression({ expression: z.placeholder, @@ -42,7 +42,7 @@ export const arrayToAst = ({ // at least one item is guaranteed const itemExpressions = schema.items!.map((item, index) => { - const zodSchema = irSchemaToAst({ + const itemAst = irSchemaToAst({ plugin, schema: item, state: { @@ -50,10 +50,10 @@ export const arrayToAst = ({ _path: [...state._path, 'items', index], }, }); - if (zodSchema.hasCircularReference) { + if (itemAst.hasCircularReference) { result.hasCircularReference = true; } - return zodSchema.expression; + return itemAst.expression; }); if (itemExpressions.length === 1) { diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts index 88fc167e3..ad4b0fbae 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts @@ -11,7 +11,7 @@ export const booleanToAst = ({ }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (typeof schema.const === 'boolean') { result.expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts index 8ac9fd311..e02d09ea7 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts @@ -15,7 +15,7 @@ export const enumToAst = ({ }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); const enumMembers: Array = []; const literalMembers: Array = []; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts index 971cea475..a47363240 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts @@ -9,7 +9,7 @@ export const neverToAst = ({ schema: SchemaWithType<'never'>; }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts index ea1ef65db..725bfa73b 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts @@ -9,7 +9,7 @@ export const nullToAst = ({ schema: SchemaWithType<'null'>; }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts index f8ff11779..0a5cc1364 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts @@ -14,7 +14,7 @@ export const numberToAst = ({ const isBigInt = schema.type === 'integer' && schema.format === 'int64'; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (typeof schema.const === 'number') { // TODO: parser - handle bigint constants diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts index 954398c4f..ed3da3ec1 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts @@ -22,13 +22,13 @@ export const objectToAst = ({ const required = schema.required ?? []; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); for (const name in schema.properties) { const property = schema.properties[name]!; const isRequired = required.includes(name); - const propertySchema = irSchemaToAst({ + const propertyAst = irSchemaToAst({ optional: !isRequired, plugin, schema: property, @@ -37,7 +37,7 @@ export const objectToAst = ({ _path: [...state._path, 'properties', name], }, }); - if (propertySchema.hasCircularReference) { + if (propertyAst.hasCircularReference) { result.hasCircularReference = true; } @@ -61,20 +61,20 @@ export const objectToAst = ({ propertyName = `'${name}'`; } - if (propertySchema.hasCircularReference) { + if (propertyAst.hasCircularReference) { properties.push( tsc.getAccessorDeclaration({ name: propertyName, // @ts-expect-error - returnType: propertySchema.typeName + returnType: propertyAst.typeName ? tsc.propertyAccessExpression({ expression: z.placeholder, - name: propertySchema.typeName, + name: propertyAst.typeName, }) : undefined, statements: [ tsc.returnStatement({ - expression: propertySchema.expression, + expression: propertyAst.expression, }), ], }), @@ -82,7 +82,7 @@ export const objectToAst = ({ } else { properties.push( tsc.propertyAssignment({ - initializer: propertySchema.expression, + initializer: propertyAst.expression, name: propertyName, }), ); @@ -93,7 +93,7 @@ export const objectToAst = ({ schema.additionalProperties && (!schema.properties || !Object.keys(schema.properties).length) ) { - const zodSchema = irSchemaToAst({ + const additionalAst = irSchemaToAst({ plugin, schema: schema.additionalProperties, state: { @@ -114,10 +114,10 @@ export const objectToAst = ({ }), parameters: [], }), - zodSchema.expression, + additionalAst.expression, ], }); - if (zodSchema.hasCircularReference) { + if (additionalAst.hasCircularReference) { result.hasCircularReference = true; } diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts index 1075f100f..e6a2c71ab 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts @@ -11,7 +11,7 @@ export const stringToAst = ({ }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (typeof schema.const === 'string') { result.expression = tsc.callExpression({ diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts index f0cf2acec..b5f9f9407 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts @@ -15,7 +15,7 @@ export const tupleToAst = ({ }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.const && Array.isArray(schema.const)) { const tupleElements = schema.const.map((value) => diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts index 076bae8fb..294fba483 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts @@ -9,7 +9,7 @@ export const undefinedToAst = ({ schema: SchemaWithType<'undefined'>; }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts index 7a824e351..7f1163aec 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts @@ -9,7 +9,7 @@ export const unknownToAst = ({ schema: SchemaWithType<'unknown'>; }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts index 596b2fe8f..63324d503 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts @@ -9,7 +9,7 @@ export const voidToAst = ({ schema: SchemaWithType<'void'>; }): Omit => { const result: Partial> = {}; - const z = plugin.referenceSymbol(plugin.api.getSelector('external', 'zod.z')); + const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); result.expression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/utils/__tests__/handlebars.test.ts b/packages/openapi-ts/src/utils/__tests__/handlebars.test.ts index 79cbf7c05..e9d378053 100644 --- a/packages/openapi-ts/src/utils/__tests__/handlebars.test.ts +++ b/packages/openapi-ts/src/utils/__tests__/handlebars.test.ts @@ -82,7 +82,7 @@ describe('registerHandlebarHelpers', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -94,7 +94,7 @@ describe('registerHandlebarHelpers', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -105,8 +105,8 @@ describe('registerHandlebarHelpers', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', @@ -213,7 +213,7 @@ describe('registerHandlebarTemplates', () => { plugins: { '@hey-api/schemas': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/schemas', @@ -225,7 +225,7 @@ describe('registerHandlebarTemplates', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -236,8 +236,8 @@ describe('registerHandlebarTemplates', () => { }, '@hey-api/typescript': { api: { - getSelector: () => [], schemaToType: () => ({}) as ts.TypeNode, + selector: () => [], }, config: { enums: 'javascript', diff --git a/packages/openapi-ts/src/utils/__tests__/parse.test.ts b/packages/openapi-ts/src/utils/__tests__/parse.test.ts index 90cc82667..129b476f5 100644 --- a/packages/openapi-ts/src/utils/__tests__/parse.test.ts +++ b/packages/openapi-ts/src/utils/__tests__/parse.test.ts @@ -72,7 +72,7 @@ describe('operationNameFn', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -103,7 +103,7 @@ describe('operationNameFn', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -124,7 +124,7 @@ describe('operationNameFn', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -144,7 +144,7 @@ describe('operationNameFn', () => { plugins: { '@hey-api/client-fetch': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/client-fetch', @@ -157,7 +157,7 @@ describe('operationNameFn', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', @@ -177,7 +177,7 @@ describe('operationNameFn', () => { plugins: { '@hey-api/client-fetch': { api: { - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/client-fetch', @@ -190,7 +190,7 @@ describe('operationNameFn', () => { '@hey-api/sdk': { api: { createOperationComment: () => undefined, - getSelector: () => [], + selector: () => [], }, config: { name: '@hey-api/sdk', -- 2.51.2 From 69783d19be37014b5116a2ede5bb74adadbb8110 Mon Sep 17 00:00:00 2001 From: Lubos Date: Wed, 22 Oct 2025 20:13:41 +0800 Subject: [PATCH 3/5] chore: add graph to context --- .changeset/cyan-regions-invite.md | 5 + .../plugins/valibot/default/valibot.gen.ts | 180 ++--- .../plugins/valibot/default/valibot.gen.ts | 644 ++++++++--------- .../3.0.x/validators/valibot.gen.ts | 14 +- .../plugins/valibot/default/valibot.gen.ts | 672 +++++++++--------- .../validators-circular-ref-2/valibot.gen.ts | 2 +- .../validators-circular-ref/valibot.gen.ts | 12 +- .../3.1.x/validators-metadata/valibot.gen.ts | 42 +- .../3.1.x/validators-types/valibot.gen.ts | 38 +- .../validators-union-merge/valibot.gen.ts | 14 +- .../3.1.x/validators/valibot.gen.ts | 38 +- .../main/test/openapi-ts.config.ts | 10 +- .../2.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../2.0.x/mini/default/zod.gen.ts | 170 ++--- .../2.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v3/default/zod.gen.ts | 166 ++--- .../2.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v4/default/zod.gen.ts | 170 ++--- .../3.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/mini/circular/zod.gen.ts | 36 +- .../3.0.x/mini/default/zod.gen.ts | 644 ++++++++--------- .../3.0.x/mini/validators/zod.gen.ts | 18 +- .../3.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v3/circular/zod.gen.ts | 14 +- .../__snapshots__/3.0.x/v3/default/zod.gen.ts | 644 ++++++++--------- .../3.0.x/v3/validators/zod.gen.ts | 14 +- .../3.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v4/circular/zod.gen.ts | 36 +- .../__snapshots__/3.0.x/v4/default/zod.gen.ts | 644 ++++++++--------- .../3.0.x/v4/validators/zod.gen.ts | 18 +- .../3.1.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.1.x/mini/default/zod.gen.ts | 672 +++++++++--------- .../mini/validators-circular-ref/zod.gen.ts | 14 +- .../3.1.x/mini/validators-dates/zod.gen.ts | 42 +- .../3.1.x/mini/validators-metadata/zod.gen.ts | 46 +- .../3.1.x/mini/validators-types/zod.gen.ts | 54 +- .../mini/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/mini/validators/zod.gen.ts | 42 +- .../3.1.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v3/default/zod.gen.ts | 652 ++++++++--------- .../v3/validators-circular-ref/zod.gen.ts | 12 +- .../3.1.x/v3/validators-dates/zod.gen.ts | 38 +- .../3.1.x/v3/validators-metadata/zod.gen.ts | 38 +- .../3.1.x/v3/validators-types/zod.gen.ts | 50 +- .../v3/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v3/validators/zod.gen.ts | 38 +- .../3.1.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v4/default/zod.gen.ts | 672 +++++++++--------- .../v4/validators-circular-ref/zod.gen.ts | 14 +- .../3.1.x/v4/validators-dates/zod.gen.ts | 42 +- .../3.1.x/v4/validators-metadata/zod.gen.ts | 46 +- .../3.1.x/v4/validators-types/zod.gen.ts | 54 +- .../v4/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v4/validators/zod.gen.ts | 42 +- .../2.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../2.0.x/mini/default/zod.gen.ts | 170 ++--- .../2.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v3/default/zod.gen.ts | 166 ++--- .../2.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v4/default/zod.gen.ts | 170 ++--- .../3.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/mini/circular/zod.gen.ts | 36 +- .../3.0.x/mini/default/zod.gen.ts | 644 ++++++++--------- .../3.0.x/mini/validators/zod.gen.ts | 18 +- .../3.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v3/circular/zod.gen.ts | 14 +- .../__snapshots__/3.0.x/v3/default/zod.gen.ts | 644 ++++++++--------- .../3.0.x/v3/validators/zod.gen.ts | 14 +- .../3.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v4/circular/zod.gen.ts | 36 +- .../__snapshots__/3.0.x/v4/default/zod.gen.ts | 644 ++++++++--------- .../3.0.x/v4/validators/zod.gen.ts | 18 +- .../3.1.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.1.x/mini/default/zod.gen.ts | 672 +++++++++--------- .../mini/validators-circular-ref/zod.gen.ts | 14 +- .../3.1.x/mini/validators-dates/zod.gen.ts | 42 +- .../3.1.x/mini/validators-metadata/zod.gen.ts | 46 +- .../3.1.x/mini/validators-types/zod.gen.ts | 54 +- .../mini/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/mini/validators/zod.gen.ts | 42 +- .../3.1.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v3/default/zod.gen.ts | 652 ++++++++--------- .../v3/validators-circular-ref/zod.gen.ts | 12 +- .../3.1.x/v3/validators-dates/zod.gen.ts | 38 +- .../3.1.x/v3/validators-metadata/zod.gen.ts | 38 +- .../3.1.x/v3/validators-types/zod.gen.ts | 50 +- .../v3/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v3/validators/zod.gen.ts | 38 +- .../3.1.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v4/default/zod.gen.ts | 672 +++++++++--------- .../v4/validators-circular-ref/zod.gen.ts | 14 +- .../3.1.x/v4/validators-dates/zod.gen.ts | 42 +- .../3.1.x/v4/validators-metadata/zod.gen.ts | 46 +- .../3.1.x/v4/validators-types/zod.gen.ts | 54 +- .../v4/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v4/validators/zod.gen.ts | 42 +- packages/openapi-ts/package.json | 2 +- packages/openapi-ts/src/createClient.ts | 7 +- .../openapi-ts/src/ir/__tests__/graph.test.ts | 222 ++++++ packages/openapi-ts/src/ir/context.ts | 5 + packages/openapi-ts/src/ir/graph.ts | 84 +++ packages/openapi-ts/src/ir/types.d.ts | 8 - .../src/openApi/2.0.x/parser/parameter.ts | 1 - .../src/openApi/2.0.x/parser/schema.ts | 16 - .../src/openApi/3.0.x/parser/parameter.ts | 1 - .../src/openApi/3.0.x/parser/requestBody.ts | 1 - .../src/openApi/3.0.x/parser/schema.ts | 16 - .../src/openApi/3.1.x/parser/parameter.ts | 1 - .../src/openApi/3.1.x/parser/requestBody.ts | 1 - .../src/openApi/3.1.x/parser/schema.ts | 16 - .../src/openApi/shared/graph/meta.ts | 8 +- .../src/openApi/shared/types/schema.d.ts | 13 - .../shared/utils/__tests__/graph.test.ts | 104 +++ .../src/openApi/shared/utils/graph.ts | 187 +++-- .../plugins/@angular/common/httpRequests.ts | 156 ++-- .../plugins/@angular/common/httpResources.ts | 154 ++-- .../src/plugins/@hey-api/sdk/plugin.ts | 408 +++++------ .../plugins/@hey-api/transformers/plugin.ts | 120 ++-- .../src/plugins/@hey-api/typescript/plugin.ts | 3 + .../src/plugins/@pinia/colada/plugin.ts | 84 ++- .../plugins/@tanstack/query-core/plugin.ts | 96 +-- .../openapi-ts/src/plugins/fastify/plugin.ts | 18 +- .../src/plugins/shared/types/instance.d.ts | 35 +- .../src/plugins/shared/utils/instance.ts | 303 +++++--- .../src/plugins/valibot/shared/types.d.ts | 3 +- .../src/plugins/valibot/v1/plugin.ts | 153 ++-- .../openapi-ts/src/plugins/zod/mini/plugin.ts | 168 ++--- .../src/plugins/zod/mini/toAst/array.ts | 7 +- .../src/plugins/zod/mini/toAst/object.ts | 22 +- .../src/plugins/zod/mini/toAst/tuple.ts | 7 +- .../src/plugins/zod/shared/operation.ts | 4 +- .../src/plugins/zod/shared/types.d.ts | 16 +- .../src/plugins/zod/shared/webhook.ts | 2 +- .../openapi-ts/src/plugins/zod/v3/plugin.ts | 141 ++-- .../src/plugins/zod/v3/toAst/array.ts | 11 +- .../src/plugins/zod/v3/toAst/object.ts | 15 +- .../src/plugins/zod/v3/toAst/tuple.ts | 13 +- .../openapi-ts/src/plugins/zod/v4/plugin.ts | 168 ++--- .../src/plugins/zod/v4/toAst/array.ts | 7 +- .../src/plugins/zod/v4/toAst/object.ts | 26 +- .../src/plugins/zod/v4/toAst/tuple.ts | 7 +- pnpm-lock.yaml | 220 +++--- 142 files changed, 7816 insertions(+), 7427 deletions(-) create mode 100644 .changeset/cyan-regions-invite.md create mode 100644 packages/openapi-ts/src/ir/__tests__/graph.test.ts create mode 100644 packages/openapi-ts/src/ir/graph.ts create mode 100644 packages/openapi-ts/src/openApi/shared/utils/__tests__/graph.test.ts diff --git a/.changeset/cyan-regions-invite.md b/.changeset/cyan-regions-invite.md new file mode 100644 index 000000000..29ba2b47b --- /dev/null +++ b/.changeset/cyan-regions-invite.md @@ -0,0 +1,5 @@ +--- +'@hey-api/openapi-ts': patch +--- + +fix(validators): do not reference variables before they are declared diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts index b7df7b1fe..97f80d312 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts @@ -7,6 +7,96 @@ export const vExternalSharedExternalSharedModel = v.object({ name: v.optional(v.string()) }); +/** + * This is a model with one string property + */ +export const vModelWithString = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a model with one nested property + */ +export const vModelWithProperties = v.object({ + required: v.string(), + requiredAndReadOnly: v.pipe(v.string(), v.readonly()), + string: v.optional(v.string()), + number: v.optional(v.number()), + boolean: v.optional(v.boolean()), + reference: v.optional(vModelWithString), + 'property with space': v.optional(v.string()), + default: v.optional(v.string()), + try: v.optional(v.string()), + '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), + '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const vModelWithCircularReference: v.GenericSchema = v.object({ + prop: v.optional(v.lazy(() => { + return vModelWithCircularReference; + })) +}); + +/** + * This is a model that extends another model + */ +export const vModelThatExtends = v.intersect([ + vModelWithString, + v.object({ + propExtendsA: v.optional(v.string()), + propExtendsB: v.optional(vModelWithString) + }) +]); + +/** + * This is a model with one string property + */ +export const vModelWithStringError = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a model that extends another model + */ +export const vModelThatExtendsExtends = v.intersect([ + vModelWithString, + vModelThatExtends, + v.object({ + propExtendsC: v.optional(v.string()), + propExtendsD: v.optional(vModelWithString) + }) +]); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); + +export const vParameterActivityParams = v.object({ + description: v.optional(v.string()), + graduate_id: v.optional(v.pipe(v.number(), v.integer())), + organization_id: v.optional(v.pipe(v.number(), v.integer())), + parent_activity: v.optional(v.pipe(v.number(), v.integer())), + post_id: v.optional(v.pipe(v.number(), v.integer())) +}); + +export const vResponsePostActivityResponse = v.object({ + description: v.optional(v.string()), + graduate_id: v.optional(v.pipe(v.number(), v.integer())), + organization_id: v.optional(v.pipe(v.number(), v.integer())), + parent_activity_id: v.optional(v.pipe(v.number(), v.integer())), + post_id: v.optional(v.pipe(v.number(), v.integer())) +}); + +export const vFailureFailure = v.object({ + error: v.optional(v.string()), + message: v.optional(v.string()), + reference_code: v.optional(v.string()) +}); + export const vExternalRefA = vExternalSharedExternalSharedModel; export const vExternalRefB = vExternalSharedExternalSharedModel; @@ -64,23 +154,11 @@ export const vSimpleBoolean = v.boolean(); */ export const vSimpleString = v.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); - /** * This is a simple file */ export const vSimpleFile = v.string(); -/** - * This is a model with one string property - */ -export const vModelWithString = v.object({ - prop: v.optional(v.string()) -}); - export const vSimpleReference = vModelWithString; /** @@ -195,13 +273,6 @@ export const vModelWithBoolean = v.object({ prop: v.optional(v.boolean()) }); -/** - * This is a model with one string property - */ -export const vModelWithStringError = v.object({ - prop: v.optional(v.string()) -}); - /** * This is a model with one string property */ @@ -258,23 +329,6 @@ export const vModelWithNestedEnums = v.object({ arrayWithDescription: v.optional(v.array(v.pipe(v.number(), v.integer()))) }); -/** - * This is a model with one nested property - */ -export const vModelWithProperties = v.object({ - required: v.string(), - requiredAndReadOnly: v.pipe(v.string(), v.readonly()), - string: v.optional(v.string()), - number: v.optional(v.number()), - boolean: v.optional(v.boolean()), - reference: v.optional(vModelWithString), - 'property with space': v.optional(v.string()), - default: v.optional(v.string()), - try: v.optional(v.string()), - '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), - '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) -}); - /** * This is a model with one property containing a reference */ @@ -298,15 +352,6 @@ export const vModelWithDictionary = v.object({ prop: v.optional(v.object({})) }); -/** - * This is a model with one property containing a circular reference - */ -export const vModelWithCircularReference: v.GenericSchema = v.object({ - prop: v.optional(v.lazy(() => { - return vModelWithCircularReference; - })) -}); - /** * This is a model with one nested property */ @@ -343,29 +388,6 @@ export const vModelWithDuplicateImports = v.object({ propC: v.optional(vModelWithString) }); -/** - * This is a model that extends another model - */ -export const vModelThatExtends = v.intersect([ - vModelWithString, - v.object({ - propExtendsA: v.optional(v.string()), - propExtendsB: v.optional(vModelWithString) - }) -]); - -/** - * This is a model that extends another model - */ -export const vModelThatExtendsExtends = v.intersect([ - vModelWithString, - vModelThatExtends, - v.object({ - propExtendsC: v.optional(v.string()), - propExtendsD: v.optional(vModelWithString) - }) -]); - export const vDefault = v.object({ name: v.optional(v.string()) }); @@ -385,28 +407,6 @@ export const vModelWithPattern = v.object({ patternWithBacktick: v.optional(v.pipe(v.string(), v.regex(/aaa`bbb/))) }); -export const vParameterActivityParams = v.object({ - description: v.optional(v.string()), - graduate_id: v.optional(v.pipe(v.number(), v.integer())), - organization_id: v.optional(v.pipe(v.number(), v.integer())), - parent_activity: v.optional(v.pipe(v.number(), v.integer())), - post_id: v.optional(v.pipe(v.number(), v.integer())) -}); - -export const vResponsePostActivityResponse = v.object({ - description: v.optional(v.string()), - graduate_id: v.optional(v.pipe(v.number(), v.integer())), - organization_id: v.optional(v.pipe(v.number(), v.integer())), - parent_activity_id: v.optional(v.pipe(v.number(), v.integer())), - post_id: v.optional(v.pipe(v.number(), v.integer())) -}); - -export const vFailureFailure = v.object({ - error: v.optional(v.string()), - message: v.optional(v.string()), - reference_code: v.optional(v.string()) -}); - /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts index ce60806c7..4f235019c 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts @@ -3,19 +3,17 @@ import * as v from 'valibot'; /** - * Model with number-only name + * This is a model with one string property */ -export const v400 = v.string(); +export const vModelWithString = v.object({ + prop: v.optional(v.string()) +}); export const vExternalSharedExternalSharedModel = v.object({ id: v.string(), name: v.optional(v.string()) }); -export const vExternalRefA = vExternalSharedExternalSharedModel; - -export const vExternalRefB = vExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -24,6 +22,322 @@ export const vExternalRefB = vExternalSharedExternalSharedModel; */ export const vCamelCaseCommentWithBreaks = v.pipe(v.number(), v.integer()); +/** + * This is a model with one nested property + */ +export const vModelWithProperties = v.object({ + required: v.string(), + requiredAndReadOnly: v.pipe(v.string(), v.readonly()), + requiredAndNullable: v.union([ + v.string(), + v.null() + ]), + string: v.optional(v.string()), + number: v.optional(v.number()), + boolean: v.optional(v.boolean()), + reference: v.optional(vModelWithString), + 'property with space': v.optional(v.string()), + default: v.optional(v.string()), + try: v.optional(v.string()), + '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), + '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) +}); + +export const vModelWithReadOnlyAndWriteOnly = v.object({ + foo: v.string(), + bar: v.pipe(v.string(), v.readonly()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const vModelWithCircularReference: v.GenericSchema = v.object({ + prop: v.optional(v.lazy(() => { + return vModelWithCircularReference; + })) +}); + +/** + * This is a model with one enum + */ +export const vModelWithEnum = v.object({ + 'foo_bar-enum': v.optional(v.picklist([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: v.optional(v.picklist([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: v.optional(v.unknown()) +}); + +/** + * This is a model with one property containing an array + */ +export const vModelWithArray = v.object({ + prop: v.optional(v.array(vModelWithString)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const vModelWithDictionary = v.object({ + prop: v.optional(v.object({})) +}); + +/** + * Circle + */ +export const vModelCircle = v.object({ + kind: v.string(), + radius: v.optional(v.number()) +}); + +/** + * Square + */ +export const vModelSquare = v.object({ + kind: v.string(), + sideLength: v.optional(v.number()) +}); + +export const v3eNum1Период = v.picklist([ + 'Bird', + 'Dog' +]); + +export const vConstValue = v.picklist([ + 'ConstValue' +]); + +/** + * This is a base model with two simple optional properties + */ +export const vCompositionBaseModel = v.object({ + firstName: v.optional(v.string()), + lastname: v.optional(v.string()) +}); + +/** + * This is a model that extends another model + */ +export const vModelThatExtends = v.intersect([ + vModelWithString, + v.object({ + propExtendsA: v.optional(v.string()), + propExtendsB: v.optional(vModelWithString) + }) +]); + +/** + * This is a reusable parameter + */ +export const vSimpleParameter = v.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); + +/** + * An object that can be null + */ +export const vNullableObject = v.optional(v.union([ + v.object({ + foo: v.optional(v.string()) + }), + v.null() +]), null); + +export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ + 'foo', + 'bar' +]); + +export const vModelWithNestedArrayEnumsDataBar = v.picklist([ + 'baz', + 'qux' +]); + +export const vModelWithNestedArrayEnumsData = v.object({ + foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), + bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const vModelWithInteger = v.object({ + prop: v.optional(v.pipe(v.number(), v.integer())) +}); + +/** + * Model with restricted keyword name + */ +export const vImport = v.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ + resourceVersion: v.optional(v.string()), + uid: v.optional(v.string()) +}); + +export const vAdditionalPropertiesUnknownIssue = v.object({}); + +export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ + item: v.optional(v.boolean()), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())), + data: v.optional(v.object({})) +}); + +export const vGenericSchemaDuplicateIssue1SystemString = v.object({ + item: v.optional(v.union([ + v.string(), + v.null() + ])), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())) +}); + +export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ + foo: v.string(), + baz: v.string() +}); + +/** + * This is a model with one property containing an array + */ +export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ + prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const vModelFromZendesk = v.string(); + +/** + * This is a model with one boolean property + */ +export const vModelWithBoolean = v.object({ + prop: v.optional(v.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const vDeprecatedModel = v.object({ + prop: v.optional(v.string()) +}); + +export const vModelWithOneOfEnum = v.union([ + v.object({ + foo: v.picklist([ + 'Bar' + ]) + }), + v.object({ + foo: v.picklist([ + 'Baz' + ]) + }), + v.object({ + foo: v.picklist([ + 'Qux' + ]) + }), + v.object({ + content: v.pipe(v.string(), v.isoTimestamp()), + foo: v.picklist([ + 'Quux' + ]) + }), + v.object({ + content: v.tuple([ + v.pipe(v.string(), v.isoTimestamp()), + v.pipe(v.string(), v.isoTimestamp()) + ]), + foo: v.picklist([ + 'Corge' + ]) + }) +]); + +export const vPageable = v.object({ + page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), + size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), + sort: v.optional(v.array(v.string())) +}); + +export const vSimpleRequestBody = vModelWithString; + +export const vSimpleFormData = vModelWithString; + +/** + * Model with number-only name + */ +export const v400 = v.string(); + +/** + * This is a model with one string property + */ +export const vModelWithStringError = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a complex dictionary + */ +export const vDictionaryWithArray = v.object({}); + +/** + * This is a model that extends another model + */ +export const vModelThatExtendsExtends = v.intersect([ + vModelWithString, + vModelThatExtends, + v.object({ + propExtendsC: v.optional(v.string()), + propExtendsD: v.optional(vModelWithString) + }) +]); + +/** + * This is a simple array with strings + */ +export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); + +/** + * Parameter with illegal characters + */ +export const vXFooBar = vModelWithString; + +export const vExternalRefA = vExternalSharedExternalSharedModel; + +export const vExternalRefB = vExternalSharedExternalSharedModel; + /** * Testing multiline comments in string: First line * Second line @@ -77,23 +391,11 @@ export const vSimpleBoolean = v.boolean(); */ export const vSimpleString = v.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); - /** * This is a simple file */ export const vSimpleFile = v.string(); -/** - * This is a model with one string property - */ -export const vModelWithString = v.object({ - prop: v.optional(v.string()) -}); - /** * This is a simple reference */ @@ -153,11 +455,6 @@ export const vArrayWithNumbers = v.array(v.pipe(v.number(), v.integer())); */ export const vArrayWithBooleans = v.array(v.boolean()); -/** - * This is a simple array with strings - */ -export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); - /** * This is a simple array with references */ @@ -207,11 +504,6 @@ export const vDictionaryWithPropertiesAndAdditionalProperties = v.object({ */ export const vDictionaryWithReference = v.object({}); -/** - * This is a complex dictionary - */ -export const vDictionaryWithArray = v.object({}); - /** * This is a string dictionary */ @@ -225,32 +517,6 @@ export const vDictionaryWithProperties = v.record(v.string(), v.object({ bar: v.optional(v.string()) })); -/** - * This is a model with one number property - */ -export const vModelWithInteger = v.object({ - prop: v.optional(v.pipe(v.number(), v.integer())) -}); - -/** - * This is a model with one boolean property - */ -export const vModelWithBoolean = v.object({ - prop: v.optional(v.boolean()) -}); - -/** - * This is a model with one string property - */ -export const vModelWithStringError = v.object({ - prop: v.optional(v.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const vModelFromZendesk = v.string(); - /** * This is a model with one string property */ @@ -270,34 +536,13 @@ export const vModelWithNullableString = v.object({ nullableRequiredProp2: v.union([ v.string(), v.null() - ]), - 'foo_bar-enum': v.optional(v.picklist([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const vModelWithEnum = v.object({ + ]), 'foo_bar-enum': v.optional(v.picklist([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ])), - statusCode: v.optional(v.picklist([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: v.optional(v.unknown()) + ])) }); /** @@ -336,27 +581,6 @@ export const vModelWithNestedEnums = v.object({ ])) }); -/** - * This is a model with one nested property - */ -export const vModelWithProperties = v.object({ - required: v.string(), - requiredAndReadOnly: v.pipe(v.string(), v.readonly()), - requiredAndNullable: v.union([ - v.string(), - v.null() - ]), - string: v.optional(v.string()), - number: v.optional(v.number()), - boolean: v.optional(v.boolean()), - reference: v.optional(vModelWithString), - 'property with space': v.optional(v.string()), - default: v.optional(v.string()), - try: v.optional(v.string()), - '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), - '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) -}); - /** * This is a model with one property containing a reference */ @@ -364,54 +588,6 @@ export const vModelWithReference = v.object({ prop: v.optional(vModelWithProperties) }); -export const vModelWithReadOnlyAndWriteOnly = v.object({ - foo: v.string(), - bar: v.pipe(v.string(), v.readonly()) -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ - prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArray = v.object({ - prop: v.optional(v.array(vModelWithString)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const vModelWithDictionary = v.object({ - prop: v.optional(v.object({})) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const vDeprecatedModel = v.object({ - prop: v.optional(v.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const vModelWithCircularReference: v.GenericSchema = v.object({ - prop: v.optional(v.lazy(() => { - return vModelWithCircularReference; - })) -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -437,22 +613,6 @@ export const vCompositionWithOneOfAnonymous = v.object({ ])) }); -/** - * Circle - */ -export const vModelCircle = v.object({ - kind: v.string(), - radius: v.optional(v.number()) -}); - -/** - * Square - */ -export const vModelSquare = v.object({ - kind: v.string(), - sideLength: v.optional(v.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -512,15 +672,6 @@ export const vCompositionWithNestedAnyAndTypeNull = v.object({ ])) }); -export const v3eNum1Период = v.picklist([ - 'Bird', - 'Dog' -]); - -export const vConstValue = v.picklist([ - 'ConstValue' -]); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -608,14 +759,6 @@ export const vCompositionWithAnyOfAndNullable = v.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const vCompositionBaseModel = v.object({ - firstName: v.optional(v.string()), - lastname: v.optional(v.string()) -}); - /** * This is a model that extends the base model */ @@ -673,29 +816,6 @@ export const vModelWithDuplicateImports = v.object({ propC: v.optional(vModelWithString) }); -/** - * This is a model that extends another model - */ -export const vModelThatExtends = v.intersect([ - vModelWithString, - v.object({ - propExtendsA: v.optional(v.string()), - propExtendsB: v.optional(vModelWithString) - }) -]); - -/** - * This is a model that extends another model - */ -export const vModelThatExtendsExtends = v.intersect([ - vModelWithString, - vModelThatExtends, - v.object({ - propExtendsC: v.optional(v.string()), - propExtendsD: v.optional(vModelWithString) - }) -]); - /** * This is a model that contains a some patterns */ @@ -723,12 +843,6 @@ export const vDefault = v.object({ name: v.optional(v.string()) }); -export const vPageable = v.object({ - page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), - size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), - sort: v.optional(v.array(v.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -769,11 +883,6 @@ export const vNestedAnyOfArraysNullable = v.object({ ])) }); -/** - * This is a reusable parameter - */ -export const vSimpleParameter = v.unknown(); - export const vCompositionWithOneOfAndProperties = v.intersect([ v.union([ v.object({ @@ -792,16 +901,6 @@ export const vCompositionWithOneOfAndProperties = v.intersect([ }) ]); -/** - * An object that can be null - */ -export const vNullableObject = v.optional(v.union([ - v.object({ - foo: v.optional(v.string()) - }), - v.null() -]), null); - /** * Some % character */ @@ -811,54 +910,6 @@ export const vModelWithNullableObject = v.object({ data: v.optional(vNullableObject) }); -export const vModelWithOneOfEnum = v.union([ - v.object({ - foo: v.picklist([ - 'Bar' - ]) - }), - v.object({ - foo: v.picklist([ - 'Baz' - ]) - }), - v.object({ - foo: v.picklist([ - 'Qux' - ]) - }), - v.object({ - content: v.pipe(v.string(), v.isoTimestamp()), - foo: v.picklist([ - 'Quux' - ]) - }), - v.object({ - content: v.tuple([ - v.pipe(v.string(), v.isoTimestamp()), - v.pipe(v.string(), v.isoTimestamp()) - ]), - foo: v.picklist([ - 'Corge' - ]) - }) -]); - -export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ - 'foo', - 'bar' -]); - -export const vModelWithNestedArrayEnumsDataBar = v.picklist([ - 'baz', - 'qux' -]); - -export const vModelWithNestedArrayEnumsData = v.object({ - foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), - bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) -}); - export const vModelWithNestedArrayEnums = v.object({ array_strings: v.optional(v.array(v.string())), data: v.optional(vModelWithNestedArrayEnumsData) @@ -908,11 +959,6 @@ export const vModelWithAnyOfConstantSizeArrayNullable = v.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const vImport = v.string(); - export const vModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = v.tuple([ v.union([ v.number(), @@ -1007,14 +1053,6 @@ export const vSchemaWithFormRestrictedKeys = v.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ - resourceVersion: v.optional(v.string()), - uid: v.optional(v.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1022,8 +1060,6 @@ export const vIoK8sApimachineryPkgApisMetaV1DeleteOptions = v.object({ preconditions: v.optional(vIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const vAdditionalPropertiesUnknownIssue = v.object({}); - export const vAdditionalPropertiesUnknownIssue2 = v.object({}); export const vAdditionalPropertiesUnknownIssue3 = v.intersect([ @@ -1037,28 +1073,6 @@ export const vAdditionalPropertiesIntegerIssue = v.object({ value: v.pipe(v.number(), v.integer()) }); -export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ - item: v.optional(v.boolean()), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())), - data: v.optional(v.object({})) -}); - -export const vGenericSchemaDuplicateIssue1SystemString = v.object({ - item: v.optional(v.union([ - v.string(), - v.null() - ])), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())) -}); - export const vOneOfAllOfIssue = v.union([ v.intersect([ v.union([ @@ -1105,11 +1119,6 @@ export const vFileWritable = v.object({ mime: v.pipe(v.string(), v.minLength(1), v.maxLength(24)) }); -export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ - foo: v.string(), - baz: v.string() -}); - export const vAdditionalPropertiesUnknownIssueWritable = v.object({}); export const vGenericSchemaDuplicateIssue1SystemBooleanWritable = v.object({ @@ -1132,15 +1141,6 @@ export const vGenericSchemaDuplicateIssue1SystemStringWritable = v.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const vXFooBar = vModelWithString; - -export const vSimpleRequestBody = vModelWithString; - -export const vSimpleFormData = vModelWithString; - export const vExportData = v.object({ body: v.optional(v.never()), path: v.optional(v.never()), diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts index 9769db8fa..5f2394594 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts @@ -2,16 +2,12 @@ import * as v from 'valibot'; -export const vBar: v.GenericSchema = v.object({ - foo: v.optional(v.lazy(() => { - return vFoo; - })) -}); - export const vFoo: v.GenericSchema = v.optional(v.union([ v.object({ foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: v.optional(vBar), + bar: v.optional(v.lazy(() => { + return vBar; + })), baz: v.optional(v.array(v.lazy(() => { return vFoo; }))), @@ -20,4 +16,8 @@ export const vFoo: v.GenericSchema = v.optional(v.union([ v.null() ]), null); +export const vBar = v.object({ + foo: v.optional(vFoo) +}); + export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts index fa8440fa9..6d7c9cdf3 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts @@ -3,9 +3,11 @@ import * as v from 'valibot'; /** - * Model with number-only name + * This is a model with one string property */ -export const v400 = v.string(); +export const vModelWithString = v.object({ + prop: v.optional(v.string()) +}); export const vExternalSharedExternalSharedModel = v.object({ id: v.string(), @@ -13,22 +15,338 @@ export const vExternalSharedExternalSharedModel = v.object({ }); /** - * External ref to shared model (A) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const vExternalRefA = vExternalSharedExternalSharedModel; +export const vCamelCaseCommentWithBreaks = v.pipe(v.number(), v.integer()); /** - * External ref to shared model (B) + * This is a model with one nested property */ -export const vExternalRefB = vExternalSharedExternalSharedModel; +export const vModelWithProperties = v.object({ + required: v.string(), + requiredAndReadOnly: v.pipe(v.string(), v.readonly()), + requiredAndNullable: v.union([ + v.string(), + v.null() + ]), + string: v.optional(v.string()), + number: v.optional(v.number()), + boolean: v.optional(v.boolean()), + reference: v.optional(vModelWithString), + 'property with space': v.optional(v.string()), + default: v.optional(v.string()), + try: v.optional(v.string()), + '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), + '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) +}); + +export const vModelWithReadOnlyAndWriteOnly = v.object({ + foo: v.string(), + bar: v.pipe(v.string(), v.readonly()) +}); /** - * Testing multiline comments in string: First line - * Second line + * This is a model with one property containing a circular reference + */ +export const vModelWithCircularReference: v.GenericSchema = v.object({ + prop: v.optional(v.lazy(() => { + return vModelWithCircularReference; + })) +}); + +/** + * This is a model with one enum + */ +export const vModelWithEnum = v.object({ + 'foo_bar-enum': v.optional(v.picklist([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: v.optional(v.picklist([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: v.optional(v.unknown()) +}); + +/** + * This is a model with one property containing an array + */ +export const vModelWithArray = v.object({ + prop: v.optional(v.array(vModelWithString)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const vModelWithDictionary = v.object({ + prop: v.optional(v.object({})) +}); + +/** + * Circle + */ +export const vModelCircle = v.object({ + kind: v.string(), + radius: v.optional(v.number()) +}); + +/** + * Square + */ +export const vModelSquare = v.object({ + kind: v.string(), + sideLength: v.optional(v.number()) +}); + +export const v3eNum1Период = v.picklist([ + 'Bird', + 'Dog' +]); + +export const vConstValue = v.literal('ConstValue'); + +/** + * This is a base model with two simple optional properties + */ +export const vCompositionBaseModel = v.object({ + firstName: v.optional(v.string()), + lastname: v.optional(v.string()) +}); + +/** + * This is a model that extends another model + */ +export const vModelThatExtends = v.intersect([ + vModelWithString, + v.object({ + propExtendsA: v.optional(v.string()), + propExtendsB: v.optional(vModelWithString) + }) +]); + +/** + * This is a reusable parameter + */ +export const vSimpleParameter = v.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); + +/** + * An object that can be null + */ +export const vNullableObject = v.optional(v.union([ + v.object({ + foo: v.optional(v.string()) + }), + v.null() +]), null); + +export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ + 'foo', + 'bar' +]); + +export const vModelWithNestedArrayEnumsDataBar = v.picklist([ + 'baz', + 'qux' +]); + +export const vModelWithNestedArrayEnumsData = v.object({ + foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), + bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const vModelWithInteger = v.object({ + prop: v.optional(v.pipe(v.number(), v.integer())) +}); + +/** + * Model with restricted keyword name + */ +export const vImport = v.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ + resourceVersion: v.optional(v.string()), + uid: v.optional(v.string()) +}); + +export const vAdditionalPropertiesUnknownIssue = v.object({}); + +export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ + item: v.optional(v.boolean()), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())), + data: v.optional(v.object({})) +}); + +export const vGenericSchemaDuplicateIssue1SystemString = v.object({ + item: v.optional(v.union([ + v.string(), + v.null() + ])), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())) +}); + +export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ + foo: v.string(), + baz: v.string() +}); + +/** + * This is a model with one property containing an array + */ +export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ + prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const vModelFromZendesk = v.string(); + +/** + * This is a model with one boolean property + */ +export const vModelWithBoolean = v.object({ + prop: v.optional(v.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property * - * Fourth line + * @deprecated */ -export const vCamelCaseCommentWithBreaks = v.pipe(v.number(), v.integer()); +export const vDeprecatedModel = v.object({ + prop: v.optional(v.string()) +}); + +export const vModelWithOneOfEnum = v.union([ + v.object({ + foo: v.picklist([ + 'Bar' + ]) + }), + v.object({ + foo: v.picklist([ + 'Baz' + ]) + }), + v.object({ + foo: v.picklist([ + 'Qux' + ]) + }), + v.object({ + content: v.pipe(v.string(), v.isoTimestamp()), + foo: v.picklist([ + 'Quux' + ]) + }), + v.object({ + content: v.tuple([ + v.pipe(v.string(), v.isoTimestamp()), + v.string() + ]), + foo: v.picklist([ + 'Corge' + ]) + }) +]); + +export const vPageable = v.object({ + page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), + size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), + sort: v.optional(v.array(v.string())) +}); + +/** + * A reusable request body + */ +export const vSimpleRequestBody = vModelWithString; + +/** + * A reusable request body + */ +export const vSimpleFormData = vModelWithString; + +/** + * Model with number-only name + */ +export const v400 = v.string(); + +/** + * This is a model with one string property + */ +export const vModelWithStringError = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a complex dictionary + */ +export const vDictionaryWithArray = v.object({}); + +/** + * This is a model that extends another model + */ +export const vModelThatExtendsExtends = v.intersect([ + vModelWithString, + vModelThatExtends, + v.object({ + propExtendsC: v.optional(v.string()), + propExtendsD: v.optional(vModelWithString) + }) +]); + +/** + * This is a simple array with strings + */ +export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); + +/** + * Parameter with illegal characters + */ +export const vXFooBar = vModelWithString; + +/** + * External ref to shared model (A) + */ +export const vExternalRefA = vExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const vExternalRefB = vExternalSharedExternalSharedModel; /** * Testing multiline comments in string: First line @@ -83,23 +401,11 @@ export const vSimpleBoolean = v.boolean(); */ export const vSimpleString = v.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); - /** * This is a simple file */ export const vSimpleFile = v.string(); -/** - * This is a model with one string property - */ -export const vModelWithString = v.object({ - prop: v.optional(v.string()) -}); - /** * This is a simple reference */ @@ -159,11 +465,6 @@ export const vArrayWithNumbers = v.array(v.pipe(v.number(), v.integer())); */ export const vArrayWithBooleans = v.array(v.boolean()); -/** - * This is a simple array with strings - */ -export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); - /** * This is a simple array with references */ @@ -216,11 +517,6 @@ export const vDictionaryWithPropertiesAndAdditionalProperties = v.object({ */ export const vDictionaryWithReference = v.object({}); -/** - * This is a complex dictionary - */ -export const vDictionaryWithArray = v.object({}); - /** * This is a string dictionary */ @@ -234,32 +530,6 @@ export const vDictionaryWithProperties = v.record(v.string(), v.object({ bar: v.optional(v.string()) })); -/** - * This is a model with one number property - */ -export const vModelWithInteger = v.object({ - prop: v.optional(v.pipe(v.number(), v.integer())) -}); - -/** - * This is a model with one boolean property - */ -export const vModelWithBoolean = v.object({ - prop: v.optional(v.boolean()) -}); - -/** - * This is a model with one string property - */ -export const vModelWithStringError = v.object({ - prop: v.optional(v.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const vModelFromZendesk = v.string(); - /** * This is a model with one string property */ @@ -278,35 +548,14 @@ export const vModelWithNullableString = v.object({ ])), nullableRequiredProp2: v.union([ v.string(), - v.null() - ]), - 'foo_bar-enum': v.optional(v.picklist([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const vModelWithEnum = v.object({ - 'foo_bar-enum': v.optional(v.picklist([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: v.optional(v.picklist([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: v.optional(v.unknown()) + v.null() + ]), + 'foo_bar-enum': v.optional(v.picklist([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])) }); /** @@ -345,27 +594,6 @@ export const vModelWithNestedEnums = v.object({ ])) }); -/** - * This is a model with one nested property - */ -export const vModelWithProperties = v.object({ - required: v.string(), - requiredAndReadOnly: v.pipe(v.string(), v.readonly()), - requiredAndNullable: v.union([ - v.string(), - v.null() - ]), - string: v.optional(v.string()), - number: v.optional(v.number()), - boolean: v.optional(v.boolean()), - reference: v.optional(vModelWithString), - 'property with space': v.optional(v.string()), - default: v.optional(v.string()), - try: v.optional(v.string()), - '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), - '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) -}); - /** * This is a model with one property containing a reference */ @@ -373,54 +601,6 @@ export const vModelWithReference = v.object({ prop: v.optional(vModelWithProperties) }); -export const vModelWithReadOnlyAndWriteOnly = v.object({ - foo: v.string(), - bar: v.pipe(v.string(), v.readonly()) -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ - prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArray = v.object({ - prop: v.optional(v.array(vModelWithString)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const vModelWithDictionary = v.object({ - prop: v.optional(v.object({})) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const vDeprecatedModel = v.object({ - prop: v.optional(v.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const vModelWithCircularReference: v.GenericSchema = v.object({ - prop: v.optional(v.lazy(() => { - return vModelWithCircularReference; - })) -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -446,22 +626,6 @@ export const vCompositionWithOneOfAnonymous = v.object({ ])) }); -/** - * Circle - */ -export const vModelCircle = v.object({ - kind: v.string(), - radius: v.optional(v.number()) -}); - -/** - * Square - */ -export const vModelSquare = v.object({ - kind: v.string(), - sideLength: v.optional(v.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -515,13 +679,6 @@ export const vCompositionWithNestedAnyAndTypeNull = v.object({ ])) }); -export const v3eNum1Период = v.picklist([ - 'Bird', - 'Dog' -]); - -export const vConstValue = v.literal('ConstValue'); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -609,14 +766,6 @@ export const vCompositionWithAnyOfAndNullable = v.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const vCompositionBaseModel = v.object({ - firstName: v.optional(v.string()), - lastname: v.optional(v.string()) -}); - /** * This is a model that extends the base model */ @@ -674,29 +823,6 @@ export const vModelWithDuplicateImports = v.object({ propC: v.optional(vModelWithString) }); -/** - * This is a model that extends another model - */ -export const vModelThatExtends = v.intersect([ - vModelWithString, - v.object({ - propExtendsA: v.optional(v.string()), - propExtendsB: v.optional(vModelWithString) - }) -]); - -/** - * This is a model that extends another model - */ -export const vModelThatExtendsExtends = v.intersect([ - vModelWithString, - vModelThatExtends, - v.object({ - propExtendsC: v.optional(v.string()), - propExtendsD: v.optional(vModelWithString) - }) -]); - /** * This is a model that contains a some patterns */ @@ -724,12 +850,6 @@ export const vDefault = v.object({ name: v.optional(v.string()) }); -export const vPageable = v.object({ - page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), - size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), - sort: v.optional(v.array(v.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -766,11 +886,6 @@ export const vNestedAnyOfArraysNullable = v.object({ ])) }); -/** - * This is a reusable parameter - */ -export const vSimpleParameter = v.unknown(); - export const vCompositionWithOneOfAndProperties = v.intersect([ v.union([ v.object({ @@ -789,16 +904,6 @@ export const vCompositionWithOneOfAndProperties = v.intersect([ }) ]); -/** - * An object that can be null - */ -export const vNullableObject = v.optional(v.union([ - v.object({ - foo: v.optional(v.string()) - }), - v.null() -]), null); - /** * Some % character */ @@ -808,54 +913,6 @@ export const vModelWithNullableObject = v.object({ data: v.optional(vNullableObject) }); -export const vModelWithOneOfEnum = v.union([ - v.object({ - foo: v.picklist([ - 'Bar' - ]) - }), - v.object({ - foo: v.picklist([ - 'Baz' - ]) - }), - v.object({ - foo: v.picklist([ - 'Qux' - ]) - }), - v.object({ - content: v.pipe(v.string(), v.isoTimestamp()), - foo: v.picklist([ - 'Quux' - ]) - }), - v.object({ - content: v.tuple([ - v.pipe(v.string(), v.isoTimestamp()), - v.string() - ]), - foo: v.picklist([ - 'Corge' - ]) - }) -]); - -export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ - 'foo', - 'bar' -]); - -export const vModelWithNestedArrayEnumsDataBar = v.picklist([ - 'baz', - 'qux' -]); - -export const vModelWithNestedArrayEnumsData = v.object({ - foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), - bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) -}); - export const vModelWithNestedArrayEnums = v.object({ array_strings: v.optional(v.array(v.string())), data: v.optional(vModelWithNestedArrayEnumsData) @@ -912,11 +969,6 @@ export const vModelWithAnyOfConstantSizeArrayNullable = v.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const vImport = v.string(); - export const vModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = v.tuple([ v.union([ v.number(), @@ -1011,14 +1063,6 @@ export const vSchemaWithFormRestrictedKeys = v.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ - resourceVersion: v.optional(v.string()), - uid: v.optional(v.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1026,8 +1070,6 @@ export const vIoK8sApimachineryPkgApisMetaV1DeleteOptions = v.object({ preconditions: v.optional(vIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const vAdditionalPropertiesUnknownIssue = v.object({}); - export const vAdditionalPropertiesUnknownIssue2 = v.object({}); export const vAdditionalPropertiesUnknownIssue3 = v.intersect([ @@ -1041,28 +1083,6 @@ export const vAdditionalPropertiesIntegerIssue = v.object({ value: v.pipe(v.number(), v.integer()) }); -export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ - item: v.optional(v.boolean()), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())), - data: v.optional(v.object({})) -}); - -export const vGenericSchemaDuplicateIssue1SystemString = v.object({ - item: v.optional(v.union([ - v.string(), - v.null() - ])), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())) -}); - export const vOneOfAllOfIssue = v.union([ v.intersect([ v.union([ @@ -1109,11 +1129,6 @@ export const vFileWritable = v.object({ mime: v.pipe(v.string(), v.minLength(1), v.maxLength(24)) }); -export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ - foo: v.string(), - baz: v.string() -}); - export const vAdditionalPropertiesUnknownIssueWritable = v.object({}); export const vGenericSchemaDuplicateIssue1SystemBooleanWritable = v.object({ @@ -1136,21 +1151,6 @@ export const vGenericSchemaDuplicateIssue1SystemStringWritable = v.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const vXFooBar = vModelWithString; - -/** - * A reusable request body - */ -export const vSimpleRequestBody = vModelWithString; - -/** - * A reusable request body - */ -export const vSimpleFormData = vModelWithString; - export const vExportData = v.object({ body: v.optional(v.never()), path: v.optional(v.never()), diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref-2/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref-2/valibot.gen.ts index 11af4d8fc..769fabf36 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref-2/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref-2/valibot.gen.ts @@ -11,6 +11,6 @@ export const vBar: v.GenericSchema = v.object({ ]) }); -export const vFoo: v.GenericSchema = v.object({ +export const vFoo = v.object({ foo: vBar }); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts index c32b0b517..22b9cd058 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts @@ -8,15 +8,15 @@ export const vBar: v.GenericSchema = v.object({ }))) }); -export const vFoo: v.GenericSchema = v.object({ - foo: v.optional(vBar) +export const vBaz: v.GenericSchema = v.lazy(() => { + return vQux; }); /** * description caused circular reference error */ -export const vQux: v.GenericSchema = v.lazy(() => { - return vQux; -}); +export const vQux = vBaz; -export const vBaz: v.GenericSchema = vQux; +export const vFoo = v.object({ + foo: v.optional(vBar) +}); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts index c54dd93e8..938d20ba4 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts @@ -2,17 +2,6 @@ import * as v from 'valibot'; -/** - * This is Bar schema. - */ -export const vBar: v.GenericSchema = v.pipe(v.object({ - foo: v.optional(v.lazy(() => { - return vFoo; - })) -}), v.metadata({ - description: 'This is Bar schema.' -})); - /** * This is Foo schema. */ @@ -21,7 +10,9 @@ export const vFoo: v.GenericSchema = v.optional(v.union([ foo: v.optional(v.pipe(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/)), v.metadata({ description: 'This is foo property.' }))), - bar: v.optional(vBar), + bar: v.optional(v.lazy(() => { + return vBar; + })), baz: v.optional(v.pipe(v.array(v.lazy(() => { return vFoo; })), v.metadata({ @@ -34,22 +25,31 @@ export const vFoo: v.GenericSchema = v.optional(v.union([ v.null() ]), null); -export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); - -export const vQux = v.record(v.string(), v.object({ - qux: v.optional(v.string()) +/** + * This is Bar schema. + */ +export const vBar = v.pipe(v.object({ + foo: v.optional(vFoo) +}), v.metadata({ + description: 'This is Bar schema.' })); +export const vFoo2 = v.object({ + foo: v.optional(vBar) +}); + /** * This is Foo parameter. */ -export const vFoo2 = v.pipe(v.string(), v.metadata({ +export const vFoo3 = v.pipe(v.string(), v.metadata({ description: 'This is Foo parameter.' })); -export const vFoo3 = v.object({ - foo: v.optional(vBar) -}); +export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); + +export const vQux = v.record(v.string(), v.object({ + qux: v.optional(v.string()) +})); export const vPatchFooData = v.object({ body: v.object({ @@ -70,7 +70,7 @@ export const vPatchFooData = v.object({ }); export const vPostFooData = v.object({ - body: vFoo3, + body: vFoo2, path: v.optional(v.never()), query: v.optional(v.never()) }); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts index 3c12cbfaa..e86881ed3 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts @@ -2,22 +2,15 @@ import * as v from 'valibot'; -/** - * This is Bar schema. - */ -export const vBar: v.GenericSchema = v.object({ - foo: v.optional(v.lazy(() => { - return vFoo; - })) -}); - /** * This is Foo schema. */ export const vFoo: v.GenericSchema = v.optional(v.union([ v.object({ foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: v.optional(vBar), + bar: v.optional(v.lazy(() => { + return vBar; + })), baz: v.optional(v.array(v.lazy(() => { return vFoo; }))), @@ -26,20 +19,27 @@ export const vFoo: v.GenericSchema = v.optional(v.union([ v.null() ]), null); -export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); +/** + * This is Bar schema. + */ +export const vBar = v.object({ + foo: v.optional(vFoo) +}); -export const vQux = v.record(v.string(), v.object({ - qux: v.optional(v.string()) -})); +export const vFoo2 = v.object({ + foo: v.optional(vBar) +}); /** * This is Foo parameter. */ -export const vFoo2 = v.string(); +export const vFoo3 = v.string(); -export const vFoo3 = v.object({ - foo: v.optional(vBar) -}); +export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); + +export const vQux = v.record(v.string(), v.object({ + qux: v.optional(v.string()) +})); export const vPatchFooData = v.object({ body: v.object({ @@ -58,7 +58,7 @@ export const vPatchFooData = v.object({ }); export const vPostFooData = v.object({ - body: vFoo3, + body: vFoo2, path: v.optional(v.never()), query: v.optional(v.never()) }); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts index 3662ff489..0cf876256 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts @@ -11,13 +11,6 @@ export const vContact = v.union([ }) ]); -export const vUser = v.intersect([ - vContact, - v.object({ - username: v.string() - }) -]); - export const vDogDetails = v.object({ breed: v.string(), barkVolume: v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(10)) @@ -32,6 +25,13 @@ export const vCatDetails = v.object({ purrs: v.boolean() }); +export const vUser = v.intersect([ + vContact, + v.object({ + username: v.string() + }) +]); + export const vPetStore = v.object({ animals: v.array(v.object({ name: v.string(), diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts index 3c12cbfaa..e86881ed3 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts @@ -2,22 +2,15 @@ import * as v from 'valibot'; -/** - * This is Bar schema. - */ -export const vBar: v.GenericSchema = v.object({ - foo: v.optional(v.lazy(() => { - return vFoo; - })) -}); - /** * This is Foo schema. */ export const vFoo: v.GenericSchema = v.optional(v.union([ v.object({ foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: v.optional(vBar), + bar: v.optional(v.lazy(() => { + return vBar; + })), baz: v.optional(v.array(v.lazy(() => { return vFoo; }))), @@ -26,20 +19,27 @@ export const vFoo: v.GenericSchema = v.optional(v.union([ v.null() ]), null); -export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); +/** + * This is Bar schema. + */ +export const vBar = v.object({ + foo: v.optional(vFoo) +}); -export const vQux = v.record(v.string(), v.object({ - qux: v.optional(v.string()) -})); +export const vFoo2 = v.object({ + foo: v.optional(vBar) +}); /** * This is Foo parameter. */ -export const vFoo2 = v.string(); +export const vFoo3 = v.string(); -export const vFoo3 = v.object({ - foo: v.optional(vBar) -}); +export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); + +export const vQux = v.record(v.string(), v.object({ + qux: v.optional(v.string()) +})); export const vPatchFooData = v.object({ body: v.object({ @@ -58,7 +58,7 @@ export const vPatchFooData = v.object({ }); export const vPostFooData = v.object({ - body: vFoo3, + body: vFoo2, path: v.optional(v.never()), query: v.optional(v.never()) }); diff --git a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts index 1411badb7..dcb8e8db7 100644 --- a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts +++ b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts @@ -46,8 +46,8 @@ export default defineConfig(() => { // 'string-with-format.yaml', // 'transformers.json', // 'type-format.yaml', - // 'validators.yaml', - 'validators-circular-ref.json', + 'validators.yaml', + // 'validators-circular-ref.json', // 'validators-circular-ref-2.yaml', // 'zoom-video-sdk.json', ), @@ -342,7 +342,7 @@ export default defineConfig(() => { { // case: 'snake_case', // comments: false, - compatibilityVersion: 'mini', + compatibilityVersion: 4, dates: { local: true, // offset: true, @@ -354,8 +354,8 @@ export default defineConfig(() => { // }, }, exportFromIndex: true, - metadata: true, - // name: 'zod', + // metadata: true, + name: 'zod', // requests: { // // case: 'SCREAMING_SNAKE_CASE', // // name: 'z{{name}}TestData', diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts index ef475d167..52e5fbb4d 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import * as z from 'zod/v4-mini'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts index a914024a3..8f4a8e569 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts @@ -7,6 +7,91 @@ export const zExternalSharedExternalSharedModel = z.object({ name: z.optional(z.string()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + export const zExternalRefA = zExternalSharedExternalSharedModel; export const zExternalRefB = zExternalSharedExternalSharedModel; @@ -64,23 +149,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - export const zSimpleReference = zModelWithString; /** @@ -215,13 +288,6 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a model with one string property */ @@ -282,23 +348,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - /** * This is a model with one property containing a reference */ @@ -322,17 +371,6 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodMiniOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one nested property */ @@ -369,22 +407,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -404,28 +426,6 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().check(z.regex(/aaa`bbb/))) }); -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts index c4af44ea7..a5674d6cc 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod'; +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts index fbea7e50b..e556e245c 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts @@ -7,6 +7,89 @@ export const zExternalSharedExternalSharedModel = z.object({ name: z.string().optional() }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +export const zParameterActivityParams = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity_id: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zFailureFailure = z.object({ + error: z.string().optional(), + message: z.string().optional(), + reference_code: z.string().optional() +}); + export const zExternalRefA = zExternalSharedExternalSharedModel; export const zExternalRefB = zExternalSharedExternalSharedModel; @@ -64,23 +147,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - export const zSimpleReference = zModelWithString; /** @@ -215,13 +286,6 @@ export const zModelWithBoolean = z.object({ prop: z.boolean().optional() }); -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - /** * This is a model with one string property */ @@ -282,23 +346,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.array(z.number().int()).optional() }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - /** * This is a model with one property containing a reference */ @@ -322,15 +369,6 @@ export const zModelWithDictionary = z.object({ prop: z.record(z.string()).optional() }); -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - /** * This is a model with one nested property */ @@ -367,22 +405,6 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - export const zDefault = z.object({ name: z.string().optional() }); @@ -402,28 +424,6 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.string().regex(/aaa`bbb/).optional() }); -export const zParameterActivityParams = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity_id: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zFailureFailure = z.object({ - error: z.string().optional(), - message: z.string().optional(), - reference_code: z.string().optional() -}); - /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts index 87e0c90b2..b523ec8bc 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod/v4'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts index bfe8ab735..849675345 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts @@ -7,6 +7,91 @@ export const zExternalSharedExternalSharedModel = z.object({ name: z.optional(z.string()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + export const zExternalRefA = zExternalSharedExternalSharedModel; export const zExternalRefB = zExternalSharedExternalSharedModel; @@ -64,23 +149,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - export const zSimpleReference = zModelWithString; /** @@ -215,13 +288,6 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a model with one string property */ @@ -282,23 +348,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - /** * This is a model with one property containing a reference */ @@ -322,17 +371,6 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one nested property */ @@ -369,22 +407,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -404,28 +426,6 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().regex(/aaa`bbb/)) }); -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts index ef475d167..52e5fbb4d 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import * as z from 'zod/v4-mini'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts index 379f9a044..e859963ad 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts @@ -3,47 +3,39 @@ import * as z from 'zod/v4-mini'; export const zBaz = z.object({ - get quux(): z.ZodMiniOptional { - return z.optional(zQuux); + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); } }); export const zCorge = z.object({ - get baz(): z.ZodMiniOptional { - return z.optional(z.array(zBaz)); - } + baz: z.optional(z.array(zBaz)) +}); + +export const zFoo = z.object({ + quux: z.optional(zQuux) }); export const zQux = z.union([ z.intersection(z.object({ type: z.literal('struct') - }), z.lazy(() => { - return zCorge; - })), + }), zCorge), z.intersection(z.object({ type: z.literal('array') - }), z.lazy(() => { - return zFoo; - })) + }), zFoo) ]); export const zQuux = z.object({ - get qux(): z.ZodMiniOptional { - return z.optional(zQux); - } -}); - -export const zFoo = z.object({ - quux: z.optional(zQuux) + qux: z.optional(zQux) }); export const zBar = z.object({ - get bar(): z.ZodMiniOptional { + get bar() { return z.optional(z.lazy((): any => { return zBar; })); }, - get baz(): z.ZodMiniOptional { - return z.optional(zBaz); - } + baz: z.optional(zBaz) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts index b2e3f459b..0c02ccd61 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts @@ -3,19 +3,17 @@ import * as z from 'zod/v4-mini'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), name: z.optional(z.string()) }); -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -24,6 +22,320 @@ export const zExternalRefB = zExternalSharedExternalSharedModel; */ export const zCamelCaseCommentWithBreaks = z.int(); +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]), null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * Testing multiline comments in string: First line * Second line @@ -77,23 +389,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -178,11 +478,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - /** * This is a simple array with references */ @@ -246,11 +541,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -264,32 +554,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -307,36 +571,15 @@ export const zModelWithNullableString = z.object({ z.null() ])), nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + z.string(), + z.null() + ]), 'foo_bar-enum': z.optional(z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + ])) }); /** @@ -379,27 +622,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - /** * This is a model with one property containing a reference */ @@ -407,56 +629,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodMiniOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -482,22 +654,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -551,15 +707,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -648,14 +795,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -710,22 +849,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -753,12 +876,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -802,11 +919,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -822,16 +934,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().check(z.gte(0)) })); -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - /** * Some % character */ @@ -841,54 +943,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -942,11 +996,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1040,14 +1089,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1055,11 +1096,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1073,28 +1109,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1138,11 +1152,6 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1168,15 +1177,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts index a8be5f51c..7e7a4bfd4 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts @@ -2,17 +2,15 @@ import * as z from 'zod/v4-mini'; -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -22,4 +20,8 @@ export const zFoo = z._default(z.union([ z.null() ]), null); +export const zBar = z.object({ + foo: z.optional(zFoo) +}); + export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts index c4af44ea7..a5674d6cc 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod'; +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts index abc26f9e6..c4be6a26c 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts @@ -12,25 +12,23 @@ export const zCorge = z.object({ baz: z.array(zBaz).optional() }); -export const zQux: z.ZodTypeAny = z.union([ +export const zFoo = z.object({ + quux: zQuux.optional() +}); + +export const zQux = z.union([ z.object({ type: z.literal('struct') }).and(zCorge), z.object({ type: z.literal('array') - }).and(z.lazy(() => { - return zFoo; - })) + }).and(zFoo) ]); export const zQuux = z.object({ qux: zQux.optional() }); -export const zFoo = z.object({ - quux: zQuux.optional() -}); - export const zBar: z.AnyZodObject = z.object({ bar: z.lazy(() => { return zBar; diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts index a669c9867..045bff905 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts @@ -3,19 +3,17 @@ import { z } from 'zod'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.string().optional() +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), name: z.string().optional() }); -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -24,6 +22,318 @@ export const zExternalRefB = zExternalSharedExternalSharedModel; */ export const zCamelCaseCommentWithBreaks = z.number().int(); +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.string().optional() + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string().datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * Testing multiline comments in string: First line * Second line @@ -77,23 +387,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - /** * This is a simple reference */ @@ -178,11 +476,6 @@ export const zArrayWithNumbers = z.array(z.number().int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -246,11 +539,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -264,32 +552,6 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -305,38 +567,17 @@ export const zModelWithNullableString = z.object({ nullableProp2: z.union([ z.string(), z.null() - ]).optional(), - nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + ]).optional(), + nullableRequiredProp2: z.union([ + z.string(), + z.null() + ]), 'foo_bar-enum': z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() + ]).optional() }); /** @@ -379,27 +620,6 @@ export const zModelWithNestedEnums = z.object({ ]).optional() }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - /** * This is a model with one property containing a reference */ @@ -407,54 +627,6 @@ export const zModelWithReference = z.object({ prop: zModelWithProperties.optional() }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -480,22 +652,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -549,15 +705,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -646,14 +793,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ]).optional() }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - /** * This is a model that extends the base model */ @@ -708,22 +847,6 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - /** * This is a model that contains a some patterns */ @@ -751,12 +874,6 @@ export const zDefault = z.object({ name: z.string().optional() }); -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - /** * This is a free-form object without additionalProperties. */ @@ -800,11 +917,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.unknown(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -820,16 +932,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.number().int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - /** * Some % character */ @@ -839,54 +941,6 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string().datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -940,11 +994,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1038,14 +1087,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1053,11 +1094,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1071,28 +1107,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1136,11 +1150,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1166,15 +1175,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts index a6537a847..d2fb1f1be 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts @@ -2,16 +2,12 @@ import { z } from 'zod'; -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -20,4 +16,8 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); +export const zBar = z.object({ + foo: zFoo.optional() +}); + export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts index 87e0c90b2..b523ec8bc 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod/v4'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts index 24c4880fa..f28fd1739 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts @@ -3,47 +3,39 @@ import { z } from 'zod/v4'; export const zBaz = z.object({ - get quux(): z.ZodOptional { - return z.optional(zQuux); + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); } }); export const zCorge = z.object({ - get baz(): z.ZodOptional { - return z.optional(z.array(zBaz)); - } + baz: z.optional(z.array(zBaz)) +}); + +export const zFoo = z.object({ + quux: z.optional(zQuux) }); export const zQux = z.union([ z.object({ type: z.literal('struct') - }).and(z.lazy(() => { - return zCorge; - })), + }).and(zCorge), z.object({ type: z.literal('array') - }).and(z.lazy(() => { - return zFoo; - })) + }).and(zFoo) ]); export const zQuux = z.object({ - get qux(): z.ZodOptional { - return z.optional(zQux); - } -}); - -export const zFoo = z.object({ - quux: z.optional(zQuux) + qux: z.optional(zQux) }); export const zBar = z.object({ - get bar(): z.ZodOptional { + get bar() { return z.optional(z.lazy((): any => { return zBar; })); }, - get baz(): z.ZodOptional { - return z.optional(zBaz); - } + baz: z.optional(zBaz) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts index 2ac7855c3..846fe2eed 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts @@ -3,19 +3,17 @@ import { z } from 'zod/v4'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), name: z.optional(z.string()) }); -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -24,6 +22,320 @@ export const zExternalRefB = zExternalSharedExternalSharedModel; */ export const zCamelCaseCommentWithBreaks = z.int(); +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * Testing multiline comments in string: First line * Second line @@ -77,23 +389,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -178,11 +478,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -246,11 +541,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -264,32 +554,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -307,36 +571,15 @@ export const zModelWithNullableString = z.object({ z.null() ])), nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + z.string(), + z.null() + ]), 'foo_bar-enum': z.optional(z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + ])) }); /** @@ -379,27 +622,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - /** * This is a model with one property containing a reference */ @@ -407,56 +629,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -482,22 +654,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -551,15 +707,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -648,14 +795,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -710,22 +849,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -753,12 +876,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -802,11 +919,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -822,16 +934,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - /** * Some % character */ @@ -841,54 +943,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -942,11 +996,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1040,14 +1089,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1055,11 +1096,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1073,28 +1109,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1138,11 +1152,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1168,15 +1177,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts index d0fb4e7a8..59fe79b1c 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts @@ -2,17 +2,15 @@ import { z } from 'zod/v4'; -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -22,4 +20,8 @@ export const zFoo = z.union([ z.null() ]).default(null); +export const zBar = z.object({ + foo: z.optional(zFoo) +}); + export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts index ef475d167..52e5fbb4d 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import * as z from 'zod/v4-mini'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts index 3875498e0..ed15288d9 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts @@ -3,9 +3,11 @@ import * as z from 'zod/v4-mini'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), @@ -13,22 +15,336 @@ export const zExternalSharedExternalSharedModel = z.object({ }); /** - * External ref to shared model (A) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefA = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** - * External ref to shared model (B) + * This is a model with one nested property */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); /** - * Testing multiline comments in string: First line - * Second line + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]), null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property * - * Fourth line + * @deprecated */ -export const zCamelCaseCommentWithBreaks = z.int(); +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; /** * Testing multiline comments in string: First line @@ -83,23 +399,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -184,11 +488,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - /** * This is a simple array with references */ @@ -255,11 +554,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -273,32 +567,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -315,37 +583,16 @@ export const zModelWithNullableString = z.object({ z.string(), z.null() ])), - nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + nullableRequiredProp2: z.union([ + z.string(), + z.null() + ]), + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])) }); /** @@ -388,27 +635,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - /** * This is a model with one property containing a reference */ @@ -416,56 +642,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodMiniOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -491,22 +667,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -560,13 +720,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -655,14 +808,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -717,22 +862,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -760,12 +889,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -805,11 +928,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -825,16 +943,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().check(z.gte(0)) })); -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - /** * Some % character */ @@ -844,54 +952,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -948,11 +1008,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1046,14 +1101,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1061,11 +1108,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1079,28 +1121,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1144,11 +1164,6 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1174,21 +1189,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts index 5fa9cefc1..a4ffcf30d 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts @@ -3,22 +3,22 @@ import * as z from 'zod/v4-mini'; export const zBar = z.object({ - get bar(): z.ZodMiniOptional { + get bar() { return z.optional(z.array(z.lazy((): any => { return zBar; }))); } }); -export const zFoo = z.object({ - foo: z.optional(zBar) +export const zBaz = z.lazy((): any => { + return zQux; }); /** * description caused circular reference error */ -export const zQux = z.lazy((): any => { - return zQux; -}); +export const zQux = zBaz; -export const zBaz = zQux; +export const zFoo = z.object({ + foo: z.optional(zBar) +}); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts index 3fd3ebc43..d254a4bc5 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts @@ -2,23 +2,18 @@ import * as z from 'zod/v4-mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z._default(z.union([ z.null() ]), null); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts index 1a5edf41c..2f15ff8c4 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts @@ -2,17 +2,6 @@ import * as z from 'zod/v4-mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}).register(z.globalRegistry, { - description: 'This is Bar schema.' -}); - /** * This is Foo schema. */ @@ -21,8 +10,12 @@ export const zFoo = z._default(z.union([ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/)).register(z.globalRegistry, { description: 'This is foo property.' })), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; })).register(z.globalRegistry, { @@ -36,22 +29,31 @@ export const zFoo = z._default(z.union([ z.null() ]), null); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}).register(z.globalRegistry, { + description: 'This is Bar schema.' +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string().register(z.globalRegistry, { +export const zFoo3 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts index a3170d6a4..03c756a5c 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts @@ -2,25 +2,18 @@ import * as z from 'zod/v4-mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - -export type BarZodType = z.infer; - /** * This is Foo schema. */ export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -32,28 +25,37 @@ export const zFoo = z._default(z.union([ export type FooZodType = z.infer; -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export type BazZodType = z.infer; +export type BarZodType = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType2 = z.infer; /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export type FooZodType2 = z.infer; +export type FooZodType3 = z.infer; -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); -export type FooZodType3 = z.infer; +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts index bd49adf30..c087678cb 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts @@ -11,10 +11,6 @@ export const zContact = z.union([ }) ]); -export const zUser = z.intersection(zContact, z.object({ - username: z.string() -})); - export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().check(z.gte(1), z.lte(10)) @@ -29,6 +25,10 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); +export const zUser = z.intersection(zContact, z.object({ + username: z.string() +})); + export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts index 5bcc24c6f..c685386d8 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts @@ -2,23 +2,18 @@ import * as z from 'zod/v4-mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z._default(z.union([ z.null() ]), null); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts index c4af44ea7..a5674d6cc 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod'; +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts index ee984a195..02954f6d8 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts @@ -3,9 +3,11 @@ import { z } from 'zod'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.string().optional() +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), @@ -13,22 +15,334 @@ export const zExternalSharedExternalSharedModel = z.object({ }); /** - * External ref to shared model (A) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefA = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.number().int(); /** - * External ref to shared model (B) + * This is a model with one nested property */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); /** - * Testing multiline comments in string: First line - * Second line + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.string().optional() + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a deprecated model with a deprecated property * - * Fourth line + * @deprecated */ -export const zCamelCaseCommentWithBreaks = z.number().int(); +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; /** * Testing multiline comments in string: First line @@ -83,23 +397,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - /** * This is a simple reference */ @@ -184,11 +486,6 @@ export const zArrayWithNumbers = z.array(z.number().int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -255,11 +552,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -273,32 +565,6 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -318,34 +584,13 @@ export const zModelWithNullableString = z.object({ nullableRequiredProp2: z.union([ z.string(), z.null() - ]), - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + ]), 'foo_bar-enum': z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() + ]).optional() }); /** @@ -388,27 +633,6 @@ export const zModelWithNestedEnums = z.object({ ]).optional() }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - /** * This is a model with one property containing a reference */ @@ -416,54 +640,6 @@ export const zModelWithReference = z.object({ prop: zModelWithProperties.optional() }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -489,22 +665,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -558,13 +718,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -653,14 +806,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ]).optional() }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - /** * This is a model that extends the base model */ @@ -715,22 +860,6 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - /** * This is a model that contains a some patterns */ @@ -758,12 +887,6 @@ export const zDefault = z.object({ name: z.string().optional() }); -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - /** * This is a free-form object without additionalProperties. */ @@ -803,11 +926,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.unknown(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -823,16 +941,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.number().int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - /** * Some % character */ @@ -842,54 +950,6 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -946,11 +1006,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1044,14 +1099,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1059,11 +1106,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1077,28 +1119,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1142,11 +1162,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1172,21 +1187,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts index 528fe0d3d..bb9456cac 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts @@ -8,15 +8,15 @@ export const zBar: z.AnyZodObject = z.object({ })).optional() }); -export const zFoo = z.object({ - foo: zBar.optional() +export const zBaz: z.ZodTypeAny = z.lazy(() => { + return zQux; }); /** * description caused circular reference error */ -export const zQux: z.ZodTypeAny = z.lazy(() => { - return zQux; -}); +export const zQux = zBaz; -export const zBaz = zQux; +export const zFoo = z.object({ + foo: zBar.optional() +}); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts index 6eb670417..4fa8604d2 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts @@ -2,22 +2,15 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -26,20 +19,27 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +60,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts index 585fddbb2..98c61ab68 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts @@ -2,22 +2,15 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}).describe('This is Bar schema.'); - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).describe('This is foo property.').optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).describe('This is baz property.').optional(), @@ -26,20 +19,27 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}).describe('This is Bar schema.'); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string().describe('This is Foo parameter.'); +export const zFoo3 = z.string().describe('This is Foo parameter.'); -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts index ace814995..0a78b1a84 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts @@ -2,24 +2,15 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - -export type BarZodType = z.infer; - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -30,28 +21,37 @@ export const zFoo: z.ZodTypeAny = z.union([ export type FooZodType = z.infer; -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}); -export type BazZodType = z.infer; +export type BarZodType = z.infer; -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); -export type QuxZodType = z.infer; +export type FooZodType2 = z.infer; /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export type FooZodType2 = z.infer; +export type FooZodType3 = z.infer; -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); -export type FooZodType3 = z.infer; +export type BazZodType = z.infer; + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + +export type QuxZodType = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +72,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts index 83c308d68..cd4b38eb3 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts @@ -11,10 +11,6 @@ export const zContact = z.union([ }) ]); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.number().int().gte(1).lte(10) @@ -29,6 +25,10 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts index 83f84b2d0..c92aaa8c3 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts @@ -2,22 +2,15 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -26,20 +19,27 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts index 87e0c90b2..b523ec8bc 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod/v4'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts index dcfa65b36..6a8ba44ab 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts @@ -3,9 +3,11 @@ import { z } from 'zod/v4'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), @@ -13,22 +15,336 @@ export const zExternalSharedExternalSharedModel = z.object({ }); /** - * External ref to shared model (A) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefA = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** - * External ref to shared model (B) + * This is a model with one nested property */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); /** - * Testing multiline comments in string: First line - * Second line + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property * - * Fourth line + * @deprecated */ -export const zCamelCaseCommentWithBreaks = z.int(); +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; /** * Testing multiline comments in string: First line @@ -83,23 +399,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -184,11 +488,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -255,11 +554,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -273,32 +567,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -315,37 +583,16 @@ export const zModelWithNullableString = z.object({ z.string(), z.null() ])), - nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + nullableRequiredProp2: z.union([ + z.string(), + z.null() + ]), + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])) }); /** @@ -388,27 +635,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - /** * This is a model with one property containing a reference */ @@ -416,56 +642,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -491,22 +667,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -560,13 +720,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -655,14 +808,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -717,22 +862,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -760,12 +889,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -805,11 +928,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -825,16 +943,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - /** * Some % character */ @@ -844,54 +952,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -948,11 +1008,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1046,14 +1101,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1061,11 +1108,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1079,28 +1121,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1144,11 +1164,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1174,21 +1189,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts index cc305b0c7..03f609684 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts @@ -3,22 +3,22 @@ import { z } from 'zod/v4'; export const zBar = z.object({ - get bar(): z.ZodOptional { + get bar() { return z.optional(z.array(z.lazy((): any => { return zBar; }))); } }); -export const zFoo = z.object({ - foo: z.optional(zBar) +export const zBaz = z.lazy((): any => { + return zQux; }); /** * description caused circular reference error */ -export const zQux = z.lazy((): any => { - return zQux; -}); +export const zQux = zBaz; -export const zBaz = zQux; +export const zFoo = z.object({ + foo: z.optional(zBar) +}); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts index fa1234e0a..5ec778bc0 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts @@ -2,23 +2,18 @@ import { z } from 'zod/v4'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts index 5e997ec27..8a1b9d745 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts @@ -2,17 +2,6 @@ import { z } from 'zod/v4'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}).register(z.globalRegistry, { - description: 'This is Bar schema.' -}); - /** * This is Foo schema. */ @@ -21,8 +10,12 @@ export const zFoo = z.union([ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/).register(z.globalRegistry, { description: 'This is foo property.' })), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; })).register(z.globalRegistry, { @@ -36,22 +29,31 @@ export const zFoo = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}).register(z.globalRegistry, { + description: 'This is Bar schema.' +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string().register(z.globalRegistry, { +export const zFoo3 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts index 2794c6a1e..9640f042c 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts @@ -2,25 +2,18 @@ import { z } from 'zod/v4'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - -export type BarZodType = z.infer; - /** * This is Foo schema. */ export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -32,28 +25,37 @@ export const zFoo = z.union([ export type FooZodType = z.infer; -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export type BazZodType = z.infer; +export type BarZodType = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType2 = z.infer; /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export type FooZodType2 = z.infer; +export type FooZodType3 = z.infer; -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); -export type FooZodType3 = z.infer; +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts index 197f5de1d..5a6d291d5 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts @@ -11,10 +11,6 @@ export const zContact = z.union([ }) ]); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().gte(1).lte(10) @@ -29,6 +25,10 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts index f9c56845a..a06a056ec 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts @@ -2,23 +2,18 @@ import { z } from 'zod/v4'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts index 95f43fa31..ba4358862 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import * as z from 'zod/mini'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts index b2bc94a14..41b3cb99a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts @@ -7,6 +7,91 @@ export const zExternalSharedExternalSharedModel = z.object({ name: z.optional(z.string()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + export const zExternalRefA = zExternalSharedExternalSharedModel; export const zExternalRefB = zExternalSharedExternalSharedModel; @@ -64,23 +149,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - export const zSimpleReference = zModelWithString; /** @@ -215,13 +288,6 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a model with one string property */ @@ -282,23 +348,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - /** * This is a model with one property containing a reference */ @@ -322,17 +371,6 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodMiniOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one nested property */ @@ -369,22 +407,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -404,28 +426,6 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().check(z.regex(/aaa`bbb/))) }); -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts index b5f786712..5de251bf9 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod/v3'; +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts index 151cc9a84..fcd7bfbfe 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts @@ -7,6 +7,89 @@ export const zExternalSharedExternalSharedModel = z.object({ name: z.string().optional() }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +export const zParameterActivityParams = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity_id: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zFailureFailure = z.object({ + error: z.string().optional(), + message: z.string().optional(), + reference_code: z.string().optional() +}); + export const zExternalRefA = zExternalSharedExternalSharedModel; export const zExternalRefB = zExternalSharedExternalSharedModel; @@ -64,23 +147,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - export const zSimpleReference = zModelWithString; /** @@ -215,13 +286,6 @@ export const zModelWithBoolean = z.object({ prop: z.boolean().optional() }); -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - /** * This is a model with one string property */ @@ -282,23 +346,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.array(z.number().int()).optional() }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - /** * This is a model with one property containing a reference */ @@ -322,15 +369,6 @@ export const zModelWithDictionary = z.object({ prop: z.record(z.string()).optional() }); -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - /** * This is a model with one nested property */ @@ -367,22 +405,6 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - export const zDefault = z.object({ name: z.string().optional() }); @@ -402,28 +424,6 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.string().regex(/aaa`bbb/).optional() }); -export const zParameterActivityParams = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity_id: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zFailureFailure = z.object({ - error: z.string().optional(), - message: z.string().optional(), - reference_code: z.string().optional() -}); - /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts index a17f5ae50..48d3f5740 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts index 54967ed06..b910804c8 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts @@ -7,6 +7,91 @@ export const zExternalSharedExternalSharedModel = z.object({ name: z.optional(z.string()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + export const zExternalRefA = zExternalSharedExternalSharedModel; export const zExternalRefB = zExternalSharedExternalSharedModel; @@ -64,23 +149,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - export const zSimpleReference = zModelWithString; /** @@ -215,13 +288,6 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a model with one string property */ @@ -282,23 +348,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - /** * This is a model with one property containing a reference */ @@ -322,17 +371,6 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one nested property */ @@ -369,22 +407,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -404,28 +426,6 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().regex(/aaa`bbb/)) }); -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts index 95f43fa31..ba4358862 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import * as z from 'zod/mini'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts index c93fc275c..7fa128445 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts @@ -3,47 +3,39 @@ import * as z from 'zod/mini'; export const zBaz = z.object({ - get quux(): z.ZodMiniOptional { - return z.optional(zQuux); + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); } }); export const zCorge = z.object({ - get baz(): z.ZodMiniOptional { - return z.optional(z.array(zBaz)); - } + baz: z.optional(z.array(zBaz)) +}); + +export const zFoo = z.object({ + quux: z.optional(zQuux) }); export const zQux = z.union([ z.intersection(z.object({ type: z.literal('struct') - }), z.lazy(() => { - return zCorge; - })), + }), zCorge), z.intersection(z.object({ type: z.literal('array') - }), z.lazy(() => { - return zFoo; - })) + }), zFoo) ]); export const zQuux = z.object({ - get qux(): z.ZodMiniOptional { - return z.optional(zQux); - } -}); - -export const zFoo = z.object({ - quux: z.optional(zQuux) + qux: z.optional(zQux) }); export const zBar = z.object({ - get bar(): z.ZodMiniOptional { + get bar() { return z.optional(z.lazy((): any => { return zBar; })); }, - get baz(): z.ZodMiniOptional { - return z.optional(zBaz); - } + baz: z.optional(zBaz) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts index 40e4f6ed4..4bab9551c 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts @@ -3,19 +3,17 @@ import * as z from 'zod/mini'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), name: z.optional(z.string()) }); -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -24,6 +22,320 @@ export const zExternalRefB = zExternalSharedExternalSharedModel; */ export const zCamelCaseCommentWithBreaks = z.int(); +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]), null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * Testing multiline comments in string: First line * Second line @@ -77,23 +389,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -178,11 +478,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - /** * This is a simple array with references */ @@ -246,11 +541,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -264,32 +554,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -307,36 +571,15 @@ export const zModelWithNullableString = z.object({ z.null() ])), nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + z.string(), + z.null() + ]), 'foo_bar-enum': z.optional(z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + ])) }); /** @@ -379,27 +622,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - /** * This is a model with one property containing a reference */ @@ -407,56 +629,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodMiniOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -482,22 +654,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -551,15 +707,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -648,14 +795,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -710,22 +849,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -753,12 +876,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -802,11 +919,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -822,16 +934,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().check(z.gte(0)) })); -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - /** * Some % character */ @@ -841,54 +943,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -942,11 +996,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1040,14 +1089,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1055,11 +1096,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1073,28 +1109,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1138,11 +1152,6 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1168,15 +1177,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts index 77ab57f60..a5ad31e3a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts @@ -2,17 +2,15 @@ import * as z from 'zod/mini'; -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -22,4 +20,8 @@ export const zFoo = z._default(z.union([ z.null() ]), null); +export const zBar = z.object({ + foo: z.optional(zFoo) +}); + export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts index b5f786712..5de251bf9 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod/v3'; +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts index e73840e1d..6a90b5b65 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts @@ -12,25 +12,23 @@ export const zCorge = z.object({ baz: z.array(zBaz).optional() }); -export const zQux: z.ZodTypeAny = z.union([ +export const zFoo = z.object({ + quux: zQuux.optional() +}); + +export const zQux = z.union([ z.object({ type: z.literal('struct') }).and(zCorge), z.object({ type: z.literal('array') - }).and(z.lazy(() => { - return zFoo; - })) + }).and(zFoo) ]); export const zQuux = z.object({ qux: zQux.optional() }); -export const zFoo = z.object({ - quux: zQuux.optional() -}); - export const zBar: z.AnyZodObject = z.object({ bar: z.lazy(() => { return zBar; diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts index 2e56ac18c..15140cf0e 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts @@ -3,19 +3,17 @@ import { z } from 'zod/v3'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.string().optional() +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), name: z.string().optional() }); -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -24,6 +22,318 @@ export const zExternalRefB = zExternalSharedExternalSharedModel; */ export const zCamelCaseCommentWithBreaks = z.number().int(); +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.string().optional() + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string().datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * Testing multiline comments in string: First line * Second line @@ -77,23 +387,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - /** * This is a simple reference */ @@ -178,11 +476,6 @@ export const zArrayWithNumbers = z.array(z.number().int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -246,11 +539,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -264,32 +552,6 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -305,38 +567,17 @@ export const zModelWithNullableString = z.object({ nullableProp2: z.union([ z.string(), z.null() - ]).optional(), - nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + ]).optional(), + nullableRequiredProp2: z.union([ + z.string(), + z.null() + ]), 'foo_bar-enum': z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() + ]).optional() }); /** @@ -379,27 +620,6 @@ export const zModelWithNestedEnums = z.object({ ]).optional() }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - /** * This is a model with one property containing a reference */ @@ -407,54 +627,6 @@ export const zModelWithReference = z.object({ prop: zModelWithProperties.optional() }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -480,22 +652,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -549,15 +705,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -646,14 +793,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ]).optional() }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - /** * This is a model that extends the base model */ @@ -708,22 +847,6 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - /** * This is a model that contains a some patterns */ @@ -751,12 +874,6 @@ export const zDefault = z.object({ name: z.string().optional() }); -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - /** * This is a free-form object without additionalProperties. */ @@ -800,11 +917,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.unknown(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -820,16 +932,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.number().int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - /** * Some % character */ @@ -839,54 +941,6 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string().datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -940,11 +994,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1038,14 +1087,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1053,11 +1094,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1071,28 +1107,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1136,11 +1150,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1166,15 +1175,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts index 1b13ab9cb..1712e5c8c 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts @@ -2,16 +2,12 @@ import { z } from 'zod/v3'; -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -20,4 +16,8 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); +export const zBar = z.object({ + foo: zFoo.optional() +}); + export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts index a17f5ae50..48d3f5740 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts index 50b4fafee..d8df5efc5 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts @@ -3,47 +3,39 @@ import { z } from 'zod'; export const zBaz = z.object({ - get quux(): z.ZodOptional { - return z.optional(zQuux); + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); } }); export const zCorge = z.object({ - get baz(): z.ZodOptional { - return z.optional(z.array(zBaz)); - } + baz: z.optional(z.array(zBaz)) +}); + +export const zFoo = z.object({ + quux: z.optional(zQuux) }); export const zQux = z.union([ z.object({ type: z.literal('struct') - }).and(z.lazy(() => { - return zCorge; - })), + }).and(zCorge), z.object({ type: z.literal('array') - }).and(z.lazy(() => { - return zFoo; - })) + }).and(zFoo) ]); export const zQuux = z.object({ - get qux(): z.ZodOptional { - return z.optional(zQux); - } -}); - -export const zFoo = z.object({ - quux: z.optional(zQuux) + qux: z.optional(zQux) }); export const zBar = z.object({ - get bar(): z.ZodOptional { + get bar() { return z.optional(z.lazy((): any => { return zBar; })); }, - get baz(): z.ZodOptional { - return z.optional(zBaz); - } + baz: z.optional(zBaz) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts index 0fb5789ee..c0b339a66 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts @@ -3,19 +3,17 @@ import { z } from 'zod'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), name: z.optional(z.string()) }); -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -24,6 +22,320 @@ export const zExternalRefB = zExternalSharedExternalSharedModel; */ export const zCamelCaseCommentWithBreaks = z.int(); +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * Testing multiline comments in string: First line * Second line @@ -77,23 +389,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -178,11 +478,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -246,11 +541,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -264,32 +554,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -307,36 +571,15 @@ export const zModelWithNullableString = z.object({ z.null() ])), nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + z.string(), + z.null() + ]), 'foo_bar-enum': z.optional(z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + ])) }); /** @@ -379,27 +622,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - /** * This is a model with one property containing a reference */ @@ -407,56 +629,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -482,22 +654,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -551,15 +707,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -648,14 +795,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -710,22 +849,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -753,12 +876,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -802,11 +919,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -822,16 +934,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - /** * Some % character */ @@ -841,54 +943,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -942,11 +996,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1040,14 +1089,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1055,11 +1096,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1073,28 +1109,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1138,11 +1152,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1168,15 +1177,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts index 7f3ce000b..7efe00035 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts @@ -2,17 +2,15 @@ import { z } from 'zod'; -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -22,4 +20,8 @@ export const zFoo = z.union([ z.null() ]).default(null); +export const zBar = z.object({ + foo: z.optional(zFoo) +}); + export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts index 95f43fa31..ba4358862 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import * as z from 'zod/mini'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts index 3e732e02b..34f603bdb 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts @@ -3,9 +3,11 @@ import * as z from 'zod/mini'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), @@ -13,22 +15,336 @@ export const zExternalSharedExternalSharedModel = z.object({ }); /** - * External ref to shared model (A) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefA = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** - * External ref to shared model (B) + * This is a model with one nested property */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); /** - * Testing multiline comments in string: First line - * Second line + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]), null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property * - * Fourth line + * @deprecated */ -export const zCamelCaseCommentWithBreaks = z.int(); +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; /** * Testing multiline comments in string: First line @@ -83,23 +399,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -184,11 +488,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - /** * This is a simple array with references */ @@ -255,11 +554,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -273,32 +567,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -315,37 +583,16 @@ export const zModelWithNullableString = z.object({ z.string(), z.null() ])), - nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + nullableRequiredProp2: z.union([ + z.string(), + z.null() + ]), + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])) }); /** @@ -388,27 +635,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - /** * This is a model with one property containing a reference */ @@ -416,56 +642,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodMiniOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -491,22 +667,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -560,13 +720,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -655,14 +808,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -717,22 +862,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -760,12 +889,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -805,11 +928,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -825,16 +943,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().check(z.gte(0)) })); -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - /** * Some % character */ @@ -844,54 +952,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -948,11 +1008,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1046,14 +1101,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1061,11 +1108,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1079,28 +1121,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1144,11 +1164,6 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1174,21 +1189,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts index 2e65ce311..51167f885 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts @@ -3,22 +3,22 @@ import * as z from 'zod/mini'; export const zBar = z.object({ - get bar(): z.ZodMiniOptional { + get bar() { return z.optional(z.array(z.lazy((): any => { return zBar; }))); } }); -export const zFoo = z.object({ - foo: z.optional(zBar) +export const zBaz = z.lazy((): any => { + return zQux; }); /** * description caused circular reference error */ -export const zQux = z.lazy((): any => { - return zQux; -}); +export const zQux = zBaz; -export const zBaz = zQux; +export const zFoo = z.object({ + foo: z.optional(zBar) +}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts index f5db2ce5c..beb6504c4 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts @@ -2,23 +2,18 @@ import * as z from 'zod/mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z._default(z.union([ z.null() ]), null); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts index 4b4759e2d..bdf38c095 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts @@ -2,17 +2,6 @@ import * as z from 'zod/mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}).register(z.globalRegistry, { - description: 'This is Bar schema.' -}); - /** * This is Foo schema. */ @@ -21,8 +10,12 @@ export const zFoo = z._default(z.union([ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/)).register(z.globalRegistry, { description: 'This is foo property.' })), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; })).register(z.globalRegistry, { @@ -36,22 +29,31 @@ export const zFoo = z._default(z.union([ z.null() ]), null); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}).register(z.globalRegistry, { + description: 'This is Bar schema.' +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string().register(z.globalRegistry, { +export const zFoo3 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts index 1eb0cfb95..a7e423d5a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts @@ -2,25 +2,18 @@ import * as z from 'zod/mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - -export type BarZodType = z.infer; - /** * This is Foo schema. */ export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -32,28 +25,37 @@ export const zFoo = z._default(z.union([ export type FooZodType = z.infer; -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export type BazZodType = z.infer; +export type BarZodType = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType2 = z.infer; /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export type FooZodType2 = z.infer; +export type FooZodType3 = z.infer; -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); -export type FooZodType3 = z.infer; +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts index 8a820c15d..5f72e6c0a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts @@ -11,10 +11,6 @@ export const zContact = z.union([ }) ]); -export const zUser = z.intersection(zContact, z.object({ - username: z.string() -})); - export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().check(z.gte(1), z.lte(10)) @@ -29,6 +25,10 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); +export const zUser = z.intersection(zContact, z.object({ + username: z.string() +})); + export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts index 0ee090a1a..f61036bbf 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts @@ -2,23 +2,18 @@ import * as z from 'zod/mini'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodMiniOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), - bar: z.optional(zBar), - get baz(): z.ZodMiniOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z._default(z.union([ z.null() ]), null); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts index b5f786712..5de251bf9 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod/v3'; +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts index 87921e88a..5353180a0 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts @@ -3,9 +3,11 @@ import { z } from 'zod/v3'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.string().optional() +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), @@ -13,22 +15,334 @@ export const zExternalSharedExternalSharedModel = z.object({ }); /** - * External ref to shared model (A) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefA = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.number().int(); /** - * External ref to shared model (B) + * This is a model with one nested property */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); /** - * Testing multiline comments in string: First line - * Second line + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.string().optional() + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a deprecated model with a deprecated property * - * Fourth line + * @deprecated */ -export const zCamelCaseCommentWithBreaks = z.number().int(); +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; /** * Testing multiline comments in string: First line @@ -83,23 +397,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - /** * This is a simple reference */ @@ -184,11 +486,6 @@ export const zArrayWithNumbers = z.array(z.number().int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -255,11 +552,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -273,32 +565,6 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -318,34 +584,13 @@ export const zModelWithNullableString = z.object({ nullableRequiredProp2: z.union([ z.string(), z.null() - ]), - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ + ]), 'foo_bar-enum': z.enum([ 'Success', 'Warning', 'Error', 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() + ]).optional() }); /** @@ -388,27 +633,6 @@ export const zModelWithNestedEnums = z.object({ ]).optional() }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - /** * This is a model with one property containing a reference */ @@ -416,54 +640,6 @@ export const zModelWithReference = z.object({ prop: zModelWithProperties.optional() }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -489,22 +665,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -558,13 +718,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -653,14 +806,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ]).optional() }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - /** * This is a model that extends the base model */ @@ -715,22 +860,6 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - /** * This is a model that contains a some patterns */ @@ -758,12 +887,6 @@ export const zDefault = z.object({ name: z.string().optional() }); -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - /** * This is a free-form object without additionalProperties. */ @@ -803,11 +926,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.unknown(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -823,16 +941,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.number().int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - /** * Some % character */ @@ -842,54 +950,6 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -946,11 +1006,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1044,14 +1099,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1059,11 +1106,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1077,28 +1119,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1142,11 +1162,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1172,21 +1187,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts index 7e6cc3c89..9c341db5a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts @@ -8,15 +8,15 @@ export const zBar: z.AnyZodObject = z.object({ })).optional() }); -export const zFoo = z.object({ - foo: zBar.optional() +export const zBaz: z.ZodTypeAny = z.lazy(() => { + return zQux; }); /** * description caused circular reference error */ -export const zQux: z.ZodTypeAny = z.lazy(() => { - return zQux; -}); +export const zQux = zBaz; -export const zBaz = zQux; +export const zFoo = z.object({ + foo: zBar.optional() +}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts index 140dc5e8c..f07b8c7d5 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts @@ -2,22 +2,15 @@ import { z } from 'zod/v3'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -26,20 +19,27 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +60,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts index efc9209a2..531962b73 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts @@ -2,22 +2,15 @@ import { z } from 'zod/v3'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}).describe('This is Bar schema.'); - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).describe('This is foo property.').optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).describe('This is baz property.').optional(), @@ -26,20 +19,27 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}).describe('This is Bar schema.'); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string().describe('This is Foo parameter.'); +export const zFoo3 = z.string().describe('This is Foo parameter.'); -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts index 399d2c413..c0eeb2540 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts @@ -2,24 +2,15 @@ import { z } from 'zod/v3'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - -export type BarZodType = z.infer; - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -30,28 +21,37 @@ export const zFoo: z.ZodTypeAny = z.union([ export type FooZodType = z.infer; -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}); -export type BazZodType = z.infer; +export type BarZodType = z.infer; -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); -export type QuxZodType = z.infer; +export type FooZodType2 = z.infer; /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export type FooZodType2 = z.infer; +export type FooZodType3 = z.infer; -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); -export type FooZodType3 = z.infer; +export type BazZodType = z.infer; + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + +export type QuxZodType = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +72,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts index 754b9133a..22c2a127d 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts @@ -11,10 +11,6 @@ export const zContact = z.union([ }) ]); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.number().int().gte(1).lte(10) @@ -29,6 +25,10 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts index fcc9c8c55..e4b5555a9 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts @@ -2,22 +2,15 @@ import { z } from 'zod/v3'; -/** - * This is Bar schema. - */ -export const zBar: z.AnyZodObject = z.object({ - foo: z.lazy(() => { - return zFoo; - }).optional() -}); - /** * This is Foo schema. */ export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), - bar: zBar.optional(), + bar: z.lazy(() => { + return zBar; + }).optional(), baz: z.array(z.lazy(() => { return zFoo; })).optional(), @@ -26,20 +19,27 @@ export const zFoo: z.ZodTypeAny = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: zFoo.optional() +}); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo2 = z.object({ + foo: zBar.optional() +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: zBar.optional() -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts index a17f5ae50..48d3f5740 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts @@ -2,6 +2,11 @@ import { z } from 'zod'; +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -10,11 +15,6 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts index 6def19e85..aeea9df12 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts @@ -3,9 +3,11 @@ import { z } from 'zod'; /** - * Model with number-only name + * This is a model with one string property */ -export const z400 = z.string(); +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); export const zExternalSharedExternalSharedModel = z.object({ id: z.string(), @@ -13,22 +15,336 @@ export const zExternalSharedExternalSharedModel = z.object({ }); /** - * External ref to shared model (A) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefA = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** - * External ref to shared model (B) + * This is a model with one nested property */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); /** - * Testing multiline comments in string: First line - * Second line + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + +/** + * An object that can be null + */ +export const zNullableObject = z.union([ + z.object({ + foo: z.optional(z.string()) + }), + z.null() +]).default(null); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a deprecated model with a deprecated property * - * Fourth line + * @deprecated */ -export const zCamelCaseCommentWithBreaks = z.int(); +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + +/** + * Model with number-only name + */ +export const z400 = z.string(); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + +/** + * This is a simple array with strings + */ +export const zArrayWithStrings = z.array(z.string()).default(['test']); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; /** * Testing multiline comments in string: First line @@ -83,23 +399,11 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - /** * This is a simple file */ export const zSimpleFile = z.string(); -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - /** * This is a simple reference */ @@ -184,11 +488,6 @@ export const zArrayWithNumbers = z.array(z.int()); */ export const zArrayWithBooleans = z.array(z.boolean()); -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - /** * This is a simple array with references */ @@ -255,11 +554,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ */ export const zDictionaryWithReference = z.record(z.string(), zModelWithString); -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -273,32 +567,6 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - /** * This is a model with one string property */ @@ -315,37 +583,16 @@ export const zModelWithNullableString = z.object({ z.string(), z.null() ])), - nullableRequiredProp2: z.union([ - z.string(), - z.null() - ]), - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])) -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) + nullableRequiredProp2: z.union([ + z.string(), + z.null() + ]), + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])) }); /** @@ -388,27 +635,6 @@ export const zModelWithNestedEnums = z.object({ ])) }); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - /** * This is a model with one property containing a reference */ @@ -416,56 +642,6 @@ export const zModelWithReference = z.object({ prop: z.optional(zModelWithProperties) }); -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop(): z.ZodOptional { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - /** * This is a model with one property with a 'one of' relationship */ @@ -491,22 +667,6 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -560,13 +720,6 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -655,14 +808,6 @@ export const zCompositionWithAnyOfAndNullable = z.object({ ])) }); -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - /** * This is a model that extends the base model */ @@ -717,22 +862,6 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - /** * This is a model that contains a some patterns */ @@ -760,12 +889,6 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - /** * This is a free-form object without additionalProperties. */ @@ -805,11 +928,6 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ z.object({ foo: zSimpleParameter @@ -825,16 +943,6 @@ export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ qux: z.int().gte(0) })); -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - /** * Some % character */ @@ -844,54 +952,6 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -948,11 +1008,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ z.union([ z.number(), @@ -1046,14 +1101,6 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - /** * This schema was giving PascalCase transformations a hard time */ @@ -1061,11 +1108,6 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1079,28 +1121,6 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1144,11 +1164,6 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1174,21 +1189,6 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts index 1fa3416dd..a5ebfea1f 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts @@ -3,22 +3,22 @@ import { z } from 'zod'; export const zBar = z.object({ - get bar(): z.ZodOptional { + get bar() { return z.optional(z.array(z.lazy((): any => { return zBar; }))); } }); -export const zFoo = z.object({ - foo: z.optional(zBar) +export const zBaz = z.lazy((): any => { + return zQux; }); /** * description caused circular reference error */ -export const zQux = z.lazy((): any => { - return zQux; -}); +export const zQux = zBaz; -export const zBaz = zQux; +export const zFoo = z.object({ + foo: z.optional(zBar) +}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts index 9b0132631..0a4c89c85 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts @@ -2,23 +2,18 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts index 34e6fe096..45cced796 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts @@ -2,17 +2,6 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}).register(z.globalRegistry, { - description: 'This is Bar schema.' -}); - /** * This is Foo schema. */ @@ -21,8 +10,12 @@ export const zFoo = z.union([ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/).register(z.globalRegistry, { description: 'This is foo property.' })), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; })).register(z.globalRegistry, { @@ -36,22 +29,31 @@ export const zFoo = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}).register(z.globalRegistry, { + description: 'This is Bar schema.' +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string().register(z.globalRegistry, { +export const zFoo3 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts index 649cd0534..84e110060 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts @@ -2,25 +2,18 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - -export type BarZodType = z.infer; - /** * This is Foo schema. */ export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -32,28 +25,37 @@ export const zFoo = z.union([ export type FooZodType = z.infer; -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export type BazZodType = z.infer; +export type BarZodType = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType2 = z.infer; /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export type FooZodType2 = z.infer; +export type FooZodType3 = z.infer; -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); -export type FooZodType3 = z.infer; +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts index 3971835be..298ff43d2 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts @@ -11,10 +11,6 @@ export const zContact = z.union([ }) ]); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().gte(1).lte(10) @@ -29,6 +25,10 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts index 921e41c46..016248f05 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts @@ -2,23 +2,18 @@ import { z } from 'zod'; -/** - * This is Bar schema. - */ -export const zBar = z.object({ - get foo(): z.ZodOptional { - return z.optional(zFoo); - } -}); - /** * This is Foo schema. */ export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), - bar: z.optional(zBar), - get baz(): z.ZodOptional { + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { return z.optional(z.array(z.lazy((): any => { return zFoo; }))); @@ -28,20 +23,27 @@ export const zFoo = z.union([ z.null() ]).default(null); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +/** + * This is Bar schema. + */ +export const zBar = z.object({ + foo: z.optional(zFoo) +}); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo2 = z.object({ + foo: z.optional(zBar) +}); /** * This is Foo parameter. */ -export const zFoo2 = z.string(); +export const zFoo3 = z.string(); -export const zFoo3 = z.object({ - foo: z.optional(zBar) -}); +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo3, + body: zFoo2, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts/package.json b/packages/openapi-ts/package.json index ed7a15244..fe0f11489 100644 --- a/packages/openapi-ts/package.json +++ b/packages/openapi-ts/package.json @@ -91,7 +91,7 @@ }, "dependencies": { "@hey-api/codegen-core": "workspace:^0.3.0", - "@hey-api/json-schema-ref-parser": "1.2.0", + "@hey-api/json-schema-ref-parser": "1.2.1", "ansi-colors": "4.1.3", "c12": "3.3.1", "color-support": "1.1.3", diff --git a/packages/openapi-ts/src/createClient.ts b/packages/openapi-ts/src/createClient.ts index ecdf586d2..23f63ce23 100644 --- a/packages/openapi-ts/src/createClient.ts +++ b/packages/openapi-ts/src/createClient.ts @@ -8,6 +8,7 @@ import { generateOutput } from './generate/output'; import { getSpec } from './getSpec'; import type { IR } from './ir/types'; import { parseLegacy, parseOpenApiSpec } from './openApi'; +import { buildGraph } from './openApi/shared/utils/graph'; import { patchOpenApiSpec } from './openApi/shared/utils/patch'; import { processOutput } from './processOutput'; import type { Client } from './types/client'; @@ -332,8 +333,10 @@ export const createClient = async ({ context = parseOpenApiSpec({ config, dependencies, logger, spec: data }); } - // fallback to legacy parser - if (!context) { + if (context) { + context.graph = buildGraph(context.ir, logger).graph; + } else { + // fallback to legacy parser const parsed = parseLegacy({ openApi: data }); client = postProcessClient(parsed, config); } diff --git a/packages/openapi-ts/src/ir/__tests__/graph.test.ts b/packages/openapi-ts/src/ir/__tests__/graph.test.ts new file mode 100644 index 000000000..6e747025e --- /dev/null +++ b/packages/openapi-ts/src/ir/__tests__/graph.test.ts @@ -0,0 +1,222 @@ +import { describe, expect, it } from 'vitest'; + +import type { Graph } from '../../openApi/shared/utils/graph'; +import { buildGraph } from '../../openApi/shared/utils/graph'; +import type { IrTopLevelKind } from '../graph'; +import { matchIrTopLevelPointer, walkTopological } from '../graph'; + +// simple logger stub for buildGraph +const loggerStub = { + timeEvent: () => ({ timeEnd: () => {} }), +} as any; + +describe('matchIrTopLevelPointer', () => { + const cases: Array< + [ + string, + IrTopLevelKind | undefined, + { kind?: IrTopLevelKind; matched: boolean }, + ] + > = [ + ['#/components/schemas/Foo', undefined, { kind: 'schema', matched: true }], + ['#/components/schemas/Foo', 'schema', { kind: 'schema', matched: true }], + ['#/components/schemas/Foo', 'parameter', { matched: false }], + [ + '#/components/parameters/Bar', + undefined, + { kind: 'parameter', matched: true }, + ], + [ + '#/components/parameters/Bar', + 'parameter', + { kind: 'parameter', matched: true }, + ], + ['#/components/parameters/Bar', 'schema', { matched: false }], + [ + '#/components/requestBodies/Baz', + undefined, + { kind: 'requestBody', matched: true }, + ], + [ + '#/components/requestBodies/Baz', + 'requestBody', + { kind: 'requestBody', matched: true }, + ], + ['#/components/requestBodies/Baz', 'schema', { matched: false }], + ['#/servers/0', undefined, { kind: 'server', matched: true }], + ['#/servers/foo', undefined, { kind: 'server', matched: true }], + ['#/paths/~1users/get', undefined, { kind: 'operation', matched: true }], + ['#/paths/~1users/post', 'operation', { kind: 'operation', matched: true }], + ['#/webhooks/foo/get', undefined, { kind: 'webhook', matched: true }], + ['#/webhooks/foo/patch', 'webhook', { kind: 'webhook', matched: true }], + ['#/not/a/top/level', undefined, { matched: false }], + ['#/components/unknown/Foo', undefined, { matched: false }], + ]; + + for (const [pointer, kind, expected] of cases) { + it(`matches ${pointer} with kind=${kind}`, () => { + const result = matchIrTopLevelPointer(pointer, kind as IrTopLevelKind); + expect(result.matched).toBe(expected.matched); + if (expected.matched) { + expect(result.kind).toBe(expected.kind); + } else { + expect(result.kind).toBeUndefined(); + } + }); + } +}); + +describe('walkTopological', () => { + const makeGraph = (deps: Array<[string, string[]]>, nodes: string[]) => { + const nodeDependencies = new Map>(); + const subtreeDependencies = new Map>(); + const reverseNodeDependencies = new Map>(); + const nodesMap = new Map(); + + for (const name of nodes) { + nodesMap.set(name, { key: null, node: {}, parentPointer: null }); + } + + for (const [from, toList] of deps) { + const s = new Set(toList); + nodeDependencies.set(from, s); + subtreeDependencies.set(from, new Set(toList)); + for (const to of toList) { + if (!reverseNodeDependencies.has(to)) + reverseNodeDependencies.set(to, new Set()); + reverseNodeDependencies.get(to)!.add(from); + } + } + + return { + nodeDependencies, + nodes: nodesMap, + reverseNodeDependencies, + subtreeDependencies, + transitiveDependencies: new Map>(), + } as unknown as Graph; + }; + + it('walks nodes in topological order for a simple acyclic graph', () => { + // Graph: A -> B -> C + const graph = makeGraph( + [ + ['A', ['B']], + ['B', ['C']], + ], + ['A', 'B', 'C'], + ); + const order: string[] = []; + walkTopological(graph, (pointer) => order.push(pointer)); + expect(order.indexOf('C')).toBeLessThan(order.indexOf('B')); + expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); + expect(order).toHaveLength(3); + }); + + it('walks nodes in topological order for multiple roots', () => { + // Graph: A -> B, C -> D + const graph = makeGraph( + [ + ['A', ['B']], + ['C', ['D']], + ], + ['A', 'B', 'C', 'D'], + ); + const order: string[] = []; + walkTopological(graph, (pointer) => order.push(pointer)); + expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); + expect(order.indexOf('D')).toBeLessThan(order.indexOf('C')); + expect(order).toHaveLength(4); + }); + + it('walks nodes in topological order for a disconnected graph', () => { + // Graph: A -> B, C (no deps), D (no deps) + const graph = makeGraph([['A', ['B']]], ['A', 'B', 'C', 'D']); + const order: string[] = []; + walkTopological(graph, (pointer) => order.push(pointer)); + expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); + expect(order).toHaveLength(4); + expect(order).toContain('C'); + expect(order).toContain('D'); + }); + + it('walks nodes in topological order for a diamond dependency', () => { + // Graph: A + // / \ + // B C + // \ / + // D + const graph = makeGraph( + [ + ['A', ['B', 'C']], + ['B', ['D']], + ['C', ['D']], + ], + ['A', 'B', 'C', 'D'], + ); + const order: string[] = []; + walkTopological(graph, (pointer) => order.push(pointer)); + expect(order.indexOf('D')).toBeLessThan(order.indexOf('B')); + expect(order.indexOf('D')).toBeLessThan(order.indexOf('C')); + expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); + expect(order.indexOf('C')).toBeLessThan(order.indexOf('A')); + expect(order).toHaveLength(4); + }); + + it('walks nodes in topological order for a long chain', () => { + // Graph: A -> B -> C -> D -> E + const graph = makeGraph( + [ + ['A', ['B']], + ['B', ['C']], + ['C', ['D']], + ['D', ['E']], + ], + ['A', 'B', 'C', 'D', 'E'], + ); + const order: string[] = []; + walkTopological(graph, (pointer) => order.push(pointer)); + expect(order.indexOf('E')).toBeLessThan(order.indexOf('D')); + expect(order.indexOf('D')).toBeLessThan(order.indexOf('C')); + expect(order.indexOf('C')).toBeLessThan(order.indexOf('B')); + expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); + expect(order).toHaveLength(5); + }); + + it('walks all nodes, including cycles', () => { + // Graph: A <-> B (cycle), C (no deps) + const graph = makeGraph( + [ + ['A', ['B']], + ['B', ['A']], + ], + ['A', 'B', 'C'], + ); + const order: string[] = []; + walkTopological(graph, (pointer) => order.push(pointer)); + expect(order.sort()).toEqual(['A', 'B', 'C']); + }); + + it('matches ordering for validators-circular-ref spec', async () => { + const specModule = await import( + '../../../../../specs/3.1.x/validators-circular-ref.json' + ); + const spec = specModule.default ?? specModule; + const { graph } = buildGraph(spec, loggerStub); + + const order: string[] = []; + walkTopological(graph, (pointer) => order.push(pointer)); + + const foo = '#/components/schemas/Foo'; + const bar = '#/components/schemas/Bar'; + const baz = '#/components/schemas/Baz'; + const qux = '#/components/schemas/Qux'; + + // Bar should come before Foo because Foo depends on Bar + expect(order.indexOf(bar)).toBeLessThan(order.indexOf(foo)); + + // Baz and Qux form a mutual $ref cycle; both must be present + expect(order).toContain(baz); + expect(order).toContain(qux); + }); +}); diff --git a/packages/openapi-ts/src/ir/context.ts b/packages/openapi-ts/src/ir/context.ts index 3fb4d8f90..b7abc6c92 100644 --- a/packages/openapi-ts/src/ir/context.ts +++ b/packages/openapi-ts/src/ir/context.ts @@ -3,6 +3,7 @@ import { Project } from '@hey-api/codegen-core'; import type { Package } from '../config/utils/package'; import { packageFactory } from '../config/utils/package'; import { TypeScriptRenderer } from '../generate/renderer'; +import type { Graph } from '../openApi/shared/utils/graph'; import { buildName } from '../openApi/shared/utils/name'; import type { PluginConfigMap } from '../plugins/config'; import { PluginInstance } from '../plugins/shared/utils/instance'; @@ -22,6 +23,10 @@ export class IRContext = any> { * The code generation project instance used to manage files, symbols, */ gen: Project; + /** + * The dependency graph built from the intermediate representation. + */ + graph: Graph | undefined; /** * Intermediate representation model obtained from `spec`. */ diff --git a/packages/openapi-ts/src/ir/graph.ts b/packages/openapi-ts/src/ir/graph.ts new file mode 100644 index 000000000..c4aedb03d --- /dev/null +++ b/packages/openapi-ts/src/ir/graph.ts @@ -0,0 +1,84 @@ +import type { Graph, NodeInfo } from '../openApi/shared/utils/graph'; + +/** + * Walks the nodes of the graph in topological order (dependencies before dependents). + * Calls the callback for each node pointer in order. + * Nodes in cycles are grouped together and emitted in arbitrary order within the group. + * + * @param graph - The dependency graph + * @param callback - Function to call for each node pointer + */ +export const walkTopological = ( + graph: Graph, + callback: (pointer: string, nodeInfo: NodeInfo) => void, +) => { + const visited = new Set(); + + const visit = (pointer: string) => { + if (visited.has(pointer)) return; + visited.add(pointer); + + const dependencies = graph.subtreeDependencies.get(pointer); + if (dependencies) { + for (const dependency of dependencies) { + visit(dependency); + } + } + + callback(pointer, graph.nodes.get(pointer)!); + }; + + for (const pointer of graph.nodes.keys()) { + visit(pointer); + } +}; + +export const irTopLevelKinds = [ + 'operation', + 'parameter', + 'requestBody', + 'schema', + 'server', + 'webhook', +] as const; + +export type IrTopLevelKind = (typeof irTopLevelKinds)[number]; + +export type IrTopLevelPointerMatch = + | { kind: IrTopLevelKind; matched: true } + | { kind?: undefined; matched: false }; + +/** + * Checks if a pointer matches a known top-level IR component (schema, parameter, etc) and returns match info. + * + * @param pointer - The IR pointer string (e.g. '#/components/schemas/Foo') + * @param kind - (Optional) The component kind to check + * @returns { matched: true, kind: IrTopLevelKind } | { matched: false } - Whether it matched, and the matched kind if so + */ +export const matchIrTopLevelPointer = ( + pointer: string, + kind?: IrTopLevelKind, +): IrTopLevelPointerMatch => { + const patterns: Record = { + operation: + /^#\/paths\/[^/]+\/(get|put|post|delete|options|head|patch|trace)$/, + parameter: /^#\/components\/parameters\/[^/]+$/, + requestBody: /^#\/components\/requestBodies\/[^/]+$/, + schema: /^#\/components\/schemas\/[^/]+$/, + server: /^#\/servers\/(\d+|[^/]+)$/, + webhook: + /^#\/webhooks\/[^/]+\/(get|put|post|delete|options|head|patch|trace)$/, + }; + if (kind) { + return patterns[kind].test(pointer) + ? { kind, matched: true } + : { matched: false }; + } + for (const key of Object.keys(patterns)) { + const kind = key as IrTopLevelKind; + if (patterns[kind].test(pointer)) { + return { kind, matched: true }; + } + } + return { matched: false }; +}; diff --git a/packages/openapi-ts/src/ir/types.d.ts b/packages/openapi-ts/src/ir/types.d.ts index 309053d34..118647198 100644 --- a/packages/openapi-ts/src/ir/types.d.ts +++ b/packages/openapi-ts/src/ir/types.d.ts @@ -249,13 +249,6 @@ interface IRSchemaObject * properties altogether. */ additionalProperties?: IRSchemaObject | false; - /** - * If this schema is a $ref and is circular (points to itself or is in the current resolution stack), - * this flag is set to true. - * - * @default undefined - */ - circular?: boolean; /** * Any string value is accepted as `format`. */ @@ -281,7 +274,6 @@ interface IRSchemaObject * When type is `object`, `properties` will contain a map of its properties. */ properties?: Record; - /** * The names of `properties` can be validated against a schema, irrespective * of their values. This can be useful if you don't want to enforce specific diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts index cc1117d46..3e51a6304 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts @@ -146,7 +146,6 @@ const parameterToIrParameter = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }), style, diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts index 6b03b24f1..0208de48b 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts @@ -535,7 +535,6 @@ const parseRef = ({ const isComponentsRef = schema.$ref.startsWith('#/definitions/'); if (!isComponentsRef) { if (!state.circularReferenceTracker.has(schema.$ref)) { - state.refStack.push(schema.$ref); const refSchema = context.resolveRef(schema.$ref); const originalRef = state.$ref; state.$ref = schema.$ref; @@ -545,7 +544,6 @@ const parseRef = ({ state, }); state.$ref = originalRef; - state.refStack.pop(); return irSchema; } // Fallback to preserving the ref if circular @@ -562,15 +560,7 @@ const parseRef = ({ '#/components/schemas/$1', ); - if (state.refStack.includes(schema.$ref)) { - if (state.refStack[0] === schema.$ref) { - state.circularRef = schema.$ref; - } - irSchema.circular = true; - } - if (!state.circularReferenceTracker.has(schema.$ref)) { - state.refStack.push(schema.$ref); const refSchema = context.resolveRef(schema.$ref); const originalRef = state.$ref; state.$ref = schema.$ref; @@ -579,11 +569,7 @@ const parseRef = ({ schema: refSchema, state, }); - if (state.circularRef && state.refStack[0] === state.circularRef) { - irSchema.circular = true; - } state.$ref = originalRef; - state.refStack.pop(); } return irSchema; @@ -771,7 +757,6 @@ export const schemaToIrSchema = ({ if (!state) { state = { circularReferenceTracker: new Set(), - refStack: [], }; } @@ -838,7 +823,6 @@ export const parseSchema = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }); }; diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts index 75f1a5fa6..56307923e 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts @@ -150,7 +150,6 @@ const parameterToIrParameter = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }), style, diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts index 2aa311f4d..3f45e7e1a 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts @@ -32,7 +32,6 @@ const requestBodyToIrRequestBody = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }), }; diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts index b80ca1949..e5679f6a6 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts @@ -741,7 +741,6 @@ const parseRef = ({ const isComponentsRef = schema.$ref.startsWith('#/components/'); if (!isComponentsRef) { if (!state.circularReferenceTracker.has(schema.$ref)) { - state.refStack.push(schema.$ref); const refSchema = context.resolveRef(schema.$ref); const originalRef = state.$ref; state.$ref = schema.$ref; @@ -751,7 +750,6 @@ const parseRef = ({ state, }); state.$ref = originalRef; - state.refStack.pop(); return irSchema; } // Fallback to preserving the ref if circular @@ -763,15 +761,7 @@ const parseRef = ({ // but the suspicion is this comes from `@hey-api/json-schema-ref-parser` irSchema.$ref = decodeURI(schema.$ref); - if (state.refStack.includes(schema.$ref)) { - if (state.refStack[0] === schema.$ref) { - state.circularRef = schema.$ref; - } - irSchema.circular = true; - } - if (!state.circularReferenceTracker.has(schema.$ref)) { - state.refStack.push(schema.$ref); const refSchema = context.resolveRef(schema.$ref); const originalRef = state.$ref; state.$ref = schema.$ref; @@ -780,11 +770,7 @@ const parseRef = ({ schema: refSchema, state, }); - if (state.circularRef && state.refStack[0] === state.circularRef) { - irSchema.circular = true; - } state.$ref = originalRef; - state.refStack.pop(); } return irSchema; @@ -972,7 +958,6 @@ export const schemaToIrSchema = ({ if (!state) { state = { circularReferenceTracker: new Set(), - refStack: [], }; } @@ -1055,7 +1040,6 @@ export const parseSchema = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }); }; diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts index 13748445d..be0c87510 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts @@ -143,7 +143,6 @@ const parameterToIrParameter = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }), style, diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts index 2aa311f4d..3f45e7e1a 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts @@ -32,7 +32,6 @@ const requestBodyToIrRequestBody = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }), }; diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts index b2ac1c3f7..444fc5984 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts @@ -800,7 +800,6 @@ const parseRef = ({ const isComponentsRef = schema.$ref.startsWith('#/components/'); if (!isComponentsRef) { if (!state.circularReferenceTracker.has(schema.$ref)) { - state.refStack.push(schema.$ref); const refSchema = context.resolveRef(schema.$ref); const originalRef = state.$ref; state.$ref = schema.$ref; @@ -810,7 +809,6 @@ const parseRef = ({ state, }); state.$ref = originalRef; - state.refStack.pop(); return irSchema; } // Fallback to preserving the ref if circular @@ -824,15 +822,7 @@ const parseRef = ({ // but the suspicion is this comes from `@hey-api/json-schema-ref-parser` irRefSchema.$ref = decodeURI(schema.$ref); - if (state.refStack.includes(schema.$ref)) { - if (state.refStack[0] === schema.$ref) { - state.circularRef = schema.$ref; - } - irSchema.circular = true; - } - if (!state.circularReferenceTracker.has(schema.$ref)) { - state.refStack.push(schema.$ref); const refSchema = context.resolveRef(schema.$ref); const originalRef = state.$ref; state.$ref = schema.$ref; @@ -841,11 +831,7 @@ const parseRef = ({ schema: refSchema, state, }); - if (state.circularRef && state.refStack[0] === state.circularRef) { - irSchema.circular = true; - } state.$ref = originalRef; - state.refStack.pop(); } const schemaItems: Array = []; @@ -1054,7 +1040,6 @@ export const schemaToIrSchema = ({ if (!state) { state = { circularReferenceTracker: new Set(), - refStack: [], }; } @@ -1137,7 +1122,6 @@ export const parseSchema = ({ state: { $ref, circularReferenceTracker: new Set(), - refStack: [$ref], }, }); }; diff --git a/packages/openapi-ts/src/openApi/shared/graph/meta.ts b/packages/openapi-ts/src/openApi/shared/graph/meta.ts index 4d96e0940..326ab3b69 100644 --- a/packages/openapi-ts/src/openApi/shared/graph/meta.ts +++ b/packages/openapi-ts/src/openApi/shared/graph/meta.ts @@ -67,10 +67,10 @@ export const buildResourceMetadata = ( const getDependencies = (pointer: string): Set => { const dependencies = new Set(); - const nodeDeps = graph.allDependencies.get(pointer); - if (nodeDeps?.size) { - for (const dep of nodeDeps) { - const path = jsonPointerToPath(dep); + const nodeDependencies = graph.transitiveDependencies.get(pointer); + if (nodeDependencies?.size) { + for (const dependency of nodeDependencies) { + const path = jsonPointerToPath(dependency); const type = path[path.length - 2]; const name = path[path.length - 1]; if (type && name) { diff --git a/packages/openapi-ts/src/openApi/shared/types/schema.d.ts b/packages/openapi-ts/src/openApi/shared/types/schema.d.ts index 5b5bb9ecd..f52469bb9 100644 --- a/packages/openapi-ts/src/openApi/shared/types/schema.d.ts +++ b/packages/openapi-ts/src/openApi/shared/types/schema.d.ts @@ -4,17 +4,9 @@ export interface SchemaState { * from the OpenAPI specification. */ $ref?: string; - /** - * If a reference is detected as circular, this will hold the $ref string. - * This is used to back-propagate circular reference information to the - * original schema that started the circular reference chain. - */ - circularRef?: string; /** * Set of $refs currently being resolved that are circular. This is used to * avoid infinite loops when resolving schemas with circular references. - * - * @deprecated Use `refStack` instead. */ circularReferenceTracker: Set; /** @@ -24,11 +16,6 @@ export interface SchemaState { * properties from other schemas in the composition. */ inAllOf?: boolean; - /** - * Stack of $refs currently being resolved. This is used to detect circular - * references and avoid infinite loops. - */ - refStack: Array; } export type SchemaWithRequired< diff --git a/packages/openapi-ts/src/openApi/shared/utils/__tests__/graph.test.ts b/packages/openapi-ts/src/openApi/shared/utils/__tests__/graph.test.ts new file mode 100644 index 000000000..e9d1d9d30 --- /dev/null +++ b/packages/openapi-ts/src/openApi/shared/utils/__tests__/graph.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, it } from 'vitest'; + +import { buildGraph } from '../graph'; + +// simple logger stub for buildGraph +const loggerStub = { + timeEvent: () => ({ timeEnd: () => {} }), +} as any; + +describe('buildGraph', () => { + it('computes referenced and transitive dependencies for validators-circular-ref.json', async () => { + const mod = await import( + '../../../../../../../specs/3.1.x/validators-circular-ref.json' + ); + const spec = (mod as any).default ?? mod; + + const { graph } = buildGraph(spec, loggerStub); + + const foo = '#/components/schemas/Foo'; + const bar = '#/components/schemas/Bar'; + const baz = '#/components/schemas/Baz'; + const qux = '#/components/schemas/Qux'; + + // Foo has a child property that $ref's Bar, so Foo should have Bar in subtreeDependencies + expect(graph.subtreeDependencies.get(foo)).toBeDefined(); + expect(Array.from(graph.subtreeDependencies.get(foo)!).sort()).toEqual( + [bar].sort(), + ); + + // Foo transitively depends on Bar (via the child), so transitiveDependencies should include Bar + expect(graph.transitiveDependencies.get(foo)).toBeDefined(); + expect(Array.from(graph.transitiveDependencies.get(foo)!).sort()).toEqual( + [bar].sort(), + ); + + // Bar references itself via an array item; Bar should reference Bar + expect(Array.from(graph.subtreeDependencies.get(bar)!).sort()).toEqual( + [bar].sort(), + ); + + // Baz and Qux form a mutual $ref cycle; each should reference the other in subtreeDependencies + expect(Array.from(graph.subtreeDependencies.get(baz)!).sort()).toEqual( + [qux].sort(), + ); + expect(Array.from(graph.subtreeDependencies.get(qux)!).sort()).toEqual( + [baz].sort(), + ); + + // Qux node should exist and have a direct dependency to Baz (node-level $ref) + expect(graph.nodes.has(qux)).toBe(true); + expect(graph.nodeDependencies.get(qux)).toBeDefined(); + expect(Array.from(graph.nodeDependencies.get(qux)!).sort()).toEqual( + [baz].sort(), + ); + + // Qux transitive deps should include Baz (and vice-versa because of the cycle) + expect(graph.transitiveDependencies.get(qux)).toBeDefined(); + expect(Array.from(graph.transitiveDependencies.get(qux)!).sort()).toEqual( + [baz].sort(), + ); + + // Reverse dependencies should reflect the mutual references + expect(graph.reverseNodeDependencies.get(qux)).toBeDefined(); + expect(Array.from(graph.reverseNodeDependencies.get(qux)!).sort()).toEqual( + [baz].sort(), + ); + expect(Array.from(graph.reverseNodeDependencies.get(baz)!).sort()).toEqual( + [qux].sort(), + ); + }); + + it('handles a small hand-constructed tree with child-level $ref', () => { + const spec = { + components: { + schemas: { + A: { + properties: { p: { $ref: '#/components/schemas/B' } }, + type: 'object', + }, + B: { type: 'object' }, + }, + }, + }; + + const { graph } = buildGraph(spec, loggerStub); + + const a = '#/components/schemas/A'; + const b = '#/components/schemas/B'; + + expect(Array.from(graph.subtreeDependencies.get(a)!).sort()).toEqual( + [b].sort(), + ); + expect(Array.from(graph.transitiveDependencies.get(a)!).sort()).toEqual( + [b].sort(), + ); + // reverseNodeDependencies should record that b is referenced by the property child as well + expect(graph.reverseNodeDependencies.get(b)).toBeDefined(); + expect( + Array.from(graph.reverseNodeDependencies.get(b)!).some((p) => + p.startsWith(a), + ), + ).toBe(true); + }); +}); diff --git a/packages/openapi-ts/src/openApi/shared/utils/graph.ts b/packages/openapi-ts/src/openApi/shared/utils/graph.ts index d00f01952..562d7b8eb 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/graph.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/graph.ts @@ -20,7 +20,7 @@ export type Scope = 'normal' | 'read' | 'write'; * @property scopes - The set of access scopes for this node, if any. Optional. * @property tags - The set of tags for this node, if any. Optional. */ -type NodeInfo = { +export type NodeInfo = { /** Whether the node is deprecated. Optional. */ deprecated?: boolean; /** The property name or array index in the parent, or null for root. */ @@ -38,22 +38,37 @@ type NodeInfo = { /** * The main graph structure for OpenAPI node analysis. * - * @property dependencies - For each node, the set of normalized JSON Pointers it references via $ref. + * @property nodeDependencies - For each node with at least one dependency, the set of normalized JSON Pointers it references via $ref. Nodes with no dependencies are omitted. * @property nodes - Map from normalized JSON Pointer to NodeInfo for every node in the spec. - * @property reverseDependencies - For each node, the set of nodes that reference it via $ref. + * @property reverseNodeDependencies - For each node with at least one dependent, the set of nodes that reference it via $ref. Nodes with no dependents are omitted. */ export type Graph = { + /** + * For each node with at least one dependency, the set of normalized JSON Pointers it references via $ref. + * Nodes with no dependencies are omitted from this map. + */ + nodeDependencies: Map>; + /** + * Map from normalized JSON Pointer to NodeInfo for every node in the spec. + */ + nodes: Map; + /** + * For each node with at least one dependent, the set of nodes that reference it via $ref. + * Nodes with no dependents are omitted from this map. + */ + reverseNodeDependencies: Map>; + /** + * For each node, the set of direct $ref targets that appear anywhere inside the node's + * subtree (the node itself and its children). This is populated during graph construction + * and is used to compute top-level dependency relationships where $ref may be attached to + * child pointers instead of the parent. + */ + subtreeDependencies: Map>; /** * For each node, the set of all (transitive) normalized JSON Pointers it references via $ref anywhere in its subtree. * This includes both direct and indirect dependencies, making it useful for filtering, codegen, and tree-shaking. */ - allDependencies: Map>; - /** For each node, the set of normalized JSON Pointers it references via $ref. */ - dependencies: Map>; - /** Map from normalized JSON Pointer to NodeInfo for every node in the spec. */ - nodes: Map; - /** For each node, the set of nodes that reference it via $ref. */ - reverseDependencies: Map>; + transitiveDependencies: Map>; }; /** @@ -76,15 +91,20 @@ export const annotateChildScopes = (nodes: Graph['nodes']): void => { }; interface Cache { - allDependencies: Map>; - childDependencies: Map>; parentToChildren: Map>; + subtreeDependencies: Map>; + transitiveDependencies: Map>; } +type PointerDependenciesResult = { + subtreeDependencies: Set; + transitiveDependencies: Set; +}; + /** * Recursively collects all $ref dependencies in the subtree rooted at `pointer`. */ -const collectAllDependenciesForPointer = ({ +const collectPointerDependencies = ({ cache, graph, pointer, @@ -94,67 +114,94 @@ const collectAllDependenciesForPointer = ({ graph: Graph; pointer: string; visited: Set; -}): Set => { - const cached = cache.allDependencies.get(pointer); +}): PointerDependenciesResult => { + const cached = cache.transitiveDependencies.get(pointer); if (cached) { - return cached; + return { + subtreeDependencies: cache.subtreeDependencies.get(pointer)!, + transitiveDependencies: cached, + }; } if (visited.has(pointer)) { - return new Set(); + return { + subtreeDependencies: new Set(), + transitiveDependencies: new Set(), + }; } - visited.add(pointer); const nodeInfo = graph.nodes.get(pointer); if (!nodeInfo) { - return new Set(); + return { + subtreeDependencies: new Set(), + transitiveDependencies: new Set(), + }; } - const allDependencies = new Set(); + const transitiveDependencies = new Set(); + const subtreeDependencies = new Set(); // Add direct $ref dependencies for this node // (from the dependencies map, or by checking nodeInfo.node directly) // We'll use the dependencies map for consistency: - const dependencies = graph.dependencies.get(pointer); - if (dependencies) { - for (const depPointer of dependencies) { - allDependencies.add(depPointer); + const nodeDependencies = graph.nodeDependencies.get(pointer); + if (nodeDependencies) { + for (const depPointer of nodeDependencies) { + transitiveDependencies.add(depPointer); + subtreeDependencies.add(depPointer); // Recursively collect dependencies of the referenced node - const transitiveDependencies = collectAllDependenciesForPointer({ + const depResult = collectPointerDependencies({ cache, graph, pointer: depPointer, visited, }); - for (const dep of transitiveDependencies) { - allDependencies.add(dep); + for (const dependency of depResult.transitiveDependencies) { + transitiveDependencies.add(dependency); } } } - // Recursively collect dependencies of all children - const children = cache.parentToChildren.get(pointer); - if (children) { - for (const childPointer of children) { - let transitiveDependencies = cache.childDependencies.get(childPointer); - if (!transitiveDependencies) { - transitiveDependencies = collectAllDependenciesForPointer({ - cache, - graph, - pointer: childPointer, - visited, - }); - cache.childDependencies.set(childPointer, transitiveDependencies); - } - for (const dep of transitiveDependencies) { - allDependencies.add(dep); - } + const children = cache.parentToChildren.get(pointer) ?? []; + for (const childPointer of children) { + let childResult: Partial = { + subtreeDependencies: cache.subtreeDependencies.get(childPointer), + transitiveDependencies: cache.transitiveDependencies.get(childPointer), + }; + if ( + !childResult.subtreeDependencies || + !childResult.transitiveDependencies + ) { + childResult = collectPointerDependencies({ + cache, + graph, + pointer: childPointer, + visited, + }); + cache.transitiveDependencies.set( + childPointer, + childResult.transitiveDependencies!, + ); + cache.subtreeDependencies.set( + childPointer, + childResult.subtreeDependencies!, + ); + } + for (const dependency of childResult.transitiveDependencies!) { + transitiveDependencies.add(dependency); + } + for (const dependency of childResult.subtreeDependencies!) { + subtreeDependencies.add(dependency); } } - cache.allDependencies.set(pointer, allDependencies); - return allDependencies; + cache.transitiveDependencies.set(pointer, transitiveDependencies); + cache.subtreeDependencies.set(pointer, subtreeDependencies); + return { + subtreeDependencies, + transitiveDependencies, + }; }; /** @@ -167,7 +214,7 @@ const collectAllDependenciesForPointer = ({ * - All nodes that reference it via $ref (reverse dependencies) * - Combinator parents (allOf/anyOf/oneOf) if applicable * - * @param graph - The Graph structure containing nodes, dependencies, and reverseDependencies. + * @param graph - The Graph structure containing nodes, dependencies, and reverseNodeDependencies. */ export const propagateScopes = (graph: Graph): void => { const worklist: Set = new Set( @@ -195,9 +242,9 @@ export const propagateScopes = (graph: Graph): void => { if (nodeInfo.parentPointer) { worklist.add(nodeInfo.parentPointer); } - const reverseDependencies = graph.reverseDependencies.get(pointer); - if (reverseDependencies) { - for (const dependentPointer of reverseDependencies) { + const reverseNodeDependencies = graph.reverseNodeDependencies.get(pointer); + if (reverseNodeDependencies) { + for (const dependentPointer of reverseNodeDependencies) { worklist.add(dependentPointer); } } @@ -290,9 +337,9 @@ export const propagateScopes = (graph: Graph): void => { } // Propagate scopes from $ref dependencies - const dependencies = graph.dependencies.get(pointer); - if (dependencies) { - for (const depPointer of dependencies) { + const nodeDependencies = graph.nodeDependencies.get(pointer); + if (nodeDependencies) { + for (const depPointer of nodeDependencies) { const depNode = graph.nodes.get(depPointer); if (depNode?.scopes) { const changed = propagateScopesToNode(depNode, nodeInfo); @@ -384,14 +431,14 @@ export const seedLocalScopes = (nodes: Graph['nodes']): void => { * - All keys in the returned maps are normalized JSON Pointers (RFC 6901, always starting with '#'). * - The `nodes` map allows fast lookup of any node and its parent/key context. * - The `dependencies` map records, for each node, the set of normalized pointers it references via $ref. - * - The `reverseDependencies` map records, for each node, the set of nodes that reference it via $ref. + * - The `reverseNodeDependencies` map records, for each node, the set of nodes that reference it via $ref. * - After construction, all nodes will have their local and propagated scopes annotated. * * @param root The root object (e.g., the OpenAPI spec) * @returns An object with: * - nodes: Map from normalized JSON Pointer string to NodeInfo * - dependencies: Map from normalized JSON Pointer string to Set of referenced normalized JSON Pointers - * - reverseDependencies: Map from normalized JSON Pointer string to Set of referencing normalized JSON Pointers + * - reverseNodeDependencies: Map from normalized JSON Pointer string to Set of referencing normalized JSON Pointers */ export const buildGraph = ( root: unknown, @@ -401,10 +448,11 @@ export const buildGraph = ( } => { const eventBuildGraph = logger.timeEvent('build-graph'); const graph: Graph = { - allDependencies: new Map(), - dependencies: new Map(), + nodeDependencies: new Map(), nodes: new Map(), - reverseDependencies: new Map(), + reverseNodeDependencies: new Map(), + subtreeDependencies: new Map(), + transitiveDependencies: new Map(), }; const walk = ({ @@ -432,10 +480,10 @@ export const buildGraph = ( // If this node has a $ref, record the dependency if ('$ref' in node && typeof node.$ref === 'string') { const refPointer = normalizeJsonPointer(node.$ref); - if (!graph.dependencies.has(pointer)) { - graph.dependencies.set(pointer, new Set()); + if (!graph.nodeDependencies.has(pointer)) { + graph.nodeDependencies.set(pointer, new Set()); } - graph.dependencies.get(pointer)!.add(refPointer); + graph.nodeDependencies.get(pointer)!.add(refPointer); } // Check for tags property (should be an array of strings) if ('tags' in node && node.tags instanceof Array) { @@ -474,9 +522,9 @@ export const buildGraph = ( }); const cache: Cache = { - allDependencies: new Map(), - childDependencies: new Map(), parentToChildren: new Map(), + subtreeDependencies: new Map(), + transitiveDependencies: new Map(), }; for (const [pointer, nodeInfo] of graph.nodes) { @@ -488,12 +536,12 @@ export const buildGraph = ( cache.parentToChildren.get(parent)!.push(pointer); } - for (const [pointerFrom, pointers] of graph.dependencies) { + for (const [pointerFrom, pointers] of graph.nodeDependencies) { for (const pointerTo of pointers) { - if (!graph.reverseDependencies.has(pointerTo)) { - graph.reverseDependencies.set(pointerTo, new Set()); + if (!graph.reverseNodeDependencies.has(pointerTo)) { + graph.reverseNodeDependencies.set(pointerTo, new Set()); } - graph.reverseDependencies.get(pointerTo)!.add(pointerFrom); + graph.reverseNodeDependencies.get(pointerTo)!.add(pointerFrom); } } @@ -502,13 +550,14 @@ export const buildGraph = ( annotateChildScopes(graph.nodes); for (const pointer of graph.nodes.keys()) { - const allDependencies = collectAllDependenciesForPointer({ + const result = collectPointerDependencies({ cache, graph, pointer, visited: new Set(), }); - graph.allDependencies.set(pointer, allDependencies); + graph.transitiveDependencies.set(pointer, result.transitiveDependencies); + graph.subtreeDependencies.set(pointer, result.subtreeDependencies); } eventBuildGraph.timeEnd(); diff --git a/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts b/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts index bb4ad9d69..7874f75ad 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts @@ -28,70 +28,76 @@ const generateAngularClassRequests = ({ const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); - plugin.forEach('operation', ({ operation }) => { - const isRequiredOptions = isOperationOptionsRequired({ - context: plugin.context, - operation, - }); + plugin.forEach( + 'operation', + ({ operation }) => { + const isRequiredOptions = isOperationOptionsRequired({ + context: plugin.context, + operation, + }); - const classes = operationClasses({ - context: plugin.context, - operation, - plugin: sdkPlugin, - }); + const classes = operationClasses({ + context: plugin.context, + operation, + plugin: sdkPlugin, + }); - for (const entry of classes.values()) { - entry.path.forEach((currentClassName, index) => { - if (!requestClasses.has(currentClassName)) { - requestClasses.set(currentClassName, { - className: currentClassName, - classes: new Set(), - methods: new Set(), - nodes: [], - root: !index, - }); - } + for (const entry of classes.values()) { + entry.path.forEach((currentClassName, index) => { + if (!requestClasses.has(currentClassName)) { + requestClasses.set(currentClassName, { + className: currentClassName, + classes: new Set(), + methods: new Set(), + nodes: [], + root: !index, + }); + } - const parentClassName = entry.path[index - 1]; - if (parentClassName && parentClassName !== currentClassName) { - const parentClass = requestClasses.get(parentClassName)!; - parentClass.classes.add(currentClassName); - requestClasses.set(parentClassName, parentClass); - } + const parentClassName = entry.path[index - 1]; + if (parentClassName && parentClassName !== currentClassName) { + const parentClass = requestClasses.get(parentClassName)!; + parentClass.classes.add(currentClassName); + requestClasses.set(parentClassName, parentClass); + } - const isLast = entry.path.length === index + 1; - if (!isLast) { - return; - } + const isLast = entry.path.length === index + 1; + if (!isLast) { + return; + } - const currentClass = requestClasses.get(currentClassName)!; + const currentClass = requestClasses.get(currentClassName)!; - const requestMethodName = - plugin.config.httpRequests.methodNameBuilder(operation); + const requestMethodName = + plugin.config.httpRequests.methodNameBuilder(operation); - if (currentClass.methods.has(requestMethodName)) { - return; - } + if (currentClass.methods.has(requestMethodName)) { + return; + } - const methodNode = generateAngularRequestMethod({ - isRequiredOptions, - methodName: requestMethodName, - operation, - plugin, - }); + const methodNode = generateAngularRequestMethod({ + isRequiredOptions, + methodName: requestMethodName, + operation, + plugin, + }); - if (!currentClass.nodes.length) { - currentClass.nodes.push(methodNode); - } else { - // @ts-expect-error - currentClass.nodes.push(tsc.identifier({ text: '\n' }), methodNode); - } + if (!currentClass.nodes.length) { + currentClass.nodes.push(methodNode); + } else { + // @ts-expect-error + currentClass.nodes.push(tsc.identifier({ text: '\n' }), methodNode); + } - currentClass.methods.add(requestMethodName); - requestClasses.set(currentClassName, currentClass); - }); - } - }); + currentClass.methods.add(requestMethodName); + requestClasses.set(currentClassName, currentClass); + }); + } + }, + { + order: 'declarations', + }, + ); const generateClass = (currentClass: AngularRequestClassEntry) => { if (generatedClasses.has(currentClass.className)) { @@ -166,25 +172,31 @@ const generateAngularFunctionRequests = ({ }: { plugin: AngularCommonPlugin['Instance']; }) => { - plugin.forEach('operation', ({ operation }) => { - const isRequiredOptions = isOperationOptionsRequired({ - context: plugin.context, - operation, - }); + plugin.forEach( + 'operation', + ({ operation }) => { + const isRequiredOptions = isOperationOptionsRequired({ + context: plugin.context, + operation, + }); - const symbol = plugin.registerSymbol({ - exported: true, - name: plugin.config.httpRequests.methodNameBuilder(operation), - selector: plugin.api.selector('httpRequest', operation.id), - }); - const node = generateAngularRequestFunction({ - isRequiredOptions, - operation, - plugin, - symbol, - }); - plugin.setSymbolValue(symbol, node); - }); + const symbol = plugin.registerSymbol({ + exported: true, + name: plugin.config.httpRequests.methodNameBuilder(operation), + selector: plugin.api.selector('httpRequest', operation.id), + }); + const node = generateAngularRequestFunction({ + isRequiredOptions, + operation, + plugin, + symbol, + }); + plugin.setSymbolValue(symbol, node); + }, + { + order: 'declarations', + }, + ); }; const generateRequestCallExpression = ({ diff --git a/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts b/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts index 4ce8d0d14..2d7fbb818 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts @@ -27,70 +27,76 @@ const generateAngularClassServices = ({ const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); - plugin.forEach('operation', ({ operation }) => { - const isRequiredOptions = isOperationOptionsRequired({ - context: plugin.context, - operation, - }); + plugin.forEach( + 'operation', + ({ operation }) => { + const isRequiredOptions = isOperationOptionsRequired({ + context: plugin.context, + operation, + }); - const classes = operationClasses({ - context: plugin.context, - operation, - plugin: sdkPlugin, - }); + const classes = operationClasses({ + context: plugin.context, + operation, + plugin: sdkPlugin, + }); - for (const entry of classes.values()) { - entry.path.forEach((currentClassName, index) => { - if (!serviceClasses.has(currentClassName)) { - serviceClasses.set(currentClassName, { - className: currentClassName, - classes: new Set(), - methods: new Set(), - nodes: [], - root: !index, - }); - } + for (const entry of classes.values()) { + entry.path.forEach((currentClassName, index) => { + if (!serviceClasses.has(currentClassName)) { + serviceClasses.set(currentClassName, { + className: currentClassName, + classes: new Set(), + methods: new Set(), + nodes: [], + root: !index, + }); + } - const parentClassName = entry.path[index - 1]; - if (parentClassName && parentClassName !== currentClassName) { - const parentClass = serviceClasses.get(parentClassName)!; - parentClass.classes.add(currentClassName); - serviceClasses.set(parentClassName, parentClass); - } + const parentClassName = entry.path[index - 1]; + if (parentClassName && parentClassName !== currentClassName) { + const parentClass = serviceClasses.get(parentClassName)!; + parentClass.classes.add(currentClassName); + serviceClasses.set(parentClassName, parentClass); + } - const isLast = entry.path.length === index + 1; - if (!isLast) { - return; - } + const isLast = entry.path.length === index + 1; + if (!isLast) { + return; + } - const currentClass = serviceClasses.get(currentClassName)!; + const currentClass = serviceClasses.get(currentClassName)!; - const resourceMethodName = - plugin.config.httpResources.methodNameBuilder(operation); + const resourceMethodName = + plugin.config.httpResources.methodNameBuilder(operation); - if (currentClass.methods.has(resourceMethodName)) { - return; - } + if (currentClass.methods.has(resourceMethodName)) { + return; + } - const methodNode = generateAngularResourceMethod({ - isRequiredOptions, - methodName: resourceMethodName, - operation, - plugin, - }); + const methodNode = generateAngularResourceMethod({ + isRequiredOptions, + methodName: resourceMethodName, + operation, + plugin, + }); - if (!currentClass.nodes.length) { - currentClass.nodes.push(methodNode); - } else { - // @ts-expect-error - currentClass.nodes.push(tsc.identifier({ text: '\n' }), methodNode); - } + if (!currentClass.nodes.length) { + currentClass.nodes.push(methodNode); + } else { + // @ts-expect-error + currentClass.nodes.push(tsc.identifier({ text: '\n' }), methodNode); + } - currentClass.methods.add(resourceMethodName); - serviceClasses.set(currentClassName, currentClass); - }); - } - }); + currentClass.methods.add(resourceMethodName); + serviceClasses.set(currentClassName, currentClass); + }); + } + }, + { + order: 'declarations', + }, + ); const generateClass = (currentClass: AngularServiceClassEntry) => { if (generatedClasses.has(currentClass.className)) { @@ -164,24 +170,30 @@ const generateAngularFunctionServices = ({ }: { plugin: AngularCommonPlugin['Instance']; }) => { - plugin.forEach('operation', ({ operation }) => { - const isRequiredOptions = isOperationOptionsRequired({ - context: plugin.context, - operation, - }); + plugin.forEach( + 'operation', + ({ operation }) => { + const isRequiredOptions = isOperationOptionsRequired({ + context: plugin.context, + operation, + }); - const symbol = plugin.registerSymbol({ - exported: true, - name: plugin.config.httpResources.methodNameBuilder(operation), - }); - const node = generateAngularResourceFunction({ - isRequiredOptions, - operation, - plugin, - symbol, - }); - plugin.setSymbolValue(symbol, node); - }); + const symbol = plugin.registerSymbol({ + exported: true, + name: plugin.config.httpResources.methodNameBuilder(operation), + }); + const node = generateAngularResourceFunction({ + isRequiredOptions, + operation, + plugin, + symbol, + }); + plugin.setSymbolValue(symbol, node); + }, + { + order: 'declarations', + }, + ); }; const generateResourceCallExpression = ({ diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts index a7736dcaf..f583ecac7 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts @@ -136,138 +136,144 @@ const generateClassSdk = ({ ? createClientClassNodes({ plugin }) : []; - plugin.forEach('operation', ({ operation }) => { - const isRequiredOptions = isOperationOptionsRequired({ - context: plugin.context, - operation, - }); - const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); - const symbolResponse = isNuxtClient - ? plugin.getSymbol( - pluginTypeScript.api.selector('response', operation.id), - ) - : undefined; - - const classes = operationClasses({ - context: plugin.context, - operation, - plugin, - }); - - for (const entry of classes.values()) { - entry.path.forEach((currentClassName, index) => { - const symbolCurrentClass = plugin.referenceSymbol( - plugin.api.selector('class', currentClassName), - ); - if (!sdkClasses.has(symbolCurrentClass.id)) { - sdkClasses.set(symbolCurrentClass.id, { - className: currentClassName, - classes: new Set(), - id: symbolCurrentClass.id, - methods: new Set(), - nodes: [], - root: !index, - }); - } + plugin.forEach( + 'operation', + ({ operation }) => { + const isRequiredOptions = isOperationOptionsRequired({ + context: plugin.context, + operation, + }); + const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); + const symbolResponse = isNuxtClient + ? plugin.getSymbol( + pluginTypeScript.api.selector('response', operation.id), + ) + : undefined; + + const classes = operationClasses({ + context: plugin.context, + operation, + plugin, + }); - const parentClassName = entry.path[index - 1]; - if (parentClassName) { - const symbolParentClass = plugin.referenceSymbol( - plugin.api.selector('class', parentClassName), + for (const entry of classes.values()) { + entry.path.forEach((currentClassName, index) => { + const symbolCurrentClass = plugin.referenceSymbol( + plugin.api.selector('class', currentClassName), ); - if ( - symbolParentClass.placeholder !== symbolCurrentClass.placeholder - ) { - const parentClass = sdkClasses.get(symbolParentClass.id)!; - parentClass.classes.add(symbolCurrentClass.id); - sdkClasses.set(symbolParentClass.id, parentClass); + if (!sdkClasses.has(symbolCurrentClass.id)) { + sdkClasses.set(symbolCurrentClass.id, { + className: currentClassName, + classes: new Set(), + id: symbolCurrentClass.id, + methods: new Set(), + nodes: [], + root: !index, + }); } - } - const isLast = entry.path.length === index + 1; - // add methods only to the last class - if (!isLast) { - return; - } + const parentClassName = entry.path[index - 1]; + if (parentClassName) { + const symbolParentClass = plugin.referenceSymbol( + plugin.api.selector('class', parentClassName), + ); + if ( + symbolParentClass.placeholder !== symbolCurrentClass.placeholder + ) { + const parentClass = sdkClasses.get(symbolParentClass.id)!; + parentClass.classes.add(symbolCurrentClass.id); + sdkClasses.set(symbolParentClass.id, parentClass); + } + } - const currentClass = sdkClasses.get(symbolCurrentClass.id)!; + const isLast = entry.path.length === index + 1; + // add methods only to the last class + if (!isLast) { + return; + } - // avoid duplicate methods - if (currentClass.methods.has(entry.methodName)) { - return; - } + const currentClass = sdkClasses.get(symbolCurrentClass.id)!; - const opParameters = operationParameters({ - isRequiredOptions, - operation, - plugin, - }); - const statements = operationStatements({ - isRequiredOptions, - opParameters, - operation, - plugin, - }); - const functionNode = tsc.methodDeclaration({ - accessLevel: 'public', - comment: plugin.api.createOperationComment({ operation }), - isStatic: isAngularClient ? false : !plugin.config.instance, - name: entry.methodName, - parameters: opParameters.parameters, - returnType: undefined, - statements, - types: isNuxtClient - ? [ - { - default: tsc.ots.string('$fetch'), - extends: tsc.typeNode( - plugin.referenceSymbol(plugin.api.selector('Composable')) - .placeholder, - ), - name: nuxtTypeComposable, - }, - { - default: symbolResponse - ? tsc.typeReferenceNode({ - typeName: symbolResponse.placeholder, - }) - : tsc.typeNode('undefined'), - extends: symbolResponse - ? tsc.typeReferenceNode({ - typeName: symbolResponse.placeholder, - }) - : undefined, - name: nuxtTypeDefault, - }, - ] - : [ - { - default: - ('throwOnError' in client.config - ? client.config.throwOnError - : false) ?? false, - extends: 'boolean', - name: 'ThrowOnError', - }, - ], - }); + // avoid duplicate methods + if (currentClass.methods.has(entry.methodName)) { + return; + } - if (!currentClass.nodes.length) { - currentClass.nodes.push(functionNode); - } else { - currentClass.nodes.push( - // @ts-expect-error - tsc.identifier({ text: '\n' }), - functionNode, - ); - } + const opParameters = operationParameters({ + isRequiredOptions, + operation, + plugin, + }); + const statements = operationStatements({ + isRequiredOptions, + opParameters, + operation, + plugin, + }); + const functionNode = tsc.methodDeclaration({ + accessLevel: 'public', + comment: plugin.api.createOperationComment({ operation }), + isStatic: isAngularClient ? false : !plugin.config.instance, + name: entry.methodName, + parameters: opParameters.parameters, + returnType: undefined, + statements, + types: isNuxtClient + ? [ + { + default: tsc.ots.string('$fetch'), + extends: tsc.typeNode( + plugin.referenceSymbol(plugin.api.selector('Composable')) + .placeholder, + ), + name: nuxtTypeComposable, + }, + { + default: symbolResponse + ? tsc.typeReferenceNode({ + typeName: symbolResponse.placeholder, + }) + : tsc.typeNode('undefined'), + extends: symbolResponse + ? tsc.typeReferenceNode({ + typeName: symbolResponse.placeholder, + }) + : undefined, + name: nuxtTypeDefault, + }, + ] + : [ + { + default: + ('throwOnError' in client.config + ? client.config.throwOnError + : false) ?? false, + extends: 'boolean', + name: 'ThrowOnError', + }, + ], + }); + + if (!currentClass.nodes.length) { + currentClass.nodes.push(functionNode); + } else { + currentClass.nodes.push( + // @ts-expect-error + tsc.identifier({ text: '\n' }), + functionNode, + ); + } - currentClass.methods.add(entry.methodName); + currentClass.methods.add(entry.methodName); - sdkClasses.set(symbolCurrentClass.id, currentClass); - }); - } - }); + sdkClasses.set(symbolCurrentClass.id, currentClass); + }); + } + }, + { + order: 'declarations', + }, + ); const symbolHeyApiClient = plugin.registerSymbol({ exported: false, @@ -372,83 +378,89 @@ const generateFlatSdk = ({ const client = getClientPlugin(plugin.context.config); const isNuxtClient = client.name === '@hey-api/client-nuxt'; - plugin.forEach('operation', ({ operation }) => { - const isRequiredOptions = isOperationOptionsRequired({ - context: plugin.context, - operation, - }); - const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); - const symbolResponse = isNuxtClient - ? plugin.getSymbol( - pluginTypeScript.api.selector('response', operation.id), - ) - : undefined; - const opParameters = operationParameters({ - isRequiredOptions, - operation, - plugin, - }); - const statements = operationStatements({ - isRequiredOptions, - opParameters, - operation, - plugin, - }); - const symbol = plugin.registerSymbol({ - name: serviceFunctionIdentifier({ - config: plugin.context.config, - handleIllegal: true, - id: operation.id, + plugin.forEach( + 'operation', + ({ operation }) => { + const isRequiredOptions = isOperationOptionsRequired({ + context: plugin.context, operation, - }), - selector: plugin.api.selector('function', operation.id), - }); - const node = tsc.constVariable({ - comment: plugin.api.createOperationComment({ operation }), - exportConst: true, - expression: tsc.arrowFunction({ - parameters: opParameters.parameters, - returnType: undefined, - statements, - types: isNuxtClient - ? [ - { - default: tsc.ots.string('$fetch'), - extends: tsc.typeNode( - plugin.referenceSymbol(plugin.api.selector('Composable')) - .placeholder, - ), - name: nuxtTypeComposable, - }, - { - default: symbolResponse - ? tsc.typeReferenceNode({ - typeName: symbolResponse.placeholder, - }) - : tsc.typeNode('undefined'), - extends: symbolResponse - ? tsc.typeReferenceNode({ - typeName: symbolResponse.placeholder, - }) - : undefined, - name: nuxtTypeDefault, - }, - ] - : [ - { - default: - ('throwOnError' in client.config - ? client.config.throwOnError - : false) ?? false, - extends: 'boolean', - name: 'ThrowOnError', - }, - ], - }), - name: symbol.placeholder, - }); - plugin.setSymbolValue(symbol, node); - }); + }); + const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); + const symbolResponse = isNuxtClient + ? plugin.getSymbol( + pluginTypeScript.api.selector('response', operation.id), + ) + : undefined; + const opParameters = operationParameters({ + isRequiredOptions, + operation, + plugin, + }); + const statements = operationStatements({ + isRequiredOptions, + opParameters, + operation, + plugin, + }); + const symbol = plugin.registerSymbol({ + name: serviceFunctionIdentifier({ + config: plugin.context.config, + handleIllegal: true, + id: operation.id, + operation, + }), + selector: plugin.api.selector('function', operation.id), + }); + const node = tsc.constVariable({ + comment: plugin.api.createOperationComment({ operation }), + exportConst: true, + expression: tsc.arrowFunction({ + parameters: opParameters.parameters, + returnType: undefined, + statements, + types: isNuxtClient + ? [ + { + default: tsc.ots.string('$fetch'), + extends: tsc.typeNode( + plugin.referenceSymbol(plugin.api.selector('Composable')) + .placeholder, + ), + name: nuxtTypeComposable, + }, + { + default: symbolResponse + ? tsc.typeReferenceNode({ + typeName: symbolResponse.placeholder, + }) + : tsc.typeNode('undefined'), + extends: symbolResponse + ? tsc.typeReferenceNode({ + typeName: symbolResponse.placeholder, + }) + : undefined, + name: nuxtTypeDefault, + }, + ] + : [ + { + default: + ('throwOnError' in client.config + ? client.config.throwOnError + : false) ?? false, + extends: 'boolean', + name: 'ThrowOnError', + }, + ], + }), + name: symbol.placeholder, + }); + plugin.setSymbolValue(symbol, node); + }, + { + order: 'declarations', + }, + ); }; export const handler: HeyApiSdkPlugin['Handler'] = ({ plugin }) => { diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts index c67ca4c05..485871b92 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts @@ -322,65 +322,77 @@ const processSchemaType = ({ // handles only response transformers for now export const handler: HeyApiTransformersPlugin['Handler'] = ({ plugin }) => { - plugin.forEach('operation', ({ operation }) => { - const { response } = operationResponsesMap(operation); - if (!response) return; + plugin.forEach( + 'operation', + ({ operation }) => { + const { response } = operationResponsesMap(operation); + if (!response) return; - if (response.items && response.items.length > 1) { - if (plugin.context.config.logs.level === 'debug') { - console.warn( - `❗️ Transformers warning: route ${createOperationKey(operation)} has ${response.items.length} non-void success responses. This is currently not handled and we will not generate a response transformer. Please open an issue if you'd like this feature https://github.com/hey-api/openapi-ts/issues`, - ); + if (response.items && response.items.length > 1) { + if (plugin.context.config.logs.level === 'debug') { + console.warn( + `❗️ Transformers warning: route ${createOperationKey(operation)} has ${response.items.length} non-void success responses. This is currently not handled and we will not generate a response transformer. Please open an issue if you'd like this feature https://github.com/hey-api/openapi-ts/issues`, + ); + } + return; } - return; - } - const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); - const symbolResponse = plugin.getSymbol( - pluginTypeScript.api.selector('response', operation.id), - ); - if (!symbolResponse) return; + const pluginTypeScript = plugin.getPluginOrThrow('@hey-api/typescript'); + const symbolResponse = plugin.getSymbol( + pluginTypeScript.api.selector('response', operation.id), + ); + if (!symbolResponse) return; - const symbolResponseTransformer = plugin.registerSymbol({ - exported: true, - name: buildName({ - config: { - case: 'camelCase', - name: '{{name}}ResponseTransformer', - }, - name: operation.id, - }), - selector: plugin.api.selector('response', operation.id), - }); + const symbolResponseTransformer = plugin.registerSymbol({ + exported: true, + name: buildName({ + config: { + case: 'camelCase', + name: '{{name}}ResponseTransformer', + }, + name: operation.id, + }), + selector: plugin.api.selector('response', operation.id), + }); - // TODO: parser - consider handling simple string response which is also a date - const nodes = schemaResponseTransformerNodes({ plugin, schema: response }); - if (nodes.length) { - const responseTransformerNode = tsc.constVariable({ - exportConst: symbolResponseTransformer.exported, - expression: tsc.arrowFunction({ - async: true, - multiLine: true, - parameters: [ - { - name: dataVariableName, - // TODO: parser - add types, generate types without transforms - type: tsc.keywordTypeNode({ keyword: 'any' }), - }, - ], - returnType: tsc.typeReferenceNode({ - typeArguments: [ - tsc.typeReferenceNode({ typeName: symbolResponse.placeholder }), + // TODO: parser - consider handling simple string response which is also a date + const nodes = schemaResponseTransformerNodes({ + plugin, + schema: response, + }); + if (nodes.length) { + const responseTransformerNode = tsc.constVariable({ + exportConst: symbolResponseTransformer.exported, + expression: tsc.arrowFunction({ + async: true, + multiLine: true, + parameters: [ + { + name: dataVariableName, + // TODO: parser - add types, generate types without transforms + type: tsc.keywordTypeNode({ keyword: 'any' }), + }, ], - typeName: 'Promise', + returnType: tsc.typeReferenceNode({ + typeArguments: [ + tsc.typeReferenceNode({ typeName: symbolResponse.placeholder }), + ], + typeName: 'Promise', + }), + statements: ensureStatements(nodes), }), - statements: ensureStatements(nodes), - }), - name: symbolResponseTransformer.placeholder, - }); - plugin.setSymbolValue(symbolResponseTransformer, responseTransformerNode); - } else { - plugin.setSymbolValue(symbolResponseTransformer, null); - } - }); + name: symbolResponseTransformer.placeholder, + }); + plugin.setSymbolValue( + symbolResponseTransformer, + responseTransformerNode, + ); + } else { + plugin.setSymbolValue(symbolResponseTransformer, null); + } + }, + { + order: 'declarations', + }, + ); }; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts index cb1a1249c..0de46c7ba 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts @@ -595,6 +595,9 @@ export const handler: HeyApiTypeScriptPlugin['Handler'] = ({ plugin }) => { break; } }, + { + order: 'declarations', + }, ); createClientOptions({ plugin, servers, symbolClientOptions }); diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts index 963791266..2adb8654b 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts @@ -40,47 +40,53 @@ export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); - plugin.forEach('operation', ({ operation }) => { - const classes = sdkPlugin.config.asClass - ? operationClasses({ - context: plugin.context, - operation, - plugin: sdkPlugin, - }) - : undefined; - const entry = classes ? classes.values().next().value : undefined; - const queryFn = - // TODO: this should use class graph to determine correct path string - // as it's really easy to break once we change the class casing - entry - ? [ - plugin.referenceSymbol( - sdkPlugin.api.selector('class', entry.path[0]), - ).placeholder, - ...entry.path.slice(1).map((className: string) => - stringCase({ - case: 'camelCase', - value: className, - }), - ), - entry.methodName, - ] - .filter(Boolean) - .join('.') - : plugin.referenceSymbol( - sdkPlugin.api.selector('function', operation.id), - ).placeholder; + plugin.forEach( + 'operation', + ({ operation }) => { + const classes = sdkPlugin.config.asClass + ? operationClasses({ + context: plugin.context, + operation, + plugin: sdkPlugin, + }) + : undefined; + const entry = classes ? classes.values().next().value : undefined; + const queryFn = + // TODO: this should use class graph to determine correct path string + // as it's really easy to break once we change the class casing + entry + ? [ + plugin.referenceSymbol( + sdkPlugin.api.selector('class', entry.path[0]), + ).placeholder, + ...entry.path.slice(1).map((className: string) => + stringCase({ + case: 'camelCase', + value: className, + }), + ), + entry.methodName, + ] + .filter(Boolean) + .join('.') + : plugin.referenceSymbol( + sdkPlugin.api.selector('function', operation.id), + ).placeholder; - if (plugin.hooks.operation.isQuery(operation)) { - if (plugin.config.queryOptions.enabled) { - createQueryOptions({ operation, plugin, queryFn }); + if (plugin.hooks.operation.isQuery(operation)) { + if (plugin.config.queryOptions.enabled) { + createQueryOptions({ operation, plugin, queryFn }); + } } - } - if (plugin.hooks.operation.isMutation(operation)) { - if (plugin.config.mutationOptions.enabled) { - createMutationOptions({ operation, plugin, queryFn }); + if (plugin.hooks.operation.isMutation(operation)) { + if (plugin.config.mutationOptions.enabled) { + createMutationOptions({ operation, plugin, queryFn }); + } } - } - }); + }, + { + order: 'declarations', + }, + ); }; diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts index 4e762c4b3..38e10ae6b 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts @@ -63,55 +63,61 @@ export const handler: PluginHandler = ({ plugin }) => { const sdkPlugin = plugin.getPluginOrThrow('@hey-api/sdk'); - plugin.forEach('operation', ({ operation }) => { - const classes = sdkPlugin.config.asClass - ? operationClasses({ - context: plugin.context, - operation, - plugin: sdkPlugin, - }) - : undefined; - const entry = classes ? classes.values().next().value : undefined; - const queryFn = - // TODO: this should use class graph to determine correct path string - // as it's really easy to break once we change the class casing - entry - ? [ - plugin.referenceSymbol( - sdkPlugin.api.selector('class', entry.path[0]), - ).placeholder, - ...entry.path.slice(1).map((className) => - stringCase({ - case: 'camelCase', - value: className, - }), - ), - entry.methodName, - ] - .filter(Boolean) - .join('.') - : plugin.referenceSymbol( - sdkPlugin.api.selector('function', operation.id), - ).placeholder; + plugin.forEach( + 'operation', + ({ operation }) => { + const classes = sdkPlugin.config.asClass + ? operationClasses({ + context: plugin.context, + operation, + plugin: sdkPlugin, + }) + : undefined; + const entry = classes ? classes.values().next().value : undefined; + const queryFn = + // TODO: this should use class graph to determine correct path string + // as it's really easy to break once we change the class casing + entry + ? [ + plugin.referenceSymbol( + sdkPlugin.api.selector('class', entry.path[0]), + ).placeholder, + ...entry.path.slice(1).map((className) => + stringCase({ + case: 'camelCase', + value: className, + }), + ), + entry.methodName, + ] + .filter(Boolean) + .join('.') + : plugin.referenceSymbol( + sdkPlugin.api.selector('function', operation.id), + ).placeholder; - if (plugin.hooks.operation.isQuery(operation)) { - if (plugin.config.queryOptions.enabled) { - createQueryOptions({ operation, plugin, queryFn }); - } + if (plugin.hooks.operation.isQuery(operation)) { + if (plugin.config.queryOptions.enabled) { + createQueryOptions({ operation, plugin, queryFn }); + } - if (plugin.config.infiniteQueryOptions.enabled) { - createInfiniteQueryOptions({ operation, plugin, queryFn }); - } + if (plugin.config.infiniteQueryOptions.enabled) { + createInfiniteQueryOptions({ operation, plugin, queryFn }); + } - if ('useQuery' in plugin.config && plugin.config.useQuery.enabled) { - createUseQuery({ operation, plugin }); + if ('useQuery' in plugin.config && plugin.config.useQuery.enabled) { + createUseQuery({ operation, plugin }); + } } - } - if (plugin.hooks.operation.isMutation(operation)) { - if (plugin.config.mutationOptions.enabled) { - createMutationOptions({ operation, plugin, queryFn }); + if (plugin.hooks.operation.isMutation(operation)) { + if (plugin.config.mutationOptions.enabled) { + createMutationOptions({ operation, plugin, queryFn }); + } } - } - }); + }, + { + order: 'declarations', + }, + ); }; diff --git a/packages/openapi-ts/src/plugins/fastify/plugin.ts b/packages/openapi-ts/src/plugins/fastify/plugin.ts index 914ac43a5..617693239 100644 --- a/packages/openapi-ts/src/plugins/fastify/plugin.ts +++ b/packages/openapi-ts/src/plugins/fastify/plugin.ts @@ -171,12 +171,18 @@ export const handler: FastifyPlugin['Handler'] = ({ plugin }) => { const routeHandlers: Array = []; - plugin.forEach('operation', ({ operation }) => { - const routeHandler = operationToRouteHandler({ operation, plugin }); - if (routeHandler) { - routeHandlers.push(routeHandler); - } - }); + plugin.forEach( + 'operation', + ({ operation }) => { + const routeHandler = operationToRouteHandler({ operation, plugin }); + if (routeHandler) { + routeHandlers.push(routeHandler); + } + }, + { + order: 'declarations', + }, + ); const node = tsc.typeAliasDeclaration({ exportType: symbolRouteHandlers.exported, diff --git a/packages/openapi-ts/src/plugins/shared/types/instance.d.ts b/packages/openapi-ts/src/plugins/shared/types/instance.d.ts index 7527eb57b..c8e770f3f 100644 --- a/packages/openapi-ts/src/plugins/shared/types/instance.d.ts +++ b/packages/openapi-ts/src/plugins/shared/types/instance.d.ts @@ -1,3 +1,4 @@ +import type { IrTopLevelKind } from '../../../ir/graph'; import type { IR } from '../../../ir/types'; type WalkEvents = @@ -6,44 +7,62 @@ type WalkEvents = method: keyof IR.PathItemObject; operation: IR.OperationObject; path: string; - type: 'operation'; + type: Extract; } | { $ref: string; _path: ReadonlyArray; name: string; parameter: IR.ParameterObject; - type: 'parameter'; + type: Extract; } | { $ref: string; _path: ReadonlyArray; name: string; requestBody: IR.RequestBodyObject; - type: 'requestBody'; + type: Extract; } | { $ref: string; _path: ReadonlyArray; name: string; schema: IR.SchemaObject; - type: 'schema'; + type: Extract; } | { _path: ReadonlyArray; server: IR.ServerObject; - type: 'server'; + type: Extract; } | { _path: ReadonlyArray; key: string; method: keyof IR.PathItemObject; operation: IR.OperationObject; - type: 'webhook'; + type: Extract; }; -export type WalkEventType = WalkEvents['type']; -export type WalkEvent = Extract< +export type WalkEvent = Extract< WalkEvents, { type: T } >; + +export type WalkOptions = { + /** + * Order of walking schemas. + * + * The "declarations" option ensures that schemas are walked in the order + * they are declared in the input document. This is useful for scenarios where + * the order of declaration matters, such as when generating code that relies + * on the sequence of schema definitions. + * + * The "topological" option ensures that schemas are walked in an order + * where dependencies are visited before the schemas that depend on them. + * This is useful for scenarios where you need to process or generate + * schemas in a way that respects their interdependencies. + * + * @default 'topological' + */ + order?: 'declarations' | 'topological'; +}; diff --git a/packages/openapi-ts/src/plugins/shared/utils/instance.ts b/packages/openapi-ts/src/plugins/shared/utils/instance.ts index ae77a6e30..cd14270d2 100644 --- a/packages/openapi-ts/src/plugins/shared/utils/instance.ts +++ b/packages/openapi-ts/src/plugins/shared/utils/instance.ts @@ -8,11 +8,18 @@ import type { } from '@hey-api/codegen-core'; import { HeyApiError } from '../../../error'; +import type { IrTopLevelKind } from '../../../ir/graph'; +import { + irTopLevelKinds, + matchIrTopLevelPointer, + walkTopological, +} from '../../../ir/graph'; import type { IR } from '../../../ir/types'; import type { OpenApi } from '../../../openApi/types'; +import { jsonPointerToPath, pathToJsonPointer } from '../../../utils/ref'; import type { PluginConfigMap } from '../../config'; import type { Plugin } from '../../types'; -import type { WalkEvent, WalkEventType } from '../types/instance'; +import type { WalkEvent, WalkOptions } from '../types/instance'; const defaultGetFilePath = (symbol: Symbol): string | undefined => { if (!symbol.meta?.pluginName || typeof symbol.meta.pluginName !== 'string') { @@ -108,108 +115,105 @@ export class PluginInstance { * } * }); */ - forEach( + forEach( ...args: [ ...events: ReadonlyArray, callback: (event: WalkEvent) => void, ] + ): void; + forEach( + ...args: [ + ...events: ReadonlyArray, + callback: (event: WalkEvent) => void, + options: WalkOptions, + ] + ): void; + forEach( + ...args: [ + ...events: ReadonlyArray, + callback: (event: WalkEvent) => void, + options: any, + ] ): void { - const events = args.slice(0, -1) as ReadonlyArray; - const callback = args[args.length - 1] as (event: WalkEvent) => void; - const eventSet = new Set( - events.length - ? events - : ([ - 'operation', - 'parameter', - 'requestBody', - 'schema', - 'server', - 'webhook', - ] as ReadonlyArray), - ); - - if (eventSet.has('server') && this.context.ir.servers) { - for (const server of this.context.ir.servers) { - const event: WalkEvent<'server'> = { - _path: ['servers', String(this.context.ir.servers.indexOf(server))], - server, - type: 'server', - }; - try { - callback(event as WalkEvent); - } catch (error) { - this.forEachError(error, event); - } - } + let callback: (event: WalkEvent) => void; + let events: ReadonlyArray; + let options: Required = { + order: 'topological', + }; + if (typeof args[args.length - 1] === 'function') { + events = args.slice(0, -1); + callback = args[args.length - 1]; + } else { + events = args.slice(0, -2); + callback = args[args.length - 2]; + options = { + ...options, + ...args[args.length - 1], + }; } + const eventSet = new Set(events.length ? events : irTopLevelKinds); - if (eventSet.has('schema') && this.context.ir.components?.schemas) { - for (const name in this.context.ir.components.schemas) { - const event: WalkEvent<'schema'> = { - $ref: `#/components/schemas/${name}`, - _path: ['components', 'schemas', name], - name, - schema: this.context.ir.components.schemas[name]!, - type: 'schema', - }; - try { - callback(event as WalkEvent); - } catch (error) { - this.forEachError(error, event); + if (options.order === 'declarations') { + if (eventSet.has('server') && this.context.ir.servers) { + for (const server of this.context.ir.servers) { + const event: WalkEvent<'server'> = { + _path: ['servers', String(this.context.ir.servers.indexOf(server))], + server, + type: 'server', + }; + try { + callback(event as WalkEvent); + } catch (error) { + this.forEachError(error, event); + } } } - } - if (eventSet.has('parameter') && this.context.ir.components?.parameters) { - for (const name in this.context.ir.components.parameters) { - const event: WalkEvent<'parameter'> = { - $ref: `#/components/parameters/${name}`, - _path: ['components', 'parameters', name], - name, - parameter: this.context.ir.components.parameters[name]!, - type: 'parameter', - }; - try { - callback(event as WalkEvent); - } catch (error) { - this.forEachError(error, event); + if (eventSet.has('schema') && this.context.ir.components?.schemas) { + for (const name in this.context.ir.components.schemas) { + const event: WalkEvent<'schema'> = { + $ref: `#/components/schemas/${name}`, + _path: ['components', 'schemas', name], + name, + schema: this.context.ir.components.schemas[name]!, + type: 'schema', + }; + try { + callback(event as WalkEvent); + } catch (error) { + this.forEachError(error, event); + } } } - } - if ( - eventSet.has('requestBody') && - this.context.ir.components?.requestBodies - ) { - for (const name in this.context.ir.components.requestBodies) { - const event: WalkEvent<'requestBody'> = { - $ref: `#/components/requestBodies/${name}`, - _path: ['components', 'requestBodies', name], - name, - requestBody: this.context.ir.components.requestBodies[name]!, - type: 'requestBody', - }; - try { - callback(event as WalkEvent); - } catch (error) { - this.forEachError(error, event); + if (eventSet.has('parameter') && this.context.ir.components?.parameters) { + for (const name in this.context.ir.components.parameters) { + const event: WalkEvent<'parameter'> = { + $ref: `#/components/parameters/${name}`, + _path: ['components', 'parameters', name], + name, + parameter: this.context.ir.components.parameters[name]!, + type: 'parameter', + }; + try { + callback(event as WalkEvent); + } catch (error) { + this.forEachError(error, event); + } } } - } - if (eventSet.has('operation') && this.context.ir.paths) { - for (const path in this.context.ir.paths) { - const pathItem = - this.context.ir.paths[path as keyof typeof this.context.ir.paths]; - for (const _method in pathItem) { - const method = _method as keyof typeof pathItem; - const event: WalkEvent<'operation'> = { - _path: ['paths', path, method], - method, - operation: pathItem[method]!, - path, - type: 'operation', + if ( + eventSet.has('requestBody') && + this.context.ir.components?.requestBodies + ) { + for (const name in this.context.ir.components.requestBodies) { + const event: WalkEvent<'requestBody'> = { + $ref: `#/components/requestBodies/${name}`, + _path: ['components', 'requestBodies', name], + name, + requestBody: this.context.ir.components.requestBodies[name]!, + type: 'requestBody', }; try { callback(event as WalkEvent); @@ -218,27 +222,116 @@ export class PluginInstance { } } } - } - if (eventSet.has('webhook') && this.context.ir.webhooks) { - for (const key in this.context.ir.webhooks) { - const webhook = this.context.ir.webhooks[key]; - for (const _method in webhook) { - const method = _method as keyof typeof webhook; - const event: WalkEvent<'webhook'> = { - _path: ['webhooks', key, method], - key, - method, - operation: webhook[method]!, - type: 'webhook', - }; + if (eventSet.has('operation') && this.context.ir.paths) { + for (const path in this.context.ir.paths) { + const pathItem = + this.context.ir.paths[path as keyof typeof this.context.ir.paths]; + for (const _method in pathItem) { + const method = _method as keyof typeof pathItem; + const event: WalkEvent<'operation'> = { + _path: ['paths', path, method], + method, + operation: pathItem[method]!, + path, + type: 'operation', + }; + try { + callback(event as WalkEvent); + } catch (error) { + this.forEachError(error, event); + } + } + } + } + + if (eventSet.has('webhook') && this.context.ir.webhooks) { + for (const key in this.context.ir.webhooks) { + const webhook = this.context.ir.webhooks[key]; + for (const _method in webhook) { + const method = _method as keyof typeof webhook; + const event: WalkEvent<'webhook'> = { + _path: ['webhooks', key, method], + key, + method, + operation: webhook[method]!, + type: 'webhook', + }; + try { + callback(event as WalkEvent); + } catch (error) { + this.forEachError(error, event); + } + } + } + } + } else if (options.order === 'topological' && this.context.graph) { + walkTopological(this.context.graph, (pointer, nodeInfo) => { + const result = matchIrTopLevelPointer(pointer); + if (!result.matched || !eventSet.has(result.kind)) return; + let event: WalkEvent | undefined; + switch (result.kind) { + case 'operation': + event = { + _path: jsonPointerToPath(pointer), + method: nodeInfo.key as keyof IR.PathItemObject, + operation: nodeInfo.node as IR.OperationObject, + path: jsonPointerToPath(pointer)[1]!, + type: result.kind, + } satisfies WalkEvent<'operation'>; + break; + case 'parameter': + event = { + $ref: pointer, + _path: jsonPointerToPath(pointer), + name: nodeInfo.key as string, + parameter: nodeInfo.node as IR.ParameterObject, + type: result.kind, + } satisfies WalkEvent<'parameter'>; + break; + case 'requestBody': + event = { + $ref: pointer, + _path: jsonPointerToPath(pointer), + name: nodeInfo.key as string, + requestBody: nodeInfo.node as IR.RequestBodyObject, + type: result.kind, + } satisfies WalkEvent<'requestBody'>; + break; + case 'schema': + event = { + $ref: pointer, + _path: jsonPointerToPath(pointer), + name: nodeInfo.key as string, + schema: nodeInfo.node as IR.SchemaObject, + type: result.kind, + } satisfies WalkEvent<'schema'>; + break; + case 'server': + event = { + _path: jsonPointerToPath(pointer), + server: nodeInfo.node as IR.ServerObject, + type: result.kind, + } satisfies WalkEvent<'server'>; + break; + case 'webhook': + event = { + _path: jsonPointerToPath(pointer), + key: jsonPointerToPath(pointer)[1]!, + method: nodeInfo.key as keyof IR.PathItemObject, + operation: nodeInfo.node as IR.OperationObject, + type: result.kind, + } satisfies WalkEvent<'webhook'>; + break; + } + if (event) { try { callback(event as WalkEvent); } catch (error) { this.forEachError(error, event); } } - } + }); } } @@ -354,6 +447,14 @@ export class PluginInstance { return (defaultGetKind(operation) ?? []).includes(kind); } + isSelfReference( + pointer: string, + path: ReadonlyArray, + ): boolean { + const pathPointer = pathToJsonPointer(path); + return pathPointer === pointer || pathPointer.startsWith(`${pointer}/`); + } + referenceSymbol(symbolIdOrSelector: number | Selector): Symbol { return this.gen.symbols.reference(symbolIdOrSelector); } diff --git a/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts index 67986f1de..0c4bfeb31 100644 --- a/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts @@ -13,8 +13,7 @@ export type PluginState = { * Path to the schema in the intermediary representation. */ _path: ReadonlyArray; - circularReferenceTracker: Set; - hasCircularReference: boolean; + hasLazyExpression: boolean; nameCase: StringCase; nameTransformer: StringName; }; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts index a47dc507e..9290f3b01 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts @@ -5,7 +5,7 @@ import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import { refToName } from '../../../utils/ref'; import type { SchemaWithType } from '../../shared/types/schema'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; import { toRef, toRefs } from '../../shared/utils/refs'; @@ -38,84 +38,56 @@ export const irSchemaToAst = ({ */ optional?: boolean; schema: IR.SchemaObject; + /** + * When symbol is supplied, the AST node will be set as its value. + */ symbol?: Symbol; }): Array => { - let anyType: string | undefined; - let pipes: Array = []; - - if ($ref) { - state.circularReferenceTracker.value.add($ref); - - if (!symbol) { - const selector = plugin.api.selector('ref', $ref); - if (!plugin.getSymbol(selector)) { - symbol = plugin.referenceSymbol(selector); - } - } + if ($ref && !symbol) { + const selector = plugin.api.selector('ref', $ref); + symbol = plugin.getSymbol(selector) || plugin.referenceSymbol(selector); } const v = plugin.referenceSymbol( plugin.api.selector('external', 'valibot.v'), ); + let anyType: string | undefined; + let pipes: Array = []; if (schema.$ref) { - const isCircularReference = state.circularReferenceTracker.value.has( - schema.$ref, - ); - - // if $ref hasn't been processed yet, inline it to avoid the - // "Block-scoped variable used before its declaration." error - // this could be (maybe?) fixed by reshuffling the generation order const selector = plugin.api.selector('ref', schema.$ref); let refSymbol = plugin.getSymbol(selector); - if (!refSymbol) { - const ref = plugin.context.resolveIrRef(schema.$ref); - const schemaPipes = irSchemaToAst({ - $ref: schema.$ref, - plugin, - schema: ref, - state: { - ...state, - _path: toRef(jsonPointerToPath(schema.$ref)), - }, - }); - pipes.push(...schemaPipes); - - refSymbol = plugin.getSymbol(selector); - } - - if (refSymbol) { - const refIdentifier = tsc.identifier({ text: refSymbol.placeholder }); - if (isCircularReference) { - const lazyExpression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: v.placeholder, - name: identifiers.schemas.lazy, + if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const ref = tsc.identifier({ text: refSymbol.placeholder }); + pipes.push(ref); + } else { + refSymbol = plugin.referenceSymbol(selector); + const lazyExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: v.placeholder, + name: identifiers.schemas.lazy, + }), + parameters: [ + tsc.arrowFunction({ + statements: [ + tsc.returnStatement({ + expression: tsc.identifier({ text: refSymbol.placeholder }), + }), + ], }), - parameters: [ - tsc.arrowFunction({ - statements: [ - tsc.returnStatement({ - expression: refIdentifier, - }), - ], - }), - ], - }); - pipes.push(lazyExpression); - state.hasCircularReference.value = true; - } else { - pipes.push(refIdentifier); - } + ], + }); + pipes.push(lazyExpression); + state.hasLazyExpression.value = true; } } else if (schema.type) { - const valibotSchema = irSchemaWithTypeToAst({ + const ast = irSchemaWithTypeToAst({ plugin, schema: schema as SchemaWithType, state, }); - anyType = valibotSchema.anyType; - pipes.push(valibotSchema.expression); + anyType = ast.anyType; + pipes.push(ast.expression); if (plugin.config.metadata && schema.description) { const expression = tsc.callExpression({ @@ -140,8 +112,8 @@ export const irSchemaToAst = ({ schema = deduplicateSchema({ schema }); if (schema.items) { - const itemTypes = schema.items.map((item, index) => { - const schemaPipes = irSchemaToAst({ + const itemsAst = schema.items.map((item, index) => { + const itemAst = irSchemaToAst({ plugin, schema: item, state: { @@ -149,7 +121,7 @@ export const irSchemaToAst = ({ _path: toRef([...state._path.value, 'items', index]), }, }); - return pipesToAst({ pipes: schemaPipes, plugin }); + return pipesToAst({ pipes: itemAst, plugin }); }); if (schema.logicalOperator === 'and') { @@ -160,7 +132,7 @@ export const irSchemaToAst = ({ }), parameters: [ tsc.arrayLiteralExpression({ - elements: itemTypes, + elements: itemsAst, }), ], }); @@ -173,35 +145,27 @@ export const irSchemaToAst = ({ }), parameters: [ tsc.arrayLiteralExpression({ - elements: itemTypes, + elements: itemsAst, }), ], }); pipes.push(unionExpression); } } else { - const schemaPipes = irSchemaToAst({ - plugin, - schema, - state, - }); + const schemaPipes = irSchemaToAst({ plugin, schema, state }); pipes.push(...schemaPipes); } } else { // catch-all fallback for failed schemas - const valibotSchema = irSchemaWithTypeToAst({ + const ast = irSchemaWithTypeToAst({ plugin, schema: { type: 'unknown', }, state, }); - anyType = valibotSchema.anyType; - pipes.push(valibotSchema.expression); - } - - if ($ref) { - state.circularReferenceTracker.value.delete($ref); + anyType = ast.anyType; + pipes.push(ast.expression); } if (pipes.length) { @@ -270,7 +234,7 @@ export const irSchemaToAst = ({ exportConst: symbol.exported, expression: pipesToAst({ pipes, plugin }), name: symbol.placeholder, - typeName: state.hasCircularReference + typeName: state.hasLazyExpression.value ? (tsc.propertyAccessExpression({ expression: v.placeholder, name: anyType || identifiers.types.GenericSchema.text, @@ -299,9 +263,9 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { 'schema', 'webhook', (event) => { - const state = toRefs>({ - circularReferenceTracker: new Set(), - hasCircularReference: false, + const state = toRefs({ + _path: event._path, + hasLazyExpression: false, nameCase: plugin.config.definitions.case, nameTransformer: plugin.config.definitions.name, }); @@ -311,10 +275,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { irOperationToAst({ operation: event.operation, plugin, - state: { - ...state, - _path: toRef(event._path), - }, + state, }); break; case 'parameter': @@ -322,10 +283,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { $ref: event.$ref, plugin, schema: event.parameter.schema, - state: { - ...state, - _path: toRef(event._path), - }, + state, }); break; case 'requestBody': @@ -333,10 +291,7 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { $ref: event.$ref, plugin, schema: event.requestBody.schema, - state: { - ...state, - _path: toRef(event._path), - }, + state, }); break; case 'schema': @@ -344,20 +299,14 @@ export const handlerV1: ValibotPlugin['Handler'] = ({ plugin }) => { $ref: event.$ref, plugin, schema: event.schema, - state: { - ...state, - _path: toRef(event._path), - }, + state, }); break; case 'webhook': irWebhookToAst({ operation: event.operation, plugin, - state: { - ...state, - _path: toRef(event._path), - }, + state, }); break; } diff --git a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts index 2f88abb08..92b75009e 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts @@ -2,15 +2,16 @@ import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import { refToName } from '../../../utils/ref'; import type { SchemaWithType } from '../../shared/types/schema'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { toRef, toRefs } from '../../shared/utils/refs'; import { identifiers } from '../constants'; import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; import { numberParameter } from '../shared/numbers'; import { irOperationToAst } from '../shared/operation'; -import type { Ast, IrSchemaToAstOptions } from '../shared/types'; +import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; import { irWebhookToAst } from '../shared/webhook'; import type { ZodPlugin } from '../types'; import { irSchemaWithTypeToAst } from './toAst'; @@ -34,67 +35,33 @@ export const irSchemaToAst = ({ const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.$ref) { - const isCircularReference = state.circularReferenceTracker.includes( - schema.$ref, - ); - const isSelfReference = state.currentReferenceTracker.includes(schema.$ref); - state.circularReferenceTracker.push(schema.$ref); - state.currentReferenceTracker.push(schema.$ref); - const selector = plugin.api.selector('ref', schema.$ref); - let symbol = plugin.getSymbol(selector); - - if (isCircularReference) { - if (!symbol) { - symbol = plugin.referenceSymbol(selector); - } - - if (isSelfReference) { - ast.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: z.placeholder, - name: identifiers.lazy, - }), - parameters: [ - tsc.arrowFunction({ - returnType: tsc.keywordTypeNode({ keyword: 'any' }), - statements: [ - tsc.returnStatement({ - expression: tsc.identifier({ text: symbol.placeholder }), - }), - ], - }), - ], - }); - } else { - ast.expression = tsc.identifier({ text: symbol.placeholder }); - } - ast.hasCircularReference = schema.circular; + let refSymbol = plugin.getSymbol(selector); + if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const ref = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = ref; } else { - if (!symbol) { - // if $ref hasn't been processed yet, inline it to avoid the - // "Block-scoped variable used before its declaration." error - // this could be (maybe?) fixed by reshuffling the generation order - const ref = plugin.context.resolveIrRef(schema.$ref); - handleComponent({ - id: schema.$ref, - plugin, - schema: ref, - state: { - ...state, - _path: jsonPointerToPath(schema.$ref), - }, - }); - } else { - ast.hasCircularReference = schema.circular; - } - - const refSymbol = plugin.referenceSymbol(selector); - ast.expression = tsc.identifier({ text: refSymbol.placeholder }); + refSymbol = plugin.referenceSymbol(selector); + const lazyExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.lazy, + }), + parameters: [ + tsc.arrowFunction({ + returnType: tsc.keywordTypeNode({ keyword: 'any' }), + statements: [ + tsc.returnStatement({ + expression: tsc.identifier({ text: refSymbol.placeholder }), + }), + ], + }), + ], + }); + ast.expression = lazyExpression; + ast.hasLazyExpression = true; + state.hasLazyExpression.value = true; } - - state.circularReferenceTracker.pop(); - state.currentReferenceTracker.pop(); } else if (schema.type) { const typeAst = irSchemaWithTypeToAst({ plugin, @@ -102,7 +69,7 @@ export const irSchemaToAst = ({ state, }); ast.expression = typeAst.expression; - ast.hasCircularReference = typeAst.hasCircularReference; + ast.hasLazyExpression = typeAst.hasLazyExpression; if (plugin.config.metadata && schema.description) { ast.expression = tsc.callExpression({ @@ -136,7 +103,7 @@ export const irSchemaToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }), ); @@ -167,7 +134,7 @@ export const irSchemaToAst = ({ }), parameters: [ ast.expression, - schema.hasCircularReference + schema.hasLazyExpression ? tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, @@ -260,30 +227,18 @@ export const irSchemaToAst = ({ }; const handleComponent = ({ - id, + $ref, plugin, schema, - state: _state, -}: Omit & { - id: string; + state, +}: IrSchemaToAstOptions & { + $ref: string; schema: IR.SchemaObject; - state?: Partial; }): void => { - const state: IrSchemaToAstOptions['state'] = { - _path: _state?._path ?? [], - circularReferenceTracker: _state?.circularReferenceTracker ?? [id], - currentReferenceTracker: _state?.currentReferenceTracker ?? [id], - hasCircularReference: _state?.hasCircularReference ?? false, - }; - - const selector = plugin.api.selector('ref', id); - let symbol = plugin.getSymbol(selector); - if (symbol && !plugin.getSymbolValue(symbol)) return; - const ast = irSchemaToAst({ plugin, schema, state }); - const baseName = refToName(id); - const resourceType = pathToSymbolResourceType(state._path); - symbol = plugin.registerSymbol({ + const baseName = refToName($ref); + const resourceType = pathToSymbolResourceType(state._path.value); + const symbol = plugin.registerSymbol({ exported: true, meta: { resourceType, @@ -292,7 +247,7 @@ const handleComponent = ({ config: plugin.config.definitions, name: baseName, }), - selector, + selector: plugin.api.selector('ref', $ref), }); const typeInferSymbol = plugin.config.definitions.types.infer.enabled ? plugin.registerSymbol({ @@ -305,7 +260,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.selector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', $ref), }) : undefined; exportAst({ @@ -336,67 +291,66 @@ export const handlerMini: ZodPlugin['Handler'] = ({ plugin }) => { case 'operation': irOperationToAst({ getAst: (schema, path) => { - const state: IrSchemaToAstOptions['state'] = { + const state = toRefs({ _path: path, - circularReferenceTracker: [], - currentReferenceTracker: [], - hasCircularReference: false, - }; + hasLazyExpression: false, + }); return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, - state: { + state: toRefs({ _path: event._path, - }, + }), }); break; case 'parameter': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.parameter.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'requestBody': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.requestBody.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'schema': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'webhook': irWebhookToAst({ getAst: (schema, path) => { - const state: IrSchemaToAstOptions['state'] = { + const state = toRefs({ _path: path, - circularReferenceTracker: [], - currentReferenceTracker: [], - hasCircularReference: false, - }; + hasLazyExpression: false, + }); return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, - state: { + state: toRefs({ _path: event._path, - }, + }), }); break; } diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts index 7afc02633..3b77176ce 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts @@ -3,6 +3,7 @@ import type ts from 'typescript'; import { deduplicateSchema } from '../../../../ir/schema'; import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -47,11 +48,11 @@ export const arrayToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); - if (itemAst.hasCircularReference) { - result.hasCircularReference = true; + if (itemAst.hasLazyExpression) { + result.hasLazyExpression = true; } return itemAst.expression; }); diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts index d172666c0..9d066adb7 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts @@ -3,6 +3,7 @@ import ts from 'typescript'; import { tsc } from '../../../../tsc'; import { numberRegExp } from '../../../../utils/regexp'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -34,11 +35,11 @@ export const objectToAst = ({ schema: property, state: { ...state, - _path: [...state._path, 'properties', name], + _path: toRef([...state._path.value, 'properties', name]), }, }); - if (propertyAst.hasCircularReference) { - result.hasCircularReference = true; + if (propertyAst.hasLazyExpression) { + result.hasLazyExpression = true; } numberRegExp.lastIndex = 0; @@ -61,17 +62,10 @@ export const objectToAst = ({ propertyName = `'${name}'`; } - if (propertyAst.hasCircularReference) { + if (propertyAst.hasLazyExpression) { properties.push( tsc.getAccessorDeclaration({ name: propertyName, - // @ts-expect-error - returnType: propertyAst.typeName - ? tsc.propertyAccessExpression({ - expression: z.placeholder, - name: propertyAst.typeName, - }) - : undefined, statements: [ tsc.returnStatement({ expression: propertyAst.expression, @@ -98,7 +92,7 @@ export const objectToAst = ({ schema: schema.additionalProperties, state: { ...state, - _path: [...state._path, 'additionalProperties'], + _path: toRef([...state._path.value, 'additionalProperties']), }, }); result.expression = tsc.callExpression({ @@ -117,8 +111,8 @@ export const objectToAst = ({ additionalAst.expression, ], }); - if (additionalAst.hasCircularReference) { - result.hasCircularReference = true; + if (additionalAst.hasLazyExpression) { + result.hasLazyExpression = true; } return result as Omit; } diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts index 6022e00b3..0ea4ac4c8 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts @@ -2,6 +2,7 @@ import type ts from 'typescript'; import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -50,12 +51,12 @@ export const tupleToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); tupleElements.push(itemSchema.expression); - if (itemSchema.hasCircularReference) { - result.hasCircularReference = true; + if (itemSchema.hasLazyExpression) { + result.hasLazyExpression = true; } }); } diff --git a/packages/openapi-ts/src/plugins/zod/shared/operation.ts b/packages/openapi-ts/src/plugins/zod/shared/operation.ts index e8362f9cb..2d5187f42 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/operation.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/operation.ts @@ -116,7 +116,7 @@ export const irOperationToAst = ({ schemaData.required = [...requiredProperties]; - const path = state._path || []; + const path = state._path?.value || []; const ast = getAst(schemaData, path); const resourceType = pathToSymbolResourceType(path); const symbol = plugin.registerSymbol({ @@ -158,7 +158,7 @@ export const irOperationToAst = ({ const { response } = operationResponsesMap(operation); if (response) { - const path = [...(state._path || []), 'responses']; + const path = [...(state._path?.value || []), 'responses']; const ast = getAst(response, path); const resourceType = pathToSymbolResourceType(path); const symbol = plugin.registerSymbol({ diff --git a/packages/openapi-ts/src/plugins/zod/shared/types.d.ts b/packages/openapi-ts/src/plugins/zod/shared/types.d.ts index 474d9965e..197cea2d5 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/types.d.ts @@ -1,32 +1,26 @@ import type ts from 'typescript'; import type { IR } from '../../../ir/types'; +import type { ToRefs } from '../../shared/types/refs'; import type { ZodPlugin } from '../types'; export type Ast = { expression: ts.Expression; - hasCircularReference?: boolean; + hasLazyExpression?: boolean; typeName?: string | ts.Identifier; }; export type IrSchemaToAstOptions = { plugin: ZodPlugin['Instance']; - state: State; + state: ToRefs; }; -export type State = { +export type PluginState = { /** * Path to the schema in the intermediary representation. */ _path: ReadonlyArray; - circularReferenceTracker: Array; - /** - * Works the same as `circularReferenceTracker`, but it resets whenever we - * walk inside another schema. This can be used to detect if a schema - * directly references itself. - */ - currentReferenceTracker: Array; - hasCircularReference: boolean; + hasLazyExpression: boolean; }; export type ValidatorArgs = { diff --git a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts index e8e47a0f6..74926f894 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts @@ -115,7 +115,7 @@ export const irWebhookToAst = ({ schemaData.required = [...requiredProperties]; - const path = state._path || []; + const path = state._path?.value || []; const ast = getAst(schemaData, path); const resourceType = pathToSymbolResourceType(path); const symbol = plugin.registerSymbol({ diff --git a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts index 9100d83a9..67c414d19 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts @@ -2,15 +2,16 @@ import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import { refToName } from '../../../utils/ref'; import type { SchemaWithType } from '../../shared/types/schema'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { toRef, toRefs } from '../../shared/utils/refs'; import { identifiers } from '../constants'; import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; import { numberParameter } from '../shared/numbers'; import { irOperationToAst } from '../shared/operation'; -import type { Ast, IrSchemaToAstOptions } from '../shared/types'; +import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; import { irWebhookToAst } from '../shared/webhook'; import type { ZodPlugin } from '../types'; import { irSchemaWithTypeToAst } from './toAst'; @@ -34,21 +35,14 @@ export const irSchemaToAst = ({ const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.$ref) { - const isCircularReference = state.circularReferenceTracker.includes( - schema.$ref, - ); - state.circularReferenceTracker.push(schema.$ref); - state.currentReferenceTracker.push(schema.$ref); - const selector = plugin.api.selector('ref', schema.$ref); - let symbol = plugin.getSymbol(selector); - - if (isCircularReference) { - if (!symbol) { - symbol = plugin.referenceSymbol(selector); - } - - ast.expression = tsc.callExpression({ + let refSymbol = plugin.getSymbol(selector); + if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const ref = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = ref; + } else { + refSymbol = plugin.referenceSymbol(selector); + const lazyExpression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, name: identifiers.lazy, @@ -57,39 +51,16 @@ export const irSchemaToAst = ({ tsc.arrowFunction({ statements: [ tsc.returnStatement({ - expression: tsc.identifier({ text: symbol.placeholder }), + expression: tsc.identifier({ text: refSymbol.placeholder }), }), ], }), ], }); - ast.hasCircularReference = schema.circular; - } else { - if (!symbol) { - // if $ref hasn't been processed yet, inline it to avoid the - // "Block-scoped variable used before its declaration." error - // this could be (maybe?) fixed by reshuffling the generation order - const ref = plugin.context.resolveIrRef(schema.$ref); - handleComponent({ - id: schema.$ref, - plugin, - schema: ref, - state: { - ...state, - _path: jsonPointerToPath(schema.$ref), - currentReferenceTracker: [schema.$ref], - }, - }); - } else { - ast.hasCircularReference = schema.circular; - } - - const refSymbol = plugin.referenceSymbol(selector); - ast.expression = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = lazyExpression; + ast.hasLazyExpression = true; + state.hasLazyExpression.value = true; } - - state.circularReferenceTracker.pop(); - state.currentReferenceTracker.pop(); } else if (schema.type) { const typeAst = irSchemaWithTypeToAst({ plugin, @@ -97,7 +68,6 @@ export const irSchemaToAst = ({ state, }); ast.expression = typeAst.expression; - ast.hasCircularReference = typeAst.hasCircularReference; ast.typeName = typeAst.anyType; if (plugin.config.metadata && schema.description) { @@ -119,12 +89,9 @@ export const irSchemaToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); - if (typeAst.hasCircularReference) { - ast.hasCircularReference = true; - } return typeAst.expression; }); @@ -182,7 +149,6 @@ export const irSchemaToAst = ({ state, }); ast.expression = typeAst.expression; - ast.hasCircularReference = typeAst.hasCircularReference; ast.typeName = typeAst.anyType; } @@ -223,7 +189,7 @@ export const irSchemaToAst = ({ } } - if (ast.hasCircularReference) { + if (state.hasLazyExpression.value) { if (!ast.typeName) { ast.typeName = 'ZodTypeAny'; } @@ -235,30 +201,18 @@ export const irSchemaToAst = ({ }; const handleComponent = ({ - id, + $ref, plugin, schema, - state: _state, -}: Omit & { - id: string; + state, +}: IrSchemaToAstOptions & { + $ref: string; schema: IR.SchemaObject; - state?: Partial; }): void => { - const state: IrSchemaToAstOptions['state'] = { - _path: _state?._path ?? [], - circularReferenceTracker: _state?.circularReferenceTracker ?? [id], - currentReferenceTracker: _state?.currentReferenceTracker ?? [id], - hasCircularReference: _state?.hasCircularReference ?? false, - }; - - const selector = plugin.api.selector('ref', id); - let symbol = plugin.getSymbol(selector); - if (symbol) return; - const ast = irSchemaToAst({ plugin, schema, state }); - const baseName = refToName(id); - const resourceType = pathToSymbolResourceType(state._path); - symbol = plugin.registerSymbol({ + const baseName = refToName($ref); + const resourceType = pathToSymbolResourceType(state._path.value); + const symbol = plugin.registerSymbol({ exported: true, meta: { resourceType, @@ -267,7 +221,7 @@ const handleComponent = ({ config: plugin.config.definitions, name: baseName, }), - selector, + selector: plugin.api.selector('ref', $ref), }); const typeInferSymbol = plugin.config.definitions.types.infer.enabled ? plugin.registerSymbol({ @@ -280,7 +234,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.selector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', $ref), }) : undefined; exportAst({ @@ -310,67 +264,66 @@ export const handlerV3: ZodPlugin['Handler'] = ({ plugin }) => { case 'operation': irOperationToAst({ getAst: (schema, path) => { - const state: IrSchemaToAstOptions['state'] = { + const state = toRefs({ _path: path, - circularReferenceTracker: [], - currentReferenceTracker: [], - hasCircularReference: false, - }; + hasLazyExpression: false, + }); return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, - state: { + state: toRefs({ _path: event._path, - }, + }), }); break; case 'parameter': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.parameter.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'requestBody': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.requestBody.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'schema': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'webhook': irWebhookToAst({ getAst: (schema, path) => { - const state: IrSchemaToAstOptions['state'] = { + const state = toRefs({ _path: path, - circularReferenceTracker: [], - currentReferenceTracker: [], - hasCircularReference: false, - }; + hasLazyExpression: false, + }); return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, - state: { + state: toRefs({ _path: event._path, - }, + }), }); break; } diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts index 72508ba2c..9f3c1f897 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts @@ -3,6 +3,7 @@ import type ts from 'typescript'; import { deduplicateSchema } from '../../../../ir/schema'; import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -25,7 +26,7 @@ export const arrayToAst = ({ }); let arrayExpression: ts.CallExpression | undefined; - let hasCircularReference = false; + let hasLazyExpression = false; if (!schema.items) { arrayExpression = tsc.callExpression({ @@ -50,11 +51,11 @@ export const arrayToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); - if (itemAst.hasCircularReference) { - hasCircularReference = true; + if (itemAst.hasLazyExpression) { + hasLazyExpression = true; } return itemAst.expression; }); @@ -155,6 +156,6 @@ export const arrayToAst = ({ return { expression: arrayExpression, - hasCircularReference, + hasLazyExpression, }; }; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts index f16bbcae9..748f4c33a 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts @@ -3,6 +3,7 @@ import ts from 'typescript'; import { tsc } from '../../../../tsc'; import { numberRegExp } from '../../../../utils/regexp'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -18,7 +19,7 @@ export const objectToAst = ({ } => { const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); - let hasCircularReference = false; + let hasLazyExpression = false; // TODO: parser - handle constants const properties: Array = []; @@ -35,12 +36,12 @@ export const objectToAst = ({ schema: property, state: { ...state, - _path: [...state._path, 'properties', name], + _path: toRef([...state._path.value, 'properties', name]), }, }); - if (propertyExpression.hasCircularReference) { - hasCircularReference = true; + if (propertyExpression.hasLazyExpression) { + hasLazyExpression = true; } numberRegExp.lastIndex = 0; @@ -79,7 +80,7 @@ export const objectToAst = ({ schema: schema.additionalProperties, state: { ...state, - _path: [...state._path, 'additionalProperties'], + _path: toRef([...state._path.value, 'additionalProperties']), }, }); const expression = tsc.callExpression({ @@ -92,7 +93,7 @@ export const objectToAst = ({ return { anyType: 'AnyZodObject', expression, - hasCircularReference: additionalAst.hasCircularReference, + hasLazyExpression: additionalAst.hasLazyExpression, }; } @@ -106,6 +107,6 @@ export const objectToAst = ({ return { anyType: 'AnyZodObject', expression, - hasCircularReference, + hasLazyExpression, }; }; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts index 8fcb4ba83..1bbcc1b54 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts @@ -2,6 +2,7 @@ import type ts from 'typescript'; import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -17,7 +18,7 @@ export const tupleToAst = ({ } => { const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); - let hasCircularReference = false; + let hasLazyExpression = false; if (schema.const && Array.isArray(schema.const)) { const tupleElements = schema.const.map((value) => @@ -42,7 +43,7 @@ export const tupleToAst = ({ }); return { expression, - hasCircularReference, + hasLazyExpression, }; } @@ -55,12 +56,12 @@ export const tupleToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); tupleElements.push(itemSchema.expression); - if (itemSchema.hasCircularReference) { - hasCircularReference = true; + if (itemSchema.hasLazyExpression) { + hasLazyExpression = true; } }); } @@ -78,6 +79,6 @@ export const tupleToAst = ({ }); return { expression, - hasCircularReference, + hasLazyExpression, }; }; diff --git a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts index 562d8ce57..6deb62580 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts @@ -2,15 +2,16 @@ import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import { refToName } from '../../../utils/ref'; import type { SchemaWithType } from '../../shared/types/schema'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { toRef, toRefs } from '../../shared/utils/refs'; import { identifiers } from '../constants'; import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; import { numberParameter } from '../shared/numbers'; import { irOperationToAst } from '../shared/operation'; -import type { Ast, IrSchemaToAstOptions } from '../shared/types'; +import type { Ast, IrSchemaToAstOptions, PluginState } from '../shared/types'; import { irWebhookToAst } from '../shared/webhook'; import type { ZodPlugin } from '../types'; import { irSchemaWithTypeToAst } from './toAst'; @@ -34,67 +35,33 @@ export const irSchemaToAst = ({ const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.$ref) { - const isCircularReference = state.circularReferenceTracker.includes( - schema.$ref, - ); - const isSelfReference = state.currentReferenceTracker.includes(schema.$ref); - state.circularReferenceTracker.push(schema.$ref); - state.currentReferenceTracker.push(schema.$ref); - const selector = plugin.api.selector('ref', schema.$ref); - let symbol = plugin.getSymbol(selector); - - if (isCircularReference) { - if (!symbol) { - symbol = plugin.referenceSymbol(selector); - } - - if (isSelfReference) { - ast.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - expression: z.placeholder, - name: identifiers.lazy, - }), - parameters: [ - tsc.arrowFunction({ - returnType: tsc.keywordTypeNode({ keyword: 'any' }), - statements: [ - tsc.returnStatement({ - expression: tsc.identifier({ text: symbol.placeholder }), - }), - ], - }), - ], - }); - } else { - ast.expression = tsc.identifier({ text: symbol.placeholder }); - } - ast.hasCircularReference = schema.circular; + let refSymbol = plugin.getSymbol(selector); + if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const ref = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = ref; } else { - if (!symbol) { - // if $ref hasn't been processed yet, inline it to avoid the - // "Block-scoped variable used before its declaration." error - // this could be (maybe?) fixed by reshuffling the generation order - const ref = plugin.context.resolveIrRef(schema.$ref); - handleComponent({ - id: schema.$ref, - plugin, - schema: ref, - state: { - ...state, - _path: jsonPointerToPath(schema.$ref), - }, - }); - } else { - ast.hasCircularReference = schema.circular; - } - - const refSymbol = plugin.referenceSymbol(selector); - ast.expression = tsc.identifier({ text: refSymbol.placeholder }); + refSymbol = plugin.referenceSymbol(selector); + const lazyExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + expression: z.placeholder, + name: identifiers.lazy, + }), + parameters: [ + tsc.arrowFunction({ + returnType: tsc.keywordTypeNode({ keyword: 'any' }), + statements: [ + tsc.returnStatement({ + expression: tsc.identifier({ text: refSymbol.placeholder }), + }), + ], + }), + ], + }); + ast.expression = lazyExpression; + ast.hasLazyExpression = true; + state.hasLazyExpression.value = true; } - - state.circularReferenceTracker.pop(); - state.currentReferenceTracker.pop(); } else if (schema.type) { const typeAst = irSchemaWithTypeToAst({ plugin, @@ -102,7 +69,7 @@ export const irSchemaToAst = ({ state, }); ast.expression = typeAst.expression; - ast.hasCircularReference = typeAst.hasCircularReference; + ast.hasLazyExpression = typeAst.hasLazyExpression; if (plugin.config.metadata && schema.description) { ast.expression = tsc.callExpression({ @@ -136,7 +103,7 @@ export const irSchemaToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }), ); @@ -166,7 +133,7 @@ export const irSchemaToAst = ({ name: identifiers.and, }), parameters: [ - schema.hasCircularReference + schema.hasLazyExpression ? tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, @@ -262,30 +229,18 @@ export const irSchemaToAst = ({ }; const handleComponent = ({ - id, + $ref, plugin, schema, - state: _state, -}: Omit & { - id: string; + state, +}: IrSchemaToAstOptions & { + $ref: string; schema: IR.SchemaObject; - state?: Partial; }): void => { - const state: IrSchemaToAstOptions['state'] = { - _path: _state?._path ?? [], - circularReferenceTracker: _state?.circularReferenceTracker ?? [id], - currentReferenceTracker: _state?.currentReferenceTracker ?? [id], - hasCircularReference: _state?.hasCircularReference ?? false, - }; - - const selector = plugin.api.selector('ref', id); - let symbol = plugin.getSymbol(selector); - if (symbol && !plugin.getSymbolValue(symbol)) return; - const ast = irSchemaToAst({ plugin, schema, state }); - const baseName = refToName(id); - const resourceType = pathToSymbolResourceType(state._path); - symbol = plugin.registerSymbol({ + const baseName = refToName($ref); + const resourceType = pathToSymbolResourceType(state._path.value); + const symbol = plugin.registerSymbol({ exported: true, meta: { resourceType, @@ -294,7 +249,7 @@ const handleComponent = ({ config: plugin.config.definitions, name: baseName, }), - selector, + selector: plugin.api.selector('ref', $ref), }); const typeInferSymbol = plugin.config.definitions.types.infer.enabled ? plugin.registerSymbol({ @@ -307,7 +262,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.selector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', $ref), }) : undefined; exportAst({ @@ -337,67 +292,66 @@ export const handlerV4: ZodPlugin['Handler'] = ({ plugin }) => { case 'operation': irOperationToAst({ getAst: (schema, path) => { - const state: IrSchemaToAstOptions['state'] = { + const state = toRefs({ _path: path, - circularReferenceTracker: [], - currentReferenceTracker: [], - hasCircularReference: false, - }; + hasLazyExpression: false, + }); return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, - state: { + state: toRefs({ _path: event._path, - }, + }), }); break; case 'parameter': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.parameter.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'requestBody': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.requestBody.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'schema': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'webhook': irWebhookToAst({ getAst: (schema, path) => { - const state: IrSchemaToAstOptions['state'] = { + const state = toRefs({ _path: path, - circularReferenceTracker: [], - currentReferenceTracker: [], - hasCircularReference: false, - }; + hasLazyExpression: false, + }); return irSchemaToAst({ plugin, schema, state }); }, operation: event.operation, plugin, - state: { + state: toRefs({ _path: event._path, - }, + }), }); break; } diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts index 75491b388..0b3848214 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts @@ -3,6 +3,7 @@ import type ts from 'typescript'; import { deduplicateSchema } from '../../../../ir/schema'; import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -47,11 +48,11 @@ export const arrayToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); - if (itemAst.hasCircularReference) { - result.hasCircularReference = true; + if (itemAst.hasLazyExpression) { + result.hasLazyExpression = true; } return itemAst.expression; }); diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts index ed3da3ec1..021840ea3 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts @@ -3,6 +3,7 @@ import ts from 'typescript'; import { tsc } from '../../../../tsc'; import { numberRegExp } from '../../../../utils/regexp'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -34,11 +35,11 @@ export const objectToAst = ({ schema: property, state: { ...state, - _path: [...state._path, 'properties', name], + _path: toRef([...state._path.value, 'properties', name]), }, }); - if (propertyAst.hasCircularReference) { - result.hasCircularReference = true; + if (propertyAst.hasLazyExpression) { + result.hasLazyExpression = true; } numberRegExp.lastIndex = 0; @@ -61,17 +62,10 @@ export const objectToAst = ({ propertyName = `'${name}'`; } - if (propertyAst.hasCircularReference) { + if (propertyAst.hasLazyExpression) { properties.push( tsc.getAccessorDeclaration({ name: propertyName, - // @ts-expect-error - returnType: propertyAst.typeName - ? tsc.propertyAccessExpression({ - expression: z.placeholder, - name: propertyAst.typeName, - }) - : undefined, statements: [ tsc.returnStatement({ expression: propertyAst.expression, @@ -98,7 +92,7 @@ export const objectToAst = ({ schema: schema.additionalProperties, state: { ...state, - _path: [...state._path, 'additionalProperties'], + _path: toRef([...state._path.value, 'additionalProperties']), }, }); result.expression = tsc.callExpression({ @@ -117,12 +111,12 @@ export const objectToAst = ({ additionalAst.expression, ], }); - if (additionalAst.hasCircularReference) { - result.hasCircularReference = true; + if (additionalAst.hasLazyExpression) { + result.hasLazyExpression = true; } // Return with typeName for circular references - if (result.hasCircularReference) { + if (result.hasLazyExpression) { return { ...result, typeName: 'ZodType', @@ -141,7 +135,7 @@ export const objectToAst = ({ }); // Return with typeName for circular references (AnyZodObject doesn't exist in Zod v4, use ZodType) - if (result.hasCircularReference) { + if (result.hasLazyExpression) { return { ...result, typeName: 'ZodType', diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts index b5f9f9407..f4c35f9a5 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts @@ -2,6 +2,7 @@ import type ts from 'typescript'; import { tsc } from '../../../../tsc'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -50,12 +51,12 @@ export const tupleToAst = ({ schema: item, state: { ...state, - _path: [...state._path, 'items', index], + _path: toRef([...state._path.value, 'items', index]), }, }); tupleElements.push(itemSchema.expression); - if (itemSchema.hasCircularReference) { - result.hasCircularReference = true; + if (itemSchema.hasLazyExpression) { + result.hasLazyExpression = true; } }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54d9aa69e..d6b080f75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1265,8 +1265,8 @@ importers: specifier: workspace:^0.3.0 version: link:../codegen-core '@hey-api/json-schema-ref-parser': - specifier: 1.2.0 - version: 1.2.0 + specifier: 1.2.1 + version: 1.2.1 ansi-colors: specifier: 4.1.3 version: 4.1.3 @@ -1336,7 +1336,7 @@ importers: version: 10.4.3 nuxt: specifier: 3.14.1592 - version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) ofetch: specifier: 1.4.1 version: 1.4.1 @@ -3959,8 +3959,8 @@ packages: '@fontsource/fira-mono@5.0.0': resolution: {integrity: sha512-IsinH/oLYJyv/sQv7SbKmjoAXZsSjm6Q1Tz5GBBXCXi3Jg9MzXmKvWm9bSLC8lFI6CDsi8GkH/DAgZ98t8bhTQ==} - '@hey-api/json-schema-ref-parser@1.2.0': - resolution: {integrity: sha512-BMnIuhVgNmSudadw1GcTsP18Yk5l8FrYrg/OSYNxz0D2E0vf4D5e4j5nUbuY8MU6p1vp7ev0xrfP6A/NWazkzQ==} + '@hey-api/json-schema-ref-parser@1.2.1': + resolution: {integrity: sha512-inPeksRLq+j3ArnuGOzQPQE//YrhezQG0+9Y9yizScBN2qatJ78fIByhEgKdNAbtguDCn4RPxmEhcrePwHxs4A==} engines: {node: '>= 16'} '@humanfs/core@0.19.1': @@ -14235,7 +14235,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0)) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.0) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14249,7 +14249,7 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) @@ -14286,8 +14286,8 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) - webpack-dev-server: 5.2.0(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) optionalDependencies: @@ -14323,7 +14323,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0)) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.0) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14337,7 +14337,7 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) @@ -14374,8 +14374,8 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) - webpack-dev-server: 5.2.0(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) optionalDependencies: @@ -14462,7 +14462,7 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.4) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) webpack-dev-server: 5.2.2(webpack@5.98.0) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) @@ -14499,7 +14499,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.18(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4)) + '@angular-devkit/build-webpack': 0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0) '@angular-devkit/core': 19.2.18(chokidar@4.0.3) '@angular/build': 19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(@angular/compiler@19.2.15)(@angular/platform-server@19.2.0(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/compiler@19.2.15)(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/animations@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1)))(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))(rxjs@7.8.1))(@angular/core@19.2.15(rxjs@7.8.1)(zone.js@0.15.1))))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.9.3)))(terser@5.39.0)(typescript@5.9.3)(yaml@2.8.0) '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3) @@ -14513,7 +14513,7 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.10) '@babel/runtime': 7.26.10 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) + '@ngtools/webpack': 19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) @@ -14537,7 +14537,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14550,7 +14550,7 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 webpack: 5.98.0(esbuild@0.25.4) - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) webpack-dev-server: 5.2.2(webpack@5.98.0) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0) @@ -14582,12 +14582,12 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0)': + '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0))': dependencies: '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.4) - webpack-dev-server: 5.2.0(webpack@5.98.0) + webpack: 5.98.0(esbuild@0.25.0) + webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) transitivePeerDependencies: - chokidar @@ -14595,16 +14595,16 @@ snapshots: dependencies: '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar - '@angular-devkit/build-webpack@0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4))': + '@angular-devkit/build-webpack@0.1902.18(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0)': dependencies: '@angular-devkit/architect': 0.1902.18(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar @@ -17386,7 +17386,7 @@ snapshots: '@fontsource/fira-mono@5.0.0': {} - '@hey-api/json-schema-ref-parser@1.2.0': + '@hey-api/json-schema-ref-parser@1.2.1': dependencies: '@jsdevtools/ono': 7.1.3 '@types/json-schema': 7.0.15 @@ -18000,23 +18000,23 @@ snapshots: '@next/swc-win32-x64-msvc@15.2.4': optional: true - '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': + '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0))': dependencies: '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) '@ngtools/webpack@19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) - '@ngtools/webpack@19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4))': + '@ngtools/webpack@19.2.18(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.9.3) typescript: 5.9.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -18093,32 +18093,32 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - magicast - supports-color - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) transitivePeerDependencies: - magicast - supports-color - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))': dependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 7.2.0 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - magicast - supports-color @@ -18183,13 +18183,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18218,9 +18218,9 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 unimport: 3.14.6(rollup@4.50.0) - vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) - vite-plugin-vue-inspector: 5.3.2(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -18230,13 +18230,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18265,9 +18265,9 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 unimport: 3.14.6(rollup@4.50.0) - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -18277,13 +18277,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@nuxt/devtools@1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@vue/devtools-core': 7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -18312,9 +18312,9 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 unimport: 3.14.6(rollup@4.50.0) - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) which: 3.0.1 ws: 8.18.3 transitivePeerDependencies: @@ -21067,38 +21067,38 @@ snapshots: dependencies: '@vue/devtools-kit': 8.0.2 - '@vue/devtools-core@7.6.8(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-hot-client: 0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) vue: 3.5.13(typescript@5.9.3) transitivePeerDependencies: - vite - '@vue/devtools-core@7.6.8(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) + vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) vue: 3.5.13(typescript@5.9.3) transitivePeerDependencies: - vite - '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': + '@vue/devtools-core@7.6.8(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.7 '@vue/devtools-shared': 7.7.7 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)) + vite-hot-client: 0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) vue: 3.5.13(typescript@5.9.3) transitivePeerDependencies: - vite @@ -21726,14 +21726,14 @@ snapshots: '@babel/core': 7.26.10 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0): dependencies: '@babel/core': 7.26.9 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.26.10): dependencies: @@ -22317,7 +22317,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) core-js-compat@3.45.1: dependencies: @@ -22396,7 +22396,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) css-select@5.2.2: dependencies: @@ -23160,8 +23160,8 @@ snapshots: '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.17.0(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.17.0(jiti@2.6.1)) @@ -23188,7 +23188,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -23199,22 +23199,22 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3) eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.17.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -23225,7 +23225,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.17.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -24933,7 +24933,7 @@ snapshots: dependencies: less: 4.2.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) less@4.2.2: dependencies: @@ -24958,7 +24958,7 @@ snapshots: dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) light-my-request@6.6.0: dependencies: @@ -25499,7 +25499,7 @@ snapshots: dependencies: schema-utils: 4.3.2 tapable: 2.2.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) minimalistic-assert@1.0.1: {} @@ -26211,10 +26211,10 @@ snapshots: - vue-tsc - xml2js - nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/telemetry': 2.6.6(magicast@0.3.5) @@ -26332,10 +26332,10 @@ snapshots: - vue-tsc - xml2js - nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.17.0(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.44)(rollup@4.50.0)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) + '@nuxt/devtools': 1.7.0(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0))(vue@3.5.13(typescript@5.9.3)) '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.50.0) '@nuxt/telemetry': 2.6.6(magicast@0.3.5) @@ -26982,18 +26982,18 @@ snapshots: postcss: 8.5.2 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - typescript - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 1.21.7 postcss: 8.5.2 semver: 7.7.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - typescript @@ -27818,7 +27818,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass: 1.85.0 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) sass@1.85.0: dependencies: @@ -28185,7 +28185,7 @@ snapshots: dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) source-map-support@0.5.21: dependencies: @@ -28618,7 +28618,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) optionalDependencies: esbuild: 0.25.0 @@ -28629,7 +28629,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) optionalDependencies: esbuild: 0.25.4 @@ -29431,10 +29431,6 @@ snapshots: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) vite-hot-client: 2.1.0(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)) - vite-hot-client@0.2.4(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): - dependencies: - vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) - vite-hot-client@0.2.4(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) @@ -29443,6 +29439,10 @@ snapshots: dependencies: vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite-hot-client@0.2.4(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + dependencies: + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite-hot-client@2.1.0(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) @@ -29587,7 +29587,7 @@ snapshots: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29598,14 +29598,14 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.1 - vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29616,14 +29616,14 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.1 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.50.0)(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.2.0(rollup@4.50.0) @@ -29634,7 +29634,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.1 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) optionalDependencies: '@nuxt/kit': 3.15.4(magicast@0.3.5) transitivePeerDependencies: @@ -29671,7 +29671,7 @@ snapshots: - supports-color - vue - vite-plugin-vue-inspector@5.3.2(vite@6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29682,11 +29682,11 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 6.3.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.3.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29697,11 +29697,11 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0)): dependencies: '@babel/core': 7.28.3 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) @@ -29712,7 +29712,7 @@ snapshots: '@vue/compiler-dom': 3.5.21 kolorist: 1.8.0 magic-string: 0.30.18 - vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.0) + vite: 7.1.5(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -30171,7 +30171,7 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-dev-middleware@7.4.2(webpack@5.98.0): + webpack-dev-middleware@7.4.2(webpack@5.98.0(esbuild@0.25.0)): dependencies: colorette: 2.0.20 memfs: 4.38.2 @@ -30180,9 +30180,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-server@5.2.0(webpack@5.98.0): + webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -30209,10 +30209,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - bufferutil - debug @@ -30247,10 +30247,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) transitivePeerDependencies: - bufferutil - debug @@ -30268,7 +30268,7 @@ snapshots: webpack-subresource-integrity@5.1.0(webpack@5.98.0): dependencies: typed-assert: 1.0.9 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0(esbuild@0.25.0) webpack-virtual-modules@0.6.2: {} -- 2.51.2 From 022f7dd0bcb74d2da855f393e2175645aa670351 Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 23 Oct 2025 07:53:14 +0800 Subject: [PATCH 4/5] feat(codegen-core): add isRegistered() method to file and symbol registry --- .changeset/wet-glasses-sleep.md | 5 + .../codegen-core/src/__tests__/files.test.ts | 15 + .../src/__tests__/symbols.test.ts | 13 + packages/codegen-core/src/files/registry.ts | 8 + packages/codegen-core/src/files/types.d.ts | 7 + packages/codegen-core/src/symbols/registry.ts | 5 + packages/codegen-core/src/symbols/types.d.ts | 7 + .../plugins/valibot/default/valibot.gen.ts | 256 +++--- .../plugins/valibot/default/valibot.gen.ts | 776 ++++++++--------- .../3.0.x/validators/valibot.gen.ts | 4 +- .../plugins/valibot/default/valibot.gen.ts | 808 +++++++++--------- .../validators-circular-ref/valibot.gen.ts | 8 +- .../3.1.x/validators-metadata/valibot.gen.ts | 22 +- .../3.1.x/validators-types/valibot.gen.ts | 22 +- .../validators-union-merge/valibot.gen.ts | 14 +- .../3.1.x/validators/valibot.gen.ts | 22 +- .../main/test/openapi-ts.config.ts | 4 +- .../2.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../2.0.x/mini/default/zod.gen.ts | 246 +++--- .../2.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v3/default/zod.gen.ts | 242 +++--- .../2.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v4/default/zod.gen.ts | 246 +++--- .../3.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/mini/circular/zod.gen.ts | 38 +- .../3.0.x/mini/default/zod.gen.ts | 766 ++++++++--------- .../3.0.x/mini/validators/zod.gen.ts | 4 +- .../3.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v3/circular/zod.gen.ts | 30 +- .../__snapshots__/3.0.x/v3/default/zod.gen.ts | 762 ++++++++--------- .../3.0.x/v3/validators/zod.gen.ts | 4 +- .../3.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v4/circular/zod.gen.ts | 38 +- .../__snapshots__/3.0.x/v4/default/zod.gen.ts | 766 ++++++++--------- .../3.0.x/v4/validators/zod.gen.ts | 4 +- .../3.1.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.1.x/mini/default/zod.gen.ts | 790 ++++++++--------- .../mini/validators-circular-ref/zod.gen.ts | 8 +- .../3.1.x/mini/validators-dates/zod.gen.ts | 22 +- .../3.1.x/mini/validators-metadata/zod.gen.ts | 22 +- .../3.1.x/mini/validators-types/zod.gen.ts | 34 +- .../mini/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/mini/validators/zod.gen.ts | 22 +- .../3.1.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v3/default/zod.gen.ts | 786 ++++++++--------- .../v3/validators-circular-ref/zod.gen.ts | 8 +- .../3.1.x/v3/validators-dates/zod.gen.ts | 22 +- .../3.1.x/v3/validators-metadata/zod.gen.ts | 22 +- .../3.1.x/v3/validators-types/zod.gen.ts | 34 +- .../v3/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v3/validators/zod.gen.ts | 22 +- .../3.1.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v4/default/zod.gen.ts | 790 ++++++++--------- .../v4/validators-circular-ref/zod.gen.ts | 8 +- .../3.1.x/v4/validators-dates/zod.gen.ts | 22 +- .../3.1.x/v4/validators-metadata/zod.gen.ts | 22 +- .../3.1.x/v4/validators-types/zod.gen.ts | 34 +- .../v4/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v4/validators/zod.gen.ts | 22 +- .../2.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../2.0.x/mini/default/zod.gen.ts | 246 +++--- .../2.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v3/default/zod.gen.ts | 242 +++--- .../2.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/2.0.x/v4/default/zod.gen.ts | 246 +++--- .../3.0.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/mini/circular/zod.gen.ts | 38 +- .../3.0.x/mini/default/zod.gen.ts | 766 ++++++++--------- .../3.0.x/mini/validators/zod.gen.ts | 4 +- .../3.0.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v3/circular/zod.gen.ts | 30 +- .../__snapshots__/3.0.x/v3/default/zod.gen.ts | 762 ++++++++--------- .../3.0.x/v3/validators/zod.gen.ts | 4 +- .../3.0.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../3.0.x/v4/circular/zod.gen.ts | 38 +- .../__snapshots__/3.0.x/v4/default/zod.gen.ts | 766 ++++++++--------- .../3.0.x/v4/validators/zod.gen.ts | 4 +- .../3.1.x/mini/array-items-all-of/zod.gen.ts | 10 +- .../3.1.x/mini/default/zod.gen.ts | 790 ++++++++--------- .../mini/validators-circular-ref/zod.gen.ts | 8 +- .../3.1.x/mini/validators-dates/zod.gen.ts | 22 +- .../3.1.x/mini/validators-metadata/zod.gen.ts | 22 +- .../3.1.x/mini/validators-types/zod.gen.ts | 34 +- .../mini/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/mini/validators/zod.gen.ts | 22 +- .../3.1.x/v3/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v3/default/zod.gen.ts | 786 ++++++++--------- .../v3/validators-circular-ref/zod.gen.ts | 8 +- .../3.1.x/v3/validators-dates/zod.gen.ts | 22 +- .../3.1.x/v3/validators-metadata/zod.gen.ts | 22 +- .../3.1.x/v3/validators-types/zod.gen.ts | 34 +- .../v3/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v3/validators/zod.gen.ts | 22 +- .../3.1.x/v4/array-items-all-of/zod.gen.ts | 10 +- .../__snapshots__/3.1.x/v4/default/zod.gen.ts | 790 ++++++++--------- .../v4/validators-circular-ref/zod.gen.ts | 8 +- .../3.1.x/v4/validators-dates/zod.gen.ts | 22 +- .../3.1.x/v4/validators-metadata/zod.gen.ts | 22 +- .../3.1.x/v4/validators-types/zod.gen.ts | 34 +- .../v4/validators-union-merge/zod.gen.ts | 8 +- .../3.1.x/v4/validators/zod.gen.ts | 22 +- .../openapi-ts/src/ir/__tests__/graph.test.ts | 120 ++- packages/openapi-ts/src/ir/graph.ts | 201 ++++- .../src/plugins/arktype/shared/types.d.ts | 16 +- .../src/plugins/arktype/v2/plugin.ts | 140 ++- .../src/plugins/arktype/v2/toAst/index.ts | 2 +- .../src/plugins/arktype/v2/toAst/object.ts | 17 +- .../src/plugins/shared/utils/instance.ts | 10 +- .../src/plugins/valibot/v1/plugin.ts | 5 +- .../openapi-ts/src/plugins/zod/mini/plugin.ts | 5 +- .../openapi-ts/src/plugins/zod/v3/plugin.ts | 5 +- .../openapi-ts/src/plugins/zod/v4/plugin.ts | 5 +- .../src/utils/__tests__/minHeap.test.ts | 59 ++ packages/openapi-ts/src/utils/minHeap.ts | 64 ++ 114 files changed, 7508 insertions(+), 7037 deletions(-) create mode 100644 .changeset/wet-glasses-sleep.md create mode 100644 packages/openapi-ts/src/utils/__tests__/minHeap.test.ts create mode 100644 packages/openapi-ts/src/utils/minHeap.ts diff --git a/.changeset/wet-glasses-sleep.md b/.changeset/wet-glasses-sleep.md new file mode 100644 index 000000000..7a8ad4ad5 --- /dev/null +++ b/.changeset/wet-glasses-sleep.md @@ -0,0 +1,5 @@ +--- +'@hey-api/codegen-core': patch +--- + +feat: add `isRegistered()` method to file and symbol registry diff --git a/packages/codegen-core/src/__tests__/files.test.ts b/packages/codegen-core/src/__tests__/files.test.ts index a90011a23..ef376d1a1 100644 --- a/packages/codegen-core/src/__tests__/files.test.ts +++ b/packages/codegen-core/src/__tests__/files.test.ts @@ -28,6 +28,10 @@ describe('FileRegistry', () => { expect(registry.get(file1.id)).toEqual(file1); expect(registry.get(['foo'])).toEqual(file1); + // isRegistered should be true for explicitly registered files + expect(registry.isRegistered(file1.id)).toBe(true); + expect(registry.isRegistered(['foo'])).toBe(true); + // Registering again with same selector returns same file const file1b = registry.register({ selector: ['foo'] }); expect(file1b).toEqual(file1); @@ -76,6 +80,17 @@ describe('FileRegistry', () => { expect(referenced).toContainEqual(file3); // Once registered, file1 is not in referenced set expect(referenced).not.toContainEqual(file1); + + // Referenced-only file should not be considered registered + expect(registry.isRegistered(file3.id)).toBe(false); + // Once registered, file3 becomes registered and no longer appears in referenced() + const file3Registered = registry.register({ + name: 'Baz', + selector: ['baz'], + }); + expect(registry.isRegistered(file3Registered.id)).toBe(true); + const referencedAfter = Array.from(registry.referenced()); + expect(referencedAfter).not.toContainEqual(file3Registered); }); it('throws on invalid register or reference', () => { diff --git a/packages/codegen-core/src/__tests__/symbols.test.ts b/packages/codegen-core/src/__tests__/symbols.test.ts index 4d905797f..dfc85b2c6 100644 --- a/packages/codegen-core/src/__tests__/symbols.test.ts +++ b/packages/codegen-core/src/__tests__/symbols.test.ts @@ -27,6 +27,10 @@ describe('SymbolRegistry', () => { expect(registry.get(symbol1.id)).toEqual(symbol1); expect(registry.get(['foo'])).toEqual(symbol1); + // isRegistered should be true for explicitly registered symbols + expect(registry.isRegistered(symbol1.id)).toBe(true); + expect(registry.isRegistered(['foo'])).toBe(true); + // Registering again with same selector returns same symbol const symbol1b = registry.register({ selector: ['foo'] }); expect(symbol1b).toEqual(symbol1); @@ -75,6 +79,15 @@ describe('SymbolRegistry', () => { registry.setValue(symbol1.id, 42); expect(registry.hasValue(symbol1.id)).toBe(true); expect(registry.getValue(symbol1.id)).toBe(42); + + // referenced-only symbol should not be registered until register() with data + const symRef = registry.reference(['qux']); + expect(registry.isRegistered(symRef.id)).toBe(false); + const symRegistered = registry.register({ + placeholder: 'Qux', + selector: ['qux'], + }); + expect(registry.isRegistered(symRegistered.id)).toBe(true); }); it('throws on invalid register or reference', () => { diff --git a/packages/codegen-core/src/files/registry.ts b/packages/codegen-core/src/files/registry.ts index f0aca2521..e5c9f47d5 100644 --- a/packages/codegen-core/src/files/registry.ts +++ b/packages/codegen-core/src/files/registry.ts @@ -43,6 +43,11 @@ export class FileRegistry implements IFileRegistry { : { selector: symbolIdOrSelector }; } + isRegistered(fileIdOrSelector: number | ISelector): boolean { + const file = this.get(fileIdOrSelector); + return file ? this.registerOrder.has(file.id) : false; + } + reference(fileIdOrSelector: number | ISelector): IFileOut { const file = this.idOrSelector(fileIdOrSelector); return this.register(file); @@ -104,6 +109,9 @@ export class FileRegistry implements IFileRegistry { if (hasOtherKeys) { this.registerOrder.add(id); + if (this.referenceOrder.has(id)) { + this.referenceOrder.delete(id); + } } else { this.referenceOrder.add(id); } diff --git a/packages/codegen-core/src/files/types.d.ts b/packages/codegen-core/src/files/types.d.ts index 6ba2f7b80..2c9f37ae2 100644 --- a/packages/codegen-core/src/files/types.d.ts +++ b/packages/codegen-core/src/files/types.d.ts @@ -82,6 +82,13 @@ export interface IFileRegistry { * @returns File ID before being incremented */ readonly id: number; + /** + * Returns whether a file is registered in the registry. + * + * @param fileIdOrSelector File ID or selector to check. + * @returns True if the file is registered, false otherwise. + */ + isRegistered(fileIdOrSelector: number | ISelector): boolean; /** * Returns a file by ID or selector, registering it if it doesn't exist. * diff --git a/packages/codegen-core/src/symbols/registry.ts b/packages/codegen-core/src/symbols/registry.ts index a98987642..22e78e61d 100644 --- a/packages/codegen-core/src/symbols/registry.ts +++ b/packages/codegen-core/src/symbols/registry.ts @@ -51,6 +51,11 @@ export class SymbolRegistry implements ISymbolRegistry { : { selector: symbolIdOrSelector }; } + isRegistered(symbolIdOrSelector: number | ISelector): boolean { + const symbol = this.get(symbolIdOrSelector); + return symbol ? this.registerOrder.has(symbol.id) : false; + } + reference(symbolIdOrSelector: number | ISelector): ISymbolOut { const symbol = this.idOrSelector(symbolIdOrSelector); return this.register(symbol); diff --git a/packages/codegen-core/src/symbols/types.d.ts b/packages/codegen-core/src/symbols/types.d.ts index cd8f68111..037575da8 100644 --- a/packages/codegen-core/src/symbols/types.d.ts +++ b/packages/codegen-core/src/symbols/types.d.ts @@ -113,6 +113,13 @@ export interface ISymbolRegistry { * @returns Symbol ID before being incremented. */ readonly id: number; + /** + * Returns whether a symbol is registered in the registry. + * + * @param symbolIdOrSelector Symbol ID or selector to check. + * @returns True if the symbol is registered, false otherwise. + */ + isRegistered(symbolIdOrSelector: number | ISelector): boolean; /** * Returns a symbol by ID or selector, registering it if it doesn't exist. * diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts index 97f80d312..c13ac1557 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/valibot/default/valibot.gen.ts @@ -2,105 +2,6 @@ import * as v from 'valibot'; -export const vExternalSharedExternalSharedModel = v.object({ - id: v.string(), - name: v.optional(v.string()) -}); - -/** - * This is a model with one string property - */ -export const vModelWithString = v.object({ - prop: v.optional(v.string()) -}); - -/** - * This is a model with one nested property - */ -export const vModelWithProperties = v.object({ - required: v.string(), - requiredAndReadOnly: v.pipe(v.string(), v.readonly()), - string: v.optional(v.string()), - number: v.optional(v.number()), - boolean: v.optional(v.boolean()), - reference: v.optional(vModelWithString), - 'property with space': v.optional(v.string()), - default: v.optional(v.string()), - try: v.optional(v.string()), - '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), - '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const vModelWithCircularReference: v.GenericSchema = v.object({ - prop: v.optional(v.lazy(() => { - return vModelWithCircularReference; - })) -}); - -/** - * This is a model that extends another model - */ -export const vModelThatExtends = v.intersect([ - vModelWithString, - v.object({ - propExtendsA: v.optional(v.string()), - propExtendsB: v.optional(vModelWithString) - }) -]); - -/** - * This is a model with one string property - */ -export const vModelWithStringError = v.object({ - prop: v.optional(v.string()) -}); - -/** - * This is a model that extends another model - */ -export const vModelThatExtendsExtends = v.intersect([ - vModelWithString, - vModelThatExtends, - v.object({ - propExtendsC: v.optional(v.string()), - propExtendsD: v.optional(vModelWithString) - }) -]); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); - -export const vParameterActivityParams = v.object({ - description: v.optional(v.string()), - graduate_id: v.optional(v.pipe(v.number(), v.integer())), - organization_id: v.optional(v.pipe(v.number(), v.integer())), - parent_activity: v.optional(v.pipe(v.number(), v.integer())), - post_id: v.optional(v.pipe(v.number(), v.integer())) -}); - -export const vResponsePostActivityResponse = v.object({ - description: v.optional(v.string()), - graduate_id: v.optional(v.pipe(v.number(), v.integer())), - organization_id: v.optional(v.pipe(v.number(), v.integer())), - parent_activity_id: v.optional(v.pipe(v.number(), v.integer())), - post_id: v.optional(v.pipe(v.number(), v.integer())) -}); - -export const vFailureFailure = v.object({ - error: v.optional(v.string()), - message: v.optional(v.string()), - reference_code: v.optional(v.string()) -}); - -export const vExternalRefA = vExternalSharedExternalSharedModel; - -export const vExternalRefB = vExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -154,13 +55,16 @@ export const vSimpleBoolean = v.boolean(); */ export const vSimpleString = v.string(); +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); + /** * This is a simple file */ export const vSimpleFile = v.string(); -export const vSimpleReference = vModelWithString; - /** * This is a simple string */ @@ -208,16 +112,6 @@ export const vArrayWithBooleans = v.array(v.boolean()); */ export const vArrayWithStrings = v.array(v.string()); -/** - * This is a simple array with references - */ -export const vArrayWithReferences = v.array(vModelWithString); - -/** - * This is a simple array containing an array - */ -export const vArrayWithArray = v.array(v.array(vModelWithString)); - /** * This is a simple array with properties */ @@ -231,16 +125,6 @@ export const vArrayWithProperties = v.array(v.object({ */ export const vDictionaryWithString = v.object({}); -/** - * This is a string reference - */ -export const vDictionaryWithReference = v.object({}); - -/** - * This is a complex dictionary - */ -export const vDictionaryWithArray = v.object({}); - /** * This is a string dictionary */ @@ -273,6 +157,42 @@ export const vModelWithBoolean = v.object({ prop: v.optional(v.boolean()) }); +/** + * This is a model with one string property + */ +export const vModelWithString = v.object({ + prop: v.optional(v.string()) +}); + +export const vSimpleReference = vModelWithString; + +/** + * This is a simple array with references + */ +export const vArrayWithReferences = v.array(vModelWithString); + +/** + * This is a simple array containing an array + */ +export const vArrayWithArray = v.array(v.array(vModelWithString)); + +/** + * This is a string reference + */ +export const vDictionaryWithReference = v.object({}); + +/** + * This is a complex dictionary + */ +export const vDictionaryWithArray = v.object({}); + +/** + * This is a model with one string property + */ +export const vModelWithStringError = v.object({ + prop: v.optional(v.string()) +}); + /** * This is a model with one string property */ @@ -329,13 +249,6 @@ export const vModelWithNestedEnums = v.object({ arrayWithDescription: v.optional(v.array(v.pipe(v.number(), v.integer()))) }); -/** - * This is a model with one property containing a reference - */ -export const vModelWithReference = v.object({ - prop: v.optional(vModelWithProperties) -}); - /** * This is a model with one property containing an array */ @@ -352,6 +265,39 @@ export const vModelWithDictionary = v.object({ prop: v.optional(v.object({})) }); +/** + * This is a model with one property containing a circular reference + */ +export const vModelWithCircularReference: v.GenericSchema = v.object({ + prop: v.optional(v.lazy(() => { + return vModelWithCircularReference; + })) +}); + +/** + * This is a model with one nested property + */ +export const vModelWithProperties = v.object({ + required: v.string(), + requiredAndReadOnly: v.pipe(v.string(), v.readonly()), + string: v.optional(v.string()), + number: v.optional(v.number()), + boolean: v.optional(v.boolean()), + reference: v.optional(vModelWithString), + 'property with space': v.optional(v.string()), + default: v.optional(v.string()), + try: v.optional(v.string()), + '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), + '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) +}); + +/** + * This is a model with one property containing a reference + */ +export const vModelWithReference = v.object({ + prop: v.optional(vModelWithProperties) +}); + /** * This is a model with one nested property */ @@ -388,6 +334,29 @@ export const vModelWithDuplicateImports = v.object({ propC: v.optional(vModelWithString) }); +/** + * This is a model that extends another model + */ +export const vModelThatExtends = v.intersect([ + vModelWithString, + v.object({ + propExtendsA: v.optional(v.string()), + propExtendsB: v.optional(vModelWithString) + }) +]); + +/** + * This is a model that extends another model + */ +export const vModelThatExtendsExtends = v.intersect([ + vModelWithString, + vModelThatExtends, + v.object({ + propExtendsC: v.optional(v.string()), + propExtendsD: v.optional(vModelWithString) + }) +]); + export const vDefault = v.object({ name: v.optional(v.string()) }); @@ -407,6 +376,37 @@ export const vModelWithPattern = v.object({ patternWithBacktick: v.optional(v.pipe(v.string(), v.regex(/aaa`bbb/))) }); +export const vParameterActivityParams = v.object({ + description: v.optional(v.string()), + graduate_id: v.optional(v.pipe(v.number(), v.integer())), + organization_id: v.optional(v.pipe(v.number(), v.integer())), + parent_activity: v.optional(v.pipe(v.number(), v.integer())), + post_id: v.optional(v.pipe(v.number(), v.integer())) +}); + +export const vResponsePostActivityResponse = v.object({ + description: v.optional(v.string()), + graduate_id: v.optional(v.pipe(v.number(), v.integer())), + organization_id: v.optional(v.pipe(v.number(), v.integer())), + parent_activity_id: v.optional(v.pipe(v.number(), v.integer())), + post_id: v.optional(v.pipe(v.number(), v.integer())) +}); + +export const vFailureFailure = v.object({ + error: v.optional(v.string()), + message: v.optional(v.string()), + reference_code: v.optional(v.string()) +}); + +export const vExternalSharedExternalSharedModel = v.object({ + id: v.string(), + name: v.optional(v.string()) +}); + +export const vExternalRefA = vExternalSharedExternalSharedModel; + +export const vExternalRefB = vExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts index 4f235019c..af07c344a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/valibot/default/valibot.gen.ts @@ -2,341 +2,18 @@ import * as v from 'valibot'; -/** - * This is a model with one string property - */ -export const vModelWithString = v.object({ - prop: v.optional(v.string()) -}); - -export const vExternalSharedExternalSharedModel = v.object({ - id: v.string(), - name: v.optional(v.string()) -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const vCamelCaseCommentWithBreaks = v.pipe(v.number(), v.integer()); - -/** - * This is a model with one nested property - */ -export const vModelWithProperties = v.object({ - required: v.string(), - requiredAndReadOnly: v.pipe(v.string(), v.readonly()), - requiredAndNullable: v.union([ - v.string(), - v.null() - ]), - string: v.optional(v.string()), - number: v.optional(v.number()), - boolean: v.optional(v.boolean()), - reference: v.optional(vModelWithString), - 'property with space': v.optional(v.string()), - default: v.optional(v.string()), - try: v.optional(v.string()), - '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), - '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) -}); - -export const vModelWithReadOnlyAndWriteOnly = v.object({ - foo: v.string(), - bar: v.pipe(v.string(), v.readonly()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const vModelWithCircularReference: v.GenericSchema = v.object({ - prop: v.optional(v.lazy(() => { - return vModelWithCircularReference; - })) -}); - -/** - * This is a model with one enum - */ -export const vModelWithEnum = v.object({ - 'foo_bar-enum': v.optional(v.picklist([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: v.optional(v.picklist([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: v.optional(v.unknown()) -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArray = v.object({ - prop: v.optional(v.array(vModelWithString)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const vModelWithDictionary = v.object({ - prop: v.optional(v.object({})) -}); - -/** - * Circle - */ -export const vModelCircle = v.object({ - kind: v.string(), - radius: v.optional(v.number()) -}); - -/** - * Square - */ -export const vModelSquare = v.object({ - kind: v.string(), - sideLength: v.optional(v.number()) -}); - -export const v3eNum1Период = v.picklist([ - 'Bird', - 'Dog' -]); - -export const vConstValue = v.picklist([ - 'ConstValue' -]); - -/** - * This is a base model with two simple optional properties - */ -export const vCompositionBaseModel = v.object({ - firstName: v.optional(v.string()), - lastname: v.optional(v.string()) -}); - -/** - * This is a model that extends another model - */ -export const vModelThatExtends = v.intersect([ - vModelWithString, - v.object({ - propExtendsA: v.optional(v.string()), - propExtendsB: v.optional(vModelWithString) - }) -]); - -/** - * This is a reusable parameter - */ -export const vSimpleParameter = v.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); - -/** - * An object that can be null - */ -export const vNullableObject = v.optional(v.union([ - v.object({ - foo: v.optional(v.string()) - }), - v.null() -]), null); - -export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ - 'foo', - 'bar' -]); - -export const vModelWithNestedArrayEnumsDataBar = v.picklist([ - 'baz', - 'qux' -]); - -export const vModelWithNestedArrayEnumsData = v.object({ - foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), - bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const vModelWithInteger = v.object({ - prop: v.optional(v.pipe(v.number(), v.integer())) -}); - -/** - * Model with restricted keyword name - */ -export const vImport = v.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ - resourceVersion: v.optional(v.string()), - uid: v.optional(v.string()) -}); - -export const vAdditionalPropertiesUnknownIssue = v.object({}); - -export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ - item: v.optional(v.boolean()), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())), - data: v.optional(v.object({})) -}); - -export const vGenericSchemaDuplicateIssue1SystemString = v.object({ - item: v.optional(v.union([ - v.string(), - v.null() - ])), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())) -}); - -export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ - foo: v.string(), - baz: v.string() -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ - prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const vModelFromZendesk = v.string(); - -/** - * This is a model with one boolean property - */ -export const vModelWithBoolean = v.object({ - prop: v.optional(v.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const vDeprecatedModel = v.object({ - prop: v.optional(v.string()) -}); - -export const vModelWithOneOfEnum = v.union([ - v.object({ - foo: v.picklist([ - 'Bar' - ]) - }), - v.object({ - foo: v.picklist([ - 'Baz' - ]) - }), - v.object({ - foo: v.picklist([ - 'Qux' - ]) - }), - v.object({ - content: v.pipe(v.string(), v.isoTimestamp()), - foo: v.picklist([ - 'Quux' - ]) - }), - v.object({ - content: v.tuple([ - v.pipe(v.string(), v.isoTimestamp()), - v.pipe(v.string(), v.isoTimestamp()) - ]), - foo: v.picklist([ - 'Corge' - ]) - }) -]); - -export const vPageable = v.object({ - page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), - size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), - sort: v.optional(v.array(v.string())) -}); - -export const vSimpleRequestBody = vModelWithString; - -export const vSimpleFormData = vModelWithString; - /** * Model with number-only name */ export const v400 = v.string(); /** - * This is a model with one string property - */ -export const vModelWithStringError = v.object({ - prop: v.optional(v.string()) -}); - -/** - * This is a complex dictionary - */ -export const vDictionaryWithArray = v.object({}); - -/** - * This is a model that extends another model - */ -export const vModelThatExtendsExtends = v.intersect([ - vModelWithString, - vModelThatExtends, - v.object({ - propExtendsC: v.optional(v.string()), - propExtendsD: v.optional(vModelWithString) - }) -]); - -/** - * This is a simple array with strings - */ -export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); - -/** - * Parameter with illegal characters + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const vXFooBar = vModelWithString; - -export const vExternalRefA = vExternalSharedExternalSharedModel; - -export const vExternalRefB = vExternalSharedExternalSharedModel; +export const vCamelCaseCommentWithBreaks = v.pipe(v.number(), v.integer()); /** * Testing multiline comments in string: First line @@ -392,14 +69,14 @@ export const vSimpleBoolean = v.boolean(); export const vSimpleString = v.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const vSimpleFile = v.string(); +export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); /** - * This is a simple reference + * This is a simple file */ -export const vSimpleReference = vModelWithString; +export const vSimpleFile = v.string(); /** * This is a simple string @@ -456,14 +133,9 @@ export const vArrayWithNumbers = v.array(v.pipe(v.number(), v.integer())); export const vArrayWithBooleans = v.array(v.boolean()); /** - * This is a simple array with references - */ -export const vArrayWithReferences = v.array(vModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const vArrayWithArray = v.array(v.array(vModelWithString)); +export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); /** * This is a simple array with properties @@ -499,11 +171,6 @@ export const vDictionaryWithPropertiesAndAdditionalProperties = v.object({ bar: v.optional(v.boolean()) }); -/** - * This is a string reference - */ -export const vDictionaryWithReference = v.object({}); - /** * This is a string dictionary */ @@ -517,6 +184,64 @@ export const vDictionaryWithProperties = v.record(v.string(), v.object({ bar: v.optional(v.string()) })); +/** + * This is a model with one number property + */ +export const vModelWithInteger = v.object({ + prop: v.optional(v.pipe(v.number(), v.integer())) +}); + +/** + * This is a model with one boolean property + */ +export const vModelWithBoolean = v.object({ + prop: v.optional(v.boolean()) +}); + +/** + * This is a model with one string property + */ +export const vModelWithString = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a simple reference + */ +export const vSimpleReference = vModelWithString; + +/** + * This is a simple array with references + */ +export const vArrayWithReferences = v.array(vModelWithString); + +/** + * This is a simple array containing an array + */ +export const vArrayWithArray = v.array(v.array(vModelWithString)); + +/** + * This is a string reference + */ +export const vDictionaryWithReference = v.object({}); + +/** + * This is a complex dictionary + */ +export const vDictionaryWithArray = v.object({}); + +/** + * This is a model with one string property + */ +export const vModelWithStringError = v.object({ + prop: v.optional(v.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const vModelFromZendesk = v.string(); + /** * This is a model with one string property */ @@ -545,6 +270,27 @@ export const vModelWithNullableString = v.object({ ])) }); +/** + * This is a model with one enum + */ +export const vModelWithEnum = v.object({ + 'foo_bar-enum': v.optional(v.picklist([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: v.optional(v.picklist([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: v.optional(v.unknown()) +}); + /** * This is a model with one enum with escaped name */ @@ -582,10 +328,37 @@ export const vModelWithNestedEnums = v.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const vModelWithReference = v.object({ - prop: v.optional(vModelWithProperties) +export const vModelWithArray = v.object({ + prop: v.optional(v.array(vModelWithString)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const vModelWithDictionary = v.object({ + prop: v.optional(v.object({})) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const vDeprecatedModel = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const vModelWithCircularReference: v.GenericSchema = v.object({ + prop: v.optional(v.lazy(() => { + return vModelWithCircularReference; + })) }); /** @@ -613,6 +386,22 @@ export const vCompositionWithOneOfAnonymous = v.object({ ])) }); +/** + * Circle + */ +export const vModelCircle = v.object({ + kind: v.string(), + radius: v.optional(v.number()) +}); + +/** + * Square + */ +export const vModelSquare = v.object({ + kind: v.string(), + sideLength: v.optional(v.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -672,6 +461,15 @@ export const vCompositionWithNestedAnyAndTypeNull = v.object({ ])) }); +export const v3eNum1Период = v.picklist([ + 'Bird', + 'Dog' +]); + +export const vConstValue = v.picklist([ + 'ConstValue' +]); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -759,6 +557,14 @@ export const vCompositionWithAnyOfAndNullable = v.object({ ])) }); +/** + * This is a base model with two simple optional properties + */ +export const vCompositionBaseModel = v.object({ + firstName: v.optional(v.string()), + lastname: v.optional(v.string()) +}); + /** * This is a model that extends the base model */ @@ -771,6 +577,34 @@ export const vCompositionExtendedModel = v.intersect([ }) ]); +/** + * This is a model with one nested property + */ +export const vModelWithProperties = v.object({ + required: v.string(), + requiredAndReadOnly: v.pipe(v.string(), v.readonly()), + requiredAndNullable: v.union([ + v.string(), + v.null() + ]), + string: v.optional(v.string()), + number: v.optional(v.number()), + boolean: v.optional(v.boolean()), + reference: v.optional(vModelWithString), + 'property with space': v.optional(v.string()), + default: v.optional(v.string()), + try: v.optional(v.string()), + '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), + '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) +}); + +/** + * This is a model with one property containing a reference + */ +export const vModelWithReference = v.object({ + prop: v.optional(vModelWithProperties) +}); + /** * This is a model with one nested property */ @@ -816,6 +650,29 @@ export const vModelWithDuplicateImports = v.object({ propC: v.optional(vModelWithString) }); +/** + * This is a model that extends another model + */ +export const vModelThatExtends = v.intersect([ + vModelWithString, + v.object({ + propExtendsA: v.optional(v.string()), + propExtendsB: v.optional(vModelWithString) + }) +]); + +/** + * This is a model that extends another model + */ +export const vModelThatExtendsExtends = v.intersect([ + vModelWithString, + vModelThatExtends, + v.object({ + propExtendsC: v.optional(v.string()), + propExtendsD: v.optional(vModelWithString) + }) +]); + /** * This is a model that contains a some patterns */ @@ -843,6 +700,12 @@ export const vDefault = v.object({ name: v.optional(v.string()) }); +export const vPageable = v.object({ + page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), + size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), + sort: v.optional(v.array(v.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -883,23 +746,15 @@ export const vNestedAnyOfArraysNullable = v.object({ ])) }); -export const vCompositionWithOneOfAndProperties = v.intersect([ - v.union([ - v.object({ - foo: vSimpleParameter - }), - v.object({ - bar: vNonAsciiStringæøåÆøÅöôêÊ字符串 - }) - ]), +/** + * An object that can be null + */ +export const vNullableObject = v.optional(v.union([ v.object({ - baz: v.union([ - v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), - v.null() - ]), - qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) - }) -]); + foo: v.optional(v.string()) + }), + v.null() +]), null); /** * Some % character @@ -910,6 +765,54 @@ export const vModelWithNullableObject = v.object({ data: v.optional(vNullableObject) }); +export const vModelWithOneOfEnum = v.union([ + v.object({ + foo: v.picklist([ + 'Bar' + ]) + }), + v.object({ + foo: v.picklist([ + 'Baz' + ]) + }), + v.object({ + foo: v.picklist([ + 'Qux' + ]) + }), + v.object({ + content: v.pipe(v.string(), v.isoTimestamp()), + foo: v.picklist([ + 'Quux' + ]) + }), + v.object({ + content: v.tuple([ + v.pipe(v.string(), v.isoTimestamp()), + v.pipe(v.string(), v.isoTimestamp()) + ]), + foo: v.picklist([ + 'Corge' + ]) + }) +]); + +export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ + 'foo', + 'bar' +]); + +export const vModelWithNestedArrayEnumsDataBar = v.picklist([ + 'baz', + 'qux' +]); + +export const vModelWithNestedArrayEnumsData = v.object({ + foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), + bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) +}); + export const vModelWithNestedArrayEnums = v.object({ array_strings: v.optional(v.array(v.string())), data: v.optional(vModelWithNestedArrayEnumsData) @@ -919,6 +822,20 @@ export const vModelWithNestedCompositionEnums = v.object({ foo: v.optional(vModelWithNestedArrayEnumsDataFoo) }); +export const vModelWithReadOnlyAndWriteOnly = v.object({ + foo: v.string(), + bar: v.pipe(v.string(), v.readonly()) +}); + +/** + * This is a model with one property containing an array + */ +export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ + prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + export const vModelWithConstantSizeArray = v.tuple([ v.number(), v.number() @@ -959,17 +876,6 @@ export const vModelWithAnyOfConstantSizeArrayNullable = v.tuple([ ]) ]); -export const vModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = v.tuple([ - v.union([ - v.number(), - vImport - ]), - v.union([ - v.number(), - vImport - ]) -]); - export const vModelWithAnyOfConstantSizeArrayAndIntersect = v.tuple([ v.intersect([ v.number(), @@ -992,20 +898,6 @@ export const vModelWithBackticksInDescription = v.object({ template: v.optional(v.string()) }); -export const vModelWithOneOfAndProperties = v.intersect([ - v.union([ - vSimpleParameter, - vNonAsciiStringæøåÆøÅöôêÊ字符串 - ]), - v.object({ - baz: v.union([ - v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), - v.null() - ]), - qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) - }) -]); - /** * Model used to test deduplication strategy (unused) */ @@ -1031,6 +923,22 @@ export const vDeleteFooData = v.string(); */ export const vDeleteFooData2 = v.string(); +/** + * Model with restricted keyword name + */ +export const vImport = v.string(); + +export const vModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = v.tuple([ + v.union([ + v.number(), + vImport + ]), + v.union([ + v.number(), + vImport + ]) +]); + export const vSchemaWithFormRestrictedKeys = v.object({ description: v.optional(v.string()), 'x-enum-descriptions': v.optional(v.string()), @@ -1053,6 +961,14 @@ export const vSchemaWithFormRestrictedKeys = v.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ + resourceVersion: v.optional(v.string()), + uid: v.optional(v.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1060,6 +976,8 @@ export const vIoK8sApimachineryPkgApisMetaV1DeleteOptions = v.object({ preconditions: v.optional(vIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const vAdditionalPropertiesUnknownIssue = v.object({}); + export const vAdditionalPropertiesUnknownIssue2 = v.object({}); export const vAdditionalPropertiesUnknownIssue3 = v.intersect([ @@ -1073,6 +991,28 @@ export const vAdditionalPropertiesIntegerIssue = v.object({ value: v.pipe(v.number(), v.integer()) }); +export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ + item: v.optional(v.boolean()), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())), + data: v.optional(v.object({})) +}); + +export const vGenericSchemaDuplicateIssue1SystemString = v.object({ + item: v.optional(v.union([ + v.string(), + v.null() + ])), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())) +}); + export const vOneOfAllOfIssue = v.union([ v.intersect([ v.union([ @@ -1084,6 +1024,15 @@ export const vOneOfAllOfIssue = v.union([ vGenericSchemaDuplicateIssue1SystemString ]); +export const vExternalSharedExternalSharedModel = v.object({ + id: v.string(), + name: v.optional(v.string()) +}); + +export const vExternalRefA = vExternalSharedExternalSharedModel; + +export const vExternalRefB = vExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1119,6 +1068,11 @@ export const vFileWritable = v.object({ mime: v.pipe(v.string(), v.minLength(1), v.maxLength(24)) }); +export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ + foo: v.string(), + baz: v.string() +}); + export const vAdditionalPropertiesUnknownIssueWritable = v.object({}); export const vGenericSchemaDuplicateIssue1SystemBooleanWritable = v.object({ @@ -1141,6 +1095,52 @@ export const vGenericSchemaDuplicateIssue1SystemStringWritable = v.object({ ])) }); +/** + * This is a reusable parameter + */ +export const vSimpleParameter = v.string(); + +export const vCompositionWithOneOfAndProperties = v.intersect([ + v.union([ + v.object({ + foo: vSimpleParameter + }), + v.object({ + bar: vNonAsciiStringæøåÆøÅöôêÊ字符串 + }) + ]), + v.object({ + baz: v.union([ + v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), + v.null() + ]), + qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) + }) +]); + +export const vModelWithOneOfAndProperties = v.intersect([ + v.union([ + vSimpleParameter, + vNonAsciiStringæøåÆøÅöôêÊ字符串 + ]), + v.object({ + baz: v.union([ + v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), + v.null() + ]), + qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) + }) +]); + +/** + * Parameter with illegal characters + */ +export const vXFooBar = vModelWithString; + +export const vSimpleRequestBody = vModelWithString; + +export const vSimpleFormData = vModelWithString; + export const vExportData = v.object({ body: v.optional(v.never()), path: v.optional(v.never()), diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts index 5f2394594..b327f2c83 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/validators/valibot.gen.ts @@ -2,6 +2,8 @@ import * as v from 'valibot'; +export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); + export const vFoo: v.GenericSchema = v.optional(v.union([ v.object({ foo: v.optional(v.pipe(v.string(), v.regex(/^\d{3}-\d{2}-\d{4}$/))), @@ -19,5 +21,3 @@ export const vFoo: v.GenericSchema = v.optional(v.union([ export const vBar = v.object({ foo: v.optional(vFoo) }); - -export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts index 6d7c9cdf3..c9ad4f08a 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts @@ -2,351 +2,18 @@ import * as v from 'valibot'; -/** - * This is a model with one string property - */ -export const vModelWithString = v.object({ - prop: v.optional(v.string()) -}); - -export const vExternalSharedExternalSharedModel = v.object({ - id: v.string(), - name: v.optional(v.string()) -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const vCamelCaseCommentWithBreaks = v.pipe(v.number(), v.integer()); - -/** - * This is a model with one nested property - */ -export const vModelWithProperties = v.object({ - required: v.string(), - requiredAndReadOnly: v.pipe(v.string(), v.readonly()), - requiredAndNullable: v.union([ - v.string(), - v.null() - ]), - string: v.optional(v.string()), - number: v.optional(v.number()), - boolean: v.optional(v.boolean()), - reference: v.optional(vModelWithString), - 'property with space': v.optional(v.string()), - default: v.optional(v.string()), - try: v.optional(v.string()), - '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), - '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) -}); - -export const vModelWithReadOnlyAndWriteOnly = v.object({ - foo: v.string(), - bar: v.pipe(v.string(), v.readonly()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const vModelWithCircularReference: v.GenericSchema = v.object({ - prop: v.optional(v.lazy(() => { - return vModelWithCircularReference; - })) -}); - -/** - * This is a model with one enum - */ -export const vModelWithEnum = v.object({ - 'foo_bar-enum': v.optional(v.picklist([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: v.optional(v.picklist([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: v.optional(v.unknown()) -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArray = v.object({ - prop: v.optional(v.array(vModelWithString)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const vModelWithDictionary = v.object({ - prop: v.optional(v.object({})) -}); - -/** - * Circle - */ -export const vModelCircle = v.object({ - kind: v.string(), - radius: v.optional(v.number()) -}); - -/** - * Square - */ -export const vModelSquare = v.object({ - kind: v.string(), - sideLength: v.optional(v.number()) -}); - -export const v3eNum1Период = v.picklist([ - 'Bird', - 'Dog' -]); - -export const vConstValue = v.literal('ConstValue'); - -/** - * This is a base model with two simple optional properties - */ -export const vCompositionBaseModel = v.object({ - firstName: v.optional(v.string()), - lastname: v.optional(v.string()) -}); - -/** - * This is a model that extends another model - */ -export const vModelThatExtends = v.intersect([ - vModelWithString, - v.object({ - propExtendsA: v.optional(v.string()), - propExtendsB: v.optional(vModelWithString) - }) -]); - -/** - * This is a reusable parameter - */ -export const vSimpleParameter = v.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); - -/** - * An object that can be null - */ -export const vNullableObject = v.optional(v.union([ - v.object({ - foo: v.optional(v.string()) - }), - v.null() -]), null); - -export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ - 'foo', - 'bar' -]); - -export const vModelWithNestedArrayEnumsDataBar = v.picklist([ - 'baz', - 'qux' -]); - -export const vModelWithNestedArrayEnumsData = v.object({ - foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), - bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const vModelWithInteger = v.object({ - prop: v.optional(v.pipe(v.number(), v.integer())) -}); - -/** - * Model with restricted keyword name - */ -export const vImport = v.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ - resourceVersion: v.optional(v.string()), - uid: v.optional(v.string()) -}); - -export const vAdditionalPropertiesUnknownIssue = v.object({}); - -export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ - item: v.optional(v.boolean()), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())), - data: v.optional(v.object({})) -}); - -export const vGenericSchemaDuplicateIssue1SystemString = v.object({ - item: v.optional(v.union([ - v.string(), - v.null() - ])), - error: v.optional(v.union([ - v.string(), - v.null() - ])), - hasError: v.optional(v.pipe(v.boolean(), v.readonly())) -}); - -export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ - foo: v.string(), - baz: v.string() -}); - -/** - * This is a model with one property containing an array - */ -export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ - prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), - propWithFile: v.optional(v.array(v.string())), - propWithNumber: v.optional(v.array(v.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const vModelFromZendesk = v.string(); - -/** - * This is a model with one boolean property - */ -export const vModelWithBoolean = v.object({ - prop: v.optional(v.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const vDeprecatedModel = v.object({ - prop: v.optional(v.string()) -}); - -export const vModelWithOneOfEnum = v.union([ - v.object({ - foo: v.picklist([ - 'Bar' - ]) - }), - v.object({ - foo: v.picklist([ - 'Baz' - ]) - }), - v.object({ - foo: v.picklist([ - 'Qux' - ]) - }), - v.object({ - content: v.pipe(v.string(), v.isoTimestamp()), - foo: v.picklist([ - 'Quux' - ]) - }), - v.object({ - content: v.tuple([ - v.pipe(v.string(), v.isoTimestamp()), - v.string() - ]), - foo: v.picklist([ - 'Corge' - ]) - }) -]); - -export const vPageable = v.object({ - page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), - size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), - sort: v.optional(v.array(v.string())) -}); - -/** - * A reusable request body - */ -export const vSimpleRequestBody = vModelWithString; - -/** - * A reusable request body - */ -export const vSimpleFormData = vModelWithString; - /** * Model with number-only name */ export const v400 = v.string(); /** - * This is a model with one string property - */ -export const vModelWithStringError = v.object({ - prop: v.optional(v.string()) -}); - -/** - * This is a complex dictionary - */ -export const vDictionaryWithArray = v.object({}); - -/** - * This is a model that extends another model - */ -export const vModelThatExtendsExtends = v.intersect([ - vModelWithString, - vModelThatExtends, - v.object({ - propExtendsC: v.optional(v.string()), - propExtendsD: v.optional(vModelWithString) - }) -]); - -/** - * This is a simple array with strings - */ -export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); - -/** - * Parameter with illegal characters - */ -export const vXFooBar = vModelWithString; - -/** - * External ref to shared model (A) - */ -export const vExternalRefA = vExternalSharedExternalSharedModel; - -/** - * External ref to shared model (B) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const vExternalRefB = vExternalSharedExternalSharedModel; +export const vCamelCaseCommentWithBreaks = v.pipe(v.number(), v.integer()); /** * Testing multiline comments in string: First line @@ -402,14 +69,14 @@ export const vSimpleBoolean = v.boolean(); export const vSimpleString = v.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const vSimpleFile = v.string(); +export const vNonAsciiStringæøåÆøÅöôêÊ字符串 = v.string(); /** - * This is a simple reference + * This is a simple file */ -export const vSimpleReference = vModelWithString; +export const vSimpleFile = v.string(); /** * This is a simple string @@ -466,14 +133,9 @@ export const vArrayWithNumbers = v.array(v.pipe(v.number(), v.integer())); export const vArrayWithBooleans = v.array(v.boolean()); /** - * This is a simple array with references - */ -export const vArrayWithReferences = v.array(vModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const vArrayWithArray = v.array(v.array(vModelWithString)); +export const vArrayWithStrings = v.optional(v.array(v.string()), ['test']); /** * This is a simple array with properties @@ -512,11 +174,6 @@ export const vDictionaryWithPropertiesAndAdditionalProperties = v.object({ bar: v.optional(v.boolean()) }); -/** - * This is a string reference - */ -export const vDictionaryWithReference = v.object({}); - /** * This is a string dictionary */ @@ -530,6 +187,64 @@ export const vDictionaryWithProperties = v.record(v.string(), v.object({ bar: v.optional(v.string()) })); +/** + * This is a model with one number property + */ +export const vModelWithInteger = v.object({ + prop: v.optional(v.pipe(v.number(), v.integer())) +}); + +/** + * This is a model with one boolean property + */ +export const vModelWithBoolean = v.object({ + prop: v.optional(v.boolean()) +}); + +/** + * This is a model with one string property + */ +export const vModelWithString = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a simple reference + */ +export const vSimpleReference = vModelWithString; + +/** + * This is a simple array with references + */ +export const vArrayWithReferences = v.array(vModelWithString); + +/** + * This is a simple array containing an array + */ +export const vArrayWithArray = v.array(v.array(vModelWithString)); + +/** + * This is a string reference + */ +export const vDictionaryWithReference = v.object({}); + +/** + * This is a complex dictionary + */ +export const vDictionaryWithArray = v.object({}); + +/** + * This is a model with one string property + */ +export const vModelWithStringError = v.object({ + prop: v.optional(v.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const vModelFromZendesk = v.string(); + /** * This is a model with one string property */ @@ -558,6 +273,27 @@ export const vModelWithNullableString = v.object({ ])) }); +/** + * This is a model with one enum + */ +export const vModelWithEnum = v.object({ + 'foo_bar-enum': v.optional(v.picklist([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: v.optional(v.picklist([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: v.optional(v.unknown()) +}); + /** * This is a model with one enum with escaped name */ @@ -595,10 +331,37 @@ export const vModelWithNestedEnums = v.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const vModelWithReference = v.object({ - prop: v.optional(vModelWithProperties) +export const vModelWithArray = v.object({ + prop: v.optional(v.array(vModelWithString)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const vModelWithDictionary = v.object({ + prop: v.optional(v.object({})) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const vDeprecatedModel = v.object({ + prop: v.optional(v.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const vModelWithCircularReference: v.GenericSchema = v.object({ + prop: v.optional(v.lazy(() => { + return vModelWithCircularReference; + })) }); /** @@ -626,6 +389,22 @@ export const vCompositionWithOneOfAnonymous = v.object({ ])) }); +/** + * Circle + */ +export const vModelCircle = v.object({ + kind: v.string(), + radius: v.optional(v.number()) +}); + +/** + * Square + */ +export const vModelSquare = v.object({ + kind: v.string(), + sideLength: v.optional(v.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -679,6 +458,13 @@ export const vCompositionWithNestedAnyAndTypeNull = v.object({ ])) }); +export const v3eNum1Период = v.picklist([ + 'Bird', + 'Dog' +]); + +export const vConstValue = v.literal('ConstValue'); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -766,6 +552,14 @@ export const vCompositionWithAnyOfAndNullable = v.object({ ])) }); +/** + * This is a base model with two simple optional properties + */ +export const vCompositionBaseModel = v.object({ + firstName: v.optional(v.string()), + lastname: v.optional(v.string()) +}); + /** * This is a model that extends the base model */ @@ -778,6 +572,34 @@ export const vCompositionExtendedModel = v.intersect([ }) ]); +/** + * This is a model with one nested property + */ +export const vModelWithProperties = v.object({ + required: v.string(), + requiredAndReadOnly: v.pipe(v.string(), v.readonly()), + requiredAndNullable: v.union([ + v.string(), + v.null() + ]), + string: v.optional(v.string()), + number: v.optional(v.number()), + boolean: v.optional(v.boolean()), + reference: v.optional(vModelWithString), + 'property with space': v.optional(v.string()), + default: v.optional(v.string()), + try: v.optional(v.string()), + '@namespace.string': v.optional(v.pipe(v.string(), v.readonly())), + '@namespace.integer': v.optional(v.pipe(v.pipe(v.number(), v.integer()), v.readonly())) +}); + +/** + * This is a model with one property containing a reference + */ +export const vModelWithReference = v.object({ + prop: v.optional(vModelWithProperties) +}); + /** * This is a model with one nested property */ @@ -815,13 +637,36 @@ export const vModelWithOrderedProperties = v.object({ }); /** - * This is a model with duplicated imports + * This is a model with duplicated imports + */ +export const vModelWithDuplicateImports = v.object({ + propA: v.optional(vModelWithString), + propB: v.optional(vModelWithString), + propC: v.optional(vModelWithString) +}); + +/** + * This is a model that extends another model + */ +export const vModelThatExtends = v.intersect([ + vModelWithString, + v.object({ + propExtendsA: v.optional(v.string()), + propExtendsB: v.optional(vModelWithString) + }) +]); + +/** + * This is a model that extends another model */ -export const vModelWithDuplicateImports = v.object({ - propA: v.optional(vModelWithString), - propB: v.optional(vModelWithString), - propC: v.optional(vModelWithString) -}); +export const vModelThatExtendsExtends = v.intersect([ + vModelWithString, + vModelThatExtends, + v.object({ + propExtendsC: v.optional(v.string()), + propExtendsD: v.optional(vModelWithString) + }) +]); /** * This is a model that contains a some patterns @@ -850,6 +695,12 @@ export const vDefault = v.object({ name: v.optional(v.string()) }); +export const vPageable = v.object({ + page: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(0)), 0), + size: v.optional(v.pipe(v.number(), v.integer(), v.minValue(-2147483648, 'Invalid value: Expected int32 to be >= -2^31'), v.maxValue(2147483647, 'Invalid value: Expected int32 to be <= 2^31-1'), v.minValue(1))), + sort: v.optional(v.array(v.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -886,23 +737,15 @@ export const vNestedAnyOfArraysNullable = v.object({ ])) }); -export const vCompositionWithOneOfAndProperties = v.intersect([ - v.union([ - v.object({ - foo: vSimpleParameter - }), - v.object({ - bar: vNonAsciiStringæøåÆøÅöôêÊ字符串 - }) - ]), +/** + * An object that can be null + */ +export const vNullableObject = v.optional(v.union([ v.object({ - baz: v.union([ - v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), - v.null() - ]), - qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) - }) -]); + foo: v.optional(v.string()) + }), + v.null() +]), null); /** * Some % character @@ -913,6 +756,54 @@ export const vModelWithNullableObject = v.object({ data: v.optional(vNullableObject) }); +export const vModelWithOneOfEnum = v.union([ + v.object({ + foo: v.picklist([ + 'Bar' + ]) + }), + v.object({ + foo: v.picklist([ + 'Baz' + ]) + }), + v.object({ + foo: v.picklist([ + 'Qux' + ]) + }), + v.object({ + content: v.pipe(v.string(), v.isoTimestamp()), + foo: v.picklist([ + 'Quux' + ]) + }), + v.object({ + content: v.tuple([ + v.pipe(v.string(), v.isoTimestamp()), + v.string() + ]), + foo: v.picklist([ + 'Corge' + ]) + }) +]); + +export const vModelWithNestedArrayEnumsDataFoo = v.picklist([ + 'foo', + 'bar' +]); + +export const vModelWithNestedArrayEnumsDataBar = v.picklist([ + 'baz', + 'qux' +]); + +export const vModelWithNestedArrayEnumsData = v.object({ + foo: v.optional(v.array(vModelWithNestedArrayEnumsDataFoo)), + bar: v.optional(v.array(vModelWithNestedArrayEnumsDataBar)) +}); + export const vModelWithNestedArrayEnums = v.object({ array_strings: v.optional(v.array(v.string())), data: v.optional(vModelWithNestedArrayEnumsData) @@ -922,6 +813,20 @@ export const vModelWithNestedCompositionEnums = v.object({ foo: v.optional(vModelWithNestedArrayEnumsDataFoo) }); +export const vModelWithReadOnlyAndWriteOnly = v.object({ + foo: v.string(), + bar: v.pipe(v.string(), v.readonly()) +}); + +/** + * This is a model with one property containing an array + */ +export const vModelWithArrayReadOnlyAndWriteOnly = v.object({ + prop: v.optional(v.array(vModelWithReadOnlyAndWriteOnly)), + propWithFile: v.optional(v.array(v.string())), + propWithNumber: v.optional(v.array(v.number())) +}); + export const vModelWithConstantSizeArray = v.tuple([ v.number(), v.number() @@ -969,17 +874,6 @@ export const vModelWithAnyOfConstantSizeArrayNullable = v.tuple([ ]) ]); -export const vModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = v.tuple([ - v.union([ - v.number(), - vImport - ]), - v.union([ - v.number(), - vImport - ]) -]); - export const vModelWithAnyOfConstantSizeArrayAndIntersect = v.tuple([ v.intersect([ v.number(), @@ -1002,20 +896,6 @@ export const vModelWithBackticksInDescription = v.object({ template: v.optional(v.string()) }); -export const vModelWithOneOfAndProperties = v.intersect([ - v.union([ - vSimpleParameter, - vNonAsciiStringæøåÆøÅöôêÊ字符串 - ]), - v.object({ - baz: v.union([ - v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), - v.null() - ]), - qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) - }) -]); - /** * Model used to test deduplication strategy (unused) */ @@ -1041,6 +921,22 @@ export const vDeleteFooData = v.string(); */ export const vDeleteFooData2 = v.string(); +/** + * Model with restricted keyword name + */ +export const vImport = v.string(); + +export const vModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = v.tuple([ + v.union([ + v.number(), + vImport + ]), + v.union([ + v.number(), + vImport + ]) +]); + export const vSchemaWithFormRestrictedKeys = v.object({ description: v.optional(v.string()), 'x-enum-descriptions': v.optional(v.string()), @@ -1063,6 +959,14 @@ export const vSchemaWithFormRestrictedKeys = v.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const vIoK8sApimachineryPkgApisMetaV1Preconditions = v.object({ + resourceVersion: v.optional(v.string()), + uid: v.optional(v.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1070,6 +974,8 @@ export const vIoK8sApimachineryPkgApisMetaV1DeleteOptions = v.object({ preconditions: v.optional(vIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const vAdditionalPropertiesUnknownIssue = v.object({}); + export const vAdditionalPropertiesUnknownIssue2 = v.object({}); export const vAdditionalPropertiesUnknownIssue3 = v.intersect([ @@ -1083,6 +989,28 @@ export const vAdditionalPropertiesIntegerIssue = v.object({ value: v.pipe(v.number(), v.integer()) }); +export const vGenericSchemaDuplicateIssue1SystemBoolean = v.object({ + item: v.optional(v.boolean()), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())), + data: v.optional(v.object({})) +}); + +export const vGenericSchemaDuplicateIssue1SystemString = v.object({ + item: v.optional(v.union([ + v.string(), + v.null() + ])), + error: v.optional(v.union([ + v.string(), + v.null() + ])), + hasError: v.optional(v.pipe(v.boolean(), v.readonly())) +}); + export const vOneOfAllOfIssue = v.union([ v.intersect([ v.union([ @@ -1094,6 +1022,21 @@ export const vOneOfAllOfIssue = v.union([ vGenericSchemaDuplicateIssue1SystemString ]); +export const vExternalSharedExternalSharedModel = v.object({ + id: v.string(), + name: v.optional(v.string()) +}); + +/** + * External ref to shared model (A) + */ +export const vExternalRefA = vExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const vExternalRefB = vExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1129,6 +1072,11 @@ export const vFileWritable = v.object({ mime: v.pipe(v.string(), v.minLength(1), v.maxLength(24)) }); +export const vModelWithReadOnlyAndWriteOnlyWritable = v.object({ + foo: v.string(), + baz: v.string() +}); + export const vAdditionalPropertiesUnknownIssueWritable = v.object({}); export const vGenericSchemaDuplicateIssue1SystemBooleanWritable = v.object({ @@ -1151,6 +1099,58 @@ export const vGenericSchemaDuplicateIssue1SystemStringWritable = v.object({ ])) }); +/** + * This is a reusable parameter + */ +export const vSimpleParameter = v.string(); + +export const vCompositionWithOneOfAndProperties = v.intersect([ + v.union([ + v.object({ + foo: vSimpleParameter + }), + v.object({ + bar: vNonAsciiStringæøåÆøÅöôêÊ字符串 + }) + ]), + v.object({ + baz: v.union([ + v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), + v.null() + ]), + qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) + }) +]); + +export const vModelWithOneOfAndProperties = v.intersect([ + v.union([ + vSimpleParameter, + vNonAsciiStringæøåÆøÅöôêÊ字符串 + ]), + v.object({ + baz: v.union([ + v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint16 to be >= 0'), v.maxValue(65535, 'Invalid value: Expected uint16 to be <= 2^16-1'), v.minValue(0)), + v.null() + ]), + qux: v.pipe(v.number(), v.integer(), v.minValue(0, 'Invalid value: Expected uint8 to be >= 0'), v.maxValue(255, 'Invalid value: Expected uint8 to be <= 2^8-1'), v.minValue(0)) + }) +]); + +/** + * Parameter with illegal characters + */ +export const vXFooBar = vModelWithString; + +/** + * A reusable request body + */ +export const vSimpleRequestBody = vModelWithString; + +/** + * A reusable request body + */ +export const vSimpleFormData = vModelWithString; + export const vExportData = v.object({ body: v.optional(v.never()), path: v.optional(v.never()), diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts index 22b9cd058..f0dfcc016 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-circular-ref/valibot.gen.ts @@ -8,6 +8,10 @@ export const vBar: v.GenericSchema = v.object({ }))) }); +export const vFoo = v.object({ + foo: v.optional(vBar) +}); + export const vBaz: v.GenericSchema = v.lazy(() => { return vQux; }); @@ -16,7 +20,3 @@ export const vBaz: v.GenericSchema = v.lazy(() => { * description caused circular reference error */ export const vQux = vBaz; - -export const vFoo = v.object({ - foo: v.optional(vBar) -}); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts index 938d20ba4..0f25138de 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-metadata/valibot.gen.ts @@ -2,6 +2,12 @@ import * as v from 'valibot'; +export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); + +export const vQux = v.record(v.string(), v.object({ + qux: v.optional(v.string()) +})); + /** * This is Foo schema. */ @@ -34,22 +40,16 @@ export const vBar = v.pipe(v.object({ description: 'This is Bar schema.' })); -export const vFoo2 = v.object({ - foo: v.optional(vBar) -}); - /** * This is Foo parameter. */ -export const vFoo3 = v.pipe(v.string(), v.metadata({ +export const vFoo2 = v.pipe(v.string(), v.metadata({ description: 'This is Foo parameter.' })); -export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); - -export const vQux = v.record(v.string(), v.object({ - qux: v.optional(v.string()) -})); +export const vFoo3 = v.object({ + foo: v.optional(vBar) +}); export const vPatchFooData = v.object({ body: v.object({ @@ -70,7 +70,7 @@ export const vPatchFooData = v.object({ }); export const vPostFooData = v.object({ - body: vFoo2, + body: vFoo3, path: v.optional(v.never()), query: v.optional(v.never()) }); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts index e86881ed3..72e55fa04 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-types/valibot.gen.ts @@ -2,6 +2,12 @@ import * as v from 'valibot'; +export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); + +export const vQux = v.record(v.string(), v.object({ + qux: v.optional(v.string()) +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const vBar = v.object({ foo: v.optional(vFoo) }); -export const vFoo2 = v.object({ - foo: v.optional(vBar) -}); - /** * This is Foo parameter. */ -export const vFoo3 = v.string(); - -export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); +export const vFoo2 = v.string(); -export const vQux = v.record(v.string(), v.object({ - qux: v.optional(v.string()) -})); +export const vFoo3 = v.object({ + foo: v.optional(vBar) +}); export const vPatchFooData = v.object({ body: v.object({ @@ -58,7 +58,7 @@ export const vPatchFooData = v.object({ }); export const vPostFooData = v.object({ - body: vFoo2, + body: vFoo3, path: v.optional(v.never()), query: v.optional(v.never()) }); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts index 0cf876256..3662ff489 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators-union-merge/valibot.gen.ts @@ -11,6 +11,13 @@ export const vContact = v.union([ }) ]); +export const vUser = v.intersect([ + vContact, + v.object({ + username: v.string() + }) +]); + export const vDogDetails = v.object({ breed: v.string(), barkVolume: v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(10)) @@ -25,13 +32,6 @@ export const vCatDetails = v.object({ purrs: v.boolean() }); -export const vUser = v.intersect([ - vContact, - v.object({ - username: v.string() - }) -]); - export const vPetStore = v.object({ animals: v.array(v.object({ name: v.string(), diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts index e86881ed3..72e55fa04 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/validators/valibot.gen.ts @@ -2,6 +2,12 @@ import * as v from 'valibot'; +export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); + +export const vQux = v.record(v.string(), v.object({ + qux: v.optional(v.string()) +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const vBar = v.object({ foo: v.optional(vFoo) }); -export const vFoo2 = v.object({ - foo: v.optional(vBar) -}); - /** * This is Foo parameter. */ -export const vFoo3 = v.string(); - -export const vBaz = v.optional(v.pipe(v.pipe(v.string(), v.regex(/foo\nbar/)), v.readonly()), 'baz'); +export const vFoo2 = v.string(); -export const vQux = v.record(v.string(), v.object({ - qux: v.optional(v.string()) -})); +export const vFoo3 = v.object({ + foo: v.optional(vBar) +}); export const vPatchFooData = v.object({ body: v.object({ @@ -58,7 +58,7 @@ export const vPatchFooData = v.object({ }); export const vPostFooData = v.object({ - body: vFoo2, + body: vFoo3, path: v.optional(v.never()), query: v.optional(v.never()) }); diff --git a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts index dcb8e8db7..68f1ac07c 100644 --- a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts +++ b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts @@ -40,13 +40,13 @@ export default defineConfig(() => { // 'dutchie.json', // 'invalid', // 'openai.yaml', - // 'full.yaml', + 'full.yaml', // 'opencode.yaml', // 'sdk-instance.yaml', // 'string-with-format.yaml', // 'transformers.json', // 'type-format.yaml', - 'validators.yaml', + // 'validators.yaml', // 'validators-circular-ref.json', // 'validators-circular-ref-2.yaml', // 'zoom-video-sdk.json', diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts index 52e5fbb4d..ef475d167 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import * as z from 'zod/v4-mini'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts index 8f4a8e569..ac1852b36 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/mini/default/zod.gen.ts @@ -2,100 +2,6 @@ import * as z from 'zod/v4-mini'; -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -149,13 +55,16 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + /** * This is a simple file */ export const zSimpleFile = z.string(); -export const zSimpleReference = zModelWithString; - /** * This is a simple string */ @@ -223,16 +132,6 @@ export const zArrayWithBooleans = z.array(z.boolean()); */ export const zArrayWithStrings = z.array(z.string()); -/** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array - */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); - /** * This is a simple array with properties */ @@ -246,16 +145,6 @@ export const zArrayWithProperties = z.array(z.object({ */ export const zDictionaryWithString = z.record(z.string(), z.string()); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -288,6 +177,42 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + /** * This is a model with one string property */ @@ -348,13 +273,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one property containing a reference - */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) -}); - /** * This is a model with one property containing an array */ @@ -371,6 +289,41 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +/** + * This is a model with one property containing a reference + */ +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); + /** * This is a model with one nested property */ @@ -407,6 +360,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -426,6 +395,37 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().check(z.regex(/aaa`bbb/))) }); +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts index a5674d6cc..c4af44ea7 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod'; -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts index e556e245c..2fe7ac3b5 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v3/default/zod.gen.ts @@ -2,98 +2,6 @@ import { z } from 'zod'; -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.string().optional() -}); - -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -export const zParameterActivityParams = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity_id: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zFailureFailure = z.object({ - error: z.string().optional(), - message: z.string().optional(), - reference_code: z.string().optional() -}); - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -147,13 +55,16 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + /** * This is a simple file */ export const zSimpleFile = z.string(); -export const zSimpleReference = zModelWithString; - /** * This is a simple string */ @@ -221,16 +132,6 @@ export const zArrayWithBooleans = z.array(z.boolean()); */ export const zArrayWithStrings = z.array(z.string()); -/** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array - */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); - /** * This is a simple array with properties */ @@ -244,16 +145,6 @@ export const zArrayWithProperties = z.array(z.object({ */ export const zDictionaryWithString = z.record(z.string()); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(zModelWithString); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -286,6 +177,42 @@ export const zModelWithBoolean = z.object({ prop: z.boolean().optional() }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + /** * This is a model with one string property */ @@ -346,13 +273,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.array(z.number().int()).optional() }); -/** - * This is a model with one property containing a reference - */ -export const zModelWithReference = z.object({ - prop: zModelWithProperties.optional() -}); - /** * This is a model with one property containing an array */ @@ -369,6 +289,39 @@ export const zModelWithDictionary = z.object({ prop: z.record(z.string()).optional() }); +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +/** + * This is a model with one property containing a reference + */ +export const zModelWithReference = z.object({ + prop: zModelWithProperties.optional() +}); + /** * This is a model with one nested property */ @@ -405,6 +358,22 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + export const zDefault = z.object({ name: z.string().optional() }); @@ -424,6 +393,37 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.string().regex(/aaa`bbb/).optional() }); +export const zParameterActivityParams = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity_id: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zFailureFailure = z.object({ + error: z.string().optional(), + message: z.string().optional(), + reference_code: z.string().optional() +}); + +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.string().optional() +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts index b523ec8bc..87e0c90b2 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod/v4'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts index 849675345..c6888a77b 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/2.0.x/v4/default/zod.gen.ts @@ -2,100 +2,6 @@ import { z } from 'zod/v4'; -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -149,13 +55,16 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + /** * This is a simple file */ export const zSimpleFile = z.string(); -export const zSimpleReference = zModelWithString; - /** * This is a simple string */ @@ -223,16 +132,6 @@ export const zArrayWithBooleans = z.array(z.boolean()); */ export const zArrayWithStrings = z.array(z.string()); -/** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array - */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); - /** * This is a simple array with properties */ @@ -246,16 +145,6 @@ export const zArrayWithProperties = z.array(z.object({ */ export const zDictionaryWithString = z.record(z.string(), z.string()); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -288,6 +177,42 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + /** * This is a model with one string property */ @@ -348,13 +273,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one property containing a reference - */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) -}); - /** * This is a model with one property containing an array */ @@ -371,6 +289,41 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +/** + * This is a model with one property containing a reference + */ +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); + /** * This is a model with one nested property */ @@ -407,6 +360,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -426,6 +395,37 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().regex(/aaa`bbb/)) }); +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts index 52e5fbb4d..ef475d167 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import * as z from 'zod/v4-mini'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts index e859963ad..9b3bf4571 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/circular/zod.gen.ts @@ -2,7 +2,7 @@ import * as z from 'zod/v4-mini'; -export const zBaz = z.object({ +export const zFoo = z.object({ get quux() { return z.optional(z.lazy((): any => { return zQuux; @@ -10,18 +10,35 @@ export const zBaz = z.object({ } }); -export const zCorge = z.object({ - baz: z.optional(z.array(zBaz)) +export const zBar = z.object({ + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { + return z.optional(z.lazy((): any => { + return zBaz; + })); + } }); -export const zFoo = z.object({ - quux: z.optional(zQuux) +export const zBaz = z.object({ + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); + } }); export const zQux = z.union([ z.intersection(z.object({ type: z.literal('struct') - }), zCorge), + }), z.lazy(() => { + return z.lazy((): any => { + return zCorge; + }); + })), z.intersection(z.object({ type: z.literal('array') }), zFoo) @@ -31,11 +48,6 @@ export const zQuux = z.object({ qux: z.optional(zQux) }); -export const zBar = z.object({ - get bar() { - return z.optional(z.lazy((): any => { - return zBar; - })); - }, - baz: z.optional(zBaz) +export const zCorge = z.object({ + baz: z.optional(z.array(zBaz)) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts index 0c02ccd61..8d5a0edfe 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/default/zod.gen.ts @@ -3,16 +3,9 @@ import * as z from 'zod/v4-mini'; /** - * This is a model with one string property + * Model with number-only name */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); +export const z400 = z.string(); /** * Testing multiline comments in string: First line @@ -22,320 +15,6 @@ export const zExternalSharedExternalSharedModel = z.object({ */ export const zCamelCaseCommentWithBreaks = z.int(); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - -/** - * Model with number-only name - */ -export const z400 = z.string(); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -390,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -479,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); /** * This is a simple array with properties @@ -536,11 +210,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -554,6 +223,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -582,6 +309,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -623,10 +371,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -654,6 +431,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -707,6 +500,15 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -792,17 +594,53 @@ export const zCompositionWithAnyOfAndNullable = z.object({ zModelWithArray, zModelWithDictionary, z.null() - ])) + ])) +}); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) }); /** - * This is a model that extends the base model + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -849,6 +687,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -876,6 +730,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -919,20 +779,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); + z.null() +]), null); /** * Some % character @@ -943,6 +798,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -952,6 +855,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -996,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1031,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1067,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1089,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1096,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1109,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1117,6 +1063,15 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1152,6 +1107,11 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1177,6 +1137,46 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts index 7e7a4bfd4..9ef941af5 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/mini/validators/zod.gen.ts @@ -2,6 +2,8 @@ import * as z from 'zod/v4-mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), @@ -23,5 +25,3 @@ export const zFoo = z._default(z.union([ export const zBar = z.object({ foo: z.optional(zFoo) }); - -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts index a5674d6cc..c4af44ea7 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod'; -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts index c4be6a26c..bcce4d295 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/circular/zod.gen.ts @@ -2,24 +2,33 @@ import { z } from 'zod'; -export const zBaz: z.AnyZodObject = z.object({ +export const zFoo: z.AnyZodObject = z.object({ quux: z.lazy(() => { return zQuux; }).optional() }); -export const zCorge = z.object({ - baz: z.array(zBaz).optional() +export const zBar: z.AnyZodObject = z.object({ + bar: z.lazy(() => { + return zBar; + }).optional(), + baz: z.lazy(() => { + return zBaz; + }).optional() }); -export const zFoo = z.object({ - quux: zQuux.optional() +export const zBaz: z.AnyZodObject = z.object({ + quux: z.lazy(() => { + return zQuux; + }).optional() }); -export const zQux = z.union([ +export const zQux: z.ZodTypeAny = z.union([ z.object({ type: z.literal('struct') - }).and(zCorge), + }).and(z.lazy(() => { + return zCorge; + })), z.object({ type: z.literal('array') }).and(zFoo) @@ -29,9 +38,6 @@ export const zQuux = z.object({ qux: zQux.optional() }); -export const zBar: z.AnyZodObject = z.object({ - bar: z.lazy(() => { - return zBar; - }).optional(), - baz: zBaz.optional() +export const zCorge = z.object({ + baz: z.array(zBaz).optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts index 045bff905..2f0f272c3 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/default/zod.gen.ts @@ -3,16 +3,9 @@ import { z } from 'zod'; /** - * This is a model with one string property + * Model with number-only name */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.string().optional() -}); +export const z400 = z.string(); /** * Testing multiline comments in string: First line @@ -22,318 +15,6 @@ export const zExternalSharedExternalSharedModel = z.object({ */ export const zCamelCaseCommentWithBreaks = z.number().int(); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string().datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - -/** - * Model with number-only name - */ -export const z400 = z.string(); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -388,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -477,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.number().int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -534,11 +210,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.boolean().optional() }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(zModelWithString); - /** * This is a string dictionary */ @@ -552,6 +223,64 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -580,6 +309,27 @@ export const zModelWithNullableString = z.object({ ]).optional() }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + /** * This is a model with one enum with escaped name */ @@ -621,10 +371,37 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: zModelWithProperties.optional() +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() }); /** @@ -652,6 +429,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -705,6 +498,15 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -790,17 +592,53 @@ export const zCompositionWithAnyOfAndNullable = z.object({ zModelWithArray, zModelWithDictionary, z.null() - ]).optional() + ]).optional() +}); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() }); /** - * This is a model that extends the base model + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: zModelWithProperties.optional() +}); /** * This is a model with one nested property @@ -847,6 +685,22 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + /** * This is a model that contains a some patterns */ @@ -874,6 +728,12 @@ export const zDefault = z.object({ name: z.string().optional() }); +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + /** * This is a free-form object without additionalProperties. */ @@ -917,20 +777,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.string().optional() }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -941,6 +796,54 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string().datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -950,6 +853,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: zModelWithNestedArrayEnumsDataFoo.optional() }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -994,17 +911,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1029,17 +935,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.string().optional() }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1065,6 +960,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.string().optional(), 'x-enum-descriptions': z.string().optional(), @@ -1087,6 +998,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1094,6 +1013,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1107,6 +1031,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1115,6 +1061,15 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.string().optional() +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1150,6 +1105,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1175,6 +1135,46 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts index d2fb1f1be..13daadc5e 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v3/validators/zod.gen.ts @@ -2,6 +2,8 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), @@ -19,5 +21,3 @@ export const zFoo: z.ZodTypeAny = z.union([ export const zBar = z.object({ foo: zFoo.optional() }); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts index b523ec8bc..87e0c90b2 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod/v4'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts index f28fd1739..073c23da8 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/circular/zod.gen.ts @@ -2,7 +2,7 @@ import { z } from 'zod/v4'; -export const zBaz = z.object({ +export const zFoo = z.object({ get quux() { return z.optional(z.lazy((): any => { return zQuux; @@ -10,18 +10,35 @@ export const zBaz = z.object({ } }); -export const zCorge = z.object({ - baz: z.optional(z.array(zBaz)) +export const zBar = z.object({ + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { + return z.optional(z.lazy((): any => { + return zBaz; + })); + } }); -export const zFoo = z.object({ - quux: z.optional(zQuux) +export const zBaz = z.object({ + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); + } }); export const zQux = z.union([ z.object({ type: z.literal('struct') - }).and(zCorge), + }).and(z.lazy(() => { + return z.lazy((): any => { + return zCorge; + }); + })), z.object({ type: z.literal('array') }).and(zFoo) @@ -31,11 +48,6 @@ export const zQuux = z.object({ qux: z.optional(zQux) }); -export const zBar = z.object({ - get bar() { - return z.optional(z.lazy((): any => { - return zBar; - })); - }, - baz: z.optional(zBaz) +export const zCorge = z.object({ + baz: z.optional(z.array(zBaz)) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts index 846fe2eed..eb441d3bb 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/default/zod.gen.ts @@ -3,16 +3,9 @@ import { z } from 'zod/v4'; /** - * This is a model with one string property + * Model with number-only name */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); +export const z400 = z.string(); /** * Testing multiline comments in string: First line @@ -22,320 +15,6 @@ export const zExternalSharedExternalSharedModel = z.object({ */ export const zCamelCaseCommentWithBreaks = z.int(); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - -/** - * Model with number-only name - */ -export const z400 = z.string(); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -390,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -479,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -536,11 +210,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -554,6 +223,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -582,6 +309,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -623,10 +371,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -654,6 +431,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -707,6 +500,15 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -792,17 +594,53 @@ export const zCompositionWithAnyOfAndNullable = z.object({ zModelWithArray, zModelWithDictionary, z.null() - ])) + ])) +}); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) }); /** - * This is a model that extends the base model + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -849,6 +687,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -876,6 +730,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -919,20 +779,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -943,6 +798,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -952,6 +855,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -996,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1031,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1067,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1089,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1096,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1109,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1117,6 +1063,15 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1152,6 +1107,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1177,6 +1137,46 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts index 59fe79b1c..529955454 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.0.x/v4/validators/zod.gen.ts @@ -2,6 +2,8 @@ import { z } from 'zod/v4'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), @@ -23,5 +25,3 @@ export const zFoo = z.union([ export const zBar = z.object({ foo: z.optional(zFoo) }); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts index 52e5fbb4d..ef475d167 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import * as z from 'zod/v4-mini'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts index ed15288d9..395a1fa84 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts @@ -2,349 +2,18 @@ import * as z from 'zod/v4-mini'; -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const zCamelCaseCommentWithBreaks = z.int(); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - /** * Model with number-only name */ export const z400 = z.string(); /** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * External ref to shared model (A) - */ -export const zExternalRefA = zExternalSharedExternalSharedModel; - -/** - * External ref to shared model (B) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** * Testing multiline comments in string: First line @@ -400,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -489,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); /** * This is a simple array with properties @@ -549,11 +213,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -567,6 +226,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -595,6 +312,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -636,10 +374,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -667,6 +434,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -720,6 +503,13 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -809,13 +599,49 @@ export const zCompositionWithAnyOfAndNullable = z.object({ }); /** - * This is a model that extends the base model + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +/** + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -862,6 +688,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -889,6 +731,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -928,20 +776,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); + z.null() +]), null); /** * Some % character @@ -952,6 +795,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -961,6 +852,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -1008,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1043,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1079,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1101,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1108,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1121,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1129,6 +1063,21 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1164,6 +1113,11 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1189,6 +1143,52 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts index a4ffcf30d..4c5643d93 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts @@ -10,6 +10,10 @@ export const zBar = z.object({ } }); +export const zFoo = z.object({ + foo: z.optional(zBar) +}); + export const zBaz = z.lazy((): any => { return zQux; }); @@ -18,7 +22,3 @@ export const zBaz = z.lazy((): any => { * description caused circular reference error */ export const zQux = zBaz; - -export const zFoo = z.object({ - foo: z.optional(zBar) -}); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts index d254a4bc5..b515009b6 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts @@ -2,6 +2,12 @@ import * as z from 'zod/v4-mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -64,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts index 2f15ff8c4..64973d66e 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts @@ -2,6 +2,12 @@ import * as z from 'zod/v4-mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -38,22 +44,16 @@ export const zBar = z.object({ description: 'This is Bar schema.' }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string().register(z.globalRegistry, { +export const zFoo2 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); - -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts index 03c756a5c..2b1c21593 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts @@ -2,6 +2,16 @@ import * as z from 'zod/v4-mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; + /** * This is Foo schema. */ @@ -34,28 +44,18 @@ export const zBar = z.object({ export type BarZodType = z.infer; -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - -export type FooZodType2 = z.infer; - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export type FooZodType3 = z.infer; +export const zFoo2 = z.string(); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); - -export type BazZodType = z.infer; +export type FooZodType2 = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType3 = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -76,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts index c087678cb..bd49adf30 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts @@ -11,6 +11,10 @@ export const zContact = z.union([ }) ]); +export const zUser = z.intersection(zContact, z.object({ + username: z.string() +})); + export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().check(z.gte(1), z.lte(10)) @@ -25,10 +29,6 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); -export const zUser = z.intersection(zContact, z.object({ - username: z.string() -})); - export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts index c685386d8..d3e255eb0 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators/zod.gen.ts @@ -2,6 +2,12 @@ import * as z from 'zod/v4-mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts index a5674d6cc..c4af44ea7 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod'; -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts index 02954f6d8..b0e99007a 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts @@ -2,347 +2,18 @@ import { z } from 'zod'; -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.string().optional() -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const zCamelCaseCommentWithBreaks = z.number().int(); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - /** * Model with number-only name */ export const z400 = z.string(); /** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * External ref to shared model (A) - */ -export const zExternalRefA = zExternalSharedExternalSharedModel; - -/** - * External ref to shared model (B) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.number().int(); /** * Testing multiline comments in string: First line @@ -398,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -487,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.number().int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -547,11 +213,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.boolean().optional() }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(zModelWithString); - /** * This is a string dictionary */ @@ -565,6 +226,64 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -593,6 +312,27 @@ export const zModelWithNullableString = z.object({ ]).optional() }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + /** * This is a model with one enum with escaped name */ @@ -634,10 +374,37 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: zModelWithProperties.optional() +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() }); /** @@ -665,6 +432,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -718,6 +501,13 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -807,13 +597,49 @@ export const zCompositionWithAnyOfAndNullable = z.object({ }); /** - * This is a model that extends the base model + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +/** + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: zModelWithProperties.optional() +}); /** * This is a model with one nested property @@ -860,6 +686,22 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + /** * This is a model that contains a some patterns */ @@ -887,6 +729,12 @@ export const zDefault = z.object({ name: z.string().optional() }); +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + /** * This is a free-form object without additionalProperties. */ @@ -926,20 +774,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.string().optional() }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -950,6 +793,54 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -959,6 +850,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: zModelWithNestedArrayEnumsDataFoo.optional() }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -1006,17 +911,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1041,17 +935,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.string().optional() }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1077,6 +960,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.string().optional(), 'x-enum-descriptions': z.string().optional(), @@ -1099,6 +998,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1106,6 +1013,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1119,6 +1031,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1127,6 +1061,21 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.string().optional() +}); + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1162,6 +1111,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1187,6 +1141,52 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts index bb9456cac..57536e866 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts @@ -8,6 +8,10 @@ export const zBar: z.AnyZodObject = z.object({ })).optional() }); +export const zFoo = z.object({ + foo: zBar.optional() +}); + export const zBaz: z.ZodTypeAny = z.lazy(() => { return zQux; }); @@ -16,7 +20,3 @@ export const zBaz: z.ZodTypeAny = z.lazy(() => { * description caused circular reference error */ export const zQux = zBaz; - -export const zFoo = z.object({ - foo: zBar.optional() -}); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts index 4fa8604d2..620a0a864 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const zBar = z.object({ foo: zFoo.optional() }); -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +60,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts index 98c61ab68..4a09748c7 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const zBar = z.object({ foo: zFoo.optional() }).describe('This is Bar schema.'); -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string().describe('This is Foo parameter.'); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string().describe('This is Foo parameter.'); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts index 0a78b1a84..d79d307e9 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts @@ -2,6 +2,16 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export type BazZodType = z.infer; + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + +export type QuxZodType = z.infer; + /** * This is Foo schema. */ @@ -30,28 +40,18 @@ export const zBar = z.object({ export type BarZodType = z.infer; -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - -export type FooZodType2 = z.infer; - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export type FooZodType3 = z.infer; +export const zFoo2 = z.string(); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); - -export type BazZodType = z.infer; +export type FooZodType2 = z.infer; -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); -export type QuxZodType = z.infer; +export type FooZodType3 = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +72,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts index cd4b38eb3..83c308d68 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts @@ -11,6 +11,10 @@ export const zContact = z.union([ }) ]); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.number().int().gte(1).lte(10) @@ -25,10 +29,6 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts index c92aaa8c3..bcda6a22d 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const zBar = z.object({ foo: zFoo.optional() }); -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts index b523ec8bc..87e0c90b2 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod/v4'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts index 6a8ba44ab..ffd280f68 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts @@ -2,349 +2,18 @@ import { z } from 'zod/v4'; -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const zCamelCaseCommentWithBreaks = z.int(); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - /** * Model with number-only name */ export const z400 = z.string(); /** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * External ref to shared model (A) - */ -export const zExternalRefA = zExternalSharedExternalSharedModel; - -/** - * External ref to shared model (B) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** * Testing multiline comments in string: First line @@ -400,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -489,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -549,11 +213,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -567,6 +226,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -595,6 +312,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -636,10 +374,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -667,6 +434,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -720,6 +503,13 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -809,13 +599,49 @@ export const zCompositionWithAnyOfAndNullable = z.object({ }); /** - * This is a model that extends the base model + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +/** + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -862,6 +688,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -889,6 +731,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -928,20 +776,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -952,6 +795,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -961,6 +852,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -1008,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1043,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1079,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1101,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1108,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1121,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1129,6 +1063,21 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1164,6 +1113,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1189,6 +1143,52 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts index 03f609684..0fba2fc8b 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts @@ -10,6 +10,10 @@ export const zBar = z.object({ } }); +export const zFoo = z.object({ + foo: z.optional(zBar) +}); + export const zBaz = z.lazy((): any => { return zQux; }); @@ -18,7 +22,3 @@ export const zBaz = z.lazy((): any => { * description caused circular reference error */ export const zQux = zBaz; - -export const zFoo = z.object({ - foo: z.optional(zBar) -}); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts index 5ec778bc0..36e6f721a 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod/v4'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -64,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts index 8a1b9d745..270bc9a0d 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod/v4'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -38,22 +44,16 @@ export const zBar = z.object({ description: 'This is Bar schema.' }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string().register(z.globalRegistry, { +export const zFoo2 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); - -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts index 9640f042c..c36f93b32 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts @@ -2,6 +2,16 @@ import { z } from 'zod/v4'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; + /** * This is Foo schema. */ @@ -34,28 +44,18 @@ export const zBar = z.object({ export type BarZodType = z.infer; -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - -export type FooZodType2 = z.infer; - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export type FooZodType3 = z.infer; +export const zFoo2 = z.string(); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); - -export type BazZodType = z.infer; +export type FooZodType2 = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType3 = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -76,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts index 5a6d291d5..197f5de1d 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts @@ -11,6 +11,10 @@ export const zContact = z.union([ }) ]); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().gte(1).lte(10) @@ -25,10 +29,6 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts index a06a056ec..2b3781398 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod/v4'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts index ba4358862..95f43fa31 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import * as z from 'zod/mini'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts index 41b3cb99a..e2f4e7158 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/mini/default/zod.gen.ts @@ -2,100 +2,6 @@ import * as z from 'zod/mini'; -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -149,13 +55,16 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + /** * This is a simple file */ export const zSimpleFile = z.string(); -export const zSimpleReference = zModelWithString; - /** * This is a simple string */ @@ -223,16 +132,6 @@ export const zArrayWithBooleans = z.array(z.boolean()); */ export const zArrayWithStrings = z.array(z.string()); -/** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array - */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); - /** * This is a simple array with properties */ @@ -246,16 +145,6 @@ export const zArrayWithProperties = z.array(z.object({ */ export const zDictionaryWithString = z.record(z.string(), z.string()); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -288,6 +177,42 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + /** * This is a model with one string property */ @@ -348,13 +273,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one property containing a reference - */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) -}); - /** * This is a model with one property containing an array */ @@ -371,6 +289,41 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +/** + * This is a model with one property containing a reference + */ +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); + /** * This is a model with one nested property */ @@ -407,6 +360,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -426,6 +395,37 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().check(z.regex(/aaa`bbb/))) }); +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts index 5de251bf9..b5f786712 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod/v3'; -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts index fcd7bfbfe..aa5e6cbd5 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v3/default/zod.gen.ts @@ -2,98 +2,6 @@ import { z } from 'zod/v3'; -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.string().optional() -}); - -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -export const zParameterActivityParams = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.string().optional(), - graduate_id: z.number().int().optional(), - organization_id: z.number().int().optional(), - parent_activity_id: z.number().int().optional(), - post_id: z.number().int().optional() -}); - -export const zFailureFailure = z.object({ - error: z.string().optional(), - message: z.string().optional(), - reference_code: z.string().optional() -}); - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -147,13 +55,16 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + /** * This is a simple file */ export const zSimpleFile = z.string(); -export const zSimpleReference = zModelWithString; - /** * This is a simple string */ @@ -221,16 +132,6 @@ export const zArrayWithBooleans = z.array(z.boolean()); */ export const zArrayWithStrings = z.array(z.string()); -/** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array - */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); - /** * This is a simple array with properties */ @@ -244,16 +145,6 @@ export const zArrayWithProperties = z.array(z.object({ */ export const zDictionaryWithString = z.record(z.string()); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(zModelWithString); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -286,6 +177,42 @@ export const zModelWithBoolean = z.object({ prop: z.boolean().optional() }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + /** * This is a model with one string property */ @@ -346,13 +273,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.array(z.number().int()).optional() }); -/** - * This is a model with one property containing a reference - */ -export const zModelWithReference = z.object({ - prop: zModelWithProperties.optional() -}); - /** * This is a model with one property containing an array */ @@ -369,6 +289,39 @@ export const zModelWithDictionary = z.object({ prop: z.record(z.string()).optional() }); +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +/** + * This is a model with one property containing a reference + */ +export const zModelWithReference = z.object({ + prop: zModelWithProperties.optional() +}); + /** * This is a model with one nested property */ @@ -405,6 +358,22 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + export const zDefault = z.object({ name: z.string().optional() }); @@ -424,6 +393,37 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.string().regex(/aaa`bbb/).optional() }); +export const zParameterActivityParams = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.string().optional(), + graduate_id: z.number().int().optional(), + organization_id: z.number().int().optional(), + parent_activity_id: z.number().int().optional(), + post_id: z.number().int().optional() +}); + +export const zFailureFailure = z.object({ + error: z.string().optional(), + message: z.string().optional(), + reference_code: z.string().optional() +}); + +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.string().optional() +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts index 48d3f5740..a17f5ae50 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts index b910804c8..9e1e6dbad 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/2.0.x/v4/default/zod.gen.ts @@ -2,100 +2,6 @@ import { z } from 'zod'; -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -export const zParameterActivityParams = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zResponsePostActivityResponse = z.object({ - description: z.optional(z.string()), - graduate_id: z.optional(z.int()), - organization_id: z.optional(z.int()), - parent_activity_id: z.optional(z.int()), - post_id: z.optional(z.int()) -}); - -export const zFailureFailure = z.object({ - error: z.optional(z.string()), - message: z.optional(z.string()), - reference_code: z.optional(z.string()) -}); - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -149,13 +55,16 @@ export const zSimpleBoolean = z.boolean(); */ export const zSimpleString = z.string(); +/** + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) + */ +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); + /** * This is a simple file */ export const zSimpleFile = z.string(); -export const zSimpleReference = zModelWithString; - /** * This is a simple string */ @@ -223,16 +132,6 @@ export const zArrayWithBooleans = z.array(z.boolean()); */ export const zArrayWithStrings = z.array(z.string()); -/** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array - */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); - /** * This is a simple array with properties */ @@ -246,16 +145,6 @@ export const zArrayWithProperties = z.array(z.object({ */ export const zDictionaryWithString = z.record(z.string(), z.string()); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - /** * This is a string dictionary */ @@ -288,6 +177,42 @@ export const zModelWithBoolean = z.object({ prop: z.optional(z.boolean()) }); +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + /** * This is a model with one string property */ @@ -348,13 +273,6 @@ export const zModelWithNestedEnums = z.object({ arrayWithDescription: z.optional(z.array(z.int())) }); -/** - * This is a model with one property containing a reference - */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) -}); - /** * This is a model with one property containing an array */ @@ -371,6 +289,41 @@ export const zModelWithDictionary = z.object({ prop: z.optional(z.record(z.string(), z.string())) }); +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } +}); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +/** + * This is a model with one property containing a reference + */ +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); + /** * This is a model with one nested property */ @@ -407,6 +360,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + export const zDefault = z.object({ name: z.optional(z.string()) }); @@ -426,6 +395,37 @@ export const zModelWithPattern = z.object({ patternWithBacktick: z.optional(z.string().regex(/aaa`bbb/)) }); +export const zParameterActivityParams = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zResponsePostActivityResponse = z.object({ + description: z.optional(z.string()), + graduate_id: z.optional(z.int()), + organization_id: z.optional(z.int()), + parent_activity_id: z.optional(z.int()), + post_id: z.optional(z.int()) +}); + +export const zFailureFailure = z.object({ + error: z.optional(z.string()), + message: z.optional(z.string()), + reference_code: z.optional(z.string()) +}); + +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts index ba4358862..95f43fa31 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import * as z from 'zod/mini'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts index 7fa128445..0c5d13033 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/circular/zod.gen.ts @@ -2,7 +2,7 @@ import * as z from 'zod/mini'; -export const zBaz = z.object({ +export const zFoo = z.object({ get quux() { return z.optional(z.lazy((): any => { return zQuux; @@ -10,18 +10,35 @@ export const zBaz = z.object({ } }); -export const zCorge = z.object({ - baz: z.optional(z.array(zBaz)) +export const zBar = z.object({ + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { + return z.optional(z.lazy((): any => { + return zBaz; + })); + } }); -export const zFoo = z.object({ - quux: z.optional(zQuux) +export const zBaz = z.object({ + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); + } }); export const zQux = z.union([ z.intersection(z.object({ type: z.literal('struct') - }), zCorge), + }), z.lazy(() => { + return z.lazy((): any => { + return zCorge; + }); + })), z.intersection(z.object({ type: z.literal('array') }), zFoo) @@ -31,11 +48,6 @@ export const zQuux = z.object({ qux: z.optional(zQux) }); -export const zBar = z.object({ - get bar() { - return z.optional(z.lazy((): any => { - return zBar; - })); - }, - baz: z.optional(zBaz) +export const zCorge = z.object({ + baz: z.optional(z.array(zBaz)) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts index 4bab9551c..c90cb4a7c 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/default/zod.gen.ts @@ -3,16 +3,9 @@ import * as z from 'zod/mini'; /** - * This is a model with one string property + * Model with number-only name */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); +export const z400 = z.string(); /** * Testing multiline comments in string: First line @@ -22,320 +15,6 @@ export const zExternalSharedExternalSharedModel = z.object({ */ export const zCamelCaseCommentWithBreaks = z.int(); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - -/** - * Model with number-only name - */ -export const z400 = z.string(); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -390,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -479,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); /** * This is a simple array with properties @@ -536,11 +210,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -554,6 +223,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -582,6 +309,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -623,10 +371,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -654,6 +431,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -707,6 +500,15 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -792,17 +594,53 @@ export const zCompositionWithAnyOfAndNullable = z.object({ zModelWithArray, zModelWithDictionary, z.null() - ])) + ])) +}); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) }); /** - * This is a model that extends the base model + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -849,6 +687,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -876,6 +730,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -919,20 +779,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); + z.null() +]), null); /** * Some % character @@ -943,6 +798,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -952,6 +855,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -996,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1031,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1067,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1089,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1096,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1109,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1117,6 +1063,15 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1152,6 +1107,11 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1177,6 +1137,46 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts index a5ad31e3a..786ac3505 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/mini/validators/zod.gen.ts @@ -2,6 +2,8 @@ import * as z from 'zod/mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + export const zFoo = z._default(z.union([ z.object({ foo: z.optional(z.string().check(z.regex(/^\d{3}-\d{2}-\d{4}$/))), @@ -23,5 +25,3 @@ export const zFoo = z._default(z.union([ export const zBar = z.object({ foo: z.optional(zFoo) }); - -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts index 5de251bf9..b5f786712 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod/v3'; -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts index 6a90b5b65..6011ab3e7 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/circular/zod.gen.ts @@ -2,24 +2,33 @@ import { z } from 'zod/v3'; -export const zBaz: z.AnyZodObject = z.object({ +export const zFoo: z.AnyZodObject = z.object({ quux: z.lazy(() => { return zQuux; }).optional() }); -export const zCorge = z.object({ - baz: z.array(zBaz).optional() +export const zBar: z.AnyZodObject = z.object({ + bar: z.lazy(() => { + return zBar; + }).optional(), + baz: z.lazy(() => { + return zBaz; + }).optional() }); -export const zFoo = z.object({ - quux: zQuux.optional() +export const zBaz: z.AnyZodObject = z.object({ + quux: z.lazy(() => { + return zQuux; + }).optional() }); -export const zQux = z.union([ +export const zQux: z.ZodTypeAny = z.union([ z.object({ type: z.literal('struct') - }).and(zCorge), + }).and(z.lazy(() => { + return zCorge; + })), z.object({ type: z.literal('array') }).and(zFoo) @@ -29,9 +38,6 @@ export const zQuux = z.object({ qux: zQux.optional() }); -export const zBar: z.AnyZodObject = z.object({ - bar: z.lazy(() => { - return zBar; - }).optional(), - baz: zBaz.optional() +export const zCorge = z.object({ + baz: z.array(zBaz).optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts index 15140cf0e..196b7b1dd 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/default/zod.gen.ts @@ -3,16 +3,9 @@ import { z } from 'zod/v3'; /** - * This is a model with one string property + * Model with number-only name */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.string().optional() -}); +export const z400 = z.string(); /** * Testing multiline comments in string: First line @@ -22,318 +15,6 @@ export const zExternalSharedExternalSharedModel = z.object({ */ export const zCamelCaseCommentWithBreaks = z.number().int(); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string().datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - -/** - * Model with number-only name - */ -export const z400 = z.string(); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -388,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -477,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.number().int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -534,11 +210,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.boolean().optional() }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(zModelWithString); - /** * This is a string dictionary */ @@ -552,6 +223,64 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -580,6 +309,27 @@ export const zModelWithNullableString = z.object({ ]).optional() }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + /** * This is a model with one enum with escaped name */ @@ -621,10 +371,37 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: zModelWithProperties.optional() +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() }); /** @@ -652,6 +429,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -705,6 +498,15 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -790,17 +592,53 @@ export const zCompositionWithAnyOfAndNullable = z.object({ zModelWithArray, zModelWithDictionary, z.null() - ]).optional() + ]).optional() +}); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() }); /** - * This is a model that extends the base model + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: zModelWithProperties.optional() +}); /** * This is a model with one nested property @@ -847,6 +685,22 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + /** * This is a model that contains a some patterns */ @@ -874,6 +728,12 @@ export const zDefault = z.object({ name: z.string().optional() }); +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + /** * This is a free-form object without additionalProperties. */ @@ -917,20 +777,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.string().optional() }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -941,6 +796,54 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string().datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -950,6 +853,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: zModelWithNestedArrayEnumsDataFoo.optional() }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -994,17 +911,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1029,17 +935,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.string().optional() }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1065,6 +960,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.string().optional(), 'x-enum-descriptions': z.string().optional(), @@ -1087,6 +998,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1094,6 +1013,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1107,6 +1031,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1115,6 +1061,15 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.string().optional() +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1150,6 +1105,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1175,6 +1135,46 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts index 1712e5c8c..884fe8b89 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v3/validators/zod.gen.ts @@ -2,6 +2,8 @@ import { z } from 'zod/v3'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + export const zFoo: z.ZodTypeAny = z.union([ z.object({ foo: z.string().regex(/^\d{3}-\d{2}-\d{4}$/).optional(), @@ -19,5 +21,3 @@ export const zFoo: z.ZodTypeAny = z.union([ export const zBar = z.object({ foo: zFoo.optional() }); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts index 48d3f5740..a17f5ae50 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts index d8df5efc5..27ac2c0cc 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/circular/zod.gen.ts @@ -2,7 +2,7 @@ import { z } from 'zod'; -export const zBaz = z.object({ +export const zFoo = z.object({ get quux() { return z.optional(z.lazy((): any => { return zQuux; @@ -10,18 +10,35 @@ export const zBaz = z.object({ } }); -export const zCorge = z.object({ - baz: z.optional(z.array(zBaz)) +export const zBar = z.object({ + get bar() { + return z.optional(z.lazy((): any => { + return zBar; + })); + }, + get baz() { + return z.optional(z.lazy((): any => { + return zBaz; + })); + } }); -export const zFoo = z.object({ - quux: z.optional(zQuux) +export const zBaz = z.object({ + get quux() { + return z.optional(z.lazy((): any => { + return zQuux; + })); + } }); export const zQux = z.union([ z.object({ type: z.literal('struct') - }).and(zCorge), + }).and(z.lazy(() => { + return z.lazy((): any => { + return zCorge; + }); + })), z.object({ type: z.literal('array') }).and(zFoo) @@ -31,11 +48,6 @@ export const zQuux = z.object({ qux: z.optional(zQux) }); -export const zBar = z.object({ - get bar() { - return z.optional(z.lazy((): any => { - return zBar; - })); - }, - baz: z.optional(zBaz) +export const zCorge = z.object({ + baz: z.optional(z.array(zBaz)) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts index c0b339a66..1eeb7d801 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/default/zod.gen.ts @@ -3,16 +3,9 @@ import { z } from 'zod'; /** - * This is a model with one string property + * Model with number-only name */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); +export const z400 = z.string(); /** * Testing multiline comments in string: First line @@ -22,320 +15,6 @@ export const zExternalSharedExternalSharedModel = z.object({ */ export const zCamelCaseCommentWithBreaks = z.int(); -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.enum([ - 'ConstValue' -]); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.iso.datetime() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - -export const zSimpleRequestBody = zModelWithString; - -export const zSimpleFormData = zModelWithString; - -/** - * Model with number-only name - */ -export const z400 = z.string(); - -/** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -export const zExternalRefA = zExternalSharedExternalSharedModel; - -export const zExternalRefB = zExternalSharedExternalSharedModel; - /** * Testing multiline comments in string: First line * Second line @@ -390,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -479,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -536,11 +210,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -554,6 +223,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -582,6 +309,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -623,10 +371,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -654,6 +431,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -707,6 +500,15 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.enum([ + 'ConstValue' +]); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -792,17 +594,53 @@ export const zCompositionWithAnyOfAndNullable = z.object({ zModelWithArray, zModelWithDictionary, z.null() - ])) + ])) +}); + +/** + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) }); /** - * This is a model that extends the base model + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -849,6 +687,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -876,6 +730,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -919,20 +779,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -943,6 +798,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.iso.datetime() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -952,6 +855,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -996,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1031,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1067,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1089,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1096,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1109,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1117,6 +1063,15 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +export const zExternalRefA = zExternalSharedExternalSharedModel; + +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1152,6 +1107,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1177,6 +1137,46 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +export const zSimpleRequestBody = zModelWithString; + +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts index 7efe00035..f6b581f0d 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.0.x/v4/validators/zod.gen.ts @@ -2,6 +2,8 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + export const zFoo = z.union([ z.object({ foo: z.optional(z.string().regex(/^\d{3}-\d{2}-\d{4}$/)), @@ -23,5 +25,3 @@ export const zFoo = z.union([ export const zBar = z.object({ foo: z.optional(zFoo) }); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts index ba4358862..95f43fa31 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import * as z from 'zod/mini'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ id: z.optional(z.int()) }), z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.intersection(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(z.intersection(zBaseModel, z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts index 34f603bdb..e03857446 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts @@ -2,349 +2,18 @@ import * as z from 'zod/mini'; -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const zCamelCaseCommentWithBreaks = z.int(); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.readonly(z.string()), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.readonly(z.string())), - '@namespace.integer': z.optional(z.readonly(z.int())) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.readonly(z.string()) -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = z.intersection(zModelWithString, z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z._default(z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]), null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.readonly(z.boolean())) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z._default(z.optional(z.int().check(z.gte(0))), 0), - size: z.optional(z.int().check(z.gte(1))), - sort: z.optional(z.array(z.string())) -}); - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - /** * Model with number-only name */ export const z400 = z.string(); /** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * External ref to shared model (A) - */ -export const zExternalRefA = zExternalSharedExternalSharedModel; - -/** - * External ref to shared model (B) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** * Testing multiline comments in string: First line @@ -400,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -489,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z._default(z.array(z.string()), ['test']); /** * This is a simple array with properties @@ -549,11 +213,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -567,6 +226,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -595,6 +312,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -636,10 +374,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -667,6 +434,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -720,6 +503,13 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -809,13 +599,49 @@ export const zCompositionWithAnyOfAndNullable = z.object({ }); /** - * This is a model that extends the base model + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.readonly(z.string()), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.readonly(z.string())), + '@namespace.integer': z.optional(z.readonly(z.int())) +}); + +/** + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = z.intersection(zCompositionBaseModel, z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -862,6 +688,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = z.intersection(zModelWithString, z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = z.intersection(z.intersection(zModelWithString, zModelThatExtends), z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -889,6 +731,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z._default(z.optional(z.int().check(z.gte(0))), 0), + size: z.optional(z.int().check(z.gte(1))), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -928,20 +776,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z._default(z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); + z.null() +]), null); /** * Some % character @@ -952,6 +795,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -961,6 +852,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.readonly(z.string()) +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -1008,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1043,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().check(z.gte(0)), - z.null() - ]), - qux: z.int().check(z.gte(0)) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1079,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1101,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1108,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1121,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.readonly(z.boolean())) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1129,6 +1063,21 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1164,6 +1113,11 @@ export const zFileWritable = z.object({ mime: z.string().check(z.minLength(1), z.maxLength(24)) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1189,6 +1143,52 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().check(z.gte(0)), + z.null() + ]), + qux: z.int().check(z.gte(0)) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts index 51167f885..33a0a739a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-circular-ref/zod.gen.ts @@ -10,6 +10,10 @@ export const zBar = z.object({ } }); +export const zFoo = z.object({ + foo: z.optional(zBar) +}); + export const zBaz = z.lazy((): any => { return zQux; }); @@ -18,7 +22,3 @@ export const zBaz = z.lazy((): any => { * description caused circular reference error */ export const zQux = zBaz; - -export const zFoo = z.object({ - foo: z.optional(zBar) -}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts index beb6504c4..d64c84a81 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-dates/zod.gen.ts @@ -2,6 +2,12 @@ import * as z from 'zod/mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -64,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts index bdf38c095..aaf540431 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-metadata/zod.gen.ts @@ -2,6 +2,12 @@ import * as z from 'zod/mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -38,22 +44,16 @@ export const zBar = z.object({ description: 'This is Bar schema.' }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string().register(z.globalRegistry, { +export const zFoo2 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); - -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts index a7e423d5a..19e14da91 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-types/zod.gen.ts @@ -2,6 +2,16 @@ import * as z from 'zod/mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; + /** * This is Foo schema. */ @@ -34,28 +44,18 @@ export const zBar = z.object({ export type BarZodType = z.infer; -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - -export type FooZodType2 = z.infer; - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export type FooZodType3 = z.infer; +export const zFoo2 = z.string(); -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); - -export type BazZodType = z.infer; +export type FooZodType2 = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType3 = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -76,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts index 5f72e6c0a..8a820c15d 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-union-merge/zod.gen.ts @@ -11,6 +11,10 @@ export const zContact = z.union([ }) ]); +export const zUser = z.intersection(zContact, z.object({ + username: z.string() +})); + export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().check(z.gte(1), z.lte(10)) @@ -25,10 +29,6 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); -export const zUser = z.intersection(zContact, z.object({ - username: z.string() -})); - export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts index f61036bbf..65b32c7ba 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators/zod.gen.ts @@ -2,6 +2,12 @@ import * as z from 'zod/mini'; +export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z._default(z.readonly(z.string().check(z.regex(/foo\nbar/))), 'baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts index 5de251bf9..b5f786712 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod/v3'; -export const zBaseModel = z.object({ - id: z.number().int().optional(), - createdAt: z.string().datetime().optional() -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.number().int().optional() }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.number().int().optional(), + createdAt: z.string().datetime().optional() +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.string().optional() }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts index 5353180a0..6dfbc46f4 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts @@ -2,347 +2,18 @@ import { z } from 'zod/v3'; -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.string().optional() -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.string().optional() -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const zCamelCaseCommentWithBreaks = z.number().int(); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.string().optional(), - number: z.number().optional(), - boolean: z.boolean().optional(), - reference: zModelWithString.optional(), - 'property with space': z.string().optional(), - default: z.string().optional(), - try: z.string().optional(), - '@namespace.string': z.string().readonly().optional(), - '@namespace.integer': z.number().int().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference: z.AnyZodObject = z.object({ - prop: z.lazy(() => { - return zModelWithCircularReference; - }).optional() -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ]).optional(), - statusCode: z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ]).optional(), - bool: z.literal(true).optional() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.array(zModelWithString).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.record(z.string()).optional() -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.number().optional() -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.number().optional() -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.string().optional(), - lastname: z.string().optional() -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.string().optional(), - propExtendsB: zModelWithString.optional() -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.string().optional() - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), - bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.number().int().optional() -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.string().optional(), - uid: z.string().optional() -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.boolean().optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional(), - data: z.record(z.never()).optional() -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.union([ - z.string(), - z.null() - ]).optional(), - error: z.union([ - z.string(), - z.null() - ]).optional(), - hasError: z.boolean().readonly().optional() -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), - propWithFile: z.array(z.string()).optional(), - propWithNumber: z.array(z.number()).optional() -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.boolean().optional() -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.string().optional() -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.string().datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.string().datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.number().int().gte(0).optional().default(0), - size: z.number().int().gte(1).optional(), - sort: z.array(z.string()).optional() -}); - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - /** * Model with number-only name */ export const z400 = z.string(); /** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.string().optional() -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.string().optional(), - propExtendsD: zModelWithString.optional() -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * External ref to shared model (A) - */ -export const zExternalRefA = zExternalSharedExternalSharedModel; - -/** - * External ref to shared model (B) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.number().int(); /** * Testing multiline comments in string: First line @@ -398,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -487,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.number().int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -547,11 +213,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.boolean().optional() }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(zModelWithString); - /** * This is a string dictionary */ @@ -565,6 +226,64 @@ export const zDictionaryWithProperties = z.record(z.object({ bar: z.string().optional() })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.number().int().optional() +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.boolean().optional() +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.string().optional() +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.string().optional() +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -593,6 +312,27 @@ export const zModelWithNullableString = z.object({ ]).optional() }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ]).optional(), + statusCode: z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ]).optional(), + bool: z.literal(true).optional() +}); + /** * This is a model with one enum with escaped name */ @@ -634,10 +374,37 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: zModelWithProperties.optional() +export const zModelWithArray = z.object({ + prop: z.array(zModelWithString).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.record(z.string()).optional() +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.string().optional() +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference: z.AnyZodObject = z.object({ + prop: z.lazy(() => { + return zModelWithCircularReference; + }).optional() }); /** @@ -665,6 +432,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ]).optional() }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.number().optional() +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.number().optional() +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -718,6 +501,13 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ]).optional() }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -807,13 +597,49 @@ export const zCompositionWithAnyOfAndNullable = z.object({ }); /** - * This is a model that extends the base model + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.string().optional(), + lastname: z.string().optional() +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.string().optional(), + number: z.number().optional(), + boolean: z.boolean().optional(), + reference: zModelWithString.optional(), + 'property with space': z.string().optional(), + default: z.string().optional(), + try: z.string().optional(), + '@namespace.string': z.string().readonly().optional(), + '@namespace.integer': z.number().int().readonly().optional() +}); + +/** + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: zModelWithProperties.optional() +}); /** * This is a model with one nested property @@ -860,6 +686,22 @@ export const zModelWithDuplicateImports = z.object({ propC: zModelWithString.optional() }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.string().optional(), + propExtendsB: zModelWithString.optional() +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.string().optional(), + propExtendsD: zModelWithString.optional() +})); + /** * This is a model that contains a some patterns */ @@ -887,6 +729,12 @@ export const zDefault = z.object({ name: z.string().optional() }); +export const zPageable = z.object({ + page: z.number().int().gte(0).optional().default(0), + size: z.number().int().gte(1).optional(), + sort: z.array(z.string()).optional() +}); + /** * This is a free-form object without additionalProperties. */ @@ -926,20 +774,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ]).optional() }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.string().optional() }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -950,6 +793,54 @@ export const zModelWithNullableObject = z.object({ data: zNullableObject.optional() }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.string().datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.string().datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.array(zModelWithNestedArrayEnumsDataFoo).optional(), + bar: z.array(zModelWithNestedArrayEnumsDataBar).optional() +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.array(z.string()).optional(), data: zModelWithNestedArrayEnumsData.optional() @@ -959,6 +850,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: zModelWithNestedArrayEnumsDataFoo.optional() }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.array(zModelWithReadOnlyAndWriteOnly).optional(), + propWithFile: z.array(z.string()).optional(), + propWithNumber: z.array(z.number()).optional() +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -1006,17 +911,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1041,17 +935,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.string().optional() }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.number().int().gte(0), - z.null() - ]), - qux: z.number().int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1077,6 +960,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.string().optional(), 'x-enum-descriptions': z.string().optional(), @@ -1099,6 +998,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ })).optional() }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.string().optional(), + uid: z.string().optional() +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1106,6 +1013,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: zIoK8sApimachineryPkgApisMetaV1Preconditions.optional() }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.union([ z.string(), z.number() @@ -1119,6 +1031,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.number().int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.boolean().optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional(), + data: z.record(z.never()).optional() +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.union([ + z.string(), + z.null() + ]).optional(), + error: z.union([ + z.string(), + z.null() + ]).optional(), + hasError: z.boolean().readonly().optional() +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1127,6 +1061,21 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.string().optional() +}); + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1162,6 +1111,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.union([ z.string(), z.number() @@ -1187,6 +1141,52 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ]).optional() }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.number().int().gte(0), + z.null() + ]), + qux: z.number().int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.never().optional(), path: z.never().optional(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts index 9c341db5a..466a9fc5b 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-circular-ref/zod.gen.ts @@ -8,6 +8,10 @@ export const zBar: z.AnyZodObject = z.object({ })).optional() }); +export const zFoo = z.object({ + foo: zBar.optional() +}); + export const zBaz: z.ZodTypeAny = z.lazy(() => { return zQux; }); @@ -16,7 +20,3 @@ export const zBaz: z.ZodTypeAny = z.lazy(() => { * description caused circular reference error */ export const zQux = zBaz; - -export const zFoo = z.object({ - foo: zBar.optional() -}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts index f07b8c7d5..746c1c574 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-dates/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod/v3'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const zBar = z.object({ foo: zFoo.optional() }); -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); export const zPatchFooData = z.object({ body: z.object({ @@ -60,7 +60,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts index 531962b73..396361b2a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-metadata/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod/v3'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const zBar = z.object({ foo: zFoo.optional() }).describe('This is Bar schema.'); -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string().describe('This is Foo parameter.'); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string().describe('This is Foo parameter.'); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts index c0eeb2540..7b92df5bc 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-types/zod.gen.ts @@ -2,6 +2,16 @@ import { z } from 'zod/v3'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export type BazZodType = z.infer; + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + +export type QuxZodType = z.infer; + /** * This is Foo schema. */ @@ -30,28 +40,18 @@ export const zBar = z.object({ export type BarZodType = z.infer; -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - -export type FooZodType2 = z.infer; - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export type FooZodType3 = z.infer; +export const zFoo2 = z.string(); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); - -export type BazZodType = z.infer; +export type FooZodType2 = z.infer; -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); -export type QuxZodType = z.infer; +export type FooZodType3 = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -72,7 +72,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts index 22c2a127d..754b9133a 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-union-merge/zod.gen.ts @@ -11,6 +11,10 @@ export const zContact = z.union([ }) ]); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.number().int().gte(1).lte(10) @@ -25,10 +29,6 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts index e4b5555a9..d4c6fe536 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod/v3'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.object({ + qux: z.string().optional() +})); + /** * This is Foo schema. */ @@ -26,20 +32,14 @@ export const zBar = z.object({ foo: zFoo.optional() }); -export const zFoo2 = z.object({ - foo: zBar.optional() -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.object({ - qux: z.string().optional() -})); +export const zFoo3 = z.object({ + foo: zBar.optional() +}); export const zPatchFooData = z.object({ body: z.object({ @@ -58,7 +58,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.never().optional(), query: z.never().optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts index 48d3f5740..a17f5ae50 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/array-items-all-of/zod.gen.ts @@ -2,11 +2,6 @@ import { z } from 'zod'; -export const zBaseModel = z.object({ - id: z.optional(z.int()), - createdAt: z.optional(z.iso.datetime()) -}); - export const zArrayWithAllOfObjects = z.array(z.object({ id: z.optional(z.int()) }).and(z.object({ @@ -15,6 +10,11 @@ export const zArrayWithAllOfObjects = z.array(z.object({ export const zArrayWithAllOfPrimitives = z.array(z.intersection(z.number(), z.string())); +export const zBaseModel = z.object({ + id: z.optional(z.int()), + createdAt: z.optional(z.iso.datetime()) +}); + export const zArrayWithAllOfRefs = z.array(zBaseModel.and(z.object({ extra: z.optional(z.string()) }))); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts index aeea9df12..2602aa743 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts @@ -2,349 +2,18 @@ import { z } from 'zod'; -/** - * This is a model with one string property - */ -export const zModelWithString = z.object({ - prop: z.optional(z.string()) -}); - -export const zExternalSharedExternalSharedModel = z.object({ - id: z.string(), - name: z.optional(z.string()) -}); - -/** - * Testing multiline comments in string: First line - * Second line - * - * Fourth line - */ -export const zCamelCaseCommentWithBreaks = z.int(); - -/** - * This is a model with one nested property - */ -export const zModelWithProperties = z.object({ - required: z.string(), - requiredAndReadOnly: z.string().readonly(), - requiredAndNullable: z.union([ - z.string(), - z.null() - ]), - string: z.optional(z.string()), - number: z.optional(z.number()), - boolean: z.optional(z.boolean()), - reference: z.optional(zModelWithString), - 'property with space': z.optional(z.string()), - default: z.optional(z.string()), - try: z.optional(z.string()), - '@namespace.string': z.optional(z.string().readonly()), - '@namespace.integer': z.optional(z.int().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnly = z.object({ - foo: z.string(), - bar: z.string().readonly() -}); - -/** - * This is a model with one property containing a circular reference - */ -export const zModelWithCircularReference = z.object({ - get prop() { - return z.optional(z.lazy((): any => { - return zModelWithCircularReference; - })); - } -}); - -/** - * This is a model with one enum - */ -export const zModelWithEnum = z.object({ - 'foo_bar-enum': z.optional(z.enum([ - 'Success', - 'Warning', - 'Error', - 'ØÆÅ字符串' - ])), - statusCode: z.optional(z.enum([ - '100', - '200 FOO', - '300 FOO_BAR', - '400 foo-bar', - '500 foo.bar', - '600 foo&bar' - ])), - bool: z.optional(z.literal(true)) -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArray = z.object({ - prop: z.optional(z.array(zModelWithString)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * This is a model with one property containing a dictionary - */ -export const zModelWithDictionary = z.object({ - prop: z.optional(z.record(z.string(), z.string())) -}); - -/** - * Circle - */ -export const zModelCircle = z.object({ - kind: z.string(), - radius: z.optional(z.number()) -}); - -/** - * Square - */ -export const zModelSquare = z.object({ - kind: z.string(), - sideLength: z.optional(z.number()) -}); - -export const z3eNum1Период = z.enum([ - 'Bird', - 'Dog' -]); - -export const zConstValue = z.literal('ConstValue'); - -/** - * This is a base model with two simple optional properties - */ -export const zCompositionBaseModel = z.object({ - firstName: z.optional(z.string()), - lastname: z.optional(z.string()) -}); - -/** - * This is a model that extends another model - */ -export const zModelThatExtends = zModelWithString.and(z.object({ - propExtendsA: z.optional(z.string()), - propExtendsB: z.optional(zModelWithString) -})); - -/** - * This is a reusable parameter - */ -export const zSimpleParameter = z.string(); - -/** - * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) - */ -export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); - -/** - * An object that can be null - */ -export const zNullableObject = z.union([ - z.object({ - foo: z.optional(z.string()) - }), - z.null() -]).default(null); - -export const zModelWithNestedArrayEnumsDataFoo = z.enum([ - 'foo', - 'bar' -]); - -export const zModelWithNestedArrayEnumsDataBar = z.enum([ - 'baz', - 'qux' -]); - -export const zModelWithNestedArrayEnumsData = z.object({ - foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), - bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) -}); - -/** - * This is a model with one number property - */ -export const zModelWithInteger = z.object({ - prop: z.optional(z.int()) -}); - -/** - * Model with restricted keyword name - */ -export const zImport = z.string(); - -/** - * This schema was giving PascalCase transformations a hard time - */ -export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ - resourceVersion: z.optional(z.string()), - uid: z.optional(z.string()) -}); - -export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ - z.string(), - z.number() -])); - -export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ - item: z.optional(z.boolean()), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()), - data: z.optional(z.record(z.string(), z.never())) -}); - -export const zGenericSchemaDuplicateIssue1SystemString = z.object({ - item: z.optional(z.union([ - z.string(), - z.null() - ])), - error: z.optional(z.union([ - z.string(), - z.null() - ])), - hasError: z.optional(z.boolean().readonly()) -}); - -export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ - foo: z.string(), - baz: z.string() -}); - -/** - * This is a model with one property containing an array - */ -export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ - prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), - propWithFile: z.optional(z.array(z.string())), - propWithNumber: z.optional(z.array(z.number())) -}); - -/** - * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) - */ -export const zModelFromZendesk = z.string(); - -/** - * This is a model with one boolean property - */ -export const zModelWithBoolean = z.object({ - prop: z.optional(z.boolean()) -}); - -/** - * This is a deprecated model with a deprecated property - * - * @deprecated - */ -export const zDeprecatedModel = z.object({ - prop: z.optional(z.string()) -}); - -export const zModelWithOneOfEnum = z.union([ - z.object({ - foo: z.enum([ - 'Bar' - ]) - }), - z.object({ - foo: z.enum([ - 'Baz' - ]) - }), - z.object({ - foo: z.enum([ - 'Qux' - ]) - }), - z.object({ - content: z.iso.datetime(), - foo: z.enum([ - 'Quux' - ]) - }), - z.object({ - content: z.tuple([ - z.iso.datetime(), - z.string() - ]), - foo: z.enum([ - 'Corge' - ]) - }) -]); - -export const zPageable = z.object({ - page: z.optional(z.int().gte(0)).default(0), - size: z.optional(z.int().gte(1)), - sort: z.optional(z.array(z.string())) -}); - -/** - * A reusable request body - */ -export const zSimpleRequestBody = zModelWithString; - -/** - * A reusable request body - */ -export const zSimpleFormData = zModelWithString; - /** * Model with number-only name */ export const z400 = z.string(); /** - * This is a model with one string property - */ -export const zModelWithStringError = z.object({ - prop: z.optional(z.string()) -}); - -/** - * This is a complex dictionary - */ -export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); - -/** - * This is a model that extends another model - */ -export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ - propExtendsC: z.optional(z.string()), - propExtendsD: z.optional(zModelWithString) -})); - -/** - * This is a simple array with strings - */ -export const zArrayWithStrings = z.array(z.string()).default(['test']); - -/** - * Parameter with illegal characters - */ -export const zXFooBar = zModelWithString; - -/** - * External ref to shared model (A) - */ -export const zExternalRefA = zExternalSharedExternalSharedModel; - -/** - * External ref to shared model (B) + * Testing multiline comments in string: First line + * Second line + * + * Fourth line */ -export const zExternalRefB = zExternalSharedExternalSharedModel; +export const zCamelCaseCommentWithBreaks = z.int(); /** * Testing multiline comments in string: First line @@ -400,14 +69,14 @@ export const zSimpleBoolean = z.boolean(); export const zSimpleString = z.string(); /** - * This is a simple file + * A string with non-ascii (unicode) characters valid in typescript identifiers (æøåÆØÅöÔèÈ字符串) */ -export const zSimpleFile = z.string(); +export const zNonAsciiStringæøåÆøÅöôêÊ字符串 = z.string(); /** - * This is a simple reference + * This is a simple file */ -export const zSimpleReference = zModelWithString; +export const zSimpleFile = z.string(); /** * This is a simple string @@ -489,14 +158,9 @@ export const zArrayWithNumbers = z.array(z.int()); export const zArrayWithBooleans = z.array(z.boolean()); /** - * This is a simple array with references - */ -export const zArrayWithReferences = z.array(zModelWithString); - -/** - * This is a simple array containing an array + * This is a simple array with strings */ -export const zArrayWithArray = z.array(z.array(zModelWithString)); +export const zArrayWithStrings = z.array(z.string()).default(['test']); /** * This is a simple array with properties @@ -549,11 +213,6 @@ export const zDictionaryWithPropertiesAndAdditionalProperties = z.object({ bar: z.optional(z.boolean()) }); -/** - * This is a string reference - */ -export const zDictionaryWithReference = z.record(z.string(), zModelWithString); - /** * This is a string dictionary */ @@ -567,6 +226,64 @@ export const zDictionaryWithProperties = z.record(z.string(), z.object({ bar: z.optional(z.string()) })); +/** + * This is a model with one number property + */ +export const zModelWithInteger = z.object({ + prop: z.optional(z.int()) +}); + +/** + * This is a model with one boolean property + */ +export const zModelWithBoolean = z.object({ + prop: z.optional(z.boolean()) +}); + +/** + * This is a model with one string property + */ +export const zModelWithString = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a simple reference + */ +export const zSimpleReference = zModelWithString; + +/** + * This is a simple array with references + */ +export const zArrayWithReferences = z.array(zModelWithString); + +/** + * This is a simple array containing an array + */ +export const zArrayWithArray = z.array(z.array(zModelWithString)); + +/** + * This is a string reference + */ +export const zDictionaryWithReference = z.record(z.string(), zModelWithString); + +/** + * This is a complex dictionary + */ +export const zDictionaryWithArray = z.record(z.string(), z.array(zModelWithString)); + +/** + * This is a model with one string property + */ +export const zModelWithStringError = z.object({ + prop: z.optional(z.string()) +}); + +/** + * `Comment` or `VoiceComment`. The JSON object for adding voice comments to tickets is different. See [Adding voice comments to tickets](/documentation/ticketing/managing-tickets/adding-voice-comments-to-tickets) + */ +export const zModelFromZendesk = z.string(); + /** * This is a model with one string property */ @@ -595,6 +312,27 @@ export const zModelWithNullableString = z.object({ ])) }); +/** + * This is a model with one enum + */ +export const zModelWithEnum = z.object({ + 'foo_bar-enum': z.optional(z.enum([ + 'Success', + 'Warning', + 'Error', + 'ØÆÅ字符串' + ])), + statusCode: z.optional(z.enum([ + '100', + '200 FOO', + '300 FOO_BAR', + '400 foo-bar', + '500 foo.bar', + '600 foo&bar' + ])), + bool: z.optional(z.literal(true)) +}); + /** * This is a model with one enum with escaped name */ @@ -636,10 +374,39 @@ export const zModelWithNestedEnums = z.object({ }); /** - * This is a model with one property containing a reference + * This is a model with one property containing an array */ -export const zModelWithReference = z.object({ - prop: z.optional(zModelWithProperties) +export const zModelWithArray = z.object({ + prop: z.optional(z.array(zModelWithString)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + +/** + * This is a model with one property containing a dictionary + */ +export const zModelWithDictionary = z.object({ + prop: z.optional(z.record(z.string(), z.string())) +}); + +/** + * This is a deprecated model with a deprecated property + * + * @deprecated + */ +export const zDeprecatedModel = z.object({ + prop: z.optional(z.string()) +}); + +/** + * This is a model with one property containing a circular reference + */ +export const zModelWithCircularReference = z.object({ + get prop() { + return z.optional(z.lazy((): any => { + return zModelWithCircularReference; + })); + } }); /** @@ -667,6 +434,22 @@ export const zCompositionWithOneOfAnonymous = z.object({ ])) }); +/** + * Circle + */ +export const zModelCircle = z.object({ + kind: z.string(), + radius: z.optional(z.number()) +}); + +/** + * Square + */ +export const zModelSquare = z.object({ + kind: z.string(), + sideLength: z.optional(z.number()) +}); + /** * This is a model with one property with a 'one of' relationship where the options are not $ref */ @@ -720,6 +503,13 @@ export const zCompositionWithNestedAnyAndTypeNull = z.object({ ])) }); +export const z3eNum1Период = z.enum([ + 'Bird', + 'Dog' +]); + +export const zConstValue = z.literal('ConstValue'); + /** * This is a model with one property with a 'any of' relationship where the options are not $ref */ @@ -809,13 +599,49 @@ export const zCompositionWithAnyOfAndNullable = z.object({ }); /** - * This is a model that extends the base model + * This is a base model with two simple optional properties + */ +export const zCompositionBaseModel = z.object({ + firstName: z.optional(z.string()), + lastname: z.optional(z.string()) +}); + +/** + * This is a model that extends the base model + */ +export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ + age: z.number(), + firstName: z.string(), + lastname: z.string() +})); + +/** + * This is a model with one nested property + */ +export const zModelWithProperties = z.object({ + required: z.string(), + requiredAndReadOnly: z.string().readonly(), + requiredAndNullable: z.union([ + z.string(), + z.null() + ]), + string: z.optional(z.string()), + number: z.optional(z.number()), + boolean: z.optional(z.boolean()), + reference: z.optional(zModelWithString), + 'property with space': z.optional(z.string()), + default: z.optional(z.string()), + try: z.optional(z.string()), + '@namespace.string': z.optional(z.string().readonly()), + '@namespace.integer': z.optional(z.int().readonly()) +}); + +/** + * This is a model with one property containing a reference */ -export const zCompositionExtendedModel = zCompositionBaseModel.and(z.object({ - age: z.number(), - firstName: z.string(), - lastname: z.string() -})); +export const zModelWithReference = z.object({ + prop: z.optional(zModelWithProperties) +}); /** * This is a model with one nested property @@ -862,6 +688,22 @@ export const zModelWithDuplicateImports = z.object({ propC: z.optional(zModelWithString) }); +/** + * This is a model that extends another model + */ +export const zModelThatExtends = zModelWithString.and(z.object({ + propExtendsA: z.optional(z.string()), + propExtendsB: z.optional(zModelWithString) +})); + +/** + * This is a model that extends another model + */ +export const zModelThatExtendsExtends = zModelWithString.and(zModelThatExtends).and(z.object({ + propExtendsC: z.optional(z.string()), + propExtendsD: z.optional(zModelWithString) +})); + /** * This is a model that contains a some patterns */ @@ -889,6 +731,12 @@ export const zDefault = z.object({ name: z.optional(z.string()) }); +export const zPageable = z.object({ + page: z.optional(z.int().gte(0)).default(0), + size: z.optional(z.int().gte(1)), + sort: z.optional(z.array(z.string())) +}); + /** * This is a free-form object without additionalProperties. */ @@ -928,20 +776,15 @@ export const zNestedAnyOfArraysNullable = z.object({ ])) }); -export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ +/** + * An object that can be null + */ +export const zNullableObject = z.union([ z.object({ - foo: zSimpleParameter + foo: z.optional(z.string()) }), - z.object({ - bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 - }) -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); + z.null() +]).default(null); /** * Some % character @@ -952,6 +795,54 @@ export const zModelWithNullableObject = z.object({ data: z.optional(zNullableObject) }); +export const zModelWithOneOfEnum = z.union([ + z.object({ + foo: z.enum([ + 'Bar' + ]) + }), + z.object({ + foo: z.enum([ + 'Baz' + ]) + }), + z.object({ + foo: z.enum([ + 'Qux' + ]) + }), + z.object({ + content: z.iso.datetime(), + foo: z.enum([ + 'Quux' + ]) + }), + z.object({ + content: z.tuple([ + z.iso.datetime(), + z.string() + ]), + foo: z.enum([ + 'Corge' + ]) + }) +]); + +export const zModelWithNestedArrayEnumsDataFoo = z.enum([ + 'foo', + 'bar' +]); + +export const zModelWithNestedArrayEnumsDataBar = z.enum([ + 'baz', + 'qux' +]); + +export const zModelWithNestedArrayEnumsData = z.object({ + foo: z.optional(z.array(zModelWithNestedArrayEnumsDataFoo)), + bar: z.optional(z.array(zModelWithNestedArrayEnumsDataBar)) +}); + export const zModelWithNestedArrayEnums = z.object({ array_strings: z.optional(z.array(z.string())), data: z.optional(zModelWithNestedArrayEnumsData) @@ -961,6 +852,20 @@ export const zModelWithNestedCompositionEnums = z.object({ foo: z.optional(zModelWithNestedArrayEnumsDataFoo) }); +export const zModelWithReadOnlyAndWriteOnly = z.object({ + foo: z.string(), + bar: z.string().readonly() +}); + +/** + * This is a model with one property containing an array + */ +export const zModelWithArrayReadOnlyAndWriteOnly = z.object({ + prop: z.optional(z.array(zModelWithReadOnlyAndWriteOnly)), + propWithFile: z.optional(z.array(z.string())), + propWithNumber: z.optional(z.array(z.number())) +}); + export const zModelWithConstantSizeArray = z.tuple([ z.number(), z.number() @@ -1008,17 +913,6 @@ export const zModelWithAnyOfConstantSizeArrayNullable = z.tuple([ ]) ]); -export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ - z.union([ - z.number(), - zImport - ]), - z.union([ - z.number(), - zImport - ]) -]); - export const zModelWithAnyOfConstantSizeArrayAndIntersect = z.tuple([ z.intersection(z.number(), z.string()), z.intersection(z.number(), z.string()) @@ -1043,17 +937,6 @@ export const zModelWithBackticksInDescription = z.object({ template: z.optional(z.string()) }); -export const zModelWithOneOfAndProperties = z.intersection(z.union([ - zSimpleParameter, - zNonAsciiStringæøåÆøÅöôêÊ字符串 -]), z.object({ - baz: z.union([ - z.int().gte(0), - z.null() - ]), - qux: z.int().gte(0) -})); - /** * Model used to test deduplication strategy (unused) */ @@ -1079,6 +962,22 @@ export const zDeleteFooData = z.string(); */ export const zDeleteFooData2 = z.string(); +/** + * Model with restricted keyword name + */ +export const zImport = z.string(); + +export const zModelWithAnyOfConstantSizeArrayWithNSizeAndOptions = z.tuple([ + z.union([ + z.number(), + zImport + ]), + z.union([ + z.number(), + zImport + ]) +]); + export const zSchemaWithFormRestrictedKeys = z.object({ description: z.optional(z.string()), 'x-enum-descriptions': z.optional(z.string()), @@ -1101,6 +1000,14 @@ export const zSchemaWithFormRestrictedKeys = z.object({ }))) }); +/** + * This schema was giving PascalCase transformations a hard time + */ +export const zIoK8sApimachineryPkgApisMetaV1Preconditions = z.object({ + resourceVersion: z.optional(z.string()), + uid: z.optional(z.string()) +}); + /** * This schema was giving PascalCase transformations a hard time */ @@ -1108,6 +1015,11 @@ export const zIoK8sApimachineryPkgApisMetaV1DeleteOptions = z.object({ preconditions: z.optional(zIoK8sApimachineryPkgApisMetaV1Preconditions) }); +export const zAdditionalPropertiesUnknownIssue = z.record(z.string(), z.union([ + z.string(), + z.number() +])); + export const zAdditionalPropertiesUnknownIssue2 = z.record(z.string(), z.union([ z.string(), z.number() @@ -1121,6 +1033,28 @@ export const zAdditionalPropertiesIntegerIssue = z.object({ value: z.int() }); +export const zGenericSchemaDuplicateIssue1SystemBoolean = z.object({ + item: z.optional(z.boolean()), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()), + data: z.optional(z.record(z.string(), z.never())) +}); + +export const zGenericSchemaDuplicateIssue1SystemString = z.object({ + item: z.optional(z.union([ + z.string(), + z.null() + ])), + error: z.optional(z.union([ + z.string(), + z.null() + ])), + hasError: z.optional(z.boolean().readonly()) +}); + export const zOneOfAllOfIssue = z.union([ z.intersection(z.union([ zConstValue, @@ -1129,6 +1063,21 @@ export const zOneOfAllOfIssue = z.union([ zGenericSchemaDuplicateIssue1SystemString ]); +export const zExternalSharedExternalSharedModel = z.object({ + id: z.string(), + name: z.optional(z.string()) +}); + +/** + * External ref to shared model (A) + */ +export const zExternalRefA = zExternalSharedExternalSharedModel; + +/** + * External ref to shared model (B) + */ +export const zExternalRefB = zExternalSharedExternalSharedModel; + /** * This is a model with one nested property */ @@ -1164,6 +1113,11 @@ export const zFileWritable = z.object({ mime: z.string().min(1).max(24) }); +export const zModelWithReadOnlyAndWriteOnlyWritable = z.object({ + foo: z.string(), + baz: z.string() +}); + export const zAdditionalPropertiesUnknownIssueWritable = z.record(z.string(), z.union([ z.string(), z.number() @@ -1189,6 +1143,52 @@ export const zGenericSchemaDuplicateIssue1SystemStringWritable = z.object({ ])) }); +/** + * This is a reusable parameter + */ +export const zSimpleParameter = z.string(); + +export const zCompositionWithOneOfAndProperties = z.intersection(z.union([ + z.object({ + foo: zSimpleParameter + }), + z.object({ + bar: zNonAsciiStringæøåÆøÅöôêÊ字符串 + }) +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +export const zModelWithOneOfAndProperties = z.intersection(z.union([ + zSimpleParameter, + zNonAsciiStringæøåÆøÅöôêÊ字符串 +]), z.object({ + baz: z.union([ + z.int().gte(0), + z.null() + ]), + qux: z.int().gte(0) +})); + +/** + * Parameter with illegal characters + */ +export const zXFooBar = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleRequestBody = zModelWithString; + +/** + * A reusable request body + */ +export const zSimpleFormData = zModelWithString; + export const zExportData = z.object({ body: z.optional(z.never()), path: z.optional(z.never()), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts index a5ebfea1f..53ce2eb2f 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-circular-ref/zod.gen.ts @@ -10,6 +10,10 @@ export const zBar = z.object({ } }); +export const zFoo = z.object({ + foo: z.optional(zBar) +}); + export const zBaz = z.lazy((): any => { return zQux; }); @@ -18,7 +22,3 @@ export const zBaz = z.lazy((): any => { * description caused circular reference error */ export const zQux = zBaz; - -export const zFoo = z.object({ - foo: z.optional(zBar) -}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts index 0a4c89c85..bc6bd1d71 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-dates/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -64,7 +64,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts index 45cced796..4e71c97c6 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-metadata/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -38,22 +44,16 @@ export const zBar = z.object({ description: 'This is Bar schema.' }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string().register(z.globalRegistry, { +export const zFoo2 = z.string().register(z.globalRegistry, { description: 'This is Foo parameter.' }); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); - -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -74,7 +74,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts index 84e110060..ba13cd685 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-types/zod.gen.ts @@ -2,6 +2,16 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export type BazZodType = z.infer; + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + +export type QuxZodType = z.infer; + /** * This is Foo schema. */ @@ -34,28 +44,18 @@ export const zBar = z.object({ export type BarZodType = z.infer; -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - -export type FooZodType2 = z.infer; - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export type FooZodType3 = z.infer; +export const zFoo2 = z.string(); -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); - -export type BazZodType = z.infer; +export type FooZodType2 = z.infer; -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); -export type QuxZodType = z.infer; +export type FooZodType3 = z.infer; export const zPatchFooData = z.object({ body: z.object({ @@ -76,7 +76,7 @@ export const zPatchFooData = z.object({ export type PatchFooDataZodType = z.infer; export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts index 298ff43d2..3971835be 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-union-merge/zod.gen.ts @@ -11,6 +11,10 @@ export const zContact = z.union([ }) ]); +export const zUser = zContact.and(z.object({ + username: z.string() +})); + export const zDogDetails = z.object({ breed: z.string(), barkVolume: z.int().gte(1).lte(10) @@ -25,10 +29,6 @@ export const zCatDetails = z.object({ purrs: z.boolean() }); -export const zUser = zContact.and(z.object({ - username: z.string() -})); - export const zPetStore = z.object({ animals: z.array(z.object({ name: z.string(), diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts index 016248f05..a48bf2186 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators/zod.gen.ts @@ -2,6 +2,12 @@ import { z } from 'zod'; +export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); + +export const zQux = z.record(z.string(), z.object({ + qux: z.optional(z.string()) +})); + /** * This is Foo schema. */ @@ -30,20 +36,14 @@ export const zBar = z.object({ foo: z.optional(zFoo) }); -export const zFoo2 = z.object({ - foo: z.optional(zBar) -}); - /** * This is Foo parameter. */ -export const zFoo3 = z.string(); - -export const zBaz = z.string().regex(/foo\nbar/).readonly().default('baz'); +export const zFoo2 = z.string(); -export const zQux = z.record(z.string(), z.object({ - qux: z.optional(z.string()) -})); +export const zFoo3 = z.object({ + foo: z.optional(zBar) +}); export const zPatchFooData = z.object({ body: z.object({ @@ -62,7 +62,7 @@ export const zPatchFooData = z.object({ }); export const zPostFooData = z.object({ - body: zFoo2, + body: zFoo3, path: z.optional(z.never()), query: z.optional(z.never()) }); diff --git a/packages/openapi-ts/src/ir/__tests__/graph.test.ts b/packages/openapi-ts/src/ir/__tests__/graph.test.ts index 6e747025e..417dbf423 100644 --- a/packages/openapi-ts/src/ir/__tests__/graph.test.ts +++ b/packages/openapi-ts/src/ir/__tests__/graph.test.ts @@ -67,7 +67,10 @@ describe('matchIrTopLevelPointer', () => { }); describe('walkTopological', () => { - const makeGraph = (deps: Array<[string, string[]]>, nodes: string[]) => { + const makeGraph = ( + deps: Array<[string, Array]>, + nodes: Array, + ) => { const nodeDependencies = new Map>(); const subtreeDependencies = new Map>(); const reverseNodeDependencies = new Map>(); @@ -106,7 +109,7 @@ describe('walkTopological', () => { ], ['A', 'B', 'C'], ); - const order: string[] = []; + const order: Array = []; walkTopological(graph, (pointer) => order.push(pointer)); expect(order.indexOf('C')).toBeLessThan(order.indexOf('B')); expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); @@ -122,7 +125,7 @@ describe('walkTopological', () => { ], ['A', 'B', 'C', 'D'], ); - const order: string[] = []; + const order: Array = []; walkTopological(graph, (pointer) => order.push(pointer)); expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); expect(order.indexOf('D')).toBeLessThan(order.indexOf('C')); @@ -132,7 +135,7 @@ describe('walkTopological', () => { it('walks nodes in topological order for a disconnected graph', () => { // Graph: A -> B, C (no deps), D (no deps) const graph = makeGraph([['A', ['B']]], ['A', 'B', 'C', 'D']); - const order: string[] = []; + const order: Array = []; walkTopological(graph, (pointer) => order.push(pointer)); expect(order.indexOf('B')).toBeLessThan(order.indexOf('A')); expect(order).toHaveLength(4); @@ -154,7 +157,7 @@ describe('walkTopological', () => { ], ['A', 'B', 'C', 'D'], ); - const order: string[] = []; + const order: Array = []; walkTopological(graph, (pointer) => order.push(pointer)); expect(order.indexOf('D')).toBeLessThan(order.indexOf('B')); expect(order.indexOf('D')).toBeLessThan(order.indexOf('C')); @@ -174,7 +177,7 @@ describe('walkTopological', () => { ], ['A', 'B', 'C', 'D', 'E'], ); - const order: string[] = []; + const order: Array = []; walkTopological(graph, (pointer) => order.push(pointer)); expect(order.indexOf('E')).toBeLessThan(order.indexOf('D')); expect(order.indexOf('D')).toBeLessThan(order.indexOf('C')); @@ -192,7 +195,7 @@ describe('walkTopological', () => { ], ['A', 'B', 'C'], ); - const order: string[] = []; + const order: Array = []; walkTopological(graph, (pointer) => order.push(pointer)); expect(order.sort()).toEqual(['A', 'B', 'C']); }); @@ -204,7 +207,7 @@ describe('walkTopological', () => { const spec = specModule.default ?? specModule; const { graph } = buildGraph(spec, loggerStub); - const order: string[] = []; + const order: Array = []; walkTopological(graph, (pointer) => order.push(pointer)); const foo = '#/components/schemas/Foo'; @@ -219,4 +222,105 @@ describe('walkTopological', () => { expect(order).toContain(baz); expect(order).toContain(qux); }); + + it('prefers schema group before parameter when safe (default)', () => { + // parameter then schema in declaration order, no deps -> schema should move before parameter + const param = '#/components/parameters/P'; + const schema = '#/components/schemas/A'; + const nodes = [param, schema]; + const graph = makeGraph([], nodes); + + const order: Array = []; + walkTopological(graph, (p) => order.push(p)); + expect(order.indexOf(schema)).toBeLessThan(order.indexOf(param)); + }); + + it('does not apply preferGroups when it would violate dependencies (fallback)', () => { + // declaration order: param, schema; schema depends on param -> cannot move before param + const param = '#/components/parameters/P'; + const schema = '#/components/schemas/S'; + const nodes = [param, schema]; + const nodeDependencies = new Map>(); + nodeDependencies.set(schema, new Set([param])); + const subtreeDependencies = new Map>(); + const reverseNodeDependencies = new Map>(); + const nodesMap = new Map(); + for (const n of nodes) + nodesMap.set(n, { key: null, node: {}, parentPointer: null }); + const graph = { + nodeDependencies, + nodes: nodesMap, + reverseNodeDependencies, + subtreeDependencies, + transitiveDependencies: new Map>(), + } as unknown as Graph; + + const order: Array = []; + walkTopological(graph, (p) => order.push(p)); + // schema depends on param so param must remain before schema + expect(order.indexOf(param)).toBeLessThan(order.indexOf(schema)); + }); + + it('ignores self-dependencies when ordering', () => { + // Foo has self-ref only, Bar references Foo -> Foo should come before Bar + const foo = '#/components/schemas/Foo'; + const bar = '#/components/schemas/Bar'; + const nodes = [foo, bar]; + const nodeDependencies = new Map>(); + nodeDependencies.set(foo, new Set([foo])); + nodeDependencies.set(bar, new Set([foo])); + + const nodesMap = new Map(); + for (const n of nodes) + nodesMap.set(n, { key: null, node: {}, parentPointer: null }); + + const graph = { + nodeDependencies, + nodes: nodesMap, + reverseNodeDependencies: new Map>(), + subtreeDependencies: new Map>(), + transitiveDependencies: new Map>(), + } as unknown as Graph; + + const order: Array = []; + walkTopological(graph, (p) => order.push(p)); + // Foo is a dependency of Bar, so Foo should come before Bar + expect(order.indexOf(foo)).toBeLessThan(order.indexOf(bar)); + }); + + it('uses subtreeDependencies when nodeDependencies are absent', () => { + const parent = '#/components/schemas/Parent'; + const child = '#/components/schemas/Child'; + const nodes = [parent, child]; + const nodeDependencies = new Map>(); + const subtreeDependencies = new Map>(); + subtreeDependencies.set(parent, new Set([child])); + + const nodesMap = new Map(); + for (const n of nodes) + nodesMap.set(n, { key: null, node: {}, parentPointer: null }); + + const graph = { + nodeDependencies, + nodes: nodesMap, + reverseNodeDependencies: new Map>(), + subtreeDependencies, + transitiveDependencies: new Map>(), + } as unknown as Graph; + + const order: Array = []; + walkTopological(graph, (p) => order.push(p)); + expect(order.indexOf(child)).toBeLessThan(order.indexOf(parent)); + }); + + it('preserves declaration order for equal-priority items (stability)', () => { + const a = '#/components/schemas/A'; + const b = '#/components/schemas/B'; + const c = '#/components/schemas/C'; + const nodes = [a, b, c]; + const graph = makeGraph([], nodes); + const order: Array = []; + walkTopological(graph, (p) => order.push(p)); + expect(order).toEqual(nodes); + }); }); diff --git a/packages/openapi-ts/src/ir/graph.ts b/packages/openapi-ts/src/ir/graph.ts index c4aedb03d..ee62ac28f 100644 --- a/packages/openapi-ts/src/ir/graph.ts +++ b/packages/openapi-ts/src/ir/graph.ts @@ -1,4 +1,9 @@ import type { Graph, NodeInfo } from '../openApi/shared/utils/graph'; +import { MinHeap } from '../utils/minHeap'; + +type KindPriority = Record; +type PreferGroups = ReadonlyArray; +type PriorityFn = (pointer: string) => number; /** * Walks the nodes of the graph in topological order (dependencies before dependents). @@ -11,25 +16,162 @@ import type { Graph, NodeInfo } from '../openApi/shared/utils/graph'; export const walkTopological = ( graph: Graph, callback: (pointer: string, nodeInfo: NodeInfo) => void, + options?: { + preferGroups?: PreferGroups; + priority?: PriorityFn; + }, ) => { - const visited = new Set(); + // Stable Kahn's algorithm that respects declaration order as a tiebreaker. + const pointers = Array.from(graph.nodes.keys()); + // Base insertion order + const baseIndex = new Map(); + pointers.forEach((pointer, index) => baseIndex.set(pointer, index)); - const visit = (pointer: string) => { - if (visited.has(pointer)) return; - visited.add(pointer); + // Composite decl index: group priority then base insertion order + const declIndex = new Map(); + const priorityFn = options?.priority ?? defaultPriorityFn; + for (const pointer of pointers) { + const group = priorityFn(pointer) ?? 10; + const composite = group * 1_000_000 + (baseIndex.get(pointer) ?? 0); + declIndex.set(pointer, composite); + } - const dependencies = graph.subtreeDependencies.get(pointer); - if (dependencies) { - for (const dependency of dependencies) { - visit(dependency); + // Build dependency sets for each pointer (prefer subtreeDependencies, fall back to nodeDependencies) + const depsOf = new Map>(); + for (const pointer of pointers) { + const raw = + graph.subtreeDependencies?.get(pointer) ?? + graph.nodeDependencies?.get(pointer) ?? + new Set(); + const filtered = new Set(); + for (const rawPointer of raw) { + if (rawPointer === pointer) continue; // ignore self-dependencies for ordering + if (graph.nodes.has(rawPointer)) { + filtered.add(rawPointer); } } + depsOf.set(pointer, filtered); + } - callback(pointer, graph.nodes.get(pointer)!); - }; + // Build inDegree and dependents adjacency + const inDegree = new Map(); + const dependents = new Map>(); + for (const pointer of pointers) { + inDegree.set(pointer, 0); + } + for (const [pointer, deps] of depsOf) { + inDegree.set(pointer, deps.size); + for (const d of deps) { + if (!dependents.has(d)) { + dependents.set(d, new Set()); + } + dependents.get(d)!.add(pointer); + } + } + + // Helper to sort pointers by declaration order + const sortByDecl = (arr: Array) => + arr.sort((a, b) => declIndex.get(a)! - declIndex.get(b)!); + + // Initialize queue with zero-inDegree nodes in declaration order + // Use a small binary min-heap prioritized by declaration index to avoid repeated full sorts. + const heap = new MinHeap(declIndex); + for (const pointer of pointers) { + if ((inDegree.get(pointer) ?? 0) === 0) { + heap.push(pointer); + } + } + + const emitted = new Set(); + const order: Array = []; + + while (!heap.isEmpty()) { + const cur = heap.pop()!; + if (emitted.has(cur)) continue; + emitted.add(cur); + order.push(cur); + + const deps = dependents.get(cur); + if (!deps) continue; + + for (const dep of deps) { + const v = (inDegree.get(dep) ?? 0) - 1; + inDegree.set(dep, v); + if (v === 0) { + heap.push(dep); + } + } + } + + // emit remaining nodes (cycles) in declaration order + const remaining = pointers.filter((pointer) => !emitted.has(pointer)); + sortByDecl(remaining); + for (const pointer of remaining) { + emitted.add(pointer); + order.push(pointer); + } + + // prefer specified groups when safe + let finalOrder = order; + const preferGroups = options?.preferGroups ?? defaultPreferGroups; + if (preferGroups && preferGroups.length > 0) { + // build group priority map (lower = earlier) + const groupPriority = new Map(); + for (let i = 0; i < preferGroups.length; i++) { + const k = preferGroups[i]; + if (k) { + groupPriority.set(k, i); + } + } - for (const pointer of graph.nodes.keys()) { - visit(pointer); + const getGroup: PriorityFn = (pointer) => { + const result = matchIrTopLevelPointer(pointer); + if (result.matched) { + return groupPriority.has(result.kind) + ? groupPriority.get(result.kind)! + : preferGroups.length; + } + return preferGroups.length; + }; + + // proposed order: sort by (groupPriority, originalIndex) + const proposed = [...order].sort((a, b) => { + const ga = getGroup(a); + const gb = getGroup(b); + return ga !== gb ? ga - gb : order.indexOf(a) - order.indexOf(b); + }); + + // Build quick lookup of original index and proposed index + const proposedIndex = new Map(); + for (let i = 0; i < proposed.length; i++) { + proposedIndex.set(proposed[i]!, i); + } + + // Micro-optimization: only validate edges where group(dep) > group(node) + const violated = (() => { + for (const [node, deps] of depsOf) { + for (const dep of deps) { + const gDep = getGroup(dep); + const gNode = getGroup(node); + if (gDep <= gNode) continue; // not a crossing edge, cannot be violated by grouping + const pDep = proposedIndex.get(dep)!; + const pNode = proposedIndex.get(node)!; + if (pDep >= pNode) { + return true; + } + } + } + return false; + })(); + + if (!violated) { + finalOrder = proposed; + } + } + + // Finally, call back in final order + for (const pointer of finalOrder) { + callback(pointer, graph.nodes.get(pointer)!); } }; @@ -82,3 +224,38 @@ export const matchIrTopLevelPointer = ( } return { matched: false }; }; + +// default grouping preference (earlier groups emitted first when safe) +export const defaultPreferGroups = [ + 'schema', + 'parameter', + 'requestBody', + 'operation', + 'server', + 'webhook', +] satisfies PreferGroups; + +// default group priority (lower = earlier) +// built from `defaultPreferGroups` so the priority order stays in sync with the prefer-groups array. +const defaultKindPriority: KindPriority = (() => { + const partial: Partial = {}; + for (let i = 0; i < defaultPreferGroups.length; i++) { + const k = defaultPreferGroups[i]; + if (k) partial[k] = i; + } + // Ensure all known kinds exist in the map (fall back to a high index). + for (const k of irTopLevelKinds) { + if (partial[k] === undefined) { + partial[k] = defaultPreferGroups.length; + } + } + return partial as KindPriority; +})(); + +const defaultPriorityFn: PriorityFn = (pointer) => { + const result = matchIrTopLevelPointer(pointer); + if (result.matched) { + return defaultKindPriority[result.kind] ?? 10; + } + return 10; +}; diff --git a/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts index 9910ba1b6..810a96dad 100644 --- a/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts @@ -1,33 +1,27 @@ import type ts from 'typescript'; import type { IR } from '../../../ir/types'; +import type { ToRefs } from '../../shared/types/refs'; import type { ArktypePlugin } from '../types'; export type Ast = { def: string; expression: ts.Expression; - hasCircularReference?: boolean; + hasLazyExpression?: boolean; typeName?: string | ts.Identifier; }; export type IrSchemaToAstOptions = { plugin: ArktypePlugin['Instance']; - state: State; + state: ToRefs; }; -export type State = { +export type PluginState = { /** * Path to the schema in the intermediary representation. */ _path: ReadonlyArray; - circularReferenceTracker: Array; - /** - * Works the same as `circularReferenceTracker`, but it resets whenever we - * walk inside another schema. This can be used to detect if a schema - * directly references itself. - */ - currentReferenceTracker: Array; - hasCircularReference: boolean; + hasLazyExpression: boolean; }; export type ValidatorArgs = { diff --git a/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts index 3ad82dc76..74707ba1f 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts @@ -2,9 +2,10 @@ import { deduplicateSchema } from '../../../ir/schema'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; import { tsc } from '../../../tsc'; -import { jsonPointerToPath, refToName } from '../../../utils/ref'; +import { refToName } from '../../../utils/ref'; import type { SchemaWithType } from '../../shared/types/schema'; import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { toRefs } from '../../shared/utils/refs'; import { exportAst } from '../shared/export'; import type { Ast, IrSchemaToAstOptions } from '../shared/types'; import type { ArktypePlugin } from '../types'; @@ -26,69 +27,37 @@ export const irSchemaToAst = ({ }): Ast => { let ast: Partial = {}; - // const z = plugin.referenceSymbol( - // plugin.api.selector('external', 'zod.z'), - // ); + // const z = plugin.referenceSymbol(plugin.api.selector('external', 'zod.z')); if (schema.$ref) { - const isCircularReference = state.circularReferenceTracker.includes( - schema.$ref, - ); - const isSelfReference = state.currentReferenceTracker.includes(schema.$ref); - state.circularReferenceTracker.push(schema.$ref); - state.currentReferenceTracker.push(schema.$ref); const selector = plugin.api.selector('ref', schema.$ref); - let symbol = plugin.getSymbol(selector); - if (isCircularReference) { - if (!symbol) { - symbol = plugin.referenceSymbol(selector); - } - if (isSelfReference) { - ast.expression = tsc.callExpression({ - functionName: tsc.propertyAccessExpression({ - // expression: z.placeholder, - expression: 'TODO', - name: 'TODO', - // name: identifiers.lazy, - }), - parameters: [ - tsc.arrowFunction({ - returnType: tsc.keywordTypeNode({ keyword: 'any' }), - statements: [ - tsc.returnStatement({ - expression: tsc.identifier({ text: symbol.placeholder }), - }), - ], - }), - ], - }); - } else { - ast.expression = tsc.identifier({ text: symbol.placeholder }); - } - ast.hasCircularReference = schema.circular; + const refSymbol = plugin.referenceSymbol(selector); + if (plugin.isSymbolRegistered(selector)) { + const ref = tsc.identifier({ text: refSymbol.placeholder }); + ast.expression = ref; } else { - if (!symbol) { - // if $ref hasn't been processed yet, inline it to avoid the - // "Block-scoped variable used before its declaration." error - // this could be (maybe?) fixed by reshuffling the generation order - const ref = plugin.context.resolveIrRef(schema.$ref); - handleComponent({ - id: schema.$ref, - plugin, - schema: ref, - state: { - ...state, - _path: jsonPointerToPath(schema.$ref), - }, - }); - } else { - ast.hasCircularReference = schema.circular; - } - const refSymbol = plugin.referenceSymbol(selector); - ast.expression = tsc.identifier({ text: refSymbol.placeholder }); + const lazyExpression = tsc.callExpression({ + functionName: tsc.propertyAccessExpression({ + // expression: z.placeholder, + expression: 'TODO', + name: 'TODO', + // name: identifiers.lazy, + }), + parameters: [ + tsc.arrowFunction({ + returnType: tsc.keywordTypeNode({ keyword: 'any' }), + statements: [ + tsc.returnStatement({ + expression: tsc.identifier({ text: refSymbol.placeholder }), + }), + ], + }), + ], + }); + ast.expression = lazyExpression; + ast.hasLazyExpression = true; + state.hasLazyExpression.value = true; } - state.circularReferenceTracker.pop(); - state.currentReferenceTracker.pop(); } else if (schema.type) { const typeAst = irSchemaWithTypeToAst({ plugin, @@ -97,7 +66,7 @@ export const irSchemaToAst = ({ }); ast.def = typeAst.def; ast.expression = typeAst.expression; - ast.hasCircularReference = typeAst.hasCircularReference; + ast.hasLazyExpression = typeAst.hasLazyExpression; if (plugin.config.metadata && schema.description) { // TODO: add description @@ -267,30 +236,18 @@ export const irSchemaToAst = ({ }; const handleComponent = ({ - id, + $ref, plugin, schema, - state: _state, -}: Omit & { - id: string; + state, +}: IrSchemaToAstOptions & { + $ref: string; schema: IR.SchemaObject; - state?: Partial; }): void => { - const state: IrSchemaToAstOptions['state'] = { - _path: _state?._path ?? [], - circularReferenceTracker: _state?.circularReferenceTracker ?? [id], - currentReferenceTracker: _state?.currentReferenceTracker ?? [id], - hasCircularReference: _state?.hasCircularReference ?? false, - }; - - const selector = plugin.api.selector('ref', id); - let symbol = plugin.getSymbol(selector); - if (symbol && !plugin.getSymbolValue(symbol)) return; - const ast = irSchemaToAst({ plugin, schema, state }); - const baseName = refToName(id); - const resourceType = pathToSymbolResourceType(state._path); - symbol = plugin.registerSymbol({ + const baseName = refToName($ref); + const resourceType = pathToSymbolResourceType(state._path.value); + const symbol = plugin.registerSymbol({ exported: true, meta: { resourceType, @@ -299,7 +256,7 @@ const handleComponent = ({ config: plugin.config.definitions, name: baseName, }), - selector, + selector: plugin.api.selector('ref', $ref), }); const typeInferSymbol = plugin.config.definitions.types.infer.enabled ? plugin.registerSymbol({ @@ -312,7 +269,7 @@ const handleComponent = ({ config: plugin.config.definitions.types.infer, name: baseName, }), - selector: plugin.api.selector('type-infer-ref', id), + selector: plugin.api.selector('type-infer-ref', $ref), }) : undefined; exportAst({ @@ -355,32 +312,35 @@ export const handlerV2: ArktypePlugin['Handler'] = ({ plugin }) => { // break; case 'parameter': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.parameter.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'requestBody': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.requestBody.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; case 'schema': handleComponent({ - id: event.$ref, + $ref: event.$ref, plugin, schema: event.schema, - state: { + state: toRefs({ _path: event._path, - }, + hasLazyExpression: false, + }), }); break; // case 'webhook': diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts index dc37a0879..9574825f6 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts @@ -118,6 +118,6 @@ export const irSchemaWithTypeToAst = ({ return { def: '', expression, - hasCircularReference: false, + hasLazyExpression: false, }; }; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts index d17157b1a..b536081df 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts @@ -3,6 +3,7 @@ import ts from 'typescript'; import { tsc } from '../../../../tsc'; import { numberRegExp } from '../../../../utils/regexp'; import type { SchemaWithType } from '../../../shared/types/schema'; +import { toRef } from '../../../shared/utils/refs'; // import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; @@ -32,11 +33,11 @@ export const objectToAst = ({ schema: property, state: { ...state, - _path: [...state._path, 'properties', name], + _path: toRef([...state._path.value, 'properties', name]), }, }); - if (propertyAst.hasCircularReference) { - result.hasCircularReference = true; + if (propertyAst.hasLazyExpression) { + result.hasLazyExpression = true; } let propertyName: @@ -117,7 +118,7 @@ export const objectToAst = ({ schema: schema.additionalProperties, state: { ...state, - _path: [...state._path, 'additionalProperties'], + _path: toRef([...state._path.value, 'additionalProperties']), }, }); result.expression = tsc.callExpression({ @@ -138,12 +139,12 @@ export const objectToAst = ({ additionalAst.expression, ], }); - if (additionalAst.hasCircularReference) { - result.hasCircularReference = true; + if (additionalAst.hasLazyExpression) { + result.hasLazyExpression = true; } // Return with typeName for circular references - if (result.hasCircularReference) { + if (result.hasLazyExpression) { return { ...result, typeName: 'TODO', @@ -159,7 +160,7 @@ export const objectToAst = ({ ); // return with typeName for circular references - if (result.hasCircularReference) { + if (result.hasLazyExpression) { return { ...result, typeName: 'TODO', diff --git a/packages/openapi-ts/src/plugins/shared/utils/instance.ts b/packages/openapi-ts/src/plugins/shared/utils/instance.ts index cd14270d2..619c76f47 100644 --- a/packages/openapi-ts/src/plugins/shared/utils/instance.ts +++ b/packages/openapi-ts/src/plugins/shared/utils/instance.ts @@ -16,7 +16,7 @@ import { } from '../../../ir/graph'; import type { IR } from '../../../ir/types'; import type { OpenApi } from '../../../openApi/types'; -import { jsonPointerToPath, pathToJsonPointer } from '../../../utils/ref'; +import { jsonPointerToPath } from '../../../utils/ref'; import type { PluginConfigMap } from '../../config'; import type { Plugin } from '../../types'; import type { WalkEvent, WalkOptions } from '../types/instance'; @@ -447,12 +447,8 @@ export class PluginInstance { return (defaultGetKind(operation) ?? []).includes(kind); } - isSelfReference( - pointer: string, - path: ReadonlyArray, - ): boolean { - const pathPointer = pathToJsonPointer(path); - return pathPointer === pointer || pathPointer.startsWith(`${pointer}/`); + isSymbolRegistered(symbolIdOrSelector: number | Selector): boolean { + return this.gen.symbols.isRegistered(symbolIdOrSelector); } referenceSymbol(symbolIdOrSelector: number | Selector): Symbol { diff --git a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts index 9290f3b01..788af9e91 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts @@ -56,12 +56,11 @@ export const irSchemaToAst = ({ if (schema.$ref) { const selector = plugin.api.selector('ref', schema.$ref); - let refSymbol = plugin.getSymbol(selector); - if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const refSymbol = plugin.referenceSymbol(selector); + if (plugin.isSymbolRegistered(selector)) { const ref = tsc.identifier({ text: refSymbol.placeholder }); pipes.push(ref); } else { - refSymbol = plugin.referenceSymbol(selector); const lazyExpression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: v.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts index 92b75009e..ae881579d 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts @@ -36,12 +36,11 @@ export const irSchemaToAst = ({ if (schema.$ref) { const selector = plugin.api.selector('ref', schema.$ref); - let refSymbol = plugin.getSymbol(selector); - if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const refSymbol = plugin.referenceSymbol(selector); + if (plugin.isSymbolRegistered(selector)) { const ref = tsc.identifier({ text: refSymbol.placeholder }); ast.expression = ref; } else { - refSymbol = plugin.referenceSymbol(selector); const lazyExpression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts index 67c414d19..bcdbb22eb 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts @@ -36,12 +36,11 @@ export const irSchemaToAst = ({ if (schema.$ref) { const selector = plugin.api.selector('ref', schema.$ref); - let refSymbol = plugin.getSymbol(selector); - if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const refSymbol = plugin.referenceSymbol(selector); + if (plugin.isSymbolRegistered(selector)) { const ref = tsc.identifier({ text: refSymbol.placeholder }); ast.expression = ref; } else { - refSymbol = plugin.referenceSymbol(selector); const lazyExpression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts index 6deb62580..ccb222667 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts @@ -36,12 +36,11 @@ export const irSchemaToAst = ({ if (schema.$ref) { const selector = plugin.api.selector('ref', schema.$ref); - let refSymbol = plugin.getSymbol(selector); - if (refSymbol && !plugin.isSelfReference(schema.$ref, state._path.value)) { + const refSymbol = plugin.referenceSymbol(selector); + if (plugin.isSymbolRegistered(selector)) { const ref = tsc.identifier({ text: refSymbol.placeholder }); ast.expression = ref; } else { - refSymbol = plugin.referenceSymbol(selector); const lazyExpression = tsc.callExpression({ functionName: tsc.propertyAccessExpression({ expression: z.placeholder, diff --git a/packages/openapi-ts/src/utils/__tests__/minHeap.test.ts b/packages/openapi-ts/src/utils/__tests__/minHeap.test.ts new file mode 100644 index 000000000..63fb3bcd1 --- /dev/null +++ b/packages/openapi-ts/src/utils/__tests__/minHeap.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest'; + +import { MinHeap } from '../minHeap'; + +describe('MinHeap', () => { + it('pops items in increasing declIndex order', () => { + const idx = new Map([ + ['a', 10], + ['b', 5], + ['c', 20], + ]); + const h = new MinHeap(idx); + h.push('a'); + h.push('b'); + h.push('c'); + + expect(h.pop()).toBe('b'); + expect(h.pop()).toBe('a'); + expect(h.pop()).toBe('c'); + expect(h.pop()).toBeUndefined(); + }); + + it('supports interleaved push/pop and maintains order', () => { + const idx = new Map([ + ['x', 0], + ['y', 1], + ['z', 2], + ]); + const h = new MinHeap(idx); + h.push('y'); + expect(h.pop()).toBe('y'); + + h.push('z'); + h.push('x'); + expect(h.pop()).toBe('x'); + expect(h.pop()).toBe('z'); + }); + + it('handles duplicates (same id pushed multiple times)', () => { + const idx = new Map([['dup', 1]]); + const h = new MinHeap(idx); + h.push('dup'); + h.push('dup'); + expect(h.pop()).toBe('dup'); + // second duplicate still present + expect(h.pop()).toBe('dup'); + expect(h.pop()).toBeUndefined(); + }); + + it('isEmpty returns true when empty and false when not', () => { + const idx = new Map([['a', 0]]); + const h = new MinHeap(idx); + expect(h.isEmpty()).toBe(true); + h.push('a'); + expect(h.isEmpty()).toBe(false); + h.pop(); + expect(h.isEmpty()).toBe(true); + }); +}); diff --git a/packages/openapi-ts/src/utils/minHeap.ts b/packages/openapi-ts/src/utils/minHeap.ts new file mode 100644 index 000000000..0c502a411 --- /dev/null +++ b/packages/openapi-ts/src/utils/minHeap.ts @@ -0,0 +1,64 @@ +export class MinHeap { + private heap: Array = []; + + constructor(public declIndex: Map) {} + + isEmpty(): boolean { + return !this.heap.length; + } + + pop(): string | undefined { + const [top] = this.heap; + if (!this.heap.length) return; + const last = this.heap.pop()!; + if (!this.heap.length) return top; + this.heap[0] = last; + this.sinkDown(0); + return top; + } + + push(item: string): void { + this.heap.push(item); + this.bubbleUp(this.heap.length - 1); + } + + private bubbleUp(index: number): void { + const heap = this.heap; + while (index > 0) { + const parent = Math.floor((index - 1) / 2); + const parentVal = heap[parent]!; + const curVal = heap[index]!; + if (this.declIndex.get(parentVal)! <= this.declIndex.get(curVal)!) break; + heap[parent] = curVal; + heap[index] = parentVal; + index = parent; + } + } + + private sinkDown(index: number): void { + const heap = this.heap; + const len = heap.length; + while (true) { + const left = 2 * index + 1; + const right = 2 * index + 2; + let smallest = index; + if (left < len) { + const leftVal = heap[left]!; + const smallestVal = heap[smallest]!; + if (this.declIndex.get(leftVal)! < this.declIndex.get(smallestVal)!) + smallest = left; + } + if (right < len) { + const rightVal = heap[right]!; + const smallestVal = heap[smallest]!; + if (this.declIndex.get(rightVal)! < this.declIndex.get(smallestVal)!) + smallest = right; + } + if (smallest === index) break; + const tmp = heap[smallest]!; + heap[smallest] = heap[index]!; + heap[index] = tmp; + index = smallest; + } + } +} -- 2.51.2 From b48b9bb65908cada858a386d1da3361712b6403c Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 23 Oct 2025 11:52:39 +0800 Subject: [PATCH 5/5] refactor: use absolute paths --- eslint-rules/local-paths.js | 233 ++++++++++++++++++ eslint.config.js | 4 + .../main/test/openapi-ts.config.ts | 2 +- .../openapi-ts-tests/main/vitest.config.ts | 14 +- .../openapi-ts-tests/zod/v3/vitest.config.ts | 14 +- .../openapi-ts-tests/zod/v4/vitest.config.ts | 14 +- .../src/__tests__/createClient.test.ts | 2 +- .../openapi-ts/src/__tests__/error.test.ts | 2 +- .../openapi-ts/src/__tests__/index.test.ts | 2 +- .../src/__tests__/interactive.test.ts | 6 +- .../src/config/__tests__/input.test.ts | 3 +- packages/openapi-ts/src/config/engine.ts | 2 +- packages/openapi-ts/src/config/init.ts | 11 +- packages/openapi-ts/src/config/input.ts | 8 +- packages/openapi-ts/src/config/logs.ts | 2 +- packages/openapi-ts/src/config/merge.ts | 2 +- packages/openapi-ts/src/config/output.ts | 5 +- packages/openapi-ts/src/config/parser.ts | 3 +- packages/openapi-ts/src/config/plugins.ts | 7 +- packages/openapi-ts/src/createClient.ts | 32 +-- packages/openapi-ts/src/debug/graph.ts | 2 +- packages/openapi-ts/src/debug/ir.ts | 4 +- packages/openapi-ts/src/error.ts | 4 +- .../src/generate/__tests__/class.test.ts | 5 +- .../src/generate/__tests__/core.test.ts | 5 +- .../src/generate/__tests__/mocks.ts | 8 +- packages/openapi-ts/src/generate/class.ts | 15 +- packages/openapi-ts/src/generate/client.ts | 9 +- packages/openapi-ts/src/generate/core.ts | 10 +- packages/openapi-ts/src/generate/file.ts | 5 +- .../generate/legacy/__tests__/index.test.ts | 3 +- .../generate/legacy/__tests__/output.test.ts | 7 +- .../src/generate/legacy/indexFile.ts | 9 +- .../openapi-ts/src/generate/legacy/output.ts | 13 +- packages/openapi-ts/src/generate/output.ts | 5 +- packages/openapi-ts/src/generate/renderer.ts | 6 +- packages/openapi-ts/src/generate/tsConfig.ts | 2 +- packages/openapi-ts/src/getSpec.ts | 6 +- packages/openapi-ts/src/index.ts | 26 +- .../openapi-ts/src/ir/__tests__/graph.test.ts | 5 +- .../src/ir/__tests__/pagination.test.ts | 5 +- packages/openapi-ts/src/ir/context.ts | 23 +- packages/openapi-ts/src/ir/graph.ts | 4 +- packages/openapi-ts/src/ir/pagination.ts | 3 +- packages/openapi-ts/src/ir/types.d.ts | 5 +- .../2.0.x/parser/__tests__/server.test.ts | 3 +- .../2.0.x/parser/__tests__/validate.test.ts | 5 +- .../src/openApi/2.0.x/parser/filter.ts | 9 +- .../src/openApi/2.0.x/parser/index.ts | 17 +- .../src/openApi/2.0.x/parser/mediaType.ts | 8 +- .../src/openApi/2.0.x/parser/operation.ts | 7 +- .../src/openApi/2.0.x/parser/pagination.ts | 7 +- .../src/openApi/2.0.x/parser/parameter.ts | 3 +- .../src/openApi/2.0.x/parser/schema.ts | 11 +- .../src/openApi/2.0.x/parser/server.ts | 4 +- .../src/openApi/2.0.x/parser/validate.ts | 9 +- .../2.0.x/types/json-schema-draft-4.d.ts | 2 +- .../src/openApi/2.0.x/types/spec.d.ts | 3 +- .../3.0.x/parser/__tests__/validate.test.ts | 5 +- .../src/openApi/3.0.x/parser/filter.ts | 9 +- .../src/openApi/3.0.x/parser/index.ts | 17 +- .../src/openApi/3.0.x/parser/mediaType.ts | 8 +- .../src/openApi/3.0.x/parser/operation.ts | 7 +- .../src/openApi/3.0.x/parser/pagination.ts | 7 +- .../src/openApi/3.0.x/parser/parameter.ts | 5 +- .../src/openApi/3.0.x/parser/requestBody.ts | 5 +- .../src/openApi/3.0.x/parser/schema.ts | 11 +- .../src/openApi/3.0.x/parser/server.ts | 4 +- .../src/openApi/3.0.x/parser/validate.ts | 9 +- .../src/openApi/3.0.x/types/spec.d.ts | 2 +- .../3.1.x/parser/__tests__/validate.test.ts | 5 +- .../src/openApi/3.1.x/parser/filter.ts | 9 +- .../src/openApi/3.1.x/parser/index.ts | 17 +- .../src/openApi/3.1.x/parser/mediaType.ts | 8 +- .../src/openApi/3.1.x/parser/operation.ts | 9 +- .../src/openApi/3.1.x/parser/pagination.ts | 7 +- .../src/openApi/3.1.x/parser/parameter.ts | 5 +- .../src/openApi/3.1.x/parser/requestBody.ts | 5 +- .../src/openApi/3.1.x/parser/schema.ts | 11 +- .../src/openApi/3.1.x/parser/server.ts | 4 +- .../src/openApi/3.1.x/parser/validate.ts | 9 +- .../src/openApi/3.1.x/parser/webhook.ts | 5 +- .../types/json-schema-draft-2020-12.d.ts | 3 +- .../src/openApi/__tests__/index.test.ts | 13 +- .../src/openApi/common/interfaces/OpenApi.ts | 4 +- .../src/openApi/common/interfaces/client.ts | 3 +- .../src/openApi/common/parser/getDefault.ts | 7 +- .../src/openApi/common/parser/getEnums.ts | 3 +- .../src/openApi/common/parser/getRef.ts | 5 +- .../src/openApi/common/parser/operation.ts | 7 +- .../src/openApi/common/parser/sanitize.ts | 2 +- .../src/openApi/common/parser/type.ts | 7 +- packages/openapi-ts/src/openApi/index.ts | 26 +- .../src/openApi/shared/graph/meta.ts | 7 +- .../src/openApi/shared/transforms/enums.ts | 3 +- .../src/openApi/shared/transforms/index.ts | 3 +- .../openApi/shared/transforms/readWrite.ts | 5 +- .../shared/utils/__tests__/patch.test.ts | 3 +- .../src/openApi/shared/utils/discriminator.ts | 2 +- .../src/openApi/shared/utils/filter.ts | 9 +- .../src/openApi/shared/utils/graph.ts | 5 +- .../src/openApi/shared/utils/identifier.ts | 2 +- .../src/openApi/shared/utils/name.ts | 3 +- .../src/openApi/shared/utils/operation.ts | 9 +- .../src/openApi/shared/utils/parameter.ts | 2 +- .../src/openApi/shared/utils/patch.ts | 3 +- .../src/openApi/shared/utils/validator.ts | 2 +- packages/openapi-ts/src/openApi/types.d.ts | 6 +- packages/openapi-ts/src/openApi/v2/index.ts | 5 +- .../src/openApi/v2/interfaces/Model.ts | 3 +- .../src/openApi/v2/interfaces/OpenApi.ts | 3 +- .../openApi/v2/interfaces/OpenApiHeader.ts | 3 +- .../src/openApi/v2/interfaces/OpenApiItems.ts | 2 +- .../openApi/v2/interfaces/OpenApiParameter.ts | 3 +- .../openApi/v2/interfaces/OpenApiResponse.ts | 3 +- .../openApi/v2/interfaces/OpenApiSchema.ts | 5 +- .../v2/interfaces/OpenApiSecurityScheme.ts | 2 +- .../src/openApi/v2/parser/getModel.ts | 9 +- .../openApi/v2/parser/getModelComposition.ts | 6 +- .../openApi/v2/parser/getModelProperties.ts | 9 +- .../src/openApi/v2/parser/getModels.ts | 5 +- .../v2/parser/getOperationParameter.ts | 15 +- .../v2/parser/getOperationParameters.ts | 7 +- .../openApi/v2/parser/getOperationResponse.ts | 9 +- .../v2/parser/getOperationResponses.ts | 9 +- .../src/openApi/v2/parser/getOperations.ts | 11 +- .../getRequiredPropertiesFromComposition.ts | 5 +- .../src/openApi/v2/parser/operation.ts | 11 +- packages/openapi-ts/src/openApi/v3/index.ts | 5 +- .../src/openApi/v3/interfaces/Model.ts | 3 +- .../v3/interfaces/OpenApiComponents.ts | 3 +- .../v3/interfaces/OpenApiDiscriminator.ts | 2 +- .../openApi/v3/interfaces/OpenApiEncoding.ts | 3 +- .../openApi/v3/interfaces/OpenApiHeader.ts | 3 +- .../src/openApi/v3/interfaces/OpenApiLink.ts | 3 +- .../openApi/v3/interfaces/OpenApiMediaType.ts | 3 +- .../openApi/v3/interfaces/OpenApiOAuthFlow.ts | 2 +- .../openApi/v3/interfaces/OpenApiOperation.ts | 3 +- .../openApi/v3/interfaces/OpenApiParameter.ts | 3 +- .../v3/interfaces/OpenApiRequestBody.ts | 3 +- .../openApi/v3/interfaces/OpenApiResponse.ts | 3 +- .../openApi/v3/interfaces/OpenApiSchema.ts | 5 +- .../openApi/v3/interfaces/OpenApiServer.ts | 3 +- .../v3/interfaces/OpenApiServerVariable.ts | 2 +- .../v3/parser/__tests__/getModel.test.ts | 3 +- .../src/openApi/v3/parser/discriminator.ts | 7 +- .../src/openApi/v3/parser/getContent.ts | 3 +- .../src/openApi/v3/parser/getModel.ts | 13 +- .../openApi/v3/parser/getModelComposition.ts | 6 +- .../openApi/v3/parser/getModelProperties.ts | 13 +- .../src/openApi/v3/parser/getModels.ts | 3 +- .../v3/parser/getOperationParameter.ts | 17 +- .../v3/parser/getOperationParameters.ts | 7 +- .../v3/parser/getOperationRequestBody.ts | 7 +- .../openApi/v3/parser/getOperationResponse.ts | 9 +- .../v3/parser/getOperationResponses.ts | 9 +- .../src/openApi/v3/parser/getOperations.ts | 11 +- .../getRequiredPropertiesFromComposition.ts | 7 +- .../src/openApi/v3/parser/operation.ts | 13 +- .../src/plugins/@angular/common/api.ts | 2 +- .../src/plugins/@angular/common/config.ts | 3 +- .../plugins/@angular/common/httpRequests.ts | 15 +- .../plugins/@angular/common/httpResources.ts | 13 +- .../src/plugins/@angular/common/types.d.ts | 5 +- .../plugins/@hey-api/client-angular/api.ts | 2 +- .../plugins/@hey-api/client-angular/config.ts | 10 +- .../@hey-api/client-angular/types.d.ts | 5 +- .../src/plugins/@hey-api/client-axios/api.ts | 2 +- .../plugins/@hey-api/client-axios/config.ts | 10 +- .../plugins/@hey-api/client-axios/types.d.ts | 5 +- .../plugins/@hey-api/client-core/client.ts | 7 +- .../client-core/createClientConfig.ts | 5 +- .../plugins/@hey-api/client-core/types.d.ts | 14 +- .../src/plugins/@hey-api/client-core/utils.ts | 4 +- .../src/plugins/@hey-api/client-fetch/api.ts | 2 +- .../plugins/@hey-api/client-fetch/config.ts | 10 +- .../plugins/@hey-api/client-fetch/types.d.ts | 5 +- .../src/plugins/@hey-api/client-next/api.ts | 2 +- .../plugins/@hey-api/client-next/config.ts | 10 +- .../plugins/@hey-api/client-next/types.d.ts | 5 +- .../src/plugins/@hey-api/client-nuxt/api.ts | 2 +- .../plugins/@hey-api/client-nuxt/config.ts | 10 +- .../plugins/@hey-api/client-nuxt/types.d.ts | 5 +- .../src/plugins/@hey-api/client-ofetch/api.ts | 2 +- .../plugins/@hey-api/client-ofetch/config.ts | 10 +- .../plugins/@hey-api/client-ofetch/types.d.ts | 5 +- .../plugins/@hey-api/legacy-angular/config.ts | 3 +- .../@hey-api/legacy-angular/types.d.ts | 4 +- .../plugins/@hey-api/legacy-axios/config.ts | 3 +- .../plugins/@hey-api/legacy-axios/types.d.ts | 4 +- .../plugins/@hey-api/legacy-fetch/config.ts | 3 +- .../plugins/@hey-api/legacy-fetch/types.d.ts | 4 +- .../plugins/@hey-api/legacy-node/config.ts | 3 +- .../plugins/@hey-api/legacy-node/types.d.ts | 4 +- .../src/plugins/@hey-api/legacy-xhr/config.ts | 3 +- .../plugins/@hey-api/legacy-xhr/types.d.ts | 4 +- .../schemas/__tests__/schemas.test.ts | 11 +- .../src/plugins/@hey-api/schemas/api.ts | 2 +- .../src/plugins/@hey-api/schemas/config.ts | 3 +- .../plugins/@hey-api/schemas/plugin-legacy.ts | 11 +- .../src/plugins/@hey-api/schemas/plugin.ts | 17 +- .../src/plugins/@hey-api/schemas/types.d.ts | 11 +- .../@hey-api/sdk/__tests__/plugin.test.ts | 15 +- .../src/plugins/@hey-api/sdk/api.ts | 3 +- .../src/plugins/@hey-api/sdk/auth.ts | 3 +- .../src/plugins/@hey-api/sdk/comment.ts | 6 +- .../src/plugins/@hey-api/sdk/config.ts | 3 +- .../src/plugins/@hey-api/sdk/operation.ts | 21 +- .../src/plugins/@hey-api/sdk/plugin-legacy.ts | 35 +-- .../src/plugins/@hey-api/sdk/plugin.ts | 11 +- .../src/plugins/@hey-api/sdk/signature.ts | 2 +- .../src/plugins/@hey-api/sdk/typeOptions.ts | 7 +- .../src/plugins/@hey-api/sdk/types.d.ts | 9 +- .../src/plugins/@hey-api/sdk/validator.ts | 3 +- .../src/plugins/@hey-api/transformers/api.ts | 2 +- .../plugins/@hey-api/transformers/config.ts | 3 +- .../@hey-api/transformers/expressions.ts | 5 +- .../@hey-api/transformers/plugin-legacy.ts | 18 +- .../plugins/@hey-api/transformers/plugin.ts | 14 +- .../plugins/@hey-api/transformers/types.d.ts | 5 +- .../typescript/__tests__/plugin.test.ts | 11 +- .../src/plugins/@hey-api/typescript/api.ts | 3 +- .../@hey-api/typescript/clientOptions.ts | 12 +- .../src/plugins/@hey-api/typescript/config.ts | 3 +- .../src/plugins/@hey-api/typescript/export.ts | 11 +- .../plugins/@hey-api/typescript/operation.ts | 11 +- .../@hey-api/typescript/plugin-legacy.ts | 34 ++- .../src/plugins/@hey-api/typescript/plugin.ts | 21 +- .../plugins/@hey-api/typescript/types.d.ts | 5 +- .../plugins/@hey-api/typescript/webhook.ts | 9 +- .../plugins/@hey-api/typescript/webhooks.ts | 3 +- .../src/plugins/@pinia/colada/api.ts | 2 +- .../src/plugins/@pinia/colada/config.ts | 3 +- .../src/plugins/@pinia/colada/meta.ts | 5 +- .../plugins/@pinia/colada/mutationOptions.ts | 7 +- .../src/plugins/@pinia/colada/plugin.ts | 5 +- .../src/plugins/@pinia/colada/queryKey.ts | 13 +- .../src/plugins/@pinia/colada/queryOptions.ts | 9 +- .../src/plugins/@pinia/colada/types.d.ts | 7 +- .../src/plugins/@pinia/colada/useType.ts | 7 +- .../src/plugins/@pinia/colada/utils.ts | 3 +- .../angular-query-experimental/api.ts | 2 +- .../angular-query-experimental/config.ts | 7 +- .../angular-query-experimental/types.d.ts | 7 +- .../query-core/infiniteQueryOptions.ts | 13 +- .../src/plugins/@tanstack/query-core/meta.ts | 5 +- .../@tanstack/query-core/mutationOptions.ts | 7 +- .../@tanstack/query-core/plugin-legacy.ts | 54 ++-- .../plugins/@tanstack/query-core/plugin.ts | 5 +- .../plugins/@tanstack/query-core/queryKey.ts | 11 +- .../@tanstack/query-core/queryOptions.ts | 9 +- .../plugins/@tanstack/query-core/types.d.ts | 10 +- .../plugins/@tanstack/query-core/useQuery.ts | 9 +- .../plugins/@tanstack/query-core/useType.ts | 7 +- .../src/plugins/@tanstack/react-query/api.ts | 2 +- .../plugins/@tanstack/react-query/config.ts | 7 +- .../plugins/@tanstack/react-query/types.d.ts | 7 +- .../src/plugins/@tanstack/solid-query/api.ts | 2 +- .../plugins/@tanstack/solid-query/config.ts | 7 +- .../plugins/@tanstack/solid-query/types.d.ts | 7 +- .../src/plugins/@tanstack/svelte-query/api.ts | 2 +- .../plugins/@tanstack/svelte-query/config.ts | 7 +- .../plugins/@tanstack/svelte-query/types.d.ts | 7 +- .../src/plugins/@tanstack/vue-query/api.ts | 2 +- .../src/plugins/@tanstack/vue-query/config.ts | 7 +- .../plugins/@tanstack/vue-query/types.d.ts | 7 +- .../openapi-ts/src/plugins/arktype/api.ts | 3 +- .../openapi-ts/src/plugins/arktype/config.ts | 3 +- .../src/plugins/arktype/constants.ts | 2 +- .../src/plugins/arktype/shared/export.ts | 7 +- .../src/plugins/arktype/shared/types.d.ts | 5 +- .../openapi-ts/src/plugins/arktype/types.d.ts | 5 +- .../openapi-ts/src/plugins/arktype/v2/api.ts | 3 +- .../src/plugins/arktype/v2/plugin.ts | 17 +- .../src/plugins/arktype/v2/toAst/index.ts | 3 +- .../src/plugins/arktype/v2/toAst/null.ts | 3 +- .../src/plugins/arktype/v2/toAst/object.ts | 9 +- .../src/plugins/arktype/v2/toAst/string.ts | 3 +- packages/openapi-ts/src/plugins/config.ts | 106 ++++---- .../openapi-ts/src/plugins/fastify/api.ts | 2 +- .../openapi-ts/src/plugins/fastify/config.ts | 3 +- .../openapi-ts/src/plugins/fastify/plugin.ts | 9 +- .../openapi-ts/src/plugins/fastify/types.d.ts | 3 +- .../src/plugins/shared/types/instance.d.ts | 4 +- .../src/plugins/shared/types/schema.d.ts | 2 +- .../src/plugins/shared/utils/case.ts | 6 +- .../src/plugins/shared/utils/config.ts | 2 +- .../src/plugins/shared/utils/instance.ts | 17 +- .../src/plugins/shared/utils/operation.ts | 6 +- .../src/plugins/shared/utils/schema.ts | 6 +- packages/openapi-ts/src/plugins/types.d.ts | 14 +- .../openapi-ts/src/plugins/valibot/api.ts | 3 +- .../openapi-ts/src/plugins/valibot/config.ts | 3 +- .../src/plugins/valibot/shared/numbers.ts | 2 +- .../src/plugins/valibot/shared/types.d.ts | 7 +- .../openapi-ts/src/plugins/valibot/types.d.ts | 5 +- .../openapi-ts/src/plugins/valibot/v1/api.ts | 3 +- .../src/plugins/valibot/v1/constants.ts | 2 +- .../src/plugins/valibot/v1/operation.ts | 11 +- .../src/plugins/valibot/v1/pipesToAst.ts | 3 +- .../src/plugins/valibot/v1/plugin.ts | 19 +- .../src/plugins/valibot/v1/toAst/array.ts | 9 +- .../src/plugins/valibot/v1/toAst/boolean.ts | 5 +- .../src/plugins/valibot/v1/toAst/enum.ts | 5 +- .../src/plugins/valibot/v1/toAst/index.ts | 3 +- .../src/plugins/valibot/v1/toAst/never.ts | 5 +- .../src/plugins/valibot/v1/toAst/null.ts | 5 +- .../src/plugins/valibot/v1/toAst/number.ts | 5 +- .../src/plugins/valibot/v1/toAst/object.ts | 9 +- .../src/plugins/valibot/v1/toAst/string.ts | 5 +- .../src/plugins/valibot/v1/toAst/tuple.ts | 7 +- .../src/plugins/valibot/v1/toAst/undefined.ts | 5 +- .../src/plugins/valibot/v1/toAst/unknown.ts | 5 +- .../src/plugins/valibot/v1/toAst/void.ts | 5 +- .../src/plugins/valibot/v1/webhook.ts | 7 +- packages/openapi-ts/src/plugins/zod/api.ts | 3 +- packages/openapi-ts/src/plugins/zod/config.ts | 3 +- .../openapi-ts/src/plugins/zod/constants.ts | 2 +- .../openapi-ts/src/plugins/zod/mini/api.ts | 3 +- .../openapi-ts/src/plugins/zod/mini/plugin.ts | 17 +- .../src/plugins/zod/mini/toAst/array.ts | 9 +- .../src/plugins/zod/mini/toAst/boolean.ts | 5 +- .../src/plugins/zod/mini/toAst/enum.ts | 5 +- .../src/plugins/zod/mini/toAst/index.ts | 3 +- .../src/plugins/zod/mini/toAst/never.ts | 5 +- .../src/plugins/zod/mini/toAst/null.ts | 5 +- .../src/plugins/zod/mini/toAst/number.ts | 5 +- .../src/plugins/zod/mini/toAst/object.ts | 9 +- .../src/plugins/zod/mini/toAst/string.ts | 5 +- .../src/plugins/zod/mini/toAst/tuple.ts | 7 +- .../src/plugins/zod/mini/toAst/undefined.ts | 5 +- .../src/plugins/zod/mini/toAst/unknown.ts | 5 +- .../src/plugins/zod/mini/toAst/void.ts | 5 +- .../src/plugins/zod/shared/export.ts | 7 +- .../src/plugins/zod/shared/numbers.ts | 2 +- .../src/plugins/zod/shared/operation.ts | 9 +- .../src/plugins/zod/shared/types.d.ts | 5 +- .../src/plugins/zod/shared/webhook.ts | 7 +- .../openapi-ts/src/plugins/zod/types.d.ts | 5 +- packages/openapi-ts/src/plugins/zod/v3/api.ts | 3 +- .../openapi-ts/src/plugins/zod/v3/plugin.ts | 17 +- .../src/plugins/zod/v3/toAst/array.ts | 9 +- .../src/plugins/zod/v3/toAst/boolean.ts | 5 +- .../src/plugins/zod/v3/toAst/enum.ts | 5 +- .../src/plugins/zod/v3/toAst/index.ts | 3 +- .../src/plugins/zod/v3/toAst/never.ts | 5 +- .../src/plugins/zod/v3/toAst/null.ts | 5 +- .../src/plugins/zod/v3/toAst/number.ts | 5 +- .../src/plugins/zod/v3/toAst/object.ts | 9 +- .../src/plugins/zod/v3/toAst/string.ts | 5 +- .../src/plugins/zod/v3/toAst/tuple.ts | 7 +- .../src/plugins/zod/v3/toAst/undefined.ts | 5 +- .../src/plugins/zod/v3/toAst/unknown.ts | 5 +- .../src/plugins/zod/v3/toAst/void.ts | 5 +- packages/openapi-ts/src/plugins/zod/v4/api.ts | 3 +- .../openapi-ts/src/plugins/zod/v4/plugin.ts | 17 +- .../src/plugins/zod/v4/toAst/array.ts | 9 +- .../src/plugins/zod/v4/toAst/boolean.ts | 5 +- .../src/plugins/zod/v4/toAst/enum.ts | 5 +- .../src/plugins/zod/v4/toAst/index.ts | 3 +- .../src/plugins/zod/v4/toAst/never.ts | 5 +- .../src/plugins/zod/v4/toAst/null.ts | 5 +- .../src/plugins/zod/v4/toAst/number.ts | 5 +- .../src/plugins/zod/v4/toAst/object.ts | 9 +- .../src/plugins/zod/v4/toAst/string.ts | 5 +- .../src/plugins/zod/v4/toAst/tuple.ts | 7 +- .../src/plugins/zod/v4/toAst/undefined.ts | 5 +- .../src/plugins/zod/v4/toAst/unknown.ts | 5 +- .../src/plugins/zod/v4/toAst/void.ts | 5 +- packages/openapi-ts/src/processOutput.ts | 4 +- packages/openapi-ts/src/tsc/transform.ts | 3 +- packages/openapi-ts/src/tsc/typedef.ts | 3 +- packages/openapi-ts/src/tsc/types.ts | 5 +- packages/openapi-ts/src/tsc/utils.ts | 5 +- packages/openapi-ts/src/types/client.d.ts | 2 +- packages/openapi-ts/src/types/config.d.ts | 5 +- packages/openapi-ts/src/types/parser.d.ts | 5 +- packages/openapi-ts/src/types/utils.d.ts | 2 +- .../src/utils/__tests__/parse.test.ts | 3 +- .../src/utils/__tests__/postprocess.test.ts | 5 +- .../src/utils/__tests__/sort.test.ts | 3 +- .../src/utils/__tests__/stringCase.test.ts | 3 +- .../src/utils/__tests__/type.test.ts | 3 +- packages/openapi-ts/src/utils/cli.ts | 2 +- packages/openapi-ts/src/utils/config.ts | 4 +- packages/openapi-ts/src/utils/enum.ts | 5 +- .../src/utils/getHttpRequestName.ts | 2 +- packages/openapi-ts/src/utils/handlebars.ts | 89 +++---- packages/openapi-ts/src/utils/input/heyApi.ts | 2 +- packages/openapi-ts/src/utils/input/index.ts | 3 +- packages/openapi-ts/src/utils/input/readme.ts | 2 +- packages/openapi-ts/src/utils/input/scalar.ts | 2 +- packages/openapi-ts/src/utils/meta.ts | 3 +- packages/openapi-ts/src/utils/postprocess.ts | 9 +- packages/openapi-ts/src/utils/stringCase.ts | 2 +- packages/openapi-ts/src/utils/transform.ts | 5 +- packages/openapi-ts/src/utils/type.ts | 9 +- packages/openapi-ts/tsconfig.base.json | 6 +- packages/openapi-ts/vitest.config.ts | 21 +- 399 files changed, 1801 insertions(+), 1212 deletions(-) create mode 100644 eslint-rules/local-paths.js diff --git a/eslint-rules/local-paths.js b/eslint-rules/local-paths.js new file mode 100644 index 000000000..10fedd255 --- /dev/null +++ b/eslint-rules/local-paths.js @@ -0,0 +1,233 @@ +import path from 'node:path'; + +function normalize(p) { + return p.split(path.sep).join('/'); +} + +function stripExtAndIndex(p) { + return p + .replace(/(\/index)?\.(ts|tsx|js|cjs|mjs|d\.ts)$/, '') + .replace(/\/index$/, ''); +} + +/** + * Single consolidated rule implementing the previous three behaviors: + * - plugins: allow relative within same plugin (plugin may be @scope/name), else require `~` + * - openApi: allow relative only within same first-level openApi folder, else require `~` + * - first-level folders: allow relative only within same first-level folder, else require `~` + */ +const enforceLocalPaths = { + create(context) { + const filename = context.getFilename(); + if (!filename || filename === '') return {}; + + const normalizedFile = normalize(filename); + const srcMarker = '/packages/openapi-ts/src/'; + const srcIdx = normalizedFile.indexOf(srcMarker); + if (srcIdx === -1) return {}; + + const after = normalizedFile.slice(srcIdx + srcMarker.length); + const parts = after.split('/'); + const firstLevel = parts[0]; + + // helpers and mode-specific roots + // compute the absolute normalized bundle root for accurate comparisons + let bundleAbsRoot = + normalizedFile.slice(0, srcIdx + srcMarker.length) + + 'plugins/@hey-api/client-core/bundle/'; + bundleAbsRoot = normalize(bundleAbsRoot); + + let mode = 'first-level'; + let pluginAbsRoot = null; + let openApiFirstRoot = null; + let firstRoot = null; + + if (firstLevel === 'plugins') { + mode = 'plugins'; + // derive plugin folder (support scoped plugin names) + let pluginFolder = parts[1] || ''; + if (pluginFolder.startsWith('@') && parts.length > 2) { + pluginFolder = `${pluginFolder}/${parts[2]}`; + } + pluginAbsRoot = + normalizedFile.slice(0, srcIdx + srcMarker.length) + + `plugins/${pluginFolder}/`; + pluginAbsRoot = normalize(pluginAbsRoot); + } else if (firstLevel === 'openApi') { + mode = 'openApi'; + const apiFirst = parts[1] || ''; + openApiFirstRoot = + normalizedFile.slice(0, srcIdx + srcMarker.length) + + `openApi/${apiFirst}/`; + openApiFirstRoot = normalize(openApiFirstRoot); + } else { + firstRoot = + normalizedFile.slice(0, srcIdx + srcMarker.length) + `${firstLevel}/`; + firstRoot = normalize(firstRoot); + } + + function resolveFromBasedir(basedir, sourceValue) { + try { + return path.resolve(basedir, sourceValue); + } catch { + return null; + } + } + + function resolveTildeToAbs(sourceValue) { + const rest = sourceValue.replace(/^~\/?/, ''); + return path.resolve(process.cwd(), 'packages/openapi-ts/src', rest); + } + + function toTilde(resolvedAbsolutePath) { + const normalized = normalize(resolvedAbsolutePath); + const marker = '/packages/openapi-ts/src/'; + const i = normalized.indexOf(marker); + if (i === -1) return null; + let rest = normalized.slice(i + marker.length); + rest = stripExtAndIndex(rest); + return `~/${rest}`; + } + + function reportReplaceWithTilde(node, sourceValue, message) { + const basedir = path.dirname(filename); + const resolved = resolveFromBasedir(basedir, sourceValue); + if (!resolved) return; + const newImport = toTilde(resolved); + if (!newImport) return; + context.report({ + fix(fixer) { + return fixer.replaceText(node.source, `'${newImport}'`); + }, + message, + node: node.source, + }); + } + + function reportReplaceWithRelative(node, sourceValue, message) { + const resolved = resolveTildeToAbs(sourceValue); + if (!resolved) return; + let relativePath = path + .relative(path.dirname(filename), resolved) + .split(path.sep) + .join('/'); + relativePath = stripExtAndIndex(relativePath); + if (!relativePath.startsWith('.')) relativePath = `./${relativePath}`; + context.report({ + fix(fixer) { + return fixer.replaceText(node.source, `'${relativePath}'`); + }, + message, + node: node.source, + }); + } + + function shouldRewriteRelative(sourceValue) { + if (typeof sourceValue !== 'string') return false; + if (!sourceValue.startsWith('.')) return false; + const basedir = path.dirname(filename); + const resolved = resolveFromBasedir(basedir, sourceValue); + if (!resolved) return false; + const nr = normalize(resolved); + if (!nr.includes('/packages/openapi-ts/src/')) return false; + + if (mode === 'plugins') { + if (nr.startsWith(pluginAbsRoot)) return false; // inside same plugin -> keep relative + if (nr.startsWith(bundleAbsRoot)) return false; // client-core bundle: keep relative + return true; + } + + if (mode === 'openApi') { + // if target is inside same first-level openApi folder -> don't rewrite + if (nr.startsWith(openApiFirstRoot)) return false; + // only rewrite if target is inside openApi at all + return nr.includes('/packages/openapi-ts/src/openApi/'); + } + + // generic first-level folder rule + if (nr.startsWith(firstRoot)) return false; + return true; + } + + function shouldRewriteTilde(sourceValue) { + if (typeof sourceValue !== 'string') return false; + if (!sourceValue.startsWith('~')) return false; + const resolved = resolveTildeToAbs(sourceValue); + if (!resolved) return false; + const nr = normalize(resolved); + + if (mode === 'plugins') { + // prefer relative imports for same-plugin targets or for the shared client-core bundle + return nr.startsWith(pluginAbsRoot) || nr.startsWith(bundleAbsRoot); + } + if (mode === 'openApi') { + return nr.startsWith(openApiFirstRoot); + } + return nr.startsWith(firstRoot); + } + + return { + ExportAllDeclaration(node) { + const sourceValue = node.source && node.source.value; + if (shouldRewriteRelative(sourceValue)) { + reportReplaceWithTilde( + node, + sourceValue, + 'Prefer `~` export for cross-boundary exports (autofixable).', + ); + return; + } + if (shouldRewriteTilde(sourceValue)) { + reportReplaceWithRelative( + node, + sourceValue, + 'Prefer relative export for intra-boundary targets (autofixable).', + ); + return; + } + }, + ImportDeclaration(node) { + const sourceValue = node.source && node.source.value; + if (shouldRewriteRelative(sourceValue)) { + reportReplaceWithTilde( + node, + sourceValue, + 'Prefer `~` import for cross-boundary imports (autofixable).', + ); + return; + } + if (shouldRewriteTilde(sourceValue)) { + reportReplaceWithRelative( + node, + sourceValue, + 'Prefer relative import for intra-boundary targets (autofixable).', + ); + return; + } + }, + }; + }, + meta: { + docs: { + description: + 'Enforce local import path boundaries for openapi-ts sources', + recommended: false, + }, + fixable: 'code', + schema: [], + type: 'suggestion', + }, +}; + +export default { + configs: { + recommended: { + rules: { + 'local-paths/enforce-local-paths': 'error', + }, + }, + }, + rules: { + 'enforce-local-paths': enforceLocalPaths, + }, +}; diff --git a/eslint.config.js b/eslint.config.js index 42826d4b0..93fb9b916 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -8,9 +8,12 @@ import pluginTypeScriptSortKeys from 'eslint-plugin-typescript-sort-keys'; import globals from 'globals'; import tseslint from 'typescript-eslint'; +import pluginLocalPaths from './eslint-rules/local-paths.js'; + export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, + pluginLocalPaths.configs.recommended, { languageOptions: { ecmaVersion: 'latest', @@ -19,6 +22,7 @@ export default tseslint.config( }, }, plugins: { + 'local-paths': pluginLocalPaths, 'simple-import-sort': pluginSimpleImportSort, 'sort-destructure-keys': pluginSortDestructureKeys, 'sort-keys-fix': pluginSortKeysFix, diff --git a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts index 68f1ac07c..fb1858316 100644 --- a/packages/openapi-ts-tests/main/test/openapi-ts.config.ts +++ b/packages/openapi-ts-tests/main/test/openapi-ts.config.ts @@ -275,7 +275,7 @@ export default defineConfig(() => { // mutationOptions: { // name: '{{name}}MO', // }, - // name: '@tanstack/react-query', + name: '@tanstack/react-query', // queryKeys: { // name: '{{name}}QK', // }, diff --git a/packages/openapi-ts-tests/main/vitest.config.ts b/packages/openapi-ts-tests/main/vitest.config.ts index d13ef5fee..7c627e64f 100644 --- a/packages/openapi-ts-tests/main/vitest.config.ts +++ b/packages/openapi-ts-tests/main/vitest.config.ts @@ -1,7 +1,15 @@ +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createVitestConfig } from '@config/vite-base'; -export default createVitestConfig( - fileURLToPath(new URL('./', import.meta.url)), -); +const rootDir = fileURLToPath(new URL('./', import.meta.url)); + +export default createVitestConfig(rootDir, { + resolve: { + alias: [ + { find: /^~\/(.*)/, replacement: path.resolve(rootDir, 'src/$1') }, + { find: '~', replacement: path.resolve(rootDir, 'src') }, + ], + }, +}); diff --git a/packages/openapi-ts-tests/zod/v3/vitest.config.ts b/packages/openapi-ts-tests/zod/v3/vitest.config.ts index d13ef5fee..7c627e64f 100644 --- a/packages/openapi-ts-tests/zod/v3/vitest.config.ts +++ b/packages/openapi-ts-tests/zod/v3/vitest.config.ts @@ -1,7 +1,15 @@ +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createVitestConfig } from '@config/vite-base'; -export default createVitestConfig( - fileURLToPath(new URL('./', import.meta.url)), -); +const rootDir = fileURLToPath(new URL('./', import.meta.url)); + +export default createVitestConfig(rootDir, { + resolve: { + alias: [ + { find: /^~\/(.*)/, replacement: path.resolve(rootDir, 'src/$1') }, + { find: '~', replacement: path.resolve(rootDir, 'src') }, + ], + }, +}); diff --git a/packages/openapi-ts-tests/zod/v4/vitest.config.ts b/packages/openapi-ts-tests/zod/v4/vitest.config.ts index d13ef5fee..7c627e64f 100644 --- a/packages/openapi-ts-tests/zod/v4/vitest.config.ts +++ b/packages/openapi-ts-tests/zod/v4/vitest.config.ts @@ -1,7 +1,15 @@ +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createVitestConfig } from '@config/vite-base'; -export default createVitestConfig( - fileURLToPath(new URL('./', import.meta.url)), -); +const rootDir = fileURLToPath(new URL('./', import.meta.url)); + +export default createVitestConfig(rootDir, { + resolve: { + alias: [ + { find: /^~\/(.*)/, replacement: path.resolve(rootDir, 'src/$1') }, + { find: '~', replacement: path.resolve(rootDir, 'src') }, + ], + }, +}); diff --git a/packages/openapi-ts/src/__tests__/createClient.test.ts b/packages/openapi-ts/src/__tests__/createClient.test.ts index 6c4acec07..6b0886dfc 100644 --- a/packages/openapi-ts/src/__tests__/createClient.test.ts +++ b/packages/openapi-ts/src/__tests__/createClient.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { compileInputPath } from '../createClient'; +import { compileInputPath } from '~/createClient'; describe('compileInputPath', () => { it('with raw OpenAPI specification', () => { diff --git a/packages/openapi-ts/src/__tests__/error.test.ts b/packages/openapi-ts/src/__tests__/error.test.ts index 57cff1d4f..15e7abcea 100644 --- a/packages/openapi-ts/src/__tests__/error.test.ts +++ b/packages/openapi-ts/src/__tests__/error.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import { shouldReportCrash } from '../error'; +import { shouldReportCrash } from '~/error'; describe('shouldReportCrash', () => { it('should return false when isInteractive is false', async () => { diff --git a/packages/openapi-ts/src/__tests__/index.test.ts b/packages/openapi-ts/src/__tests__/index.test.ts index d2c7b631a..c3374ee5c 100644 --- a/packages/openapi-ts/src/__tests__/index.test.ts +++ b/packages/openapi-ts/src/__tests__/index.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { createClient } from '../index'; +import { createClient } from '~/index'; type Config = Parameters[0]; diff --git a/packages/openapi-ts/src/__tests__/interactive.test.ts b/packages/openapi-ts/src/__tests__/interactive.test.ts index 626e357b4..7812c9546 100644 --- a/packages/openapi-ts/src/__tests__/interactive.test.ts +++ b/packages/openapi-ts/src/__tests__/interactive.test.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { detectInteractiveSession, initConfigs } from '../config/init'; -import { mergeConfigs } from '../config/merge'; -import { Logger } from '../utils/logger'; +import { detectInteractiveSession, initConfigs } from '~/config/init'; +import { mergeConfigs } from '~/config/merge'; +import { Logger } from '~/utils/logger'; describe('interactive config', () => { it('should use detectInteractiveSession when not provided', async () => { diff --git a/packages/openapi-ts/src/config/__tests__/input.test.ts b/packages/openapi-ts/src/config/__tests__/input.test.ts index b55b3246f..c71da149f 100644 --- a/packages/openapi-ts/src/config/__tests__/input.test.ts +++ b/packages/openapi-ts/src/config/__tests__/input.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; -import type { UserConfig } from '../../types/config'; +import type { UserConfig } from '~/types/config'; + import { getInput } from '../input'; describe('input config', () => { diff --git a/packages/openapi-ts/src/config/engine.ts b/packages/openapi-ts/src/config/engine.ts index 637ead0ea..c7189278b 100644 --- a/packages/openapi-ts/src/config/engine.ts +++ b/packages/openapi-ts/src/config/engine.ts @@ -1,4 +1,4 @@ -import { ConfigError } from '../error'; +import { ConfigError } from '~/error'; export const checkNodeVersion = () => { if (typeof Bun !== 'undefined') { diff --git a/packages/openapi-ts/src/config/init.ts b/packages/openapi-ts/src/config/init.ts index 7350a7401..e7d629d36 100644 --- a/packages/openapi-ts/src/config/init.ts +++ b/packages/openapi-ts/src/config/init.ts @@ -2,11 +2,12 @@ import path from 'node:path'; import colors from 'ansi-colors'; -import { ConfigError } from '../error'; -import type { Config, UserConfig } from '../types/config'; -import type { ArrayOnly } from '../types/utils'; -import { isLegacyClient, setConfig } from '../utils/config'; -import type { Logger } from '../utils/logger'; +import { ConfigError } from '~/error'; +import type { Config, UserConfig } from '~/types/config'; +import type { ArrayOnly } from '~/types/utils'; +import { isLegacyClient, setConfig } from '~/utils/config'; +import type { Logger } from '~/utils/logger'; + import { getInput } from './input'; import { getLogs } from './logs'; import { mergeConfigs } from './merge'; diff --git a/packages/openapi-ts/src/config/input.ts b/packages/openapi-ts/src/config/input.ts index 39629a80b..2aea3667d 100644 --- a/packages/openapi-ts/src/config/input.ts +++ b/packages/openapi-ts/src/config/input.ts @@ -1,7 +1,7 @@ -import type { Config, UserConfig } from '../types/config'; -import type { Input, Watch } from '../types/input'; -import { inputToApiRegistry } from '../utils/input'; -import { heyApiRegistryBaseUrl } from '../utils/input/heyApi'; +import type { Config, UserConfig } from '~/types/config'; +import type { Input, Watch } from '~/types/input'; +import { inputToApiRegistry } from '~/utils/input'; +import { heyApiRegistryBaseUrl } from '~/utils/input/heyApi'; const defaultWatch: Watch = { enabled: false, diff --git a/packages/openapi-ts/src/config/logs.ts b/packages/openapi-ts/src/config/logs.ts index ada1300ef..91d5742c5 100644 --- a/packages/openapi-ts/src/config/logs.ts +++ b/packages/openapi-ts/src/config/logs.ts @@ -1,4 +1,4 @@ -import type { Config, UserConfig } from '../types/config'; +import type { Config, UserConfig } from '~/types/config'; export const getLogs = ( userConfig: Pick | undefined, diff --git a/packages/openapi-ts/src/config/merge.ts b/packages/openapi-ts/src/config/merge.ts index 7ade55e4d..5dc7b3a86 100644 --- a/packages/openapi-ts/src/config/merge.ts +++ b/packages/openapi-ts/src/config/merge.ts @@ -1,4 +1,4 @@ -import type { UserConfig } from '../types/config'; +import type { UserConfig } from '~/types/config'; const mergeObjects = ( objA: Record | undefined, diff --git a/packages/openapi-ts/src/config/output.ts b/packages/openapi-ts/src/config/output.ts index e03fff839..98ac0563c 100644 --- a/packages/openapi-ts/src/config/output.ts +++ b/packages/openapi-ts/src/config/output.ts @@ -1,7 +1,8 @@ import ts from 'typescript'; -import { findTsConfigPath, loadTsConfig } from '../generate/tsConfig'; -import type { Config, UserConfig } from '../types/config'; +import { findTsConfigPath, loadTsConfig } from '~/generate/tsConfig'; +import type { Config, UserConfig } from '~/types/config'; + import { valueToObject } from './utils/config'; export const getOutput = (userConfig: UserConfig): Config['output'] => { diff --git a/packages/openapi-ts/src/config/parser.ts b/packages/openapi-ts/src/config/parser.ts index e44f6af86..82aeb2f39 100644 --- a/packages/openapi-ts/src/config/parser.ts +++ b/packages/openapi-ts/src/config/parser.ts @@ -1,4 +1,5 @@ -import type { Config, UserConfig } from '../types/config'; +import type { Config, UserConfig } from '~/types/config'; + import { valueToObject } from './utils/config'; export const defaultPaginationKeywords = [ diff --git a/packages/openapi-ts/src/config/plugins.ts b/packages/openapi-ts/src/config/plugins.ts index 94210a902..7ffb90b21 100644 --- a/packages/openapi-ts/src/config/plugins.ts +++ b/packages/openapi-ts/src/config/plugins.ts @@ -1,10 +1,11 @@ -import { defaultPluginConfigs } from '../plugins/config'; +import { defaultPluginConfigs } from '~/plugins/config'; import type { AnyPluginName, PluginContext, PluginNames, -} from '../plugins/types'; -import type { Config, UserConfig } from '../types/config'; +} from '~/plugins/types'; +import type { Config, UserConfig } from '~/types/config'; + import { valueToObject } from './utils/config'; import { packageFactory } from './utils/package'; diff --git a/packages/openapi-ts/src/createClient.ts b/packages/openapi-ts/src/createClient.ts index 23f63ce23..aa04acf60 100644 --- a/packages/openapi-ts/src/createClient.ts +++ b/packages/openapi-ts/src/createClient.ts @@ -3,22 +3,22 @@ import path from 'node:path'; import { $RefParser } from '@hey-api/json-schema-ref-parser'; import colors from 'ansi-colors'; -import { generateLegacyOutput } from './generate/legacy/output'; -import { generateOutput } from './generate/output'; -import { getSpec } from './getSpec'; -import type { IR } from './ir/types'; -import { parseLegacy, parseOpenApiSpec } from './openApi'; -import { buildGraph } from './openApi/shared/utils/graph'; -import { patchOpenApiSpec } from './openApi/shared/utils/patch'; -import { processOutput } from './processOutput'; -import type { Client } from './types/client'; -import type { Config } from './types/config'; -import type { Input } from './types/input'; -import type { WatchValues } from './types/types'; -import { isLegacyClient, legacyNameFromConfig } from './utils/config'; -import type { Templates } from './utils/handlebars'; -import type { Logger } from './utils/logger'; -import { postProcessClient } from './utils/postprocess'; +import { generateLegacyOutput } from '~/generate/legacy/output'; +import { generateOutput } from '~/generate/output'; +import { getSpec } from '~/getSpec'; +import type { IR } from '~/ir/types'; +import { parseLegacy, parseOpenApiSpec } from '~/openApi'; +import { buildGraph } from '~/openApi/shared/utils/graph'; +import { patchOpenApiSpec } from '~/openApi/shared/utils/patch'; +import { processOutput } from '~/processOutput'; +import type { Client } from '~/types/client'; +import type { Config } from '~/types/config'; +import type { Input } from '~/types/input'; +import type { WatchValues } from '~/types/types'; +import { isLegacyClient, legacyNameFromConfig } from '~/utils/config'; +import type { Templates } from '~/utils/handlebars'; +import type { Logger } from '~/utils/logger'; +import { postProcessClient } from '~/utils/postprocess'; export const compileInputPath = (input: Omit) => { const result: Pick< diff --git a/packages/openapi-ts/src/debug/graph.ts b/packages/openapi-ts/src/debug/graph.ts index c04f6a19a..38f3e7ca5 100644 --- a/packages/openapi-ts/src/debug/graph.ts +++ b/packages/openapi-ts/src/debug/graph.ts @@ -1,4 +1,4 @@ -import type { Graph } from '../openApi/shared/utils/graph'; +import type { Graph } from '~/openApi/shared/utils/graph'; const analyzeStructure = (graph: Graph) => { let maxDepth = 0; diff --git a/packages/openapi-ts/src/debug/ir.ts b/packages/openapi-ts/src/debug/ir.ts index 1a0f62d39..03bf852c1 100644 --- a/packages/openapi-ts/src/debug/ir.ts +++ b/packages/openapi-ts/src/debug/ir.ts @@ -1,7 +1,7 @@ import colors from 'ansi-colors'; -import type { IR } from '../ir/types'; -import { httpMethods } from '../openApi/shared/utils/operation'; +import type { IR } from '~/ir/types'; +import { httpMethods } from '~/openApi/shared/utils/operation'; export interface PrintOptions { /** diff --git a/packages/openapi-ts/src/error.ts b/packages/openapi-ts/src/error.ts index 94173f6dd..229d6ee5d 100644 --- a/packages/openapi-ts/src/error.ts +++ b/packages/openapi-ts/src/error.ts @@ -3,8 +3,8 @@ import path from 'node:path'; import colors from 'ansi-colors'; -import { loadPackageJson } from './generate/tsConfig'; -import { ensureDirSync } from './generate/utils'; +import { loadPackageJson } from '~/generate/tsConfig'; +import { ensureDirSync } from '~/generate/utils'; type IJobError = { error: Error; diff --git a/packages/openapi-ts/src/generate/__tests__/class.test.ts b/packages/openapi-ts/src/generate/__tests__/class.test.ts index 4d56bc0b2..af7c37d34 100644 --- a/packages/openapi-ts/src/generate/__tests__/class.test.ts +++ b/packages/openapi-ts/src/generate/__tests__/class.test.ts @@ -3,8 +3,9 @@ import fs from 'node:fs'; import type ts from 'typescript'; import { describe, expect, it, vi } from 'vitest'; -import type { Config } from '../../types/config'; -import { setConfig } from '../../utils/config'; +import type { Config } from '~/types/config'; +import { setConfig } from '~/utils/config'; + import { generateLegacyClientClass } from '../class'; import { mockTemplates, openApi } from './mocks'; diff --git a/packages/openapi-ts/src/generate/__tests__/core.test.ts b/packages/openapi-ts/src/generate/__tests__/core.test.ts index 89b02629d..9da697ef8 100644 --- a/packages/openapi-ts/src/generate/__tests__/core.test.ts +++ b/packages/openapi-ts/src/generate/__tests__/core.test.ts @@ -4,8 +4,9 @@ import path from 'node:path'; import type ts from 'typescript'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import type { Config } from '../../types/config'; -import { setConfig } from '../../utils/config'; +import type { Config } from '~/types/config'; +import { setConfig } from '~/utils/config'; + import { generateLegacyCore } from '../core'; import { mockTemplates } from './mocks'; diff --git a/packages/openapi-ts/src/generate/__tests__/mocks.ts b/packages/openapi-ts/src/generate/__tests__/mocks.ts index 6d5718dd3..fdffc1900 100644 --- a/packages/openapi-ts/src/generate/__tests__/mocks.ts +++ b/packages/openapi-ts/src/generate/__tests__/mocks.ts @@ -1,9 +1,9 @@ import { vi } from 'vitest'; -import type { OpenApi } from '../../openApi'; -import type { Client } from '../../types/client'; -import type { Config } from '../../types/config'; -import type { Templates } from '../../utils/handlebars'; +import type { OpenApi } from '~/openApi'; +import type { Client } from '~/types/client'; +import type { Config } from '~/types/config'; +import type { Templates } from '~/utils/handlebars'; export const client: Client = { config: {} as Config, diff --git a/packages/openapi-ts/src/generate/class.ts b/packages/openapi-ts/src/generate/class.ts index 45a93cc03..e40b9f0bb 100644 --- a/packages/openapi-ts/src/generate/class.ts +++ b/packages/openapi-ts/src/generate/class.ts @@ -1,13 +1,14 @@ import fs from 'node:fs'; import path from 'node:path'; -import type { OpenApi } from '../openApi'; -import { getClientPlugin } from '../plugins/@hey-api/client-core/utils'; -import type { Client } from '../types/client'; -import { getConfig, legacyNameFromConfig } from '../utils/config'; -import { getHttpRequestName } from '../utils/getHttpRequestName'; -import type { Templates } from '../utils/handlebars'; -import { sortByName } from '../utils/sort'; +import type { OpenApi } from '~/openApi'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import type { Client } from '~/types/client'; +import { getConfig, legacyNameFromConfig } from '~/utils/config'; +import { getHttpRequestName } from '~/utils/getHttpRequestName'; +import type { Templates } from '~/utils/handlebars'; +import { sortByName } from '~/utils/sort'; + import { ensureDirSync } from './utils'; /** diff --git a/packages/openapi-ts/src/generate/client.ts b/packages/openapi-ts/src/generate/client.ts index fb38b6307..a93d3d214 100644 --- a/packages/openapi-ts/src/generate/client.ts +++ b/packages/openapi-ts/src/generate/client.ts @@ -4,10 +4,11 @@ import { fileURLToPath } from 'node:url'; import type { IProject, ProjectRenderMeta } from '@hey-api/codegen-core'; -import type { Client } from '../plugins/@hey-api/client-core/types'; -import { getClientPlugin } from '../plugins/@hey-api/client-core/utils'; -import type { DefinePlugin } from '../plugins/types'; -import type { Config } from '../types/config'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import type { DefinePlugin } from '~/plugins/types'; +import type { Config } from '~/types/config'; + import { ensureDirSync, relativeModulePath } from './utils'; const __filename = fileURLToPath(import.meta.url); diff --git a/packages/openapi-ts/src/generate/core.ts b/packages/openapi-ts/src/generate/core.ts index d72110874..ba4581bbc 100644 --- a/packages/openapi-ts/src/generate/core.ts +++ b/packages/openapi-ts/src/generate/core.ts @@ -1,11 +1,11 @@ import fs from 'node:fs'; import path from 'node:path'; -import { getClientPlugin } from '../plugins/@hey-api/client-core/utils'; -import type { Client } from '../types/client'; -import { getConfig, legacyNameFromConfig } from '../utils/config'; -import { getHttpRequestName } from '../utils/getHttpRequestName'; -import type { Templates } from '../utils/handlebars'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import type { Client } from '~/types/client'; +import { getConfig, legacyNameFromConfig } from '~/utils/config'; +import { getHttpRequestName } from '~/utils/getHttpRequestName'; +import type { Templates } from '~/utils/handlebars'; /** * Generate OpenAPI core files, this includes the basic boilerplate code to handle requests. diff --git a/packages/openapi-ts/src/generate/file.ts b/packages/openapi-ts/src/generate/file.ts index a928ba54c..684ad95ca 100644 --- a/packages/openapi-ts/src/generate/file.ts +++ b/packages/openapi-ts/src/generate/file.ts @@ -3,8 +3,9 @@ import path from 'node:path'; import ts from 'typescript'; -import { tsc } from '../tsc'; -import { type ImportExportItemObject, tsNodeToString } from '../tsc/utils'; +import { tsc } from '~/tsc'; +import { type ImportExportItemObject, tsNodeToString } from '~/tsc/utils'; + import { ensureDirSync } from './utils'; type FileImportResult< diff --git a/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts b/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts index 03c272773..ad794d20e 100644 --- a/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts +++ b/packages/openapi-ts/src/generate/legacy/__tests__/index.test.ts @@ -4,7 +4,8 @@ import path from 'node:path'; import type ts from 'typescript'; import { describe, expect, it, vi } from 'vitest'; -import { setConfig } from '../../../utils/config'; +import { setConfig } from '~/utils/config'; + import { GeneratedFile } from '../../file'; import { generateIndexFile } from '../indexFile'; diff --git a/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts b/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts index 6148cb2eb..c0897c382 100644 --- a/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts +++ b/packages/openapi-ts/src/generate/legacy/__tests__/output.test.ts @@ -3,9 +3,10 @@ import fs from 'node:fs'; import type ts from 'typescript'; import { describe, expect, it, vi } from 'vitest'; -import type { Client } from '../../../types/client'; -import type { Config } from '../../../types/config'; -import { setConfig } from '../../../utils/config'; +import type { Client } from '~/types/client'; +import type { Config } from '~/types/config'; +import { setConfig } from '~/utils/config'; + import { mockTemplates, openApi } from '../../__tests__/mocks'; import { generateLegacyOutput } from '../output'; diff --git a/packages/openapi-ts/src/generate/legacy/indexFile.ts b/packages/openapi-ts/src/generate/legacy/indexFile.ts index 1afeb9b00..f7b68184c 100644 --- a/packages/openapi-ts/src/generate/legacy/indexFile.ts +++ b/packages/openapi-ts/src/generate/legacy/indexFile.ts @@ -1,7 +1,8 @@ -import { getClientPlugin } from '../../plugins/@hey-api/client-core/utils'; -import { tsc } from '../../tsc'; -import type { Files } from '../../types/utils'; -import { getConfig, legacyNameFromConfig } from '../../utils/config'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import { tsc } from '~/tsc'; +import type { Files } from '~/types/utils'; +import { getConfig, legacyNameFromConfig } from '~/utils/config'; + import { GeneratedFile } from '../file'; export const generateIndexFile = ({ files }: { files: Files }): void => { diff --git a/packages/openapi-ts/src/generate/legacy/output.ts b/packages/openapi-ts/src/generate/legacy/output.ts index fb1a5105a..888abd1fa 100644 --- a/packages/openapi-ts/src/generate/legacy/output.ts +++ b/packages/openapi-ts/src/generate/legacy/output.ts @@ -2,12 +2,13 @@ import path from 'node:path'; import type { ProjectRenderMeta } from '@hey-api/codegen-core'; -import type { OpenApi } from '../../openApi'; -import { getClientPlugin } from '../../plugins/@hey-api/client-core/utils'; -import type { Client } from '../../types/client'; -import type { Files } from '../../types/utils'; -import { getConfig, isLegacyClient } from '../../utils/config'; -import type { Templates } from '../../utils/handlebars'; +import type { OpenApi } from '~/openApi'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import type { Client } from '~/types/client'; +import type { Files } from '~/types/utils'; +import { getConfig, isLegacyClient } from '~/utils/config'; +import type { Templates } from '~/utils/handlebars'; + import { generateLegacyClientClass } from '../class'; import { generateClientBundle } from '../client'; import { generateLegacyCore } from '../core'; diff --git a/packages/openapi-ts/src/generate/output.ts b/packages/openapi-ts/src/generate/output.ts index b479f6da6..6022833eb 100644 --- a/packages/openapi-ts/src/generate/output.ts +++ b/packages/openapi-ts/src/generate/output.ts @@ -3,8 +3,9 @@ import path from 'node:path'; import type { ProjectRenderMeta } from '@hey-api/codegen-core'; -import type { IR } from '../ir/types'; -import { getClientPlugin } from '../plugins/@hey-api/client-core/utils'; +import type { IR } from '~/ir/types'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; + import { generateClientBundle } from './client'; import { removeDirSync } from './utils'; diff --git a/packages/openapi-ts/src/generate/renderer.ts b/packages/openapi-ts/src/generate/renderer.ts index 9a25299f2..fd0bfdb46 100644 --- a/packages/openapi-ts/src/generate/renderer.ts +++ b/packages/openapi-ts/src/generate/renderer.ts @@ -12,9 +12,9 @@ import type { import { createBinding, mergeBindings, renderIds } from '@hey-api/codegen-core'; import ts from 'typescript'; -import { ensureValidIdentifier } from '../openApi/shared/utils/identifier'; -import { tsc } from '../tsc'; -import { tsNodeToString } from '../tsc/utils'; +import { ensureValidIdentifier } from '~/openApi/shared/utils/identifier'; +import { tsc } from '~/tsc'; +import { tsNodeToString } from '~/tsc/utils'; const nodeBuiltins = new Set([ 'buffer', diff --git a/packages/openapi-ts/src/generate/tsConfig.ts b/packages/openapi-ts/src/generate/tsConfig.ts index f2040ce68..89b128294 100644 --- a/packages/openapi-ts/src/generate/tsConfig.ts +++ b/packages/openapi-ts/src/generate/tsConfig.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'; import ts from 'typescript'; -import type { UserOutput } from '../types/output'; +import type { UserOutput } from '~/types/output'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); diff --git a/packages/openapi-ts/src/getSpec.ts b/packages/openapi-ts/src/getSpec.ts index 1d5d0f0cf..0841e78bf 100644 --- a/packages/openapi-ts/src/getSpec.ts +++ b/packages/openapi-ts/src/getSpec.ts @@ -1,8 +1,8 @@ import { getResolvedInput, sendRequest } from '@hey-api/json-schema-ref-parser'; -import { mergeHeaders } from './plugins/@hey-api/client-fetch/bundle'; -import type { Input } from './types/input'; -import type { WatchValues } from './types/types'; +import { mergeHeaders } from '~/plugins/@hey-api/client-fetch/bundle'; +import type { Input } from '~/types/input'; +import type { WatchValues } from '~/types/types'; type SpecResponse = { arrayBuffer: ArrayBuffer | undefined; diff --git a/packages/openapi-ts/src/index.ts b/packages/openapi-ts/src/index.ts index 5335bfb04..21aec06ff 100644 --- a/packages/openapi-ts/src/index.ts +++ b/packages/openapi-ts/src/index.ts @@ -5,11 +5,11 @@ import colors from 'ansi-colors'; // @ts-expect-error import colorSupport from 'color-support'; -import { checkNodeVersion } from './config/engine'; -import type { Configs } from './config/init'; -import { initConfigs } from './config/init'; -import { getLogs } from './config/logs'; -import { createClient as pCreateClient } from './createClient'; +import { checkNodeVersion } from '~/config/engine'; +import type { Configs } from '~/config/init'; +import { initConfigs } from '~/config/init'; +import { getLogs } from '~/config/logs'; +import { createClient as pCreateClient } from '~/createClient'; import { ConfigValidationError, JobError, @@ -17,14 +17,14 @@ import { openGitHubIssueWithCrashReport, printCrashReport, shouldReportCrash, -} from './error'; -import type { IR } from './ir/types'; -import type { Client } from './types/client'; -import type { UserConfig } from './types/config'; -import type { LazyOrAsync, MaybeArray } from './types/utils'; -import { printCliIntro } from './utils/cli'; -import { registerHandlebarTemplates } from './utils/handlebars'; -import { Logger } from './utils/logger'; +} from '~/error'; +import type { IR } from '~/ir/types'; +import type { Client } from '~/types/client'; +import type { UserConfig } from '~/types/config'; +import type { LazyOrAsync, MaybeArray } from '~/types/utils'; +import { printCliIntro } from '~/utils/cli'; +import { registerHandlebarTemplates } from '~/utils/handlebars'; +import { Logger } from '~/utils/logger'; colors.enabled = colorSupport().hasBasic; diff --git a/packages/openapi-ts/src/ir/__tests__/graph.test.ts b/packages/openapi-ts/src/ir/__tests__/graph.test.ts index 417dbf423..10335ba61 100644 --- a/packages/openapi-ts/src/ir/__tests__/graph.test.ts +++ b/packages/openapi-ts/src/ir/__tests__/graph.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from 'vitest'; -import type { Graph } from '../../openApi/shared/utils/graph'; -import { buildGraph } from '../../openApi/shared/utils/graph'; +import type { Graph } from '~/openApi/shared/utils/graph'; +import { buildGraph } from '~/openApi/shared/utils/graph'; + import type { IrTopLevelKind } from '../graph'; import { matchIrTopLevelPointer, walkTopological } from '../graph'; diff --git a/packages/openapi-ts/src/ir/__tests__/pagination.test.ts b/packages/openapi-ts/src/ir/__tests__/pagination.test.ts index 904666d55..c2b61d951 100644 --- a/packages/openapi-ts/src/ir/__tests__/pagination.test.ts +++ b/packages/openapi-ts/src/ir/__tests__/pagination.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; -import { defaultPaginationKeywords } from '../../config/parser'; -import type { Config } from '../../types/config'; +import { defaultPaginationKeywords } from '~/config/parser'; +import type { Config } from '~/types/config'; + import { operationPagination } from '../operation'; import { getPaginationKeywordsRegExp } from '../pagination'; import type { IR } from '../types'; diff --git a/packages/openapi-ts/src/ir/context.ts b/packages/openapi-ts/src/ir/context.ts index b7abc6c92..93caf1cf9 100644 --- a/packages/openapi-ts/src/ir/context.ts +++ b/packages/openapi-ts/src/ir/context.ts @@ -1,16 +1,17 @@ import { Project } from '@hey-api/codegen-core'; -import type { Package } from '../config/utils/package'; -import { packageFactory } from '../config/utils/package'; -import { TypeScriptRenderer } from '../generate/renderer'; -import type { Graph } from '../openApi/shared/utils/graph'; -import { buildName } from '../openApi/shared/utils/name'; -import type { PluginConfigMap } from '../plugins/config'; -import { PluginInstance } from '../plugins/shared/utils/instance'; -import type { PluginNames } from '../plugins/types'; -import type { Config } from '../types/config'; -import type { Logger } from '../utils/logger'; -import { resolveRef } from '../utils/ref'; +import type { Package } from '~/config/utils/package'; +import { packageFactory } from '~/config/utils/package'; +import { TypeScriptRenderer } from '~/generate/renderer'; +import type { Graph } from '~/openApi/shared/utils/graph'; +import { buildName } from '~/openApi/shared/utils/name'; +import type { PluginConfigMap } from '~/plugins/config'; +import { PluginInstance } from '~/plugins/shared/utils/instance'; +import type { PluginNames } from '~/plugins/types'; +import type { Config } from '~/types/config'; +import type { Logger } from '~/utils/logger'; +import { resolveRef } from '~/utils/ref'; + import type { IR } from './types'; export class IRContext = any> { diff --git a/packages/openapi-ts/src/ir/graph.ts b/packages/openapi-ts/src/ir/graph.ts index ee62ac28f..d92bc9527 100644 --- a/packages/openapi-ts/src/ir/graph.ts +++ b/packages/openapi-ts/src/ir/graph.ts @@ -1,5 +1,5 @@ -import type { Graph, NodeInfo } from '../openApi/shared/utils/graph'; -import { MinHeap } from '../utils/minHeap'; +import type { Graph, NodeInfo } from '~/openApi/shared/utils/graph'; +import { MinHeap } from '~/utils/minHeap'; type KindPriority = Record; type PreferGroups = ReadonlyArray; diff --git a/packages/openapi-ts/src/ir/pagination.ts b/packages/openapi-ts/src/ir/pagination.ts index cb1a7a466..b7f29126c 100644 --- a/packages/openapi-ts/src/ir/pagination.ts +++ b/packages/openapi-ts/src/ir/pagination.ts @@ -1,4 +1,5 @@ -import type { Config } from '../types/config'; +import type { Config } from '~/types/config'; + import type { IR } from './types'; export function getPaginationKeywordsRegExp( diff --git a/packages/openapi-ts/src/ir/types.d.ts b/packages/openapi-ts/src/ir/types.d.ts index 118647198..5f9ec4522 100644 --- a/packages/openapi-ts/src/ir/types.d.ts +++ b/packages/openapi-ts/src/ir/types.d.ts @@ -1,10 +1,11 @@ import type { Symbol } from '@hey-api/codegen-core'; -import type { JsonSchemaDraft2020_12 } from '../openApi/3.1.x/types/json-schema-draft-2020-12'; +import type { JsonSchemaDraft2020_12 } from '~/openApi/3.1.x/types/json-schema-draft-2020-12'; import type { SecuritySchemeObject, ServerObject, -} from '../openApi/3.1.x/types/spec'; +} from '~/openApi/3.1.x/types/spec'; + import type { IRContext } from './context'; import type { IRMediaType } from './mediaType'; diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/server.test.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/server.test.ts index e1a64a0cc..993f21b18 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/server.test.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/server.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from 'vitest'; +import type { OpenApi } from '~/openApi/types'; + import type { IR } from '../../../../ir/types'; -import type { OpenApi } from '../../../types'; import { parseServers } from '../server'; describe('parseServers', () => { diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/validate.test.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/validate.test.ts index f4c7bf23a..676c2ddbc 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/validate.test.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/__tests__/validate.test.ts @@ -2,9 +2,10 @@ import path from 'node:path'; import { describe, expect, it } from 'vitest'; +import { getSpecsPath, specFileToJson } from '~/openApi/__tests__/utils'; +import type { ValidatorResult } from '~/openApi/shared/utils/validator'; + import { Logger } from '../../../../utils/logger'; -import { getSpecsPath, specFileToJson } from '../../../__tests__/utils'; -import type { ValidatorResult } from '../../../shared/utils/validator'; import { validateOpenApiSpec } from '../validate'; const specsFolder = path.join(getSpecsPath(), '2.0.x', 'invalid'); diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/filter.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/filter.ts index fe934c174..0894abfc2 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/filter.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/filter.ts @@ -1,7 +1,8 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { Logger } from '../../../utils/logger'; -import { addNamespace, removeNamespace } from '../../shared/utils/filter'; -import { httpMethods } from '../../shared/utils/operation'; +import { createOperationKey } from '~/ir/operation'; +import { addNamespace, removeNamespace } from '~/openApi/shared/utils/filter'; +import { httpMethods } from '~/openApi/shared/utils/operation'; +import type { Logger } from '~/utils/logger'; + import type { OpenApiV2_0_X, OperationObject, diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/index.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/index.ts index 3cf6fdd9c..6b2be37b3 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/index.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/index.ts @@ -1,15 +1,16 @@ -import type { IR } from '../../../ir/types'; -import { buildResourceMetadata } from '../../shared/graph/meta'; -import { transformOpenApiSpec } from '../../shared/transforms'; -import type { State } from '../../shared/types/state'; +import type { IR } from '~/ir/types'; +import { buildResourceMetadata } from '~/openApi/shared/graph/meta'; +import { transformOpenApiSpec } from '~/openApi/shared/transforms'; +import type { State } from '~/openApi/shared/types/state'; import { createFilteredDependencies, createFilters, hasFilters, -} from '../../shared/utils/filter'; -import { buildGraph } from '../../shared/utils/graph'; -import { mergeParametersObjects } from '../../shared/utils/parameter'; -import { handleValidatorResult } from '../../shared/utils/validator'; +} from '~/openApi/shared/utils/filter'; +import { buildGraph } from '~/openApi/shared/utils/graph'; +import { mergeParametersObjects } from '~/openApi/shared/utils/parameter'; +import { handleValidatorResult } from '~/openApi/shared/utils/validator'; + import type { OpenApiV2_0_X, OperationObject, diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/mediaType.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/mediaType.ts index 913f0967b..1c4f3e1b7 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/mediaType.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/mediaType.ts @@ -1,8 +1,6 @@ -import type { IRMediaType } from '../../../ir/mediaType'; -import { - isMediaTypeFileLike, - mediaTypeToIrMediaType, -} from '../../../ir/mediaType'; +import type { IRMediaType } from '~/ir/mediaType'; +import { isMediaTypeFileLike, mediaTypeToIrMediaType } from '~/ir/mediaType'; + import type { ReferenceObject, ResponseObject, diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/operation.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/operation.ts index 83a21f3fa..99c80c34b 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/operation.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/operation.ts @@ -1,6 +1,7 @@ -import type { IR, IRBodyObject } from '../../../ir/types'; -import type { State } from '../../shared/types/state'; -import { operationToId } from '../../shared/utils/operation'; +import type { IR, IRBodyObject } from '~/ir/types'; +import type { State } from '~/openApi/shared/types/state'; +import { operationToId } from '~/openApi/shared/utils/operation'; + import type { OperationObject, ParameterObject, diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/pagination.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/pagination.ts index bc211b973..f92b727cd 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/pagination.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/pagination.ts @@ -1,6 +1,7 @@ -import { getPaginationKeywordsRegExp } from '../../../ir/pagination'; -import type { IR } from '../../../ir/types'; -import type { SchemaType } from '../../shared/types/schema'; +import { getPaginationKeywordsRegExp } from '~/ir/pagination'; +import type { IR } from '~/ir/types'; +import type { SchemaType } from '~/openApi/shared/types/schema'; + import type { ParameterObject, ReferenceObject } from '../types/spec'; import type { SchemaObject } from '../types/spec'; import { getSchemaType } from './schema'; diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts index 3e51a6304..531f0c0d9 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/parameter.ts @@ -1,4 +1,5 @@ -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; + import type { OperationObject, ParameterObject, diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts index 0208de48b..c48f84164 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/schema.ts @@ -1,12 +1,13 @@ -import type { IR } from '../../../ir/types'; -import { addItemsToSchema } from '../../../ir/utils'; -import { refToName } from '../../../utils/ref'; +import type { IR } from '~/ir/types'; +import { addItemsToSchema } from '~/ir/utils'; import type { SchemaState, SchemaType, SchemaWithRequired, -} from '../../shared/types/schema'; -import { discriminatorValues } from '../../shared/utils/discriminator'; +} from '~/openApi/shared/types/schema'; +import { discriminatorValues } from '~/openApi/shared/utils/discriminator'; +import { refToName } from '~/utils/ref'; + import type { SchemaObject } from '../types/spec'; export const getSchemaType = ({ diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/server.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/server.ts index b33f50d30..ee8241316 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/server.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/server.ts @@ -1,5 +1,5 @@ -import type { IR } from '../../../ir/types'; -import { parseUrl } from '../../../utils/url'; +import type { IR } from '~/ir/types'; +import { parseUrl } from '~/utils/url'; export const parseServers = ({ context }: { context: IR.Context }) => { let schemes: ReadonlyArray = context.spec.schemes ?? []; diff --git a/packages/openapi-ts/src/openApi/2.0.x/parser/validate.ts b/packages/openapi-ts/src/openApi/2.0.x/parser/validate.ts index 11944c4ba..a340487ce 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/parser/validate.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/parser/validate.ts @@ -1,10 +1,11 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { Logger } from '../../../utils/logger'; -import { httpMethods } from '../../shared/utils/operation'; +import { createOperationKey } from '~/ir/operation'; +import { httpMethods } from '~/openApi/shared/utils/operation'; import type { ValidatorIssue, ValidatorResult, -} from '../../shared/utils/validator'; +} from '~/openApi/shared/utils/validator'; +import type { Logger } from '~/utils/logger'; + import type { OpenApiV2_0_X, PathItemObject, PathsObject } from '../types/spec'; export const validateOpenApiSpec = ( diff --git a/packages/openapi-ts/src/openApi/2.0.x/types/json-schema-draft-4.d.ts b/packages/openapi-ts/src/openApi/2.0.x/types/json-schema-draft-4.d.ts index 6b0ebb7e0..7f273a5b7 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/types/json-schema-draft-4.d.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/types/json-schema-draft-4.d.ts @@ -1,4 +1,4 @@ -import type { EnumExtensions } from '../../shared/types/openapi-spec-extensions'; +import type { EnumExtensions } from '~/openApi/shared/types/openapi-spec-extensions'; export interface JsonSchemaDraft4 extends EnumExtensions { /** diff --git a/packages/openapi-ts/src/openApi/2.0.x/types/spec.d.ts b/packages/openapi-ts/src/openApi/2.0.x/types/spec.d.ts index 0f41dd545..9f2d530e0 100644 --- a/packages/openapi-ts/src/openApi/2.0.x/types/spec.d.ts +++ b/packages/openapi-ts/src/openApi/2.0.x/types/spec.d.ts @@ -1,4 +1,5 @@ -import type { EnumExtensions } from '../../shared/types/openapi-spec-extensions'; +import type { EnumExtensions } from '~/openApi/shared/types/openapi-spec-extensions'; + import type { JsonSchemaDraft4 } from './json-schema-draft-4'; import type { OpenApiV2_0_X_Nullable_Extensions } from './openapi-spec-extensions'; diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/__tests__/validate.test.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/__tests__/validate.test.ts index 6d06468fc..9af472378 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/__tests__/validate.test.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/__tests__/validate.test.ts @@ -2,9 +2,10 @@ import path from 'node:path'; import { describe, expect, it } from 'vitest'; +import { getSpecsPath, specFileToJson } from '~/openApi/__tests__/utils'; +import type { ValidatorResult } from '~/openApi/shared/utils/validator'; + import { Logger } from '../../../../utils/logger'; -import { getSpecsPath, specFileToJson } from '../../../__tests__/utils'; -import type { ValidatorResult } from '../../../shared/utils/validator'; import { validateOpenApiSpec } from '../validate'; const specsFolder = path.join(getSpecsPath(), '3.0.x', 'invalid'); diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/filter.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/filter.ts index da5570e18..0bec360ac 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/filter.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/filter.ts @@ -1,7 +1,8 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { Logger } from '../../../utils/logger'; -import { addNamespace, removeNamespace } from '../../shared/utils/filter'; -import { httpMethods } from '../../shared/utils/operation'; +import { createOperationKey } from '~/ir/operation'; +import { addNamespace, removeNamespace } from '~/openApi/shared/utils/filter'; +import { httpMethods } from '~/openApi/shared/utils/operation'; +import type { Logger } from '~/utils/logger'; + import type { OpenApiV3_0_X, PathItemObject, PathsObject } from '../types/spec'; /** diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/index.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/index.ts index 60c81716c..f21729226 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/index.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/index.ts @@ -1,15 +1,16 @@ -import type { IR } from '../../../ir/types'; -import { buildResourceMetadata } from '../../shared/graph/meta'; -import { transformOpenApiSpec } from '../../shared/transforms'; -import type { State } from '../../shared/types/state'; +import type { IR } from '~/ir/types'; +import { buildResourceMetadata } from '~/openApi/shared/graph/meta'; +import { transformOpenApiSpec } from '~/openApi/shared/transforms'; +import type { State } from '~/openApi/shared/types/state'; import { createFilteredDependencies, createFilters, hasFilters, -} from '../../shared/utils/filter'; -import { buildGraph } from '../../shared/utils/graph'; -import { mergeParametersObjects } from '../../shared/utils/parameter'; -import { handleValidatorResult } from '../../shared/utils/validator'; +} from '~/openApi/shared/utils/filter'; +import { buildGraph } from '~/openApi/shared/utils/graph'; +import { mergeParametersObjects } from '~/openApi/shared/utils/parameter'; +import { handleValidatorResult } from '~/openApi/shared/utils/validator'; + import type { OpenApiV3_0_X, ParameterObject, diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/mediaType.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/mediaType.ts index 5ed7093ce..784a7ab4d 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/mediaType.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/mediaType.ts @@ -1,8 +1,6 @@ -import type { IRMediaType } from '../../../ir/mediaType'; -import { - isMediaTypeFileLike, - mediaTypeToIrMediaType, -} from '../../../ir/mediaType'; +import type { IRMediaType } from '~/ir/mediaType'; +import { isMediaTypeFileLike, mediaTypeToIrMediaType } from '~/ir/mediaType'; + import type { MediaTypeObject, ReferenceObject, diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/operation.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/operation.ts index c0967839b..6d187a97d 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/operation.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/operation.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { State } from '../../shared/types/state'; -import { operationToId } from '../../shared/utils/operation'; +import type { IR } from '~/ir/types'; +import type { State } from '~/openApi/shared/types/state'; +import { operationToId } from '~/openApi/shared/utils/operation'; + import type { OperationObject, PathItemObject, diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/pagination.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/pagination.ts index 325d626e6..589ac9153 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/pagination.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/pagination.ts @@ -1,6 +1,7 @@ -import { getPaginationKeywordsRegExp } from '../../../ir/pagination'; -import type { IR } from '../../../ir/types'; -import type { SchemaType } from '../../shared/types/schema'; +import { getPaginationKeywordsRegExp } from '~/ir/pagination'; +import type { IR } from '~/ir/types'; +import type { SchemaType } from '~/openApi/shared/types/schema'; + import type { ParameterObject, ReferenceObject, diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts index 56307923e..78622865e 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/parameter.ts @@ -1,5 +1,6 @@ -import type { IR } from '../../../ir/types'; -import { refToName } from '../../../utils/ref'; +import type { IR } from '~/ir/types'; +import { refToName } from '~/utils/ref'; + import type { ParameterObject, ReferenceObject, diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts index 3f45e7e1a..593146fa9 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/requestBody.ts @@ -1,5 +1,6 @@ -import type { IR } from '../../../ir/types'; -import { refToName } from '../../../utils/ref'; +import type { IR } from '~/ir/types'; +import { refToName } from '~/utils/ref'; + import type { RequestBodyObject, SchemaObject } from '../types/spec'; import { mediaTypeObjects } from './mediaType'; import { schemaToIrSchema } from './schema'; diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts index e5679f6a6..aba96eeac 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/schema.ts @@ -1,12 +1,13 @@ -import type { IR } from '../../../ir/types'; -import { addItemsToSchema } from '../../../ir/utils'; -import { refToName } from '../../../utils/ref'; +import type { IR } from '~/ir/types'; +import { addItemsToSchema } from '~/ir/utils'; import type { SchemaState, SchemaType, SchemaWithRequired, -} from '../../shared/types/schema'; -import { discriminatorValues } from '../../shared/utils/discriminator'; +} from '~/openApi/shared/types/schema'; +import { discriminatorValues } from '~/openApi/shared/utils/discriminator'; +import { refToName } from '~/utils/ref'; + import type { ReferenceObject, SchemaObject } from '../types/spec'; export const getSchemaType = ({ diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/server.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/server.ts index 8fb309f00..9732ada5f 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/server.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/server.ts @@ -1,5 +1,5 @@ -import type { IR } from '../../../ir/types'; -import { parseUrl } from '../../../utils/url'; +import type { IR } from '~/ir/types'; +import { parseUrl } from '~/utils/url'; export const parseServers = ({ context }: { context: IR.Context }) => { if (context.spec.servers) { diff --git a/packages/openapi-ts/src/openApi/3.0.x/parser/validate.ts b/packages/openapi-ts/src/openApi/3.0.x/parser/validate.ts index 46220981b..e44ae495c 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/parser/validate.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/parser/validate.ts @@ -1,10 +1,11 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { Logger } from '../../../utils/logger'; -import { httpMethods } from '../../shared/utils/operation'; +import { createOperationKey } from '~/ir/operation'; +import { httpMethods } from '~/openApi/shared/utils/operation'; import type { ValidatorIssue, ValidatorResult, -} from '../../shared/utils/validator'; +} from '~/openApi/shared/utils/validator'; +import type { Logger } from '~/utils/logger'; + import type { OpenApiV3_0_X, PathItemObject, PathsObject } from '../types/spec'; export const validateOpenApiSpec = ( diff --git a/packages/openapi-ts/src/openApi/3.0.x/types/spec.d.ts b/packages/openapi-ts/src/openApi/3.0.x/types/spec.d.ts index 5c6c23682..65adaa4c5 100644 --- a/packages/openapi-ts/src/openApi/3.0.x/types/spec.d.ts +++ b/packages/openapi-ts/src/openApi/3.0.x/types/spec.d.ts @@ -1,4 +1,4 @@ -import type { EnumExtensions } from '../../shared/types/openapi-spec-extensions'; +import type { EnumExtensions } from '~/openApi/shared/types/openapi-spec-extensions'; /** * This is the root object of the {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.4.md#openapi-description OpenAPI Description}. diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/__tests__/validate.test.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/__tests__/validate.test.ts index 2071c31e7..878e77990 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/__tests__/validate.test.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/__tests__/validate.test.ts @@ -2,9 +2,10 @@ import path from 'node:path'; import { describe, expect, it } from 'vitest'; +import { getSpecsPath, specFileToJson } from '~/openApi/__tests__/utils'; +import type { ValidatorResult } from '~/openApi/shared/utils/validator'; + import { Logger } from '../../../../utils/logger'; -import { getSpecsPath, specFileToJson } from '../../../__tests__/utils'; -import type { ValidatorResult } from '../../../shared/utils/validator'; import { validateOpenApiSpec } from '../validate'; const specsFolder = path.join(getSpecsPath(), '3.1.x', 'invalid'); diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/filter.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/filter.ts index 87b496390..577e00820 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/filter.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/filter.ts @@ -1,7 +1,8 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { Logger } from '../../../utils/logger'; -import { addNamespace, removeNamespace } from '../../shared/utils/filter'; -import { httpMethods } from '../../shared/utils/operation'; +import { createOperationKey } from '~/ir/operation'; +import { addNamespace, removeNamespace } from '~/openApi/shared/utils/filter'; +import { httpMethods } from '~/openApi/shared/utils/operation'; +import type { Logger } from '~/utils/logger'; + import type { OpenApiV3_1_X, PathItemObject, PathsObject } from '../types/spec'; /** diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/index.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/index.ts index 05c181eae..e5886ed36 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/index.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/index.ts @@ -1,15 +1,16 @@ -import type { IR } from '../../../ir/types'; -import { buildResourceMetadata } from '../../shared/graph/meta'; -import { transformOpenApiSpec } from '../../shared/transforms'; -import type { State } from '../../shared/types/state'; +import type { IR } from '~/ir/types'; +import { buildResourceMetadata } from '~/openApi/shared/graph/meta'; +import { transformOpenApiSpec } from '~/openApi/shared/transforms'; +import type { State } from '~/openApi/shared/types/state'; import { createFilteredDependencies, createFilters, hasFilters, -} from '../../shared/utils/filter'; -import { buildGraph } from '../../shared/utils/graph'; -import { mergeParametersObjects } from '../../shared/utils/parameter'; -import { handleValidatorResult } from '../../shared/utils/validator'; +} from '~/openApi/shared/utils/filter'; +import { buildGraph } from '~/openApi/shared/utils/graph'; +import { mergeParametersObjects } from '~/openApi/shared/utils/parameter'; +import { handleValidatorResult } from '~/openApi/shared/utils/validator'; + import type { OpenApiV3_1_X, ParameterObject, diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/mediaType.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/mediaType.ts index 5b5bf1ae6..b0f956deb 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/mediaType.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/mediaType.ts @@ -1,8 +1,6 @@ -import type { IRMediaType } from '../../../ir/mediaType'; -import { - isMediaTypeFileLike, - mediaTypeToIrMediaType, -} from '../../../ir/mediaType'; +import type { IRMediaType } from '~/ir/mediaType'; +import { isMediaTypeFileLike, mediaTypeToIrMediaType } from '~/ir/mediaType'; + import type { MediaTypeObject, SchemaObject } from '../types/spec'; interface Content { diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/operation.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/operation.ts index 393ef07cf..31824cb37 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/operation.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/operation.ts @@ -1,7 +1,8 @@ -import type { IR } from '../../../ir/types'; -import type { State } from '../../shared/types/state'; -import type { httpMethods } from '../../shared/utils/operation'; -import { operationToId } from '../../shared/utils/operation'; +import type { IR } from '~/ir/types'; +import type { State } from '~/openApi/shared/types/state'; +import type { httpMethods } from '~/openApi/shared/utils/operation'; +import { operationToId } from '~/openApi/shared/utils/operation'; + import type { OperationObject, RequestBodyObject, diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/pagination.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/pagination.ts index 500a949f0..00ac2aa95 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/pagination.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/pagination.ts @@ -1,6 +1,7 @@ -import { getPaginationKeywordsRegExp } from '../../../ir/pagination'; -import type { IR } from '../../../ir/types'; -import type { SchemaType } from '../../shared/types/schema'; +import { getPaginationKeywordsRegExp } from '~/ir/pagination'; +import type { IR } from '~/ir/types'; +import type { SchemaType } from '~/openApi/shared/types/schema'; + import type { ParameterObject, RequestBodyObject } from '../types/spec'; import type { SchemaObject } from '../types/spec'; import { mediaTypeObjects } from './mediaType'; diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts index be0c87510..607d4db45 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/parameter.ts @@ -1,5 +1,6 @@ -import type { IR } from '../../../ir/types'; -import { refToName } from '../../../utils/ref'; +import type { IR } from '~/ir/types'; +import { refToName } from '~/utils/ref'; + import type { ParameterObject, ReferenceObject, diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts index 3f45e7e1a..593146fa9 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/requestBody.ts @@ -1,5 +1,6 @@ -import type { IR } from '../../../ir/types'; -import { refToName } from '../../../utils/ref'; +import type { IR } from '~/ir/types'; +import { refToName } from '~/utils/ref'; + import type { RequestBodyObject, SchemaObject } from '../types/spec'; import { mediaTypeObjects } from './mediaType'; import { schemaToIrSchema } from './schema'; diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts index 444fc5984..8dea32e73 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts @@ -1,12 +1,13 @@ -import type { IR } from '../../../ir/types'; -import { addItemsToSchema } from '../../../ir/utils'; -import { refToName } from '../../../utils/ref'; +import type { IR } from '~/ir/types'; +import { addItemsToSchema } from '~/ir/utils'; import type { SchemaState, SchemaType, SchemaWithRequired, -} from '../../shared/types/schema'; -import { discriminatorValues } from '../../shared/utils/discriminator'; +} from '~/openApi/shared/types/schema'; +import { discriminatorValues } from '~/openApi/shared/utils/discriminator'; +import { refToName } from '~/utils/ref'; + import type { SchemaObject } from '../types/spec'; export const getSchemaTypes = ({ diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/server.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/server.ts index 8fb309f00..9732ada5f 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/server.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/server.ts @@ -1,5 +1,5 @@ -import type { IR } from '../../../ir/types'; -import { parseUrl } from '../../../utils/url'; +import type { IR } from '~/ir/types'; +import { parseUrl } from '~/utils/url'; export const parseServers = ({ context }: { context: IR.Context }) => { if (context.spec.servers) { diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/validate.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/validate.ts index 5d941e1ee..fcdb8e62e 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/validate.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/validate.ts @@ -1,10 +1,11 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { Logger } from '../../../utils/logger'; -import { httpMethods } from '../../shared/utils/operation'; +import { createOperationKey } from '~/ir/operation'; +import { httpMethods } from '~/openApi/shared/utils/operation'; import type { ValidatorIssue, ValidatorResult, -} from '../../shared/utils/validator'; +} from '~/openApi/shared/utils/validator'; +import type { Logger } from '~/utils/logger'; + import type { OpenApiV3_1_X, PathItemObject, PathsObject } from '../types/spec'; export const validateOpenApiSpec = ( diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/webhook.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/webhook.ts index 653cab559..cbeaf4ac5 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/webhook.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/webhook.ts @@ -1,5 +1,6 @@ -import type { IR } from '../../../ir/types'; -import { mergeParametersObjects } from '../../shared/utils/parameter'; +import type { IR } from '~/ir/types'; +import { mergeParametersObjects } from '~/openApi/shared/utils/parameter'; + import type { OpenApiV3_1_X, PathItemObject } from '../types/spec'; import { parseWebhookOperation } from './operation'; import { parametersArrayToObject } from './parameter'; diff --git a/packages/openapi-ts/src/openApi/3.1.x/types/json-schema-draft-2020-12.d.ts b/packages/openapi-ts/src/openApi/3.1.x/types/json-schema-draft-2020-12.d.ts index 68ce48afe..0acd88981 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/types/json-schema-draft-2020-12.d.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/types/json-schema-draft-2020-12.d.ts @@ -1,5 +1,6 @@ +import type { EnumExtensions } from '~/openApi/shared/types/openapi-spec-extensions'; + import type { MaybeArray } from '../../../types/utils'; -import type { EnumExtensions } from '../../shared/types/openapi-spec-extensions'; import type { OpenApiSchemaExtensions } from './spec-extensions'; // TODO: left out some keywords related to structuring a complex schema and declaring a dialect diff --git a/packages/openapi-ts/src/openApi/__tests__/index.test.ts b/packages/openapi-ts/src/openApi/__tests__/index.test.ts index cee23795c..8ed46431f 100644 --- a/packages/openapi-ts/src/openApi/__tests__/index.test.ts +++ b/packages/openapi-ts/src/openApi/__tests__/index.test.ts @@ -1,13 +1,14 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; +import type { OpenApiV3_0_X } from '~/openApi/3.0.x'; +import { parseV3_0_X } from '~/openApi/3.0.x'; +import type { OpenApiV3_1_X } from '~/openApi/3.1.x'; +import { parseV3_1_X } from '~/openApi/3.1.x'; +import * as parseV2 from '~/openApi/v2'; +import * as parseV3 from '~/openApi/v3'; + import type { Config } from '../../types/config'; import { type OpenApi, parseLegacy, parseOpenApiSpec } from '..'; -import type { OpenApiV3_0_X } from '../3.0.x'; -import { parseV3_0_X } from '../3.0.x'; -import type { OpenApiV3_1_X } from '../3.1.x'; -import { parseV3_1_X } from '../3.1.x'; -import * as parseV2 from '../v2'; -import * as parseV3 from '../v3'; vi.mock('../3.0.x', () => ({ parseV3_0_X: vi.fn(), diff --git a/packages/openapi-ts/src/openApi/common/interfaces/OpenApi.ts b/packages/openapi-ts/src/openApi/common/interfaces/OpenApi.ts index 41a9fe08d..656e638d4 100644 --- a/packages/openapi-ts/src/openApi/common/interfaces/OpenApi.ts +++ b/packages/openapi-ts/src/openApi/common/interfaces/OpenApi.ts @@ -1,4 +1,4 @@ -import type { OpenApi as OpenApiV2 } from '../../v2/interfaces/OpenApi'; -import type { OpenApi as OpenApiV3 } from '../../v3/interfaces/OpenApi'; +import type { OpenApi as OpenApiV2 } from '~/openApi/v2/interfaces/OpenApi'; +import type { OpenApi as OpenApiV3 } from '~/openApi/v3/interfaces/OpenApi'; export type OpenApi = OpenApiV2 | OpenApiV3; diff --git a/packages/openapi-ts/src/openApi/common/interfaces/client.ts b/packages/openapi-ts/src/openApi/common/interfaces/client.ts index f22e2e5d9..b11f7a3c2 100644 --- a/packages/openapi-ts/src/openApi/common/interfaces/client.ts +++ b/packages/openapi-ts/src/openApi/common/interfaces/client.ts @@ -1,5 +1,6 @@ +import type { OpenApiParameter } from '~/openApi/v3/interfaces/OpenApiParameter'; + import type { Config } from '../../../types/config'; -import type { OpenApiParameter } from '../../v3/interfaces/OpenApiParameter'; export interface ModelComposition extends Pick { diff --git a/packages/openapi-ts/src/openApi/common/parser/getDefault.ts b/packages/openapi-ts/src/openApi/common/parser/getDefault.ts index a77126b5e..551f5f953 100644 --- a/packages/openapi-ts/src/openApi/common/parser/getDefault.ts +++ b/packages/openapi-ts/src/openApi/common/parser/getDefault.ts @@ -1,7 +1,8 @@ +import type { OpenApiParameter } from '~/openApi/v2/interfaces/OpenApiParameter'; +import type { OpenApiSchema } from '~/openApi/v3/interfaces/OpenApiSchema'; +import { getDefinitionTypes } from '~/openApi/v3/parser/inferType'; + import type { Model } from '../../common/interfaces/client'; -import type { OpenApiParameter } from '../../v2/interfaces/OpenApiParameter'; -import type { OpenApiSchema } from '../../v3/interfaces/OpenApiSchema'; -import { getDefinitionTypes } from '../../v3/parser/inferType'; import type { OperationParameter } from '../interfaces/client'; export const getDefault = ( diff --git a/packages/openapi-ts/src/openApi/common/parser/getEnums.ts b/packages/openapi-ts/src/openApi/common/parser/getEnums.ts index ac835a40d..81767d450 100644 --- a/packages/openapi-ts/src/openApi/common/parser/getEnums.ts +++ b/packages/openapi-ts/src/openApi/common/parser/getEnums.ts @@ -1,4 +1,5 @@ -import { unique } from '../../../utils/unique'; +import { unique } from '~/utils/unique'; + import type { Enum } from '../interfaces/client'; import type { WithEnumExtension } from '../interfaces/WithEnumExtension'; diff --git a/packages/openapi-ts/src/openApi/common/parser/getRef.ts b/packages/openapi-ts/src/openApi/common/parser/getRef.ts index e2b089b04..98cec868c 100644 --- a/packages/openapi-ts/src/openApi/common/parser/getRef.ts +++ b/packages/openapi-ts/src/openApi/common/parser/getRef.ts @@ -1,5 +1,6 @@ -import type { OpenApiReference as OpenApiReferenceV2 } from '../../v2/interfaces/OpenApiReference'; -import type { OpenApiReference as OpenApiReferenceV3 } from '../../v3/interfaces/OpenApiReference'; +import type { OpenApiReference as OpenApiReferenceV2 } from '~/openApi/v2/interfaces/OpenApiReference'; +import type { OpenApiReference as OpenApiReferenceV3 } from '~/openApi/v3/interfaces/OpenApiReference'; + import type { OpenApi } from '../interfaces/OpenApi'; const ESCAPED_REF_SLASH = /~1/g; diff --git a/packages/openapi-ts/src/openApi/common/parser/operation.ts b/packages/openapi-ts/src/openApi/common/parser/operation.ts index b7fe285d0..c4319a8c2 100644 --- a/packages/openapi-ts/src/openApi/common/parser/operation.ts +++ b/packages/openapi-ts/src/openApi/common/parser/operation.ts @@ -1,7 +1,8 @@ +import { getConfig, isLegacyClient } from '~/utils/config'; +import { stringCase } from '~/utils/stringCase'; +import { transformTypeKeyName } from '~/utils/type'; + import type { Config } from '../../../types/config'; -import { getConfig, isLegacyClient } from '../../../utils/config'; -import { stringCase } from '../../../utils/stringCase'; -import { transformTypeKeyName } from '../../../utils/type'; import type { OperationParameter, OperationResponse, diff --git a/packages/openapi-ts/src/openApi/common/parser/sanitize.ts b/packages/openapi-ts/src/openApi/common/parser/sanitize.ts index d31e565d9..27fa71955 100644 --- a/packages/openapi-ts/src/openApi/common/parser/sanitize.ts +++ b/packages/openapi-ts/src/openApi/common/parser/sanitize.ts @@ -1,4 +1,4 @@ -import { illegalStartCharactersRegExp } from '../../../utils/regexp'; +import { illegalStartCharactersRegExp } from '~/utils/regexp'; export const ensureValidTypeScriptJavaScriptIdentifier = (name: string) => { const replaced = name.replace(/[^$\u200c\u200d\p{ID_Continue}]/gu, '_'); diff --git a/packages/openapi-ts/src/openApi/common/parser/type.ts b/packages/openapi-ts/src/openApi/common/parser/type.ts index 98e8709ca..bd8bde457 100644 --- a/packages/openapi-ts/src/openApi/common/parser/type.ts +++ b/packages/openapi-ts/src/openApi/common/parser/type.ts @@ -1,6 +1,7 @@ -import { refParametersPartial } from '../../../utils/const'; -import { transformTypeName } from '../../../utils/transform'; -import { isDefinitionTypeNullable } from '../../v3/parser/inferType'; +import { isDefinitionTypeNullable } from '~/openApi/v3/parser/inferType'; +import { refParametersPartial } from '~/utils/const'; +import { transformTypeName } from '~/utils/transform'; + import type { Type } from '../interfaces/Type'; import { ensureValidTypeScriptJavaScriptIdentifier } from './sanitize'; import { stripNamespace } from './stripNamespace'; diff --git a/packages/openapi-ts/src/openApi/index.ts b/packages/openapi-ts/src/openApi/index.ts index 06594f164..f27cb4de2 100644 --- a/packages/openapi-ts/src/openApi/index.ts +++ b/packages/openapi-ts/src/openApi/index.ts @@ -1,16 +1,16 @@ -import { satisfies } from '../config/utils/package'; -import { IRContext } from '../ir/context'; -import type { IR } from '../ir/types'; -import type { Config } from '../types/config'; -import type { Logger } from '../utils/logger'; -import { parseV2_0_X } from './2.0.x'; -import { parseV3_0_X } from './3.0.x'; -import { parseV3_1_X } from './3.1.x'; -import type { Client } from './common/interfaces/client'; -import type { OpenApi as LegacyOpenApi } from './common/interfaces/OpenApi'; -import type { OpenApi } from './types'; -import { parse as parseV2 } from './v2'; -import { parse as parseV3 } from './v3'; +import { satisfies } from '~/config/utils/package'; +import { IRContext } from '~/ir/context'; +import type { IR } from '~/ir/types'; +import { parseV2_0_X } from '~/openApi/2.0.x'; +import { parseV3_0_X } from '~/openApi/3.0.x'; +import { parseV3_1_X } from '~/openApi/3.1.x'; +import type { Client } from '~/openApi/common/interfaces/client'; +import type { OpenApi as LegacyOpenApi } from '~/openApi/common/interfaces/OpenApi'; +import type { OpenApi } from '~/openApi/types'; +import { parse as parseV2 } from '~/openApi/v2'; +import { parse as parseV3 } from '~/openApi/v3'; +import type { Config } from '~/types/config'; +import type { Logger } from '~/utils/logger'; export type { Client, diff --git a/packages/openapi-ts/src/openApi/shared/graph/meta.ts b/packages/openapi-ts/src/openApi/shared/graph/meta.ts index 326ab3b69..3d7103db6 100644 --- a/packages/openapi-ts/src/openApi/shared/graph/meta.ts +++ b/packages/openapi-ts/src/openApi/shared/graph/meta.ts @@ -1,6 +1,7 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { Logger } from '../../../utils/logger'; -import { jsonPointerToPath } from '../../../utils/ref'; +import { createOperationKey } from '~/ir/operation'; +import type { Logger } from '~/utils/logger'; +import { jsonPointerToPath } from '~/utils/ref'; + import { addNamespace, stringToNamespace } from '../utils/filter'; import type { Graph } from '../utils/graph'; import { httpMethods } from '../utils/operation'; diff --git a/packages/openapi-ts/src/openApi/shared/transforms/enums.ts b/packages/openapi-ts/src/openApi/shared/transforms/enums.ts index 1552cef51..c8c4f0d86 100644 --- a/packages/openapi-ts/src/openApi/shared/transforms/enums.ts +++ b/packages/openapi-ts/src/openApi/shared/transforms/enums.ts @@ -1,5 +1,6 @@ +import { jsonPointerToPath } from '~/utils/ref'; + import type { Config } from '../../../types/config'; -import { jsonPointerToPath } from '../../../utils/ref'; import { buildName } from '../utils/name'; import { deepClone } from '../utils/schema'; import { childSchemaRelationships } from '../utils/schemaChildRelationships'; diff --git a/packages/openapi-ts/src/openApi/shared/transforms/index.ts b/packages/openapi-ts/src/openApi/shared/transforms/index.ts index 321863c0a..6f34465a8 100644 --- a/packages/openapi-ts/src/openApi/shared/transforms/index.ts +++ b/packages/openapi-ts/src/openApi/shared/transforms/index.ts @@ -1,4 +1,5 @@ -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; + import { enumsTransform } from './enums'; import { propertiesRequiredByDefaultTransform } from './propertiesRequiredByDefault'; import { readWriteTransform } from './readWrite'; diff --git a/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts b/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts index ce09fb10c..9a59ad39c 100644 --- a/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts +++ b/packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts @@ -1,6 +1,7 @@ +import type { Logger } from '~/utils/logger'; +import { jsonPointerToPath } from '~/utils/ref'; + import type { Config } from '../../../types/config'; -import type { Logger } from '../../../utils/logger'; -import { jsonPointerToPath } from '../../../utils/ref'; import deepEqual from '../utils/deepEqual'; import { buildGraph, type Graph, type Scope } from '../utils/graph'; import { buildName } from '../utils/name'; diff --git a/packages/openapi-ts/src/openApi/shared/utils/__tests__/patch.test.ts b/packages/openapi-ts/src/openApi/shared/utils/__tests__/patch.test.ts index ec9aaa718..8fbefc62e 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/__tests__/patch.test.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/__tests__/patch.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; -import type { OpenApi } from '../../../../openApi/types'; +import type { OpenApi } from '~/openApi/types'; + import { patchOpenApiSpec } from '../patch'; const specMetadataV2: Pick = { diff --git a/packages/openapi-ts/src/openApi/shared/utils/discriminator.ts b/packages/openapi-ts/src/openApi/shared/utils/discriminator.ts index 836af5986..1e79009d3 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/discriminator.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/discriminator.ts @@ -1,4 +1,4 @@ -import { refToName } from '../../../utils/ref'; +import { refToName } from '~/utils/ref'; export const discriminatorValues = ( $ref: string, diff --git a/packages/openapi-ts/src/openApi/shared/utils/filter.ts b/packages/openapi-ts/src/openApi/shared/utils/filter.ts index 9ffa90630..8c8f66d51 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/filter.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/filter.ts @@ -1,8 +1,9 @@ -import { createOperationKey } from '../../../ir/operation'; +import { createOperationKey } from '~/ir/operation'; +import type { PathItemObject, PathsObject } from '~/openApi/3.1.x/types/spec'; +import type { OpenApi } from '~/openApi/types'; +import type { Logger } from '~/utils/logger'; + import type { Config } from '../../../types/config'; -import type { Logger } from '../../../utils/logger'; -import type { PathItemObject, PathsObject } from '../../3.1.x/types/spec'; -import type { OpenApi } from '../../types'; import type { ResourceMetadata } from '../graph/meta'; import { httpMethods } from './operation'; diff --git a/packages/openapi-ts/src/openApi/shared/utils/graph.ts b/packages/openapi-ts/src/openApi/shared/utils/graph.ts index 562d7b8eb..605cd58b0 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/graph.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/graph.ts @@ -1,5 +1,6 @@ -import type { Logger } from '../../../utils/logger'; -import { normalizeJsonPointer, pathToJsonPointer } from '../../../utils/ref'; +import type { Logger } from '~/utils/logger'; +import { normalizeJsonPointer, pathToJsonPointer } from '~/utils/ref'; + import { childSchemaRelationships } from './schemaChildRelationships'; /** diff --git a/packages/openapi-ts/src/openApi/shared/utils/identifier.ts b/packages/openapi-ts/src/openApi/shared/utils/identifier.ts index 8f8c7e2df..3b9ce2464 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/identifier.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/identifier.ts @@ -5,7 +5,7 @@ import { reservedJavaScriptKeywordsRegExp, reservedNodeGlobalsRegExp, reservedTypeScriptKeywordsRegExp, -} from '../../../utils/regexp'; +} from '~/utils/regexp'; const regexps = [ reservedJavaScriptKeywordsRegExp, diff --git a/packages/openapi-ts/src/openApi/shared/utils/name.ts b/packages/openapi-ts/src/openApi/shared/utils/name.ts index 8f4ab453d..fff76b867 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/name.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/name.ts @@ -1,5 +1,6 @@ +import { stringCase } from '~/utils/stringCase'; + import type { StringCase, StringName } from '../../../types/case'; -import { stringCase } from '../../../utils/stringCase'; export const buildName = ({ config, diff --git a/packages/openapi-ts/src/openApi/shared/utils/operation.ts b/packages/openapi-ts/src/openApi/shared/utils/operation.ts index cc7e1cc1b..119159459 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/operation.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/operation.ts @@ -1,7 +1,8 @@ -import { createOperationKey } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { stringCase } from '../../../utils/stringCase'; -import { sanitizeNamespaceIdentifier } from '../../common/parser/sanitize'; +import { createOperationKey } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { sanitizeNamespaceIdentifier } from '~/openApi/common/parser/sanitize'; +import { stringCase } from '~/utils/stringCase'; + import type { State } from '../types/state'; export const httpMethods = [ diff --git a/packages/openapi-ts/src/openApi/shared/utils/parameter.ts b/packages/openapi-ts/src/openApi/shared/utils/parameter.ts index a7136ed15..ff507fe2d 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/parameter.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/parameter.ts @@ -1,4 +1,4 @@ -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; export const mergeParametersObjects = ({ source, diff --git a/packages/openapi-ts/src/openApi/shared/utils/patch.ts b/packages/openapi-ts/src/openApi/shared/utils/patch.ts index 4dd0e9566..9186ee709 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/patch.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/patch.ts @@ -1,5 +1,6 @@ +import type { OpenApi } from '~/openApi/types'; + import type { Patch } from '../../../types/parser'; -import type { OpenApi } from '../../types'; export const patchOpenApiSpec = ({ patchOptions, diff --git a/packages/openapi-ts/src/openApi/shared/utils/validator.ts b/packages/openapi-ts/src/openApi/shared/utils/validator.ts index c6be15024..db79c7740 100644 --- a/packages/openapi-ts/src/openApi/shared/utils/validator.ts +++ b/packages/openapi-ts/src/openApi/shared/utils/validator.ts @@ -1,6 +1,6 @@ import colors from 'ansi-colors'; -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; export interface ValidatorIssue { /** diff --git a/packages/openapi-ts/src/openApi/types.d.ts b/packages/openapi-ts/src/openApi/types.d.ts index 88697a24e..ff0b32f5e 100644 --- a/packages/openapi-ts/src/openApi/types.d.ts +++ b/packages/openapi-ts/src/openApi/types.d.ts @@ -1,6 +1,6 @@ -import type { OpenApiV2_0_X, OpenApiV2_0_XTypes } from './2.0.x'; -import type { OpenApiV3_0_X, OpenApiV3_0_XTypes } from './3.0.x'; -import type { OpenApiV3_1_X, OpenApiV3_1_XTypes } from './3.1.x'; +import type { OpenApiV2_0_X, OpenApiV2_0_XTypes } from '~/openApi/2.0.x'; +import type { OpenApiV3_0_X, OpenApiV3_0_XTypes } from '~/openApi/3.0.x'; +import type { OpenApiV3_1_X, OpenApiV3_1_XTypes } from '~/openApi/3.1.x'; export namespace OpenApi { export type V2_0_X = OpenApiV2_0_X; diff --git a/packages/openapi-ts/src/openApi/v2/index.ts b/packages/openapi-ts/src/openApi/v2/index.ts index 9d35fad1b..eca7c2948 100644 --- a/packages/openapi-ts/src/openApi/v2/index.ts +++ b/packages/openapi-ts/src/openApi/v2/index.ts @@ -1,5 +1,6 @@ -import type { Client } from '../common/interfaces/client'; -import { getServiceVersion } from '../common/parser/service'; +import type { Client } from '~/openApi/common/interfaces/client'; +import { getServiceVersion } from '~/openApi/common/parser/service'; + import type { OpenApi } from './interfaces/OpenApi'; import { getModels } from './parser/getModels'; import { getOperations } from './parser/getOperations'; diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/Model.ts b/packages/openapi-ts/src/openApi/v2/interfaces/Model.ts index 821f21171..c442b81a6 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/Model.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/Model.ts @@ -1,5 +1,6 @@ +import type { Model, ModelMeta } from '~/openApi/common/interfaces/client'; + import type { Client } from '../../../types/client'; -import type { Model, ModelMeta } from '../../common/interfaces/client'; import type { OpenApi } from './OpenApi'; import type { OpenApiSchema } from './OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApi.ts b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApi.ts index 477938662..5d641a917 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApi.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApi.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiExternalDocs } from './OpenApiExternalDocs'; import type { OpenApiInfo } from './OpenApiInfo'; import type { OpenApiParameter } from './OpenApiParameter'; diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiHeader.ts b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiHeader.ts index a00de7928..cc595e0df 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiHeader.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiHeader.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiItems } from './OpenApiItems'; /** diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiItems.ts b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiItems.ts index f7d0cb633..6e0025752 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiItems.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiItems.ts @@ -1,4 +1,4 @@ -import type { WithEnumExtension } from '../../common/interfaces/WithEnumExtension'; +import type { WithEnumExtension } from '~/openApi/common/interfaces/WithEnumExtension'; /** * {@link} https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#items-object) diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiParameter.ts b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiParameter.ts index 1210fd0e2..223151007 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiParameter.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiParameter.ts @@ -1,4 +1,5 @@ -import type { WithEnumExtension } from '../../common/interfaces/WithEnumExtension'; +import type { WithEnumExtension } from '~/openApi/common/interfaces/WithEnumExtension'; + import type { WithNullableExtension } from './Extensions/WithNullableExtension'; import type { OpenApiItems } from './OpenApiItems'; import type { OpenApiReference } from './OpenApiReference'; diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiResponse.ts b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiResponse.ts index 0883cff68..26c372d80 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiResponse.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiResponse.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiExample } from './OpenApiExample'; import type { OpenApiHeader } from './OpenApiHeader'; import type { OpenApiReference } from './OpenApiReference'; diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSchema.ts b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSchema.ts index 447d4f7fd..65ff39f37 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSchema.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSchema.ts @@ -1,5 +1,6 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; -import type { WithEnumExtension } from '../../common/interfaces/WithEnumExtension'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; +import type { WithEnumExtension } from '~/openApi/common/interfaces/WithEnumExtension'; + import type { WithNullableExtension } from './Extensions/WithNullableExtension'; import type { OpenApiExternalDocs } from './OpenApiExternalDocs'; import type { OpenApiReference } from './OpenApiReference'; diff --git a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSecurityScheme.ts b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSecurityScheme.ts index 341f5af56..2a096b0f6 100644 --- a/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSecurityScheme.ts +++ b/packages/openapi-ts/src/openApi/v2/interfaces/OpenApiSecurityScheme.ts @@ -1,4 +1,4 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; /** * {@link} https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#security-scheme-object diff --git a/packages/openapi-ts/src/openApi/v2/parser/getModel.ts b/packages/openapi-ts/src/openApi/v2/parser/getModel.ts index 58c4c32fd..449810818 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getModel.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getModel.ts @@ -1,8 +1,9 @@ +import type { Model, ModelMeta } from '~/openApi/common/interfaces/client'; +import { getEnums } from '~/openApi/common/parser/getEnums'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getType } from '~/openApi/common/parser/type'; + import type { Client } from '../../../types/client'; -import type { Model, ModelMeta } from '../../common/interfaces/client'; -import { getEnums } from '../../common/parser/getEnums'; -import { getPattern } from '../../common/parser/getPattern'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; import { getModelComposition } from './getModelComposition'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getModelComposition.ts b/packages/openapi-ts/src/openApi/v2/parser/getModelComposition.ts index c130c8766..d131b1e79 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getModelComposition.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getModelComposition.ts @@ -1,5 +1,9 @@ +import type { + Model, + ModelComposition, +} from '~/openApi/common/interfaces/client'; + import type { Client } from '../../../types/client'; -import type { Model, ModelComposition } from '../../common/interfaces/client'; import type { GetModelFn } from '../interfaces/Model'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getModelProperties.ts b/packages/openapi-ts/src/openApi/v2/parser/getModelProperties.ts index 25f576710..d3a51c7f5 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getModelProperties.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getModelProperties.ts @@ -1,8 +1,9 @@ +import type { Model } from '~/openApi/common/interfaces/client'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getType } from '~/openApi/common/parser/type'; +import { escapeName } from '~/utils/escape'; + import type { Client } from '../../../types/client'; -import { escapeName } from '../../../utils/escape'; -import type { Model } from '../../common/interfaces/client'; -import { getPattern } from '../../common/parser/getPattern'; -import { getType } from '../../common/parser/type'; import type { GetModelFn } from '../interfaces/Model'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getModels.ts b/packages/openapi-ts/src/openApi/v2/parser/getModels.ts index 5caf1943c..7a87247aa 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getModels.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getModels.ts @@ -1,6 +1,7 @@ +import { getType } from '~/openApi/common/parser/type'; +import { reservedJavaScriptKeywordsRegExp } from '~/utils/regexp'; + import type { Client } from '../../../types/client'; -import { reservedJavaScriptKeywordsRegExp } from '../../../utils/regexp'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import { getModel } from './getModel'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getOperationParameter.ts b/packages/openapi-ts/src/openApi/v2/parser/getOperationParameter.ts index 4a8427e5b..1016e3ea3 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getOperationParameter.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getOperationParameter.ts @@ -1,11 +1,12 @@ +import type { OperationParameter } from '~/openApi/common/interfaces/client'; +import { getDefault } from '~/openApi/common/parser/getDefault'; +import { getEnums } from '~/openApi/common/parser/getEnums'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getRef } from '~/openApi/common/parser/getRef'; +import { operationParameterNameFn } from '~/openApi/common/parser/operation'; +import { getType } from '~/openApi/common/parser/type'; + import type { Client } from '../../../types/client'; -import type { OperationParameter } from '../../common/interfaces/client'; -import { getDefault } from '../../common/parser/getDefault'; -import { getEnums } from '../../common/parser/getEnums'; -import { getPattern } from '../../common/parser/getPattern'; -import { getRef } from '../../common/parser/getRef'; -import { operationParameterNameFn } from '../../common/parser/operation'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiParameter } from '../interfaces/OpenApiParameter'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getOperationParameters.ts b/packages/openapi-ts/src/openApi/v2/parser/getOperationParameters.ts index 8611008d2..392832370 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getOperationParameters.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getOperationParameters.ts @@ -1,7 +1,8 @@ +import type { OperationParameters } from '~/openApi/common/interfaces/client'; +import { getRef } from '~/openApi/common/parser/getRef'; +import { operationParameterFilterFn } from '~/openApi/common/parser/operation'; + import type { Client } from '../../../types/client'; -import type { OperationParameters } from '../../common/interfaces/client'; -import { getRef } from '../../common/parser/getRef'; -import { operationParameterFilterFn } from '../../common/parser/operation'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiParameter } from '../interfaces/OpenApiParameter'; import { getOperationParameter } from './getOperationParameter'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getOperationResponse.ts b/packages/openapi-ts/src/openApi/v2/parser/getOperationResponse.ts index bcc00d6a2..9068c93b3 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getOperationResponse.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getOperationResponse.ts @@ -1,8 +1,9 @@ +import type { OperationResponse } from '~/openApi/common/interfaces/client'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getRef } from '~/openApi/common/parser/getRef'; +import { getType } from '~/openApi/common/parser/type'; + import type { Client } from '../../../types/client'; -import type { OperationResponse } from '../../common/interfaces/client'; -import { getPattern } from '../../common/parser/getPattern'; -import { getRef } from '../../common/parser/getRef'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiResponse } from '../interfaces/OpenApiResponse'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getOperationResponses.ts b/packages/openapi-ts/src/openApi/v2/parser/getOperationResponses.ts index 19dc6a7d1..3ebf13843 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getOperationResponses.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getOperationResponses.ts @@ -1,11 +1,12 @@ -import type { Client } from '../../../types/client'; -import type { OperationResponse } from '../../common/interfaces/client'; -import { getRef } from '../../common/parser/getRef'; +import type { OperationResponse } from '~/openApi/common/interfaces/client'; +import { getRef } from '~/openApi/common/parser/getRef'; import { parseResponseStatusCode, sorterByResponseStatusCode, tagResponseTypes, -} from '../../common/parser/operation'; +} from '~/openApi/common/parser/operation'; + +import type { Client } from '../../../types/client'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiResponse } from '../interfaces/OpenApiResponse'; import type { OpenApiResponses } from '../interfaces/OpenApiResponses'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getOperations.ts b/packages/openapi-ts/src/openApi/v2/parser/getOperations.ts index 332bd3d1f..fe297f310 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getOperations.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getOperations.ts @@ -1,8 +1,9 @@ -import { createOperationKey } from '../../../ir/operation'; -import { getConfig } from '../../../utils/config'; -import type { Client, Operation } from '../../common/interfaces/client'; -import { operationFilterFn } from '../../common/parser/operation'; -import { allowedServiceMethods } from '../../common/parser/service'; +import { createOperationKey } from '~/ir/operation'; +import type { Client, Operation } from '~/openApi/common/interfaces/client'; +import { operationFilterFn } from '~/openApi/common/parser/operation'; +import { allowedServiceMethods } from '~/openApi/common/parser/service'; +import { getConfig } from '~/utils/config'; + import type { OpenApi } from '../interfaces/OpenApi'; import { getOperationParameters } from './getOperationParameters'; import { getOperation } from './operation'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts b/packages/openapi-ts/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts index bec99562c..b67368619 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/getRequiredPropertiesFromComposition.ts @@ -1,6 +1,7 @@ +import type { Model } from '~/openApi/common/interfaces/client'; +import { getRef } from '~/openApi/common/parser/getRef'; + import type { Client } from '../../../types/client'; -import type { Model } from '../../common/interfaces/client'; -import { getRef } from '../../common/parser/getRef'; import type { GetModelFn } from '../interfaces/Model'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v2/parser/operation.ts b/packages/openapi-ts/src/openApi/v2/parser/operation.ts index 6897b2cd5..9a34b1f27 100644 --- a/packages/openapi-ts/src/openApi/v2/parser/operation.ts +++ b/packages/openapi-ts/src/openApi/v2/parser/operation.ts @@ -1,14 +1,15 @@ -import type { Client } from '../../../types/client'; -import { getConfig } from '../../../utils/config'; import type { Operation, OperationParameters, -} from '../../common/interfaces/client'; +} from '~/openApi/common/interfaces/client'; import { getOperationResponseHeader, operationNameFn, -} from '../../common/parser/operation'; -import { toSortedByRequired } from '../../common/parser/sort'; +} from '~/openApi/common/parser/operation'; +import { toSortedByRequired } from '~/openApi/common/parser/sort'; +import { getConfig } from '~/utils/config'; + +import type { Client } from '../../../types/client'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiOperation } from '../interfaces/OpenApiOperation'; import { getOperationParameters } from './getOperationParameters'; diff --git a/packages/openapi-ts/src/openApi/v3/index.ts b/packages/openapi-ts/src/openApi/v3/index.ts index 9d35fad1b..eca7c2948 100644 --- a/packages/openapi-ts/src/openApi/v3/index.ts +++ b/packages/openapi-ts/src/openApi/v3/index.ts @@ -1,5 +1,6 @@ -import type { Client } from '../common/interfaces/client'; -import { getServiceVersion } from '../common/parser/service'; +import type { Client } from '~/openApi/common/interfaces/client'; +import { getServiceVersion } from '~/openApi/common/parser/service'; + import type { OpenApi } from './interfaces/OpenApi'; import { getModels } from './parser/getModels'; import { getOperations } from './parser/getOperations'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/Model.ts b/packages/openapi-ts/src/openApi/v3/interfaces/Model.ts index 56e35812c..731d6f25c 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/Model.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/Model.ts @@ -1,5 +1,6 @@ +import type { Model, ModelMeta } from '~/openApi/common/interfaces/client'; + import type { Client } from '../../../types/client'; -import type { Model, ModelMeta } from '../../common/interfaces/client'; import type { OpenApi } from './OpenApi'; import type { OpenApiSchema } from './OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiComponents.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiComponents.ts index c6a1f9130..883acb493 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiComponents.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiComponents.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiCallback } from './OpenApiCallback'; import type { OpenApiExample } from './OpenApiExample'; import type { OpenApiHeader } from './OpenApiHeader'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiDiscriminator.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiDiscriminator.ts index 224d9d203..bb33b72b7 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiDiscriminator.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiDiscriminator.ts @@ -1,4 +1,4 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; /** * {@link} https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#discriminator-object diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiEncoding.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiEncoding.ts index 20e8bfa4a..620eedbee 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiEncoding.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiEncoding.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiHeader } from './OpenApiHeader'; /** diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiHeader.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiHeader.ts index 64044450d..e32ba419d 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiHeader.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiHeader.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiExample } from './OpenApiExample'; import type { OpenApiReference } from './OpenApiReference'; import type { OpenApiSchema } from './OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiLink.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiLink.ts index 7173970d4..6b36c907e 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiLink.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiLink.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiReference } from './OpenApiReference'; import type { OpenApiServer } from './OpenApiServer'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiMediaType.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiMediaType.ts index 3c74d4c62..01d115809 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiMediaType.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiMediaType.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiEncoding } from './OpenApiEncoding'; import type { OpenApiExample } from './OpenApiExample'; import type { OpenApiReference } from './OpenApiReference'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOAuthFlow.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOAuthFlow.ts index be01efeab..5ef94cc65 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOAuthFlow.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOAuthFlow.ts @@ -1,4 +1,4 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; /** * {@link} https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#oauth-flow-object diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOperation.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOperation.ts index b55c9a1d7..f4bcc9bd9 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOperation.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiOperation.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiCallback } from './OpenApiCallback'; import type { OpenApiExternalDocs } from './OpenApiExternalDocs'; import type { OpenApiParameter } from './OpenApiParameter'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiParameter.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiParameter.ts index 1520ec492..3bf7ec4ac 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiParameter.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiParameter.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiExample } from './OpenApiExample'; import type { OpenApiReference } from './OpenApiReference'; import type { OpenApiSchema } from './OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiRequestBody.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiRequestBody.ts index 0bbc6397a..a001a7a8a 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiRequestBody.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiRequestBody.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiMediaType } from './OpenApiMediaType'; import type { OpenApiReference } from './OpenApiReference'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiResponse.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiResponse.ts index d3914e72b..83fe0c65b 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiResponse.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiResponse.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiHeader } from './OpenApiHeader'; import type { OpenApiLink } from './OpenApiLink'; import type { OpenApiMediaType } from './OpenApiMediaType'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiSchema.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiSchema.ts index ef0b56f87..f8c66cc96 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiSchema.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiSchema.ts @@ -1,5 +1,6 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; -import type { WithEnumExtension } from '../../common/interfaces/WithEnumExtension'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; +import type { WithEnumExtension } from '~/openApi/common/interfaces/WithEnumExtension'; + import type { OpenApiDiscriminator } from './OpenApiDiscriminator'; import type { OpenApiExternalDocs } from './OpenApiExternalDocs'; import type { OpenApiReference } from './OpenApiReference'; diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServer.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServer.ts index 4c1a50c97..d09ccaf6c 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServer.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServer.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApiServerVariable } from './OpenApiServerVariable'; /** diff --git a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServerVariable.ts b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServerVariable.ts index 2673fc174..1477f8fe1 100644 --- a/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServerVariable.ts +++ b/packages/openapi-ts/src/openApi/v3/interfaces/OpenApiServerVariable.ts @@ -1,4 +1,4 @@ -import type { WithEnumExtension } from '../../common/interfaces/WithEnumExtension'; +import type { WithEnumExtension } from '~/openApi/common/interfaces/WithEnumExtension'; /** * {@link} https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#server-variable-object diff --git a/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts b/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts index 53461cd76..17a3021d7 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/__tests__/getModel.test.ts @@ -1,9 +1,10 @@ import type ts from 'typescript'; import { describe, expect, it, vi } from 'vitest'; +import { getType } from '~/openApi/common/parser/type'; + import type { Config } from '../../../../types/config'; import { reservedJavaScriptKeywordsRegExp } from '../../../../utils/regexp'; -import { getType } from '../../../common/parser/type'; import { getModel } from '../getModel'; vi.mock('../../../../utils/config', () => { diff --git a/packages/openapi-ts/src/openApi/v3/parser/discriminator.ts b/packages/openapi-ts/src/openApi/v3/parser/discriminator.ts index 4301e4aee..ea3834aec 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/discriminator.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/discriminator.ts @@ -1,6 +1,7 @@ -import type { Model } from '../../common/interfaces/client'; -import type { Dictionary } from '../../common/interfaces/Dictionary'; -import { stripNamespace } from '../../common/parser/stripNamespace'; +import type { Model } from '~/openApi/common/interfaces/client'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; +import { stripNamespace } from '~/openApi/common/parser/stripNamespace'; + import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiDiscriminator } from '../interfaces/OpenApiDiscriminator'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getContent.ts b/packages/openapi-ts/src/openApi/v3/parser/getContent.ts index 6aa21293c..26cbd7f59 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getContent.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getContent.ts @@ -1,4 +1,5 @@ -import type { Dictionary } from '../../common/interfaces/Dictionary'; +import type { Dictionary } from '~/openApi/common/interfaces/Dictionary'; + import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiMediaType } from '../interfaces/OpenApiMediaType'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getModel.ts b/packages/openapi-ts/src/openApi/v3/parser/getModel.ts index 5689222e5..e3dd272d9 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getModel.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getModel.ts @@ -1,10 +1,11 @@ +import type { Model, ModelMeta } from '~/openApi/common/interfaces/client'; +import { getDefault } from '~/openApi/common/parser/getDefault'; +import { getEnums } from '~/openApi/common/parser/getEnums'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getType } from '~/openApi/common/parser/type'; +import { enumMeta } from '~/utils/enum'; + import type { Client } from '../../../types/client'; -import { enumMeta } from '../../../utils/enum'; -import type { Model, ModelMeta } from '../../common/interfaces/client'; -import { getDefault } from '../../common/parser/getDefault'; -import { getEnums } from '../../common/parser/getEnums'; -import { getPattern } from '../../common/parser/getPattern'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; import { diff --git a/packages/openapi-ts/src/openApi/v3/parser/getModelComposition.ts b/packages/openapi-ts/src/openApi/v3/parser/getModelComposition.ts index 583eca71e..ca63a28a9 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getModelComposition.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getModelComposition.ts @@ -1,5 +1,9 @@ +import type { + Model, + ModelComposition, +} from '~/openApi/common/interfaces/client'; + import type { Client } from '../../../types/client'; -import type { Model, ModelComposition } from '../../common/interfaces/client'; import type { GetModelFn } from '../interfaces/Model'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getModelProperties.ts b/packages/openapi-ts/src/openApi/v3/parser/getModelProperties.ts index 5d94c34bc..83f69aeb9 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getModelProperties.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getModelProperties.ts @@ -1,10 +1,11 @@ +import type { Model } from '~/openApi/common/interfaces/client'; +import { getDefault } from '~/openApi/common/parser/getDefault'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getType } from '~/openApi/common/parser/type'; +import { escapeName } from '~/utils/escape'; +import { unique } from '~/utils/unique'; + import type { Client } from '../../../types/client'; -import { escapeName } from '../../../utils/escape'; -import { unique } from '../../../utils/unique'; -import type { Model } from '../../common/interfaces/client'; -import { getDefault } from '../../common/parser/getDefault'; -import { getPattern } from '../../common/parser/getPattern'; -import { getType } from '../../common/parser/type'; import type { GetModelFn } from '../interfaces/Model'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getModels.ts b/packages/openapi-ts/src/openApi/v3/parser/getModels.ts index ee410fa03..9d19db258 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getModels.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getModels.ts @@ -1,5 +1,6 @@ +import { getParametersMeta, getSchemasMeta } from '~/utils/meta'; + import type { Client } from '../../../types/client'; -import { getParametersMeta, getSchemasMeta } from '../../../utils/meta'; import type { OpenApi } from '../interfaces/OpenApi'; import { getModel } from './getModel'; import { getParameterSchema } from './parameter'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts b/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts index ac45a9ffb..6289c6038 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts @@ -1,12 +1,13 @@ +import type { OperationParameter } from '~/openApi/common/interfaces/client'; +import { getDefault } from '~/openApi/common/parser/getDefault'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getRef } from '~/openApi/common/parser/getRef'; +import { operationParameterNameFn } from '~/openApi/common/parser/operation'; +import { getType } from '~/openApi/common/parser/type'; +import { refParametersPartial } from '~/utils/const'; +import { enumMeta } from '~/utils/enum'; + import type { Client } from '../../../types/client'; -import { refParametersPartial } from '../../../utils/const'; -import { enumMeta } from '../../../utils/enum'; -import type { OperationParameter } from '../../common/interfaces/client'; -import { getDefault } from '../../common/parser/getDefault'; -import { getPattern } from '../../common/parser/getPattern'; -import { getRef } from '../../common/parser/getRef'; -import { operationParameterNameFn } from '../../common/parser/operation'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiParameter } from '../interfaces/OpenApiParameter'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getOperationParameters.ts b/packages/openapi-ts/src/openApi/v3/parser/getOperationParameters.ts index 3748308fd..359106552 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getOperationParameters.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getOperationParameters.ts @@ -1,7 +1,8 @@ +import type { OperationParameters } from '~/openApi/common/interfaces/client'; +import { getRef } from '~/openApi/common/parser/getRef'; +import { operationParameterFilterFn } from '~/openApi/common/parser/operation'; + import type { Client } from '../../../types/client'; -import type { OperationParameters } from '../../common/interfaces/client'; -import { getRef } from '../../common/parser/getRef'; -import { operationParameterFilterFn } from '../../common/parser/operation'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiParameter } from '../interfaces/OpenApiParameter'; import { getOperationParameter } from './getOperationParameter'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getOperationRequestBody.ts b/packages/openapi-ts/src/openApi/v3/parser/getOperationRequestBody.ts index 14bea9135..350f28abd 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getOperationRequestBody.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getOperationRequestBody.ts @@ -1,7 +1,8 @@ +import type { OperationParameter } from '~/openApi/common/interfaces/client'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getType } from '~/openApi/common/parser/type'; + import type { Client } from '../../../types/client'; -import type { OperationParameter } from '../../common/interfaces/client'; -import { getPattern } from '../../common/parser/getPattern'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiRequestBody } from '../interfaces/OpenApiRequestBody'; import { getContent } from './getContent'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getOperationResponse.ts b/packages/openapi-ts/src/openApi/v3/parser/getOperationResponse.ts index 73f3638a0..fb022b20f 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getOperationResponse.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getOperationResponse.ts @@ -1,8 +1,9 @@ +import type { OperationResponse } from '~/openApi/common/interfaces/client'; +import { getPattern } from '~/openApi/common/parser/getPattern'; +import { getRef } from '~/openApi/common/parser/getRef'; +import { getType } from '~/openApi/common/parser/type'; + import type { Client } from '../../../types/client'; -import type { OperationResponse } from '../../common/interfaces/client'; -import { getPattern } from '../../common/parser/getPattern'; -import { getRef } from '../../common/parser/getRef'; -import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiResponse } from '../interfaces/OpenApiResponse'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getOperationResponses.ts b/packages/openapi-ts/src/openApi/v3/parser/getOperationResponses.ts index a935bc84d..2719459ba 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getOperationResponses.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getOperationResponses.ts @@ -1,11 +1,12 @@ -import type { Client } from '../../../types/client'; -import type { OperationResponse } from '../../common/interfaces/client'; -import { getRef } from '../../common/parser/getRef'; +import type { OperationResponse } from '~/openApi/common/interfaces/client'; +import { getRef } from '~/openApi/common/parser/getRef'; import { parseResponseStatusCode, sorterByResponseStatusCode, tagResponseTypes, -} from '../../common/parser/operation'; +} from '~/openApi/common/parser/operation'; + +import type { Client } from '../../../types/client'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiResponse } from '../interfaces/OpenApiResponse'; import type { OpenApiResponses } from '../interfaces/OpenApiResponses'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getOperations.ts b/packages/openapi-ts/src/openApi/v3/parser/getOperations.ts index 332bd3d1f..fe297f310 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getOperations.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getOperations.ts @@ -1,8 +1,9 @@ -import { createOperationKey } from '../../../ir/operation'; -import { getConfig } from '../../../utils/config'; -import type { Client, Operation } from '../../common/interfaces/client'; -import { operationFilterFn } from '../../common/parser/operation'; -import { allowedServiceMethods } from '../../common/parser/service'; +import { createOperationKey } from '~/ir/operation'; +import type { Client, Operation } from '~/openApi/common/interfaces/client'; +import { operationFilterFn } from '~/openApi/common/parser/operation'; +import { allowedServiceMethods } from '~/openApi/common/parser/service'; +import { getConfig } from '~/utils/config'; + import type { OpenApi } from '../interfaces/OpenApi'; import { getOperationParameters } from './getOperationParameters'; import { getOperation } from './operation'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts b/packages/openapi-ts/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts index 187e0f92a..6f99f3288 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getRequiredPropertiesFromComposition.ts @@ -1,7 +1,8 @@ +import type { Model } from '~/openApi/common/interfaces/client'; +import { getRef } from '~/openApi/common/parser/getRef'; +import { getType } from '~/openApi/common/parser/type'; + import type { Client } from '../../../types/client'; -import type { Model } from '../../common/interfaces/client'; -import { getRef } from '../../common/parser/getRef'; -import { getType } from '../../common/parser/type'; import type { GetModelFn } from '../interfaces/Model'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; diff --git a/packages/openapi-ts/src/openApi/v3/parser/operation.ts b/packages/openapi-ts/src/openApi/v3/parser/operation.ts index 1669e2f8e..9ac0bc5e9 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/operation.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/operation.ts @@ -1,16 +1,17 @@ -import type { Client } from '../../../types/client'; -import { getConfig } from '../../../utils/config'; import type { Operation, OperationParameter, OperationParameters, -} from '../../common/interfaces/client'; -import { getRef } from '../../common/parser/getRef'; +} from '~/openApi/common/interfaces/client'; +import { getRef } from '~/openApi/common/parser/getRef'; import { getOperationResponseHeader, operationNameFn, -} from '../../common/parser/operation'; -import { toSortedByRequired } from '../../common/parser/sort'; +} from '~/openApi/common/parser/operation'; +import { toSortedByRequired } from '~/openApi/common/parser/sort'; +import { getConfig } from '~/utils/config'; + +import type { Client } from '../../../types/client'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiOperation } from '../interfaces/OpenApiOperation'; import type { OpenApiRequestBody } from '../interfaces/OpenApiRequestBody'; diff --git a/packages/openapi-ts/src/plugins/@angular/common/api.ts b/packages/openapi-ts/src/plugins/@angular/common/api.ts index 00e28a062..ba721b633 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/api.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = | 'class' diff --git a/packages/openapi-ts/src/plugins/@angular/common/config.ts b/packages/openapi-ts/src/plugins/@angular/common/config.ts index 2e4d5be80..8bc8c001b 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/config.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import type { AngularCommonPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts b/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts index 7874f75ad..0404d2d0e 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/httpRequests.ts @@ -1,13 +1,14 @@ import type { Symbol } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { stringCase } from '../../../utils/stringCase'; -import { getClientPlugin } from '../../@hey-api/client-core/utils'; -import { operationClasses } from '../../@hey-api/sdk/operation'; -import { isOperationOptionsRequired } from '../../shared/utils/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; +import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; +import { tsc } from '~/tsc'; +import { stringCase } from '~/utils/stringCase'; + import type { AngularCommonPlugin } from './types'; interface AngularRequestClassEntry { diff --git a/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts b/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts index 2d7fbb818..76f32d3b9 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/httpResources.ts @@ -1,12 +1,13 @@ import type { Symbol } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { stringCase } from '../../../utils/stringCase'; -import { operationClasses } from '../../@hey-api/sdk/operation'; -import { isOperationOptionsRequired } from '../../shared/utils/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; +import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; +import { tsc } from '~/tsc'; +import { stringCase } from '~/utils/stringCase'; + import type { AngularCommonPlugin } from './types'; interface AngularServiceClassEntry { diff --git a/packages/openapi-ts/src/plugins/@angular/common/types.d.ts b/packages/openapi-ts/src/plugins/@angular/common/types.d.ts index a7d7f73e7..680b72b85 100644 --- a/packages/openapi-ts/src/plugins/@angular/common/types.d.ts +++ b/packages/openapi-ts/src/plugins/@angular/common/types.d.ts @@ -1,5 +1,6 @@ -import type { StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@angular/common'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts index 421e8e03e..1fdc9a1f7 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-angular/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'client'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-angular/config.ts b/packages/openapi-ts/src/plugins/@hey-api/client-angular/config.ts index 206439156..f15b005af 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-angular/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-angular/config.ts @@ -1,6 +1,10 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { clientDefaultConfig, clientDefaultMeta } from '../client-core/config'; -import { clientPluginHandler } from '../client-core/plugin'; +import { + clientDefaultConfig, + clientDefaultMeta, +} from '~/plugins/@hey-api/client-core/config'; +import { clientPluginHandler } from '~/plugins/@hey-api/client-core/plugin'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { HeyApiClientAngularPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-angular/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-angular/types.d.ts index f00c11617..5bb2cff2f 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-angular/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-angular/types.d.ts @@ -1,5 +1,6 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/client-angular'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts index 08ee36a65..ee544c72c 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-axios/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'client'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-axios/config.ts b/packages/openapi-ts/src/plugins/@hey-api/client-axios/config.ts index 7cc48c700..cfb1d088f 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-axios/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-axios/config.ts @@ -1,6 +1,10 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { clientDefaultConfig, clientDefaultMeta } from '../client-core/config'; -import { clientPluginHandler } from '../client-core/plugin'; +import { + clientDefaultConfig, + clientDefaultMeta, +} from '~/plugins/@hey-api/client-core/config'; +import { clientPluginHandler } from '~/plugins/@hey-api/client-core/plugin'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { HeyApiClientAxiosPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-axios/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-axios/types.d.ts index 0d31baa36..058c44449 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-axios/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-axios/types.d.ts @@ -1,5 +1,6 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/client-axios'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts index b7180fc35..d1122ed2c 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/client.ts @@ -1,6 +1,7 @@ -import { clientFolderAbsolutePath } from '../../../generate/client'; -import { tsc } from '../../../tsc'; -import { parseUrl } from '../../../utils/url'; +import { clientFolderAbsolutePath } from '~/generate/client'; +import { tsc } from '~/tsc'; +import { parseUrl } from '~/utils/url'; + import type { PluginHandler } from './types'; import { getClientBaseUrlKey } from './utils'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts index cb39f82ff..732d5e9d7 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/createClientConfig.ts @@ -1,5 +1,6 @@ -import { clientFolderAbsolutePath } from '../../../generate/client'; -import { tsc } from '../../../tsc'; +import { clientFolderAbsolutePath } from '~/generate/client'; +import { tsc } from '~/tsc'; + import type { PluginHandler } from './types'; export const createClientConfigType = ({ diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts index 0b328ab78..d76b102fe 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/types.d.ts @@ -1,10 +1,10 @@ -import type { Plugin } from '../../types'; -import type { HeyApiClientAngularPlugin } from '../client-angular'; -import type { HeyApiClientAxiosPlugin } from '../client-axios'; -import type { HeyApiClientFetchPlugin } from '../client-fetch'; -import type { HeyApiClientNextPlugin } from '../client-next'; -import type { HeyApiClientNuxtPlugin } from '../client-nuxt'; -import type { HeyApiClientOfetchPlugin } from '../client-ofetch'; +import type { HeyApiClientAngularPlugin } from '~/plugins/@hey-api/client-angular'; +import type { HeyApiClientAxiosPlugin } from '~/plugins/@hey-api/client-axios'; +import type { HeyApiClientFetchPlugin } from '~/plugins/@hey-api/client-fetch'; +import type { HeyApiClientNextPlugin } from '~/plugins/@hey-api/client-next'; +import type { HeyApiClientNuxtPlugin } from '~/plugins/@hey-api/client-nuxt'; +import type { HeyApiClientOfetchPlugin } from '~/plugins/@hey-api/client-ofetch'; +import type { Plugin } from '~/plugins/types'; export interface PluginHandler { (...args: Parameters): void; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/utils.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/utils.ts index 9ad8f1adb..05a0cbbf0 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-core/utils.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/utils.ts @@ -1,5 +1,5 @@ -import type { Config } from '../../../types/config'; -import type { PluginClientNames } from '../../types'; +import type { PluginClientNames } from '~/plugins/types'; +import type { Config } from '~/types/config'; export const getClientBaseUrlKey = (config: Config) => { const client = getClientPlugin(config); diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts index d72153064..29e1dbc4d 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'client'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/config.ts b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/config.ts index e998359d3..0e50f65d0 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/config.ts @@ -1,6 +1,10 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { clientDefaultConfig, clientDefaultMeta } from '../client-core/config'; -import { clientPluginHandler } from '../client-core/plugin'; +import { + clientDefaultConfig, + clientDefaultMeta, +} from '~/plugins/@hey-api/client-core/config'; +import { clientPluginHandler } from '~/plugins/@hey-api/client-core/plugin'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { HeyApiClientFetchPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/types.d.ts index 89ccfec08..37d3b06f0 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/types.d.ts @@ -1,5 +1,6 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/client-fetch'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts index 6db16fbf8..d95026b35 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-next/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'client'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-next/config.ts b/packages/openapi-ts/src/plugins/@hey-api/client-next/config.ts index f97faa6ea..e219b4992 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-next/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-next/config.ts @@ -1,6 +1,10 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { clientDefaultConfig, clientDefaultMeta } from '../client-core/config'; -import { clientPluginHandler } from '../client-core/plugin'; +import { + clientDefaultConfig, + clientDefaultMeta, +} from '~/plugins/@hey-api/client-core/config'; +import { clientPluginHandler } from '~/plugins/@hey-api/client-core/plugin'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { HeyApiClientNextPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-next/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-next/types.d.ts index a4ecee19d..7319f3521 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-next/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-next/types.d.ts @@ -1,5 +1,6 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/client-next'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts index b5965c69b..e84b7849e 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'client'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/config.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/config.ts index 3a11f545b..6a686ecd4 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/config.ts @@ -1,6 +1,10 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { clientDefaultConfig, clientDefaultMeta } from '../client-core/config'; -import { clientPluginHandler } from '../client-core/plugin'; +import { + clientDefaultConfig, + clientDefaultMeta, +} from '~/plugins/@hey-api/client-core/config'; +import { clientPluginHandler } from '~/plugins/@hey-api/client-core/plugin'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { HeyApiClientNuxtPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/types.d.ts index 1c45f5704..4f297a0bb 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/types.d.ts @@ -1,5 +1,6 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/client-nuxt'> & Client.Config; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts index 479523b23..2e94cdb89 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'client'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/config.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/config.ts index 6cfaa83bf..adf180f20 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/config.ts @@ -1,6 +1,10 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { clientDefaultConfig, clientDefaultMeta } from '../client-core/config'; -import { clientPluginHandler } from '../client-core/plugin'; +import { + clientDefaultConfig, + clientDefaultMeta, +} from '~/plugins/@hey-api/client-core/config'; +import { clientPluginHandler } from '~/plugins/@hey-api/client-core/plugin'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { HeyApiClientOfetchPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/types.d.ts index 633236a4e..697618a19 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/types.d.ts @@ -1,5 +1,6 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/client-ofetch'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/config.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/config.ts index 9fb818205..7d7064629 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import type { HeyApiClientLegacyAngularPlugin } from './types'; export const defaultConfig: HeyApiClientLegacyAngularPlugin['Config'] = { diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/types.d.ts index c0286e5c7..82f85d754 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-angular/types.d.ts @@ -1,5 +1,5 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; export type UserConfig = Plugin.Name<'legacy/angular'> & Pick; diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/config.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/config.ts index b3b649214..796607fe9 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import type { HeyApiClientLegacyAxiosPlugin } from './types'; export const defaultConfig: HeyApiClientLegacyAxiosPlugin['Config'] = { diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/types.d.ts index f0f67e65f..0ca49022f 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-axios/types.d.ts @@ -1,5 +1,5 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; export type UserConfig = Plugin.Name<'legacy/axios'> & Pick; diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/config.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/config.ts index b25511763..730df72f0 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import type { HeyApiClientLegacyFetchPlugin } from './types'; export const defaultConfig: HeyApiClientLegacyFetchPlugin['Config'] = { diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/types.d.ts index 953a491c8..448d0020c 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-fetch/types.d.ts @@ -1,5 +1,5 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; export type UserConfig = Plugin.Name<'legacy/fetch'> & Pick; diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-node/config.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-node/config.ts index caa7f71ff..71e7051f0 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-node/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-node/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import type { HeyApiClientLegacyNodePlugin } from './types'; export const defaultConfig: HeyApiClientLegacyNodePlugin['Config'] = { diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-node/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-node/types.d.ts index 070b46611..9def74381 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-node/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-node/types.d.ts @@ -1,5 +1,5 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; export type UserConfig = Plugin.Name<'legacy/node'> & Pick; diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/config.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/config.ts index 617ce2ee9..499b222f1 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import type { HeyApiClientLegacyXhrPlugin } from './types'; export const defaultConfig: HeyApiClientLegacyXhrPlugin['Config'] = { diff --git a/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/types.d.ts index 6e19c613e..7b6d2623c 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/legacy-xhr/types.d.ts @@ -1,5 +1,5 @@ -import type { DefinePlugin, Plugin } from '../../types'; -import type { Client } from '../client-core/types'; +import type { Client } from '~/plugins/@hey-api/client-core/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; export type UserConfig = Plugin.Name<'legacy/xhr'> & Pick; diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts index 073916160..479ea0bcb 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/__tests__/schemas.test.ts @@ -4,11 +4,12 @@ import { Project } from '@hey-api/codegen-core'; import type ts from 'typescript'; import { describe, expect, it, vi } from 'vitest'; -import { client, openApi } from '../../../../generate/__tests__/mocks'; -import type { OpenApiV3Schema } from '../../../../openApi'; -import type { Files } from '../../../../types/utils'; -import { setConfig } from '../../../../utils/config'; -import { PluginInstance } from '../../../shared/utils/instance'; +import { client, openApi } from '~/generate/__tests__/mocks'; +import type { OpenApiV3Schema } from '~/openApi'; +import { PluginInstance } from '~/plugins/shared/utils/instance'; +import type { Files } from '~/types/utils'; +import { setConfig } from '~/utils/config'; + import { handlerLegacy } from '../plugin-legacy'; vi.mock('node:fs'); diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts index 71d0200ed..b24c4005c 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'ref'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/config.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/config.ts index 55c9a8136..7c4e6b2b2 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import { handlerLegacy } from './plugin-legacy'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin-legacy.ts index e2a16dc4f..ed0a5ebac 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin-legacy.ts @@ -1,8 +1,9 @@ -import { GeneratedFile } from '../../../generate/file'; -import type { OpenApiV2Schema, OpenApiV3Schema } from '../../../openApi'; -import { ensureValidTypeScriptJavaScriptIdentifier } from '../../../openApi'; -import { tsc } from '../../../tsc'; -import { getConfig } from '../../../utils/config'; +import { GeneratedFile } from '~/generate/file'; +import type { OpenApiV2Schema, OpenApiV3Schema } from '~/openApi'; +import { ensureValidTypeScriptJavaScriptIdentifier } from '~/openApi'; +import { tsc } from '~/tsc'; +import { getConfig } from '~/utils/config'; + import type { HeyApiSchemasPlugin } from './types'; const ensureValidSchemaOutput = ( diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts index 999f5a693..959bfb539 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/plugin.ts @@ -1,11 +1,12 @@ -import { satisfies } from '../../../config/utils/package'; -import type { IR } from '../../../ir/types'; -import type { OpenApiV2_0_XTypes } from '../../../openApi/2.0.x'; -import type { OpenApiV3_0_XTypes } from '../../../openApi/3.0.x'; -import type { OpenApiV3_1_XTypes } from '../../../openApi/3.1.x'; -import { ensureValidIdentifier } from '../../../openApi/shared/utils/identifier'; -import type { OpenApi } from '../../../openApi/types'; -import { tsc } from '../../../tsc'; +import { satisfies } from '~/config/utils/package'; +import type { IR } from '~/ir/types'; +import type { OpenApiV2_0_XTypes } from '~/openApi/2.0.x'; +import type { OpenApiV3_0_XTypes } from '~/openApi/3.0.x'; +import type { OpenApiV3_1_XTypes } from '~/openApi/3.1.x'; +import { ensureValidIdentifier } from '~/openApi/shared/utils/identifier'; +import type { OpenApi } from '~/openApi/types'; +import { tsc } from '~/tsc'; + import type { HeyApiSchemasPlugin } from './types'; const stripSchema = ({ diff --git a/packages/openapi-ts/src/plugins/@hey-api/schemas/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/schemas/types.d.ts index fdffdd1ac..856239acb 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/schemas/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/schemas/types.d.ts @@ -1,8 +1,9 @@ -import type { OpenApiV2Schema, OpenApiV3Schema } from '../../../openApi'; -import type { OpenApiV2_0_XTypes } from '../../../openApi/2.0.x'; -import type { OpenApiV3_0_XTypes } from '../../../openApi/3.0.x'; -import type { OpenApiV3_1_XTypes } from '../../../openApi/3.1.x'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { OpenApiV2Schema, OpenApiV3Schema } from '~/openApi'; +import type { OpenApiV2_0_XTypes } from '~/openApi/2.0.x'; +import type { OpenApiV3_0_XTypes } from '~/openApi/3.0.x'; +import type { OpenApiV3_1_XTypes } from '~/openApi/3.1.x'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/schemas'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts index d1e9a4ae5..86b288d48 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/__tests__/plugin.test.ts @@ -5,13 +5,14 @@ import { Project } from '@hey-api/codegen-core'; import type ts from 'typescript'; import { describe, expect, it, vi } from 'vitest'; -import { openApi } from '../../../../generate/__tests__/mocks'; -import { GeneratedFile } from '../../../../generate/file'; -import type { Operation } from '../../../../types/client'; -import type { Config } from '../../../../types/config'; -import type { Files } from '../../../../types/utils'; -import { setConfig } from '../../../../utils/config'; -import { PluginInstance } from '../../../shared/utils/instance'; +import { openApi } from '~/generate/__tests__/mocks'; +import { GeneratedFile } from '~/generate/file'; +import { PluginInstance } from '~/plugins/shared/utils/instance'; +import type { Operation } from '~/types/client'; +import type { Config } from '~/types/config'; +import type { Files } from '~/types/utils'; +import { setConfig } from '~/utils/config'; + import { handlerLegacy } from '../plugin-legacy'; vi.mock('node:fs'); diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts index 313c8a5e4..18031c2fe 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/api.ts @@ -1,6 +1,7 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; + import { createOperationComment } from './comment'; type SelectorType = diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/auth.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/auth.ts index 6b9094486..3766e0cd4 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/auth.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/auth.ts @@ -1,4 +1,5 @@ -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; + import type { Auth } from '../client-core/bundle/auth'; import type { HeyApiSdkPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/comment.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/comment.ts index 46a4f9d41..c8229c101 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/comment.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/comment.ts @@ -1,6 +1,6 @@ -import type { IR } from '../../../ir/types'; -import type { Comments } from '../../../tsc'; -import { escapeComment } from '../../../utils/escape'; +import type { IR } from '~/ir/types'; +import type { Comments } from '~/tsc'; +import { escapeComment } from '~/utils/escape'; export const createOperationComment = ({ operation, diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/config.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/config.ts index 967510fa5..956f1e584 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import { handlerLegacy } from './plugin-legacy'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts index 2c2bacce3..e63776dbe 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/operation.ts @@ -1,16 +1,17 @@ import type ts from 'typescript'; -import { statusCodeToGroup } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { sanitizeNamespaceIdentifier } from '../../../openApi'; -import { ensureValidIdentifier } from '../../../openApi/shared/utils/identifier'; -import { tsc } from '../../../tsc'; -import type { FunctionParameter, ObjectValue } from '../../../tsc/types'; -import { reservedJavaScriptKeywordsRegExp } from '../../../utils/regexp'; -import { stringCase } from '../../../utils/stringCase'; -import { transformClassName } from '../../../utils/transform'; +import { statusCodeToGroup } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { sanitizeNamespaceIdentifier } from '~/openApi'; +import { ensureValidIdentifier } from '~/openApi/shared/utils/identifier'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import { tsc } from '~/tsc'; +import type { FunctionParameter, ObjectValue } from '~/tsc/types'; +import { reservedJavaScriptKeywordsRegExp } from '~/utils/regexp'; +import { stringCase } from '~/utils/stringCase'; +import { transformClassName } from '~/utils/transform'; + import type { Field, Fields } from '../client-core/bundle/params'; -import { getClientPlugin } from '../client-core/utils'; import { operationAuth } from './auth'; import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; // import { getSignatureParameters } from './signature'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin-legacy.ts index 3a8c30c3c..0ed757f26 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin-legacy.ts @@ -1,32 +1,33 @@ import type ts from 'typescript'; -import { clientModulePath } from '../../../generate/client'; -import { GeneratedFile } from '../../../generate/file'; -import type { IR } from '../../../ir/types'; -import { isOperationParameterRequired } from '../../../openApi'; -import type { Comments, FunctionParameter } from '../../../tsc'; -import { tsc } from '../../../tsc'; -import type { FunctionTypeParameter, ObjectValue } from '../../../tsc/types'; +import { clientModulePath } from '~/generate/client'; +import { GeneratedFile } from '~/generate/file'; +import type { IR } from '~/ir/types'; +import { isOperationParameterRequired } from '~/openApi'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import type { Comments, FunctionParameter } from '~/tsc'; +import { tsc } from '~/tsc'; +import type { FunctionTypeParameter, ObjectValue } from '~/tsc/types'; import type { Client, Model, Operation, OperationParameter, Service, -} from '../../../types/client'; -import type { Config as ClientConfig } from '../../../types/config'; +} from '~/types/client'; +import type { Config as ClientConfig } from '~/types/config'; import { getConfig, isLegacyClient, legacyNameFromConfig, -} from '../../../utils/config'; -import { escapeComment, escapeName } from '../../../utils/escape'; -import { reservedJavaScriptKeywordsRegExp } from '../../../utils/regexp'; -import { stringCase } from '../../../utils/stringCase'; -import { transformClassName } from '../../../utils/transform'; -import { setUniqueTypeName } from '../../../utils/type'; -import { unique } from '../../../utils/unique'; -import { getClientPlugin } from '../client-core/utils'; +} from '~/utils/config'; +import { escapeComment, escapeName } from '~/utils/escape'; +import { reservedJavaScriptKeywordsRegExp } from '~/utils/regexp'; +import { stringCase } from '~/utils/stringCase'; +import { transformClassName } from '~/utils/transform'; +import { setUniqueTypeName } from '~/utils/type'; +import { unique } from '~/utils/unique'; + import type { HeyApiSdkPlugin } from './types'; type OnNode = (node: ts.Node) => void; diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts index f583ecac7..8407fe0cb 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts @@ -1,10 +1,11 @@ import type ts from 'typescript'; -import { clientFolderAbsolutePath } from '../../../generate/client'; -import { tsc } from '../../../tsc'; -import { stringCase } from '../../../utils/stringCase'; -import { isOperationOptionsRequired } from '../../shared/utils/operation'; -import { getClientPlugin } from '../client-core/utils'; +import { clientFolderAbsolutePath } from '~/generate/client'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; +import { tsc } from '~/tsc'; +import { stringCase } from '~/utils/stringCase'; + import { nuxtTypeComposable, nuxtTypeDefault } from './constants'; import { operationClasses, diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/signature.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/signature.ts index 32f03b753..fbd6042e6 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/signature.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/signature.ts @@ -1,4 +1,4 @@ -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; type Location = keyof IR.ParametersObject | 'body'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts index 52e5d0bc8..50efaf4c0 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/typeOptions.ts @@ -1,6 +1,7 @@ -import { clientFolderAbsolutePath } from '../../../generate/client'; -import { tsc } from '../../../tsc'; -import { getClientPlugin } from '../client-core/utils'; +import { clientFolderAbsolutePath } from '~/generate/client'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import { tsc } from '~/tsc'; + import { nuxtTypeDefault, nuxtTypeResponse } from './constants'; import type { HeyApiSdkPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/types.d.ts index ffd5d7ad7..54994441e 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/types.d.ts @@ -1,12 +1,13 @@ -import type { IR } from '../../../ir/types'; -import type { StringName } from '../../../types/case'; -import type { Operation } from '../../../types/client'; +import type { IR } from '~/ir/types'; import type { DefinePlugin, Plugin, PluginClientNames, PluginValidatorNames, -} from '../../types'; +} from '~/plugins/types'; +import type { StringName } from '~/types/case'; +import type { Operation } from '~/types/client'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@hey-api/sdk'> & diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/validator.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/validator.ts index 714207fb9..1819ca02c 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/validator.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/validator.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; + import type { HeyApiSdkPlugin } from './types'; interface ValidatorProps { diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts index 3a60212ba..5b9a9a449 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'response' | 'response-ref'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/config.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/config.ts index 53a7ef0ae..4bbf54984 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { bigIntExpressions, dateExpressions } from './expressions'; import { handler } from './plugin'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/expressions.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/expressions.ts index 4e1e94971..070bd06a9 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/expressions.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/expressions.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { tsc } from '~/tsc'; + import type { UserConfig } from './types'; export type ExpressionTransformer = ({ diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin-legacy.ts index 2d5902c17..ae6df43c9 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin-legacy.ts @@ -1,16 +1,20 @@ import type ts from 'typescript'; -import { createOperationKey } from '../../../ir/operation'; -import { tsc } from '../../../tsc'; -import type { ModelMeta, OperationResponse } from '../../../types/client'; -import { getConfig } from '../../../utils/config'; -import { isModelDate, unsetUniqueTypeName } from '../../../utils/type'; +import { createOperationKey } from '~/ir/operation'; import { modelResponseTransformerTypeName, operationResponseTransformerTypeName, operationResponseTypeName, -} from '../sdk/plugin-legacy'; -import { generateType, type TypesProps } from '../typescript/plugin-legacy'; +} from '~/plugins/@hey-api/sdk/plugin-legacy'; +import { + generateType, + type TypesProps, +} from '~/plugins/@hey-api/typescript/plugin-legacy'; +import { tsc } from '~/tsc'; +import type { ModelMeta, OperationResponse } from '~/types/client'; +import { getConfig } from '~/utils/config'; +import { isModelDate, unsetUniqueTypeName } from '~/utils/type'; + import type { HeyApiTransformersPlugin } from './types'; interface ModelProps extends TypesProps { diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts index 485871b92..6fdfca657 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/plugin.ts @@ -1,13 +1,11 @@ import ts from 'typescript'; -import { - createOperationKey, - operationResponsesMap, -} from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; +import { createOperationKey, operationResponsesMap } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { tsc } from '~/tsc'; +import { refToName } from '~/utils/ref'; + import type { HeyApiTransformersPlugin } from './types'; const dataVariableName = 'data'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/transformers/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/transformers/types.d.ts index 5ac02deac..66efda569 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/transformers/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/transformers/types.d.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { IR } from '~/ir/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; import type { ExpressionTransformer } from './expressions'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts index 3a59329d1..b1935c695 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/__tests__/plugin.test.ts @@ -4,11 +4,12 @@ import { Project } from '@hey-api/codegen-core'; import type ts from 'typescript'; import { describe, expect, it, vi } from 'vitest'; -import { openApi } from '../../../../generate/__tests__/mocks'; -import { GeneratedFile } from '../../../../generate/file'; -import type { Config } from '../../../../types/config'; -import { setConfig } from '../../../../utils/config'; -import { PluginInstance } from '../../../shared/utils/instance'; +import { openApi } from '~/generate/__tests__/mocks'; +import { GeneratedFile } from '~/generate/file'; +import { PluginInstance } from '~/plugins/shared/utils/instance'; +import type { Config } from '~/types/config'; +import { setConfig } from '~/utils/config'; + import { handlerLegacy } from '../plugin-legacy'; vi.mock('node:fs'); diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts index aacaaf16a..3b453b007 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/api.ts @@ -1,7 +1,8 @@ import type { Selector } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; + import { schemaToType } from './plugin'; type SelectorType = diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/clientOptions.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/clientOptions.ts index eafcc5e40..e5a032432 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/clientOptions.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/clientOptions.ts @@ -1,10 +1,14 @@ import type { Symbol } from '@hey-api/codegen-core'; import ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { tsc } from '../../../tsc'; -import { parseUrl } from '../../../utils/url'; -import { getClientBaseUrlKey, getClientPlugin } from '../client-core/utils'; +import type { IR } from '~/ir/types'; +import { + getClientBaseUrlKey, + getClientPlugin, +} from '~/plugins/@hey-api/client-core/utils'; +import { tsc } from '~/tsc'; +import { parseUrl } from '~/utils/url'; + import type { HeyApiTypeScriptPlugin } from './types'; const stringType = tsc.keywordTypeNode({ keyword: 'string' }); diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/config.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/config.ts index ac3575b97..4959c9ab6 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/config.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import { handlerLegacy } from './plugin-legacy'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/export.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/export.ts index 13171b12c..b84ed2e67 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/export.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/export.ts @@ -1,11 +1,12 @@ import type { Symbol } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { tsc } from '../../../tsc'; -import { numberRegExp } from '../../../utils/regexp'; -import { stringCase } from '../../../utils/stringCase'; -import { createSchemaComment } from '../../shared/utils/schema'; +import type { IR } from '~/ir/types'; +import { createSchemaComment } from '~/plugins/shared/utils/schema'; +import { tsc } from '~/tsc'; +import { numberRegExp } from '~/utils/regexp'; +import { stringCase } from '~/utils/stringCase'; + import type { HeyApiTypeScriptPlugin } from './types'; const schemaToEnumObject = ({ diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts index 1584241f8..490275976 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts @@ -1,8 +1,9 @@ -import { operationResponsesMap } from '../../../ir/operation'; -import { deduplicateSchema } from '../../../ir/schema'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; +import { operationResponsesMap } from '~/ir/operation'; +import { deduplicateSchema } from '~/ir/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { tsc } from '~/tsc'; + import { schemaToType } from './plugin'; import type { HeyApiTypeScriptPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin-legacy.ts index 5c7ab2f02..cc368fe34 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin-legacy.ts @@ -1,28 +1,24 @@ import type ts from 'typescript'; -import { GeneratedFile } from '../../../generate/file'; -import { isOperationParameterRequired } from '../../../openApi'; -import { type Comments, tsc } from '../../../tsc'; -import type { - Client, - Method, - Model, - OperationParameter, -} from '../../../types/client'; -import { getConfig, isLegacyClient } from '../../../utils/config'; -import { enumEntry, enumUnionType } from '../../../utils/enum'; -import { escapeComment } from '../../../utils/escape'; -import { sortByName, sorterByName } from '../../../utils/sort'; -import { - setUniqueTypeName, - type SetUniqueTypeNameResult, - toType, -} from '../../../utils/type'; +import { GeneratedFile } from '~/generate/file'; +import { isOperationParameterRequired } from '~/openApi'; import { operationDataTypeName, operationErrorTypeName, operationResponseTypeName, -} from '../sdk/plugin-legacy'; +} from '~/plugins/@hey-api/sdk/plugin-legacy'; +import { type Comments, tsc } from '~/tsc'; +import type { Client, Method, Model, OperationParameter } from '~/types/client'; +import { getConfig, isLegacyClient } from '~/utils/config'; +import { enumEntry, enumUnionType } from '~/utils/enum'; +import { escapeComment } from '~/utils/escape'; +import { sortByName, sorterByName } from '~/utils/sort'; +import { + setUniqueTypeName, + type SetUniqueTypeNameResult, + toType, +} from '~/utils/type'; + import type { HeyApiTypeScriptPlugin } from './types'; export interface TypesProps { diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts index 0de46c7ba..4907d28a2 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/plugin.ts @@ -1,15 +1,16 @@ import type ts from 'typescript'; -import { deduplicateSchema } from '../../../ir/schema'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import type { Property } from '../../../tsc'; -import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import { stringCase } from '../../../utils/stringCase'; -import type { SchemaWithType } from '../../shared/types/schema'; -import { fieldName } from '../../shared/utils/case'; -import { createSchemaComment } from '../../shared/utils/schema'; +import { deduplicateSchema } from '~/ir/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { fieldName } from '~/plugins/shared/utils/case'; +import { createSchemaComment } from '~/plugins/shared/utils/schema'; +import type { Property } from '~/tsc'; +import { tsc } from '~/tsc'; +import { refToName } from '~/utils/ref'; +import { stringCase } from '~/utils/stringCase'; + import { createClientOptions } from './clientOptions'; import { exportType } from './export'; import { operationToType } from './operation'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/types.d.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/types.d.ts index 5722a2fe5..fd1659c37 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/types.d.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/types.d.ts @@ -1,5 +1,6 @@ -import type { StringCase, StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type EnumsType = 'javascript' | 'typescript' | 'typescript-const'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts index 7c306aca4..d1874fae1 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/webhook.ts @@ -1,7 +1,8 @@ -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { createSchemaComment } from '../../shared/utils/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { createSchemaComment } from '~/plugins/shared/utils/schema'; +import { tsc } from '~/tsc'; + import { schemaToType } from './plugin'; import type { HeyApiTypeScriptPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/webhooks.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/webhooks.ts index 4539a468e..242373b74 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/webhooks.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/webhooks.ts @@ -1,6 +1,7 @@ import type { Symbol } from '@hey-api/codegen-core'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; + import type { HeyApiTypeScriptPlugin } from './types'; export const createWebhooks = ({ diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/api.ts b/packages/openapi-ts/src/plugins/@pinia/colada/api.ts index be883d4c4..e2eb81a04 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/api.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = | '_JSONValue' diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/config.ts b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts index e420831ff..6d3804572 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/config.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import type { PiniaColadaPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/meta.ts b/packages/openapi-ts/src/plugins/@pinia/colada/meta.ts index a2755448e..b07ba694c 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/meta.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/meta.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { tsc } from '~/tsc'; + import type { PiniaColadaPlugin } from './types'; export const handleMeta = ( diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts b/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts index 8d587d2fc..b8f308c24 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/mutationOptions.ts @@ -1,8 +1,9 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { tsc } from '~/tsc'; + import { handleMeta } from './meta'; import type { PiniaColadaPlugin } from './types'; import { useTypeData, useTypeError, useTypeResponse } from './useType'; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts index 2adb8654b..b0ccc056d 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts @@ -1,5 +1,6 @@ -import { stringCase } from '../../../utils/stringCase'; -import { operationClasses } from '../../@hey-api/sdk/operation'; +import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; +import { stringCase } from '~/utils/stringCase'; + import { createMutationOptions } from './mutationOptions'; import { createQueryOptions } from './queryOptions'; import type { PiniaColadaPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts b/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts index b53b75f6a..4e649b21b 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts @@ -1,15 +1,16 @@ import type { Symbol } from '@hey-api/codegen-core'; import type { Expression } from 'typescript'; -import { clientFolderAbsolutePath } from '../../../generate/client'; -import { hasOperationDataRequired } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { type Property, tsc } from '../../../tsc'; +import { clientFolderAbsolutePath } from '~/generate/client'; +import { hasOperationDataRequired } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; import { getClientBaseUrlKey, getClientPlugin, -} from '../../@hey-api/client-core/utils'; +} from '~/plugins/@hey-api/client-core/utils'; +import { type Property, tsc } from '~/tsc'; + import type { PiniaColadaPlugin } from './types'; import { useTypeData } from './useType'; import { getPublicTypeData } from './utils'; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts b/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts index b26ed22c2..804f168f7 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts @@ -1,12 +1,13 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; import { hasOperationSse, isOperationOptionsRequired, -} from '../../shared/utils/operation'; +} from '~/plugins/shared/utils/operation'; +import { tsc } from '~/tsc'; + import { handleMeta } from './meta'; import { createQueryKeyFunction, diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts index a69bd0445..efc2d3fd4 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { StringCase, StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { IR } from '~/ir/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@pinia/colada'> & diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts b/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts index 9bbd47396..e225c2dc8 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/useType.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import { getClientPlugin } from '../../@hey-api/client-core/utils'; -import { operationOptionsType } from '../../@hey-api/sdk/operation'; +import type { IR } from '~/ir/types'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import { operationOptionsType } from '~/plugins/@hey-api/sdk/operation'; + import type { PiniaColadaPlugin } from './types'; export const useTypeData = ({ diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts index a5757b680..9164b3ee7 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts @@ -1,4 +1,5 @@ -import { getClientPlugin } from '../../@hey-api/client-core/utils'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; + import type { PiniaColadaPlugin } from './types'; export const getPublicTypeData = ({ diff --git a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts index 89b5fd940..450cafabc 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = | 'AxiosError' diff --git a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/config.ts b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/config.ts index 42a1d2f05..873364817 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/config.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/config.ts @@ -1,6 +1,7 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { handler } from '../query-core/plugin'; -import { handlerLegacy } from '../query-core/plugin-legacy'; +import { handler } from '~/plugins/@tanstack/query-core/plugin'; +import { handlerLegacy } from '~/plugins/@tanstack/query-core/plugin-legacy'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { TanStackAngularQueryPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/types.d.ts b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/types.d.ts index 76d319b8f..80334a301 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/types.d.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/angular-query-experimental/types.d.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { StringCase, StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { IR } from '~/ir/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@tanstack/angular-query-experimental'> & diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts index adabe1c92..db1cbe65b 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/infiniteQueryOptions.ts @@ -1,11 +1,12 @@ import ts from 'typescript'; -import { operationPagination } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { tsNodeToString } from '../../../tsc/utils'; -import { isOperationOptionsRequired } from '../../shared/utils/operation'; +import { operationPagination } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { isOperationOptionsRequired } from '~/plugins/shared/utils/operation'; +import { tsc } from '~/tsc'; +import { tsNodeToString } from '~/tsc/utils'; + import { handleMeta } from './meta'; import { createQueryKeyFunction, diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/meta.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/meta.ts index f51c356ff..a5bbb901e 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/meta.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/meta.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { tsc } from '~/tsc'; + import type { PluginInstance } from './types'; export const handleMeta = ( diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts index 26d6f2a02..0d9269f8c 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/mutationOptions.ts @@ -1,8 +1,9 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { tsc } from '~/tsc'; + import { handleMeta } from './meta'; import type { PluginInstance } from './types'; import { useTypeData, useTypeError, useTypeResponse } from './useType'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts index 5825d1a46..977ca4334 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin-legacy.ts @@ -1,29 +1,15 @@ import ts from 'typescript'; -import { clientModulePath } from '../../../generate/client'; -import { relativeModulePath } from '../../../generate/utils'; -import { createOperationKey } from '../../../ir/operation'; -import { getPaginationKeywordsRegExp } from '../../../ir/pagination'; -import type { IR } from '../../../ir/types'; -import { isOperationParameterRequired } from '../../../openApi'; -import { type Property, tsc } from '../../../tsc'; -import type { ImportExportItem } from '../../../tsc/module'; -import type { ImportExportItemObject } from '../../../tsc/utils'; -import type { - Client, - Method, - Model, - Operation, - OperationParameter, -} from '../../../types/client'; -import type { Config } from '../../../types/config'; -import type { Files } from '../../../types/utils'; -import { getConfig, isLegacyClient } from '../../../utils/config'; -import { transformClassName } from '../../../utils/transform'; +import { clientModulePath } from '~/generate/client'; +import { relativeModulePath } from '~/generate/utils'; +import { createOperationKey } from '~/ir/operation'; +import { getPaginationKeywordsRegExp } from '~/ir/pagination'; +import type { IR } from '~/ir/types'; +import { isOperationParameterRequired } from '~/openApi'; import { getClientBaseUrlKey, getClientPlugin, -} from '../../@hey-api/client-core/utils'; +} from '~/plugins/@hey-api/client-core/utils'; import { generateImport, operationDataTypeName, @@ -31,12 +17,26 @@ import { operationOptionsLegacyParserType, operationResponseTypeName, serviceFunctionIdentifier, -} from '../../@hey-api/sdk/plugin-legacy'; -import type { TanStackAngularQueryPlugin } from '../angular-query-experimental'; -import type { TanStackReactQueryPlugin } from '../react-query'; -import type { TanStackSolidQueryPlugin } from '../solid-query'; -import type { TanStackSvelteQueryPlugin } from '../svelte-query'; -import type { TanStackVueQueryPlugin } from '../vue-query'; +} from '~/plugins/@hey-api/sdk/plugin-legacy'; +import type { TanStackAngularQueryPlugin } from '~/plugins/@tanstack/angular-query-experimental'; +import type { TanStackReactQueryPlugin } from '~/plugins/@tanstack/react-query'; +import type { TanStackSolidQueryPlugin } from '~/plugins/@tanstack/solid-query'; +import type { TanStackSvelteQueryPlugin } from '~/plugins/@tanstack/svelte-query'; +import type { TanStackVueQueryPlugin } from '~/plugins/@tanstack/vue-query'; +import { type Property, tsc } from '~/tsc'; +import type { ImportExportItem } from '~/tsc/module'; +import type { ImportExportItemObject } from '~/tsc/utils'; +import type { + Client, + Method, + Model, + Operation, + OperationParameter, +} from '~/types/client'; +import type { Config } from '~/types/config'; +import type { Files } from '~/types/utils'; +import { getConfig, isLegacyClient } from '~/utils/config'; +import { transformClassName } from '~/utils/transform'; const toInfiniteQueryOptionsName = (operation: Operation) => `${serviceFunctionIdentifier({ diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts index 38e10ae6b..4c773dfc2 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/plugin.ts @@ -1,5 +1,6 @@ -import { stringCase } from '../../../utils/stringCase'; -import { operationClasses } from '../../@hey-api/sdk/operation'; +import { operationClasses } from '~/plugins/@hey-api/sdk/operation'; +import { stringCase } from '~/utils/stringCase'; + import { createInfiniteQueryOptions } from './infiniteQueryOptions'; import { createMutationOptions } from './mutationOptions'; import { createQueryOptions } from './queryOptions'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts index 1adf95747..434bddbaa 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryKey.ts @@ -1,14 +1,15 @@ import type { Symbol } from '@hey-api/codegen-core'; import type { Expression } from 'typescript'; -import { hasOperationDataRequired } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { type Property, tsc } from '../../../tsc'; +import { hasOperationDataRequired } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; import { getClientBaseUrlKey, getClientPlugin, -} from '../../@hey-api/client-core/utils'; +} from '~/plugins/@hey-api/client-core/utils'; +import { type Property, tsc } from '~/tsc'; + import type { PluginInstance } from './types'; import { useTypeData } from './useType'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts index 25b52f9e4..d9392010f 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/queryOptions.ts @@ -1,12 +1,13 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; import { hasOperationSse, isOperationOptionsRequired, -} from '../../shared/utils/operation'; +} from '~/plugins/shared/utils/operation'; +import { tsc } from '~/tsc'; + import { handleMeta } from './meta'; import { createQueryKeyFunction, diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/types.d.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/types.d.ts index d765a8545..10c381ed2 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/types.d.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/types.d.ts @@ -1,8 +1,8 @@ -import type { TanStackAngularQueryPlugin } from '../angular-query-experimental/types'; -import type { TanStackReactQueryPlugin } from '../react-query/types'; -import type { TanStackSolidQueryPlugin } from '../solid-query/types'; -import type { TanStackSvelteQueryPlugin } from '../svelte-query/types'; -import type { TanStackVueQueryPlugin } from '../vue-query/types'; +import type { TanStackAngularQueryPlugin } from '~/plugins/@tanstack/angular-query-experimental/types'; +import type { TanStackReactQueryPlugin } from '~/plugins/@tanstack/react-query/types'; +import type { TanStackSolidQueryPlugin } from '~/plugins/@tanstack/solid-query/types'; +import type { TanStackSvelteQueryPlugin } from '~/plugins/@tanstack/svelte-query/types'; +import type { TanStackVueQueryPlugin } from '~/plugins/@tanstack/vue-query/types'; export interface PluginHandler { (...args: Parameters): void; diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts index 03c88edca..d9ee1d191 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/useQuery.ts @@ -1,10 +1,11 @@ -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; import { hasOperationSse, isOperationOptionsRequired, -} from '../../shared/utils/operation'; +} from '~/plugins/shared/utils/operation'; +import { tsc } from '~/tsc'; + import type { PluginInstance } from './types'; import { useTypeData } from './useType'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts b/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts index 18e85cef0..573f9b704 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/query-core/useType.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import { getClientPlugin } from '../../@hey-api/client-core/utils'; -import { operationOptionsType } from '../../@hey-api/sdk/operation'; +import type { IR } from '~/ir/types'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import { operationOptionsType } from '~/plugins/@hey-api/sdk/operation'; + import type { PluginInstance } from './types'; export const useTypeData = ({ diff --git a/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts index 1023a72a1..3d70e15b9 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/react-query/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = | 'AxiosError' diff --git a/packages/openapi-ts/src/plugins/@tanstack/react-query/config.ts b/packages/openapi-ts/src/plugins/@tanstack/react-query/config.ts index 6e6f1b1bf..87135dd47 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/react-query/config.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/react-query/config.ts @@ -1,6 +1,7 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { handler } from '../query-core/plugin'; -import { handlerLegacy } from '../query-core/plugin-legacy'; +import { handler } from '~/plugins/@tanstack/query-core/plugin'; +import { handlerLegacy } from '~/plugins/@tanstack/query-core/plugin-legacy'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { TanStackReactQueryPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/react-query/types.d.ts b/packages/openapi-ts/src/plugins/@tanstack/react-query/types.d.ts index 4a0de3251..4ac7abd75 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/react-query/types.d.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/react-query/types.d.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { StringCase, StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { IR } from '~/ir/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@tanstack/react-query'> & diff --git a/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts index b6f51efa1..ae14ba2b5 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/solid-query/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = | 'AxiosError' diff --git a/packages/openapi-ts/src/plugins/@tanstack/solid-query/config.ts b/packages/openapi-ts/src/plugins/@tanstack/solid-query/config.ts index a1e6915c3..d62a3c6bf 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/solid-query/config.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/solid-query/config.ts @@ -1,6 +1,7 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { handler } from '../query-core/plugin'; -import { handlerLegacy } from '../query-core/plugin-legacy'; +import { handler } from '~/plugins/@tanstack/query-core/plugin'; +import { handlerLegacy } from '~/plugins/@tanstack/query-core/plugin-legacy'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { TanStackSolidQueryPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/solid-query/types.d.ts b/packages/openapi-ts/src/plugins/@tanstack/solid-query/types.d.ts index 2758857b1..3f2bdbb40 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/solid-query/types.d.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/solid-query/types.d.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { StringCase, StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { IR } from '~/ir/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@tanstack/solid-query'> & diff --git a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts index 50880f8d9..8be5ab3fa 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = | 'AxiosError' diff --git a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/config.ts b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/config.ts index f4c400bae..dfa0b2f4f 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/config.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/config.ts @@ -1,6 +1,7 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { handler } from '../query-core/plugin'; -import { handlerLegacy } from '../query-core/plugin-legacy'; +import { handler } from '~/plugins/@tanstack/query-core/plugin'; +import { handlerLegacy } from '~/plugins/@tanstack/query-core/plugin-legacy'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { TanStackSvelteQueryPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/types.d.ts b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/types.d.ts index 2ff589aec..c6442afb2 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/svelte-query/types.d.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/svelte-query/types.d.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { StringCase, StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { IR } from '~/ir/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@tanstack/svelte-query'> & diff --git a/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts b/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts index 334f0efed..bb4cd391c 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/vue-query/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = | 'AxiosError' diff --git a/packages/openapi-ts/src/plugins/@tanstack/vue-query/config.ts b/packages/openapi-ts/src/plugins/@tanstack/vue-query/config.ts index c07182142..fbff14dea 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/vue-query/config.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/vue-query/config.ts @@ -1,6 +1,7 @@ -import { definePluginConfig } from '../../shared/utils/config'; -import { handler } from '../query-core/plugin'; -import { handlerLegacy } from '../query-core/plugin-legacy'; +import { handler } from '~/plugins/@tanstack/query-core/plugin'; +import { handlerLegacy } from '~/plugins/@tanstack/query-core/plugin-legacy'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import type { TanStackVueQueryPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@tanstack/vue-query/types.d.ts b/packages/openapi-ts/src/plugins/@tanstack/vue-query/types.d.ts index 99562511e..1c546c59b 100644 --- a/packages/openapi-ts/src/plugins/@tanstack/vue-query/types.d.ts +++ b/packages/openapi-ts/src/plugins/@tanstack/vue-query/types.d.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { StringCase, StringName } from '../../../types/case'; -import type { DefinePlugin, Plugin } from '../../types'; +import type { IR } from '~/ir/types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'@tanstack/vue-query'> & diff --git a/packages/openapi-ts/src/plugins/arktype/api.ts b/packages/openapi-ts/src/plugins/arktype/api.ts index 610106a3b..48d65b53d 100644 --- a/packages/openapi-ts/src/plugins/arktype/api.ts +++ b/packages/openapi-ts/src/plugins/arktype/api.ts @@ -1,7 +1,8 @@ import type { Selector } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { Plugin } from '../types'; +import type { Plugin } from '~/plugins/types'; + import type { ValidatorArgs } from './shared/types'; import { createRequestValidatorV2, createResponseValidatorV2 } from './v2/api'; diff --git a/packages/openapi-ts/src/plugins/arktype/config.ts b/packages/openapi-ts/src/plugins/arktype/config.ts index 60bde7267..36a2107d1 100644 --- a/packages/openapi-ts/src/plugins/arktype/config.ts +++ b/packages/openapi-ts/src/plugins/arktype/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig, mappers } from '../shared/utils/config'; +import { definePluginConfig, mappers } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import type { ArktypePlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/arktype/constants.ts b/packages/openapi-ts/src/plugins/arktype/constants.ts index 7744ee056..4663f2f05 100644 --- a/packages/openapi-ts/src/plugins/arktype/constants.ts +++ b/packages/openapi-ts/src/plugins/arktype/constants.ts @@ -1,4 +1,4 @@ -import { tsc } from '../../tsc'; +import { tsc } from '~/tsc'; export const identifiers = { keywords: { diff --git a/packages/openapi-ts/src/plugins/arktype/shared/export.ts b/packages/openapi-ts/src/plugins/arktype/shared/export.ts index c08731bd8..76349c01b 100644 --- a/packages/openapi-ts/src/plugins/arktype/shared/export.ts +++ b/packages/openapi-ts/src/plugins/arktype/shared/export.ts @@ -1,9 +1,10 @@ import type { Symbol } from '@hey-api/codegen-core'; import ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { tsc } from '../../../tsc'; -import { createSchemaComment } from '../../shared/utils/schema'; +import type { IR } from '~/ir/types'; +import { createSchemaComment } from '~/plugins/shared/utils/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../constants'; import type { ArktypePlugin } from '../types'; import type { Ast } from './types'; diff --git a/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts index 810a96dad..dfa46bcff 100644 --- a/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/arktype/shared/types.d.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import type { ToRefs } from '../../shared/types/refs'; +import type { IR } from '~/ir/types'; +import type { ToRefs } from '~/plugins/shared/types/refs'; + import type { ArktypePlugin } from '../types'; export type Ast = { diff --git a/packages/openapi-ts/src/plugins/arktype/types.d.ts b/packages/openapi-ts/src/plugins/arktype/types.d.ts index e7f940787..6f78cded3 100644 --- a/packages/openapi-ts/src/plugins/arktype/types.d.ts +++ b/packages/openapi-ts/src/plugins/arktype/types.d.ts @@ -1,5 +1,6 @@ -import type { StringCase, StringName } from '../../types/case'; -import type { DefinePlugin, Plugin } from '../types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'arktype'> & diff --git a/packages/openapi-ts/src/plugins/arktype/v2/api.ts b/packages/openapi-ts/src/plugins/arktype/v2/api.ts index 9dfeb7bcb..5a679ad3c 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/api.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/api.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; + // import { identifiers } from '../constants'; import type { ValidatorArgs } from '../shared/types'; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts index 74707ba1f..d1faa5fac 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/plugin.ts @@ -1,11 +1,12 @@ -import { deduplicateSchema } from '../../../ir/schema'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import type { SchemaWithType } from '../../shared/types/schema'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; -import { toRefs } from '../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; +import { toRefs } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { refToName } from '~/utils/ref'; + import { exportAst } from '../shared/export'; import type { Ast, IrSchemaToAstOptions } from '../shared/types'; import type { ArktypePlugin } from '../types'; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts index 9574825f6..7d977d64c 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/index.ts @@ -1,6 +1,7 @@ import ts from 'typescript'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; + import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { nullToAst } from './null'; import { objectToAst } from './object'; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts index ba1ce6731..26c6cdf89 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/null.ts @@ -1,4 +1,5 @@ -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts index b536081df..3e974f785 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/object.ts @@ -1,9 +1,10 @@ import ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import { numberRegExp } from '../../../../utils/regexp'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { numberRegExp } from '~/utils/regexp'; + // import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts b/packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts index 9395de267..8c498d0d8 100644 --- a/packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/arktype/v2/toAst/string.ts @@ -1,4 +1,5 @@ -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/config.ts b/packages/openapi-ts/src/plugins/config.ts index 46002cb6e..e72265ae7 100644 --- a/packages/openapi-ts/src/plugins/config.ts +++ b/packages/openapi-ts/src/plugins/config.ts @@ -1,56 +1,56 @@ -import type { AngularCommonPlugin } from './@angular/common'; -import { defaultConfig as angularCommon } from './@angular/common'; -import type { HeyApiClientAngularPlugin } from './@hey-api/client-angular'; -import { defaultConfig as heyApiClientAngular } from './@hey-api/client-angular'; -import type { HeyApiClientAxiosPlugin } from './@hey-api/client-axios'; -import { defaultConfig as heyApiClientAxios } from './@hey-api/client-axios'; -import type { HeyApiClientFetchPlugin } from './@hey-api/client-fetch'; -import { defaultConfig as heyApiClientFetch } from './@hey-api/client-fetch'; -import type { HeyApiClientNextPlugin } from './@hey-api/client-next'; -import { defaultConfig as heyApiClientNext } from './@hey-api/client-next'; -import type { HeyApiClientNuxtPlugin } from './@hey-api/client-nuxt'; -import { defaultConfig as heyApiClientNuxt } from './@hey-api/client-nuxt'; -import type { HeyApiClientOfetchPlugin } from './@hey-api/client-ofetch'; -import { defaultConfig as heyApiClientOfetch } from './@hey-api/client-ofetch'; -import type { HeyApiClientLegacyAngularPlugin } from './@hey-api/legacy-angular'; -import { defaultConfig as heyApiLegacyAngular } from './@hey-api/legacy-angular'; -import type { HeyApiClientLegacyAxiosPlugin } from './@hey-api/legacy-axios'; -import { defaultConfig as heyApiLegacyAxios } from './@hey-api/legacy-axios'; -import type { HeyApiClientLegacyFetchPlugin } from './@hey-api/legacy-fetch'; -import { defaultConfig as heyApiLegacyFetch } from './@hey-api/legacy-fetch'; -import type { HeyApiClientLegacyNodePlugin } from './@hey-api/legacy-node'; -import { defaultConfig as heyApiLegacyNode } from './@hey-api/legacy-node'; -import type { HeyApiClientLegacyXhrPlugin } from './@hey-api/legacy-xhr'; -import { defaultConfig as heyApiLegacyXhr } from './@hey-api/legacy-xhr'; -import type { HeyApiSchemasPlugin } from './@hey-api/schemas'; -import { defaultConfig as heyApiSchemas } from './@hey-api/schemas'; -import type { HeyApiSdkPlugin } from './@hey-api/sdk'; -import { defaultConfig as heyApiSdk } from './@hey-api/sdk'; -import type { HeyApiTransformersPlugin } from './@hey-api/transformers'; -import { defaultConfig as heyApiTransformers } from './@hey-api/transformers'; -import type { HeyApiTypeScriptPlugin } from './@hey-api/typescript'; -import { defaultConfig as heyApiTypeScript } from './@hey-api/typescript'; -import type { PiniaColadaPlugin } from './@pinia/colada'; -import { defaultConfig as piniaColada } from './@pinia/colada'; -import type { TanStackAngularQueryPlugin } from './@tanstack/angular-query-experimental'; -import { defaultConfig as tanStackAngularQuery } from './@tanstack/angular-query-experimental'; -import type { TanStackReactQueryPlugin } from './@tanstack/react-query'; -import { defaultConfig as tanStackReactQuery } from './@tanstack/react-query'; -import type { TanStackSolidQueryPlugin } from './@tanstack/solid-query'; -import { defaultConfig as tanStackSolidQuery } from './@tanstack/solid-query'; -import type { TanStackSvelteQueryPlugin } from './@tanstack/svelte-query'; -import { defaultConfig as tanStackSvelteQuery } from './@tanstack/svelte-query'; -import type { TanStackVueQueryPlugin } from './@tanstack/vue-query'; -import { defaultConfig as tanStackVueQuery } from './@tanstack/vue-query'; -import type { ArktypePlugin } from './arktype'; -import { defaultConfig as arktype } from './arktype'; -import type { FastifyPlugin } from './fastify'; -import { defaultConfig as fastify } from './fastify'; -import type { Plugin, PluginNames } from './types'; -import type { ValibotPlugin } from './valibot'; -import { defaultConfig as valibot } from './valibot'; -import type { ZodPlugin } from './zod'; -import { defaultConfig as zod } from './zod'; +import type { AngularCommonPlugin } from '~/plugins/@angular/common'; +import { defaultConfig as angularCommon } from '~/plugins/@angular/common'; +import type { HeyApiClientAngularPlugin } from '~/plugins/@hey-api/client-angular'; +import { defaultConfig as heyApiClientAngular } from '~/plugins/@hey-api/client-angular'; +import type { HeyApiClientAxiosPlugin } from '~/plugins/@hey-api/client-axios'; +import { defaultConfig as heyApiClientAxios } from '~/plugins/@hey-api/client-axios'; +import type { HeyApiClientFetchPlugin } from '~/plugins/@hey-api/client-fetch'; +import { defaultConfig as heyApiClientFetch } from '~/plugins/@hey-api/client-fetch'; +import type { HeyApiClientNextPlugin } from '~/plugins/@hey-api/client-next'; +import { defaultConfig as heyApiClientNext } from '~/plugins/@hey-api/client-next'; +import type { HeyApiClientNuxtPlugin } from '~/plugins/@hey-api/client-nuxt'; +import { defaultConfig as heyApiClientNuxt } from '~/plugins/@hey-api/client-nuxt'; +import type { HeyApiClientOfetchPlugin } from '~/plugins/@hey-api/client-ofetch'; +import { defaultConfig as heyApiClientOfetch } from '~/plugins/@hey-api/client-ofetch'; +import type { HeyApiClientLegacyAngularPlugin } from '~/plugins/@hey-api/legacy-angular'; +import { defaultConfig as heyApiLegacyAngular } from '~/plugins/@hey-api/legacy-angular'; +import type { HeyApiClientLegacyAxiosPlugin } from '~/plugins/@hey-api/legacy-axios'; +import { defaultConfig as heyApiLegacyAxios } from '~/plugins/@hey-api/legacy-axios'; +import type { HeyApiClientLegacyFetchPlugin } from '~/plugins/@hey-api/legacy-fetch'; +import { defaultConfig as heyApiLegacyFetch } from '~/plugins/@hey-api/legacy-fetch'; +import type { HeyApiClientLegacyNodePlugin } from '~/plugins/@hey-api/legacy-node'; +import { defaultConfig as heyApiLegacyNode } from '~/plugins/@hey-api/legacy-node'; +import type { HeyApiClientLegacyXhrPlugin } from '~/plugins/@hey-api/legacy-xhr'; +import { defaultConfig as heyApiLegacyXhr } from '~/plugins/@hey-api/legacy-xhr'; +import type { HeyApiSchemasPlugin } from '~/plugins/@hey-api/schemas'; +import { defaultConfig as heyApiSchemas } from '~/plugins/@hey-api/schemas'; +import type { HeyApiSdkPlugin } from '~/plugins/@hey-api/sdk'; +import { defaultConfig as heyApiSdk } from '~/plugins/@hey-api/sdk'; +import type { HeyApiTransformersPlugin } from '~/plugins/@hey-api/transformers'; +import { defaultConfig as heyApiTransformers } from '~/plugins/@hey-api/transformers'; +import type { HeyApiTypeScriptPlugin } from '~/plugins/@hey-api/typescript'; +import { defaultConfig as heyApiTypeScript } from '~/plugins/@hey-api/typescript'; +import type { PiniaColadaPlugin } from '~/plugins/@pinia/colada'; +import { defaultConfig as piniaColada } from '~/plugins/@pinia/colada'; +import type { TanStackAngularQueryPlugin } from '~/plugins/@tanstack/angular-query-experimental'; +import { defaultConfig as tanStackAngularQuery } from '~/plugins/@tanstack/angular-query-experimental'; +import type { TanStackReactQueryPlugin } from '~/plugins/@tanstack/react-query'; +import { defaultConfig as tanStackReactQuery } from '~/plugins/@tanstack/react-query'; +import type { TanStackSolidQueryPlugin } from '~/plugins/@tanstack/solid-query'; +import { defaultConfig as tanStackSolidQuery } from '~/plugins/@tanstack/solid-query'; +import type { TanStackSvelteQueryPlugin } from '~/plugins/@tanstack/svelte-query'; +import { defaultConfig as tanStackSvelteQuery } from '~/plugins/@tanstack/svelte-query'; +import type { TanStackVueQueryPlugin } from '~/plugins/@tanstack/vue-query'; +import { defaultConfig as tanStackVueQuery } from '~/plugins/@tanstack/vue-query'; +import type { ArktypePlugin } from '~/plugins/arktype'; +import { defaultConfig as arktype } from '~/plugins/arktype'; +import type { FastifyPlugin } from '~/plugins/fastify'; +import { defaultConfig as fastify } from '~/plugins/fastify'; +import type { Plugin, PluginNames } from '~/plugins/types'; +import type { ValibotPlugin } from '~/plugins/valibot'; +import { defaultConfig as valibot } from '~/plugins/valibot'; +import type { ZodPlugin } from '~/plugins/zod'; +import { defaultConfig as zod } from '~/plugins/zod'; export interface PluginConfigMap { '@angular/common': AngularCommonPlugin['Types']; diff --git a/packages/openapi-ts/src/plugins/fastify/api.ts b/packages/openapi-ts/src/plugins/fastify/api.ts index eb1e5f9c1..98130c3db 100644 --- a/packages/openapi-ts/src/plugins/fastify/api.ts +++ b/packages/openapi-ts/src/plugins/fastify/api.ts @@ -1,6 +1,6 @@ import type { Selector } from '@hey-api/codegen-core'; -import type { Plugin } from '../types'; +import type { Plugin } from '~/plugins/types'; type SelectorType = 'RouteHandler'; diff --git a/packages/openapi-ts/src/plugins/fastify/config.ts b/packages/openapi-ts/src/plugins/fastify/config.ts index 6db188294..b094d056a 100644 --- a/packages/openapi-ts/src/plugins/fastify/config.ts +++ b/packages/openapi-ts/src/plugins/fastify/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import type { FastifyPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/fastify/plugin.ts b/packages/openapi-ts/src/plugins/fastify/plugin.ts index 617693239..1f1ec72a1 100644 --- a/packages/openapi-ts/src/plugins/fastify/plugin.ts +++ b/packages/openapi-ts/src/plugins/fastify/plugin.ts @@ -1,9 +1,10 @@ import type ts from 'typescript'; -import { operationResponsesMap } from '../../ir/operation'; -import { hasParameterGroupObjectRequired } from '../../ir/parameter'; -import type { IR } from '../../ir/types'; -import { type Property, tsc } from '../../tsc'; +import { operationResponsesMap } from '~/ir/operation'; +import { hasParameterGroupObjectRequired } from '~/ir/parameter'; +import type { IR } from '~/ir/types'; +import { type Property, tsc } from '~/tsc'; + import type { FastifyPlugin } from './types'; const operationToRouteHandler = ({ diff --git a/packages/openapi-ts/src/plugins/fastify/types.d.ts b/packages/openapi-ts/src/plugins/fastify/types.d.ts index 541c5c848..2f409a6ca 100644 --- a/packages/openapi-ts/src/plugins/fastify/types.d.ts +++ b/packages/openapi-ts/src/plugins/fastify/types.d.ts @@ -1,4 +1,5 @@ -import type { DefinePlugin, Plugin } from '../types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'fastify'> & diff --git a/packages/openapi-ts/src/plugins/shared/types/instance.d.ts b/packages/openapi-ts/src/plugins/shared/types/instance.d.ts index c8e770f3f..38047f139 100644 --- a/packages/openapi-ts/src/plugins/shared/types/instance.d.ts +++ b/packages/openapi-ts/src/plugins/shared/types/instance.d.ts @@ -1,5 +1,5 @@ -import type { IrTopLevelKind } from '../../../ir/graph'; -import type { IR } from '../../../ir/types'; +import type { IrTopLevelKind } from '~/ir/graph'; +import type { IR } from '~/ir/types'; type WalkEvents = | { diff --git a/packages/openapi-ts/src/plugins/shared/types/schema.d.ts b/packages/openapi-ts/src/plugins/shared/types/schema.d.ts index 712368428..fcef09883 100644 --- a/packages/openapi-ts/src/plugins/shared/types/schema.d.ts +++ b/packages/openapi-ts/src/plugins/shared/types/schema.d.ts @@ -1,4 +1,4 @@ -import type { IR } from '../../../ir/types'; +import type { IR } from '~/ir/types'; export interface SchemaWithType< T extends diff --git a/packages/openapi-ts/src/plugins/shared/utils/case.ts b/packages/openapi-ts/src/plugins/shared/utils/case.ts index 7bdd24404..d3253d28c 100644 --- a/packages/openapi-ts/src/plugins/shared/utils/case.ts +++ b/packages/openapi-ts/src/plugins/shared/utils/case.ts @@ -1,8 +1,8 @@ import ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { numberRegExp } from '../../../utils/regexp'; -import { stringCase } from '../../../utils/stringCase'; +import type { IR } from '~/ir/types'; +import { numberRegExp } from '~/utils/regexp'; +import { stringCase } from '~/utils/stringCase'; /** * Returns final field name for object properties. This might differ from the diff --git a/packages/openapi-ts/src/plugins/shared/utils/config.ts b/packages/openapi-ts/src/plugins/shared/utils/config.ts index 215095712..44f085eb7 100644 --- a/packages/openapi-ts/src/plugins/shared/utils/config.ts +++ b/packages/openapi-ts/src/plugins/shared/utils/config.ts @@ -1,4 +1,4 @@ -import type { Plugin } from '../../types'; +import type { Plugin } from '~/plugins/types'; export const definePluginConfig = (defaultConfig: Plugin.Config) => diff --git a/packages/openapi-ts/src/plugins/shared/utils/instance.ts b/packages/openapi-ts/src/plugins/shared/utils/instance.ts index 619c76f47..16f25ae63 100644 --- a/packages/openapi-ts/src/plugins/shared/utils/instance.ts +++ b/packages/openapi-ts/src/plugins/shared/utils/instance.ts @@ -7,18 +7,19 @@ import type { SymbolIn, } from '@hey-api/codegen-core'; -import { HeyApiError } from '../../../error'; -import type { IrTopLevelKind } from '../../../ir/graph'; +import { HeyApiError } from '~/error'; +import type { IrTopLevelKind } from '~/ir/graph'; import { irTopLevelKinds, matchIrTopLevelPointer, walkTopological, -} from '../../../ir/graph'; -import type { IR } from '../../../ir/types'; -import type { OpenApi } from '../../../openApi/types'; -import { jsonPointerToPath } from '../../../utils/ref'; -import type { PluginConfigMap } from '../../config'; -import type { Plugin } from '../../types'; +} from '~/ir/graph'; +import type { IR } from '~/ir/types'; +import type { OpenApi } from '~/openApi/types'; +import type { PluginConfigMap } from '~/plugins/config'; +import type { Plugin } from '~/plugins/types'; +import { jsonPointerToPath } from '~/utils/ref'; + import type { WalkEvent, WalkOptions } from '../types/instance'; const defaultGetFilePath = (symbol: Symbol): string | undefined => { diff --git a/packages/openapi-ts/src/plugins/shared/utils/operation.ts b/packages/openapi-ts/src/plugins/shared/utils/operation.ts index ab6e145e5..e5b66b0c1 100644 --- a/packages/openapi-ts/src/plugins/shared/utils/operation.ts +++ b/packages/openapi-ts/src/plugins/shared/utils/operation.ts @@ -1,6 +1,6 @@ -import { hasOperationDataRequired } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { getClientPlugin } from '../../@hey-api/client-core/utils'; +import { hasOperationDataRequired } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; export const isOperationOptionsRequired = ({ context, diff --git a/packages/openapi-ts/src/plugins/shared/utils/schema.ts b/packages/openapi-ts/src/plugins/shared/utils/schema.ts index 5902fd550..93e814d04 100644 --- a/packages/openapi-ts/src/plugins/shared/utils/schema.ts +++ b/packages/openapi-ts/src/plugins/shared/utils/schema.ts @@ -1,6 +1,6 @@ -import type { IR } from '../../../ir/types'; -import type { Comments } from '../../../tsc'; -import { escapeComment } from '../../../utils/escape'; +import type { IR } from '~/ir/types'; +import type { Comments } from '~/tsc'; +import { escapeComment } from '~/utils/escape'; export const createSchemaComment = ({ schema, diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 5d3172ab5..a0876813b 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -1,10 +1,10 @@ -import type { ValueToObject } from '../config/utils/config'; -import type { Package } from '../config/utils/package'; -import type { IR } from '../ir/types'; -import type { OpenApi as LegacyOpenApi } from '../openApi'; -import type { Client as LegacyClient } from '../types/client'; -import type { Files } from '../types/utils'; -import type { PluginInstance } from './shared/utils/instance'; +import type { ValueToObject } from '~/config/utils/config'; +import type { Package } from '~/config/utils/package'; +import type { IR } from '~/ir/types'; +import type { OpenApi as LegacyOpenApi } from '~/openApi'; +import type { PluginInstance } from '~/plugins/shared/utils/instance'; +import type { Client as LegacyClient } from '~/types/client'; +import type { Files } from '~/types/utils'; export type PluginClientNames = | '@hey-api/client-angular' diff --git a/packages/openapi-ts/src/plugins/valibot/api.ts b/packages/openapi-ts/src/plugins/valibot/api.ts index f59ae2b24..4eb3bf2ce 100644 --- a/packages/openapi-ts/src/plugins/valibot/api.ts +++ b/packages/openapi-ts/src/plugins/valibot/api.ts @@ -1,7 +1,8 @@ import type { Selector } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { Plugin } from '../types'; +import type { Plugin } from '~/plugins/types'; + import type { ValidatorArgs } from './shared/types'; import { createRequestValidatorV1, createResponseValidatorV1 } from './v1/api'; diff --git a/packages/openapi-ts/src/plugins/valibot/config.ts b/packages/openapi-ts/src/plugins/valibot/config.ts index a6f4d85f6..96af788c3 100644 --- a/packages/openapi-ts/src/plugins/valibot/config.ts +++ b/packages/openapi-ts/src/plugins/valibot/config.ts @@ -1,4 +1,5 @@ -import { definePluginConfig } from '../shared/utils/config'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import type { ValibotPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/valibot/shared/numbers.ts b/packages/openapi-ts/src/plugins/valibot/shared/numbers.ts index 2606f1ada..18105e917 100644 --- a/packages/openapi-ts/src/plugins/valibot/shared/numbers.ts +++ b/packages/openapi-ts/src/plugins/valibot/shared/numbers.ts @@ -1,4 +1,4 @@ -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; export const INTEGER_FORMATS = { int16: { diff --git a/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts index 0c4bfeb31..bac6dfec1 100644 --- a/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/valibot/shared/types.d.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import type { StringCase, StringName } from '../../../types/case'; -import type { ToRefs } from '../../shared/types/refs'; +import type { IR } from '~/ir/types'; +import type { ToRefs } from '~/plugins/shared/types/refs'; +import type { StringCase, StringName } from '~/types/case'; + import type { ValibotPlugin } from '../types'; export type IrSchemaToAstOptions = { diff --git a/packages/openapi-ts/src/plugins/valibot/types.d.ts b/packages/openapi-ts/src/plugins/valibot/types.d.ts index 2d560d4ea..c6e5a80be 100644 --- a/packages/openapi-ts/src/plugins/valibot/types.d.ts +++ b/packages/openapi-ts/src/plugins/valibot/types.d.ts @@ -1,5 +1,6 @@ -import type { StringCase, StringName } from '../../types/case'; -import type { DefinePlugin, Plugin } from '../types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'valibot'> & diff --git a/packages/openapi-ts/src/plugins/valibot/v1/api.ts b/packages/openapi-ts/src/plugins/valibot/v1/api.ts index 790cfbc0b..f0f40109f 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/api.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/api.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; + import type { ValidatorArgs } from '../shared/types'; import { identifiers } from './constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/constants.ts b/packages/openapi-ts/src/plugins/valibot/v1/constants.ts index b3a085d68..e49e120a4 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/constants.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/constants.ts @@ -1,4 +1,4 @@ -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; export const identifiers = { /** diff --git a/packages/openapi-ts/src/plugins/valibot/v1/operation.ts b/packages/openapi-ts/src/plugins/valibot/v1/operation.ts index 6e2ddea5b..a4c1ce784 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/operation.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/operation.ts @@ -1,8 +1,9 @@ -import { operationResponsesMap } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; -import { toRef } from '../../shared/utils/refs'; +import { operationResponsesMap } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; +import { toRef } from '~/plugins/shared/utils/refs'; + import type { IrSchemaToAstOptions } from '../shared/types'; import { irSchemaToAst } from './plugin'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts b/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts index 053e8df07..bca4920f7 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/pipesToAst.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; + import type { ValibotPlugin } from '../types'; import { identifiers } from './constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts index 788af9e91..c3e5ea5ac 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/plugin.ts @@ -1,15 +1,16 @@ import type { Symbol } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import { deduplicateSchema } from '../../../ir/schema'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import type { SchemaWithType } from '../../shared/types/schema'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; -import { toRef, toRefs } from '../../shared/utils/refs'; -import { createSchemaComment } from '../../shared/utils/schema'; +import { deduplicateSchema } from '~/ir/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; +import { toRef, toRefs } from '~/plugins/shared/utils/refs'; +import { createSchemaComment } from '~/plugins/shared/utils/schema'; +import { tsc } from '~/tsc'; +import { refToName } from '~/utils/ref'; + import { numberParameter } from '../shared/numbers'; import type { IrSchemaToAstOptions, PluginState } from '../shared/types'; import type { ValibotPlugin } from '../types'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts index 9bd2fb206..981863a26 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/array.ts @@ -1,9 +1,10 @@ import type ts from 'typescript'; -import { deduplicateSchema } from '../../../../ir/schema'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { pipesToAst } from '../pipesToAst'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts index 149f11be9..d38f3b99e 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/boolean.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts index dd139d990..233a0762e 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/enum.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { unknownToAst } from './unknown'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts index 4750ebcc6..c05801d8b 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/index.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { arrayToAst } from './array'; import { booleanToAst } from './boolean'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts index 06613a637..a58bafb33 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/never.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts index 281f3e9ae..952f604c5 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/null.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts index e6f0c66e6..c0898efd3 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/number.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { INTEGER_FORMATS, isIntegerFormat, diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts index b85bec76a..423ee7881 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/object.ts @@ -1,9 +1,10 @@ import ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import { numberRegExp } from '../../../../utils/regexp'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { numberRegExp } from '~/utils/regexp'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { pipesToAst } from '../pipesToAst'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts index e54c6808c..7a77c9c1b 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/string.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { pipesToAst } from '../pipesToAst'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts index 776103b48..d100c5bdc 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/tuple.ts @@ -1,6 +1,7 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; import { pipesToAst } from '../pipesToAst'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts index d4c1167e5..3cc866bf5 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/undefined.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts index 705515414..9df535c73 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/unknown.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts b/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts index b92cad06a..55c11dbdb 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/toAst/void.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import type { IrSchemaToAstOptions } from '../../shared/types'; import { identifiers } from '../constants'; diff --git a/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts b/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts index e4f4c3042..398398e23 100644 --- a/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts +++ b/packages/openapi-ts/src/plugins/valibot/v1/webhook.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; + import type { IrSchemaToAstOptions } from '../shared/types'; import { irSchemaToAst } from './plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/api.ts b/packages/openapi-ts/src/plugins/zod/api.ts index 1fe8d147e..95b8aa298 100644 --- a/packages/openapi-ts/src/plugins/zod/api.ts +++ b/packages/openapi-ts/src/plugins/zod/api.ts @@ -1,7 +1,8 @@ import type { Selector } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { Plugin } from '../types'; +import type { Plugin } from '~/plugins/types'; + import { createRequestValidatorMini, createResponseValidatorMini, diff --git a/packages/openapi-ts/src/plugins/zod/config.ts b/packages/openapi-ts/src/plugins/zod/config.ts index a025ed339..bf2680141 100644 --- a/packages/openapi-ts/src/plugins/zod/config.ts +++ b/packages/openapi-ts/src/plugins/zod/config.ts @@ -1,6 +1,7 @@ import colors from 'ansi-colors'; -import { definePluginConfig, mappers } from '../shared/utils/config'; +import { definePluginConfig, mappers } from '~/plugins/shared/utils/config'; + import { Api } from './api'; import { handler } from './plugin'; import type { ZodPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/zod/constants.ts b/packages/openapi-ts/src/plugins/zod/constants.ts index d0df73c8d..fe79a3555 100644 --- a/packages/openapi-ts/src/plugins/zod/constants.ts +++ b/packages/openapi-ts/src/plugins/zod/constants.ts @@ -1,4 +1,4 @@ -import { tsc } from '../../tsc'; +import { tsc } from '~/tsc'; // TODO: this is inaccurate, it combines identifiers for all supported versions export const identifiers = { diff --git a/packages/openapi-ts/src/plugins/zod/mini/api.ts b/packages/openapi-ts/src/plugins/zod/mini/api.ts index f360d67ed..0b560fbb3 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/api.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/api.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; + import { identifiers } from '../constants'; import type { ValidatorArgs } from '../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts index ae881579d..6583b6d3c 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/plugin.ts @@ -1,11 +1,12 @@ -import { deduplicateSchema } from '../../../ir/schema'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import type { SchemaWithType } from '../../shared/types/schema'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; -import { toRef, toRefs } from '../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; +import { toRef, toRefs } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { refToName } from '~/utils/ref'; + import { identifiers } from '../constants'; import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts index 3b77176ce..49374643f 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts @@ -1,9 +1,10 @@ import type ts from 'typescript'; -import { deduplicateSchema } from '../../../../ir/schema'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts index 657848a07..f555df28c 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts index 2b43769ba..97baffe9f 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/enum.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { unknownToAst } from './unknown'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts index 62c481bfd..b7400b68a 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/index.ts @@ -1,4 +1,5 @@ -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; + import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { arrayToAst } from './array'; import { booleanToAst } from './boolean'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts index 57e20c23a..a7dcc91f1 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts index b3ff566dd..082534607 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts index e04617c61..53919affd 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/number.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import { numberParameter } from '../../shared/numbers'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts index 9d066adb7..46c8a14cd 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts @@ -1,9 +1,10 @@ import ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import { numberRegExp } from '../../../../utils/regexp'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { numberRegExp } from '~/utils/regexp'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts index 4a4d98ce5..0cad6e696 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/string.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts index 0ea4ac4c8..068597dd3 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts @@ -1,8 +1,9 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts index 13cddcfa1..bf6640610 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts index 2ada823cb..73d1909cd 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts index b15a42845..c26bf8c33 100644 --- a/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/shared/export.ts b/packages/openapi-ts/src/plugins/zod/shared/export.ts index 9fa2762eb..f2a5732ef 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/export.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/export.ts @@ -1,9 +1,10 @@ import type { Symbol } from '@hey-api/codegen-core'; import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import { tsc } from '../../../tsc'; -import { createSchemaComment } from '../../shared/utils/schema'; +import type { IR } from '~/ir/types'; +import { createSchemaComment } from '~/plugins/shared/utils/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../constants'; import type { ZodPlugin } from '../types'; import type { Ast } from './types'; diff --git a/packages/openapi-ts/src/plugins/zod/shared/numbers.ts b/packages/openapi-ts/src/plugins/zod/shared/numbers.ts index 63e44916d..2da75b3bc 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/numbers.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/numbers.ts @@ -1,6 +1,6 @@ import type ts from 'typescript'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; export const numberParameter = ({ isBigInt, diff --git a/packages/openapi-ts/src/plugins/zod/shared/operation.ts b/packages/openapi-ts/src/plugins/zod/shared/operation.ts index 2d5187f42..04bcfd747 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/operation.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/operation.ts @@ -1,7 +1,8 @@ -import { operationResponsesMap } from '../../../ir/operation'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import { operationResponsesMap } from '~/ir/operation'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; + import { exportAst } from './export'; import type { Ast, IrSchemaToAstOptions } from './types'; diff --git a/packages/openapi-ts/src/plugins/zod/shared/types.d.ts b/packages/openapi-ts/src/plugins/zod/shared/types.d.ts index 197cea2d5..90fcf86f1 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/types.d.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/types.d.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import type { IR } from '../../../ir/types'; -import type { ToRefs } from '../../shared/types/refs'; +import type { IR } from '~/ir/types'; +import type { ToRefs } from '~/plugins/shared/types/refs'; + import type { ZodPlugin } from '../types'; export type Ast = { diff --git a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts index 74926f894..62ab05468 100644 --- a/packages/openapi-ts/src/plugins/zod/shared/webhook.ts +++ b/packages/openapi-ts/src/plugins/zod/shared/webhook.ts @@ -1,6 +1,7 @@ -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; + import { exportAst } from './export'; import type { Ast, IrSchemaToAstOptions } from './types'; diff --git a/packages/openapi-ts/src/plugins/zod/types.d.ts b/packages/openapi-ts/src/plugins/zod/types.d.ts index ab95797aa..6d82aff4e 100644 --- a/packages/openapi-ts/src/plugins/zod/types.d.ts +++ b/packages/openapi-ts/src/plugins/zod/types.d.ts @@ -1,5 +1,6 @@ -import type { StringCase, StringName } from '../../types/case'; -import type { DefinePlugin, Plugin } from '../types'; +import type { DefinePlugin, Plugin } from '~/plugins/types'; +import type { StringCase, StringName } from '~/types/case'; + import type { IApi } from './api'; export type UserConfig = Plugin.Name<'zod'> & diff --git a/packages/openapi-ts/src/plugins/zod/v3/api.ts b/packages/openapi-ts/src/plugins/zod/v3/api.ts index 3328950cd..e6b089f93 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/api.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/api.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; + import { identifiers } from '../constants'; import type { ValidatorArgs } from '../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts index bcdbb22eb..a625aa8ba 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/plugin.ts @@ -1,11 +1,12 @@ -import { deduplicateSchema } from '../../../ir/schema'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import type { SchemaWithType } from '../../shared/types/schema'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; -import { toRef, toRefs } from '../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; +import { toRef, toRefs } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { refToName } from '~/utils/ref'; + import { identifiers } from '../constants'; import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts index 9f3c1f897..895d83465 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts @@ -1,9 +1,10 @@ import type ts from 'typescript'; -import { deduplicateSchema } from '../../../../ir/schema'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts index 7562823d5..b818fcd46 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts index d735f09fb..282274e5c 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/enum.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; import { unknownToAst } from './unknown'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts index 50790d8c7..41c535f9f 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/index.ts @@ -1,4 +1,5 @@ -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; + import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { arrayToAst } from './array'; import { booleanToAst } from './boolean'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts index 3dbddf974..6f31ae90d 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts index fdb2a5875..5247a6663 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts index 8754becc9..3d39bb940 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/number.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import { numberParameter } from '../../shared/numbers'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts index 748f4c33a..24721e70a 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts @@ -1,9 +1,10 @@ import ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import { numberRegExp } from '../../../../utils/regexp'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { numberRegExp } from '~/utils/regexp'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts index 1512b1ed2..b45ae09fe 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/string.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts index 1bbcc1b54..ec6e8f0db 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts @@ -1,8 +1,9 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts index f6d75fa79..381cd89c0 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts index 4aa9a4274..5a534a02c 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts index fe0cb9e51..28abe5dc1 100644 --- a/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/api.ts b/packages/openapi-ts/src/plugins/zod/v4/api.ts index 9de07623f..01663c8e0 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/api.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/api.ts @@ -1,6 +1,7 @@ import type ts from 'typescript'; -import { tsc } from '../../../tsc'; +import { tsc } from '~/tsc'; + import { identifiers } from '../constants'; import type { ValidatorArgs } from '../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts index ccb222667..fcb072a6a 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/plugin.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/plugin.ts @@ -1,11 +1,12 @@ -import { deduplicateSchema } from '../../../ir/schema'; -import type { IR } from '../../../ir/types'; -import { buildName } from '../../../openApi/shared/utils/name'; -import { tsc } from '../../../tsc'; -import { refToName } from '../../../utils/ref'; -import type { SchemaWithType } from '../../shared/types/schema'; -import { pathToSymbolResourceType } from '../../shared/utils/meta'; -import { toRef, toRefs } from '../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { IR } from '~/ir/types'; +import { buildName } from '~/openApi/shared/utils/name'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { pathToSymbolResourceType } from '~/plugins/shared/utils/meta'; +import { toRef, toRefs } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { refToName } from '~/utils/ref'; + import { identifiers } from '../constants'; import { exportAst } from '../shared/export'; import { getZodModule } from '../shared/module'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts index 0b3848214..0d78f2635 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts @@ -1,9 +1,10 @@ import type ts from 'typescript'; -import { deduplicateSchema } from '../../../../ir/schema'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import { deduplicateSchema } from '~/ir/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts index ad4b0fbae..c6a23106c 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts index e02d09ea7..6882759a6 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/enum.ts @@ -1,7 +1,8 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { unknownToAst } from './unknown'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts index 62c481bfd..b7400b68a 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/index.ts @@ -1,4 +1,5 @@ -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; + import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { arrayToAst } from './array'; import { booleanToAst } from './boolean'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts index a47363240..9a6239563 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts index 725bfa73b..ec4ca5e22 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts index 0a5cc1364..800b55cbd 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/number.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import { numberParameter } from '../../shared/numbers'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts index 021840ea3..607f0ea3e 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts @@ -1,9 +1,10 @@ import ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import { numberRegExp } from '../../../../utils/regexp'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; +import { numberRegExp } from '~/utils/regexp'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts index e6a2c71ab..559d33354 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/string.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts index f4c35f9a5..f875ed7b9 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts @@ -1,8 +1,9 @@ import type ts from 'typescript'; -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; -import { toRef } from '../../../shared/utils/refs'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { toRef } from '~/plugins/shared/utils/refs'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; import { irSchemaToAst } from '../plugin'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts index 294fba483..ebdf778b5 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts index 7f1163aec..e0f991b92 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts b/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts index 63324d503..b30aff23a 100644 --- a/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts +++ b/packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts @@ -1,5 +1,6 @@ -import { tsc } from '../../../../tsc'; -import type { SchemaWithType } from '../../../shared/types/schema'; +import type { SchemaWithType } from '~/plugins/shared/types/schema'; +import { tsc } from '~/tsc'; + import { identifiers } from '../../constants'; import type { Ast, IrSchemaToAstOptions } from '../../shared/types'; diff --git a/packages/openapi-ts/src/processOutput.ts b/packages/openapi-ts/src/processOutput.ts index 7e35f0a9e..f870dbb3e 100644 --- a/packages/openapi-ts/src/processOutput.ts +++ b/packages/openapi-ts/src/processOutput.ts @@ -1,7 +1,7 @@ import { sync } from 'cross-spawn'; -import type { Config } from './types/config'; -import type { Formatters, Linters } from './types/output'; +import type { Config } from '~/types/config'; +import type { Formatters, Linters } from '~/types/output'; type OutputProcessor = { args: (path: string) => ReadonlyArray; diff --git a/packages/openapi-ts/src/tsc/transform.ts b/packages/openapi-ts/src/tsc/transform.ts index f6f3253e8..a73e0523a 100644 --- a/packages/openapi-ts/src/tsc/transform.ts +++ b/packages/openapi-ts/src/tsc/transform.ts @@ -1,6 +1,7 @@ import ts from 'typescript'; -import { validTypescriptIdentifierRegExp } from '../utils/regexp'; +import { validTypescriptIdentifierRegExp } from '~/utils/regexp'; + import { expressionToStatement } from './convert'; import { createCallExpression } from './module'; import { diff --git a/packages/openapi-ts/src/tsc/typedef.ts b/packages/openapi-ts/src/tsc/typedef.ts index 600e1179c..4313ce04e 100644 --- a/packages/openapi-ts/src/tsc/typedef.ts +++ b/packages/openapi-ts/src/tsc/typedef.ts @@ -1,6 +1,7 @@ import ts from 'typescript'; -import { validTypescriptIdentifierRegExp } from '../utils/regexp'; +import { validTypescriptIdentifierRegExp } from '~/utils/regexp'; + import { createKeywordTypeNode, createMappedTypeNode, diff --git a/packages/openapi-ts/src/tsc/types.ts b/packages/openapi-ts/src/tsc/types.ts index af869048b..9a585442b 100644 --- a/packages/openapi-ts/src/tsc/types.ts +++ b/packages/openapi-ts/src/tsc/types.ts @@ -1,7 +1,8 @@ import ts from 'typescript'; -import { escapeName } from '../utils/escape'; -import { validTypescriptIdentifierRegExp } from '../utils/regexp'; +import { escapeName } from '~/utils/escape'; +import { validTypescriptIdentifierRegExp } from '~/utils/regexp'; + import { addLeadingComments, type Comments, diff --git a/packages/openapi-ts/src/tsc/utils.ts b/packages/openapi-ts/src/tsc/utils.ts index 5ed283029..37606547e 100644 --- a/packages/openapi-ts/src/tsc/utils.ts +++ b/packages/openapi-ts/src/tsc/utils.ts @@ -1,7 +1,8 @@ import ts from 'typescript'; -import { getConfig } from '../utils/config'; -import { unescapeName } from '../utils/escape'; +import { getConfig } from '~/utils/config'; +import { unescapeName } from '~/utils/escape'; + import type { AccessLevel } from './types'; import { createStringLiteral, syntaxKindKeyword } from './types'; diff --git a/packages/openapi-ts/src/types/client.d.ts b/packages/openapi-ts/src/types/client.d.ts index 0d64908c7..54b47e7e0 100644 --- a/packages/openapi-ts/src/types/client.d.ts +++ b/packages/openapi-ts/src/types/client.d.ts @@ -6,7 +6,7 @@ import type { Operation as ParserOperation, OperationParameter, OperationResponse, -} from '../openApi'; +} from '~/openApi'; export type { Method, Model, ModelMeta, OperationParameter, OperationResponse }; diff --git a/packages/openapi-ts/src/types/config.d.ts b/packages/openapi-ts/src/types/config.d.ts index 2d585e68d..58198a637 100644 --- a/packages/openapi-ts/src/types/config.d.ts +++ b/packages/openapi-ts/src/types/config.d.ts @@ -1,5 +1,6 @@ -import type { PluginConfigMap } from '../plugins/config'; -import type { Plugin, PluginNames } from '../plugins/types'; +import type { PluginConfigMap } from '~/plugins/config'; +import type { Plugin, PluginNames } from '~/plugins/types'; + import type { Input, UserInput, Watch } from './input'; import type { Logs } from './logs'; import type { Output, UserOutput } from './output'; diff --git a/packages/openapi-ts/src/types/parser.d.ts b/packages/openapi-ts/src/types/parser.d.ts index ad4d2e05f..eff50d325 100644 --- a/packages/openapi-ts/src/types/parser.d.ts +++ b/packages/openapi-ts/src/types/parser.d.ts @@ -1,4 +1,4 @@ -import type { IR } from '../ir/types'; +import type { IR } from '~/ir/types'; import type { OpenApiMetaObject, OpenApiOperationObject, @@ -6,7 +6,8 @@ import type { OpenApiRequestBodyObject, OpenApiResponseObject, OpenApiSchemaObject, -} from '../openApi/types'; +} from '~/openApi/types'; + import type { StringCase, StringName } from './case'; type EnumsMode = 'inline' | 'root'; diff --git a/packages/openapi-ts/src/types/utils.d.ts b/packages/openapi-ts/src/types/utils.d.ts index ef9b7ae81..c73a2f710 100644 --- a/packages/openapi-ts/src/types/utils.d.ts +++ b/packages/openapi-ts/src/types/utils.d.ts @@ -1,4 +1,4 @@ -import type { GeneratedFile } from '../generate/file'; +import type { GeneratedFile } from '~/generate/file'; /** * Converts all top-level ReadonlyArray properties to Array (shallow). diff --git a/packages/openapi-ts/src/utils/__tests__/parse.test.ts b/packages/openapi-ts/src/utils/__tests__/parse.test.ts index 129b476f5..5b28c5897 100644 --- a/packages/openapi-ts/src/utils/__tests__/parse.test.ts +++ b/packages/openapi-ts/src/utils/__tests__/parse.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { operationNameFn } from '../../openApi/common/parser/operation'; +import { operationNameFn } from '~/openApi/common/parser/operation'; + import { setConfig } from '../config'; describe('operationNameFn', () => { diff --git a/packages/openapi-ts/src/utils/__tests__/postprocess.test.ts b/packages/openapi-ts/src/utils/__tests__/postprocess.test.ts index 52a78a988..44faf4cb8 100644 --- a/packages/openapi-ts/src/utils/__tests__/postprocess.test.ts +++ b/packages/openapi-ts/src/utils/__tests__/postprocess.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; -import { parseLegacy } from '../../openApi'; -import type { Config } from '../../types/config'; +import { parseLegacy } from '~/openApi'; +import type { Config } from '~/types/config'; + import { getServiceName, postProcessClient } from '../postprocess'; vi.mock('../config', () => { diff --git a/packages/openapi-ts/src/utils/__tests__/sort.test.ts b/packages/openapi-ts/src/utils/__tests__/sort.test.ts index cca8cb614..04bb1f33d 100644 --- a/packages/openapi-ts/src/utils/__tests__/sort.test.ts +++ b/packages/openapi-ts/src/utils/__tests__/sort.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; -import type { Model, Service } from '../../types/client'; +import type { Model, Service } from '~/types/client'; + import { sort, sortByName } from '../sort'; describe('sort', () => { diff --git a/packages/openapi-ts/src/utils/__tests__/stringCase.test.ts b/packages/openapi-ts/src/utils/__tests__/stringCase.test.ts index c44e24b2d..3e7f3da87 100644 --- a/packages/openapi-ts/src/utils/__tests__/stringCase.test.ts +++ b/packages/openapi-ts/src/utils/__tests__/stringCase.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; -import type { StringCase } from '../../types/case'; +import type { StringCase } from '~/types/case'; + import { stringCase } from '../stringCase'; const cases: ReadonlyArray = [ diff --git a/packages/openapi-ts/src/utils/__tests__/type.test.ts b/packages/openapi-ts/src/utils/__tests__/type.test.ts index 5999ff15e..2474d75fe 100644 --- a/packages/openapi-ts/src/utils/__tests__/type.test.ts +++ b/packages/openapi-ts/src/utils/__tests__/type.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, type MockedFunction, vi } from 'vitest'; -import type { Config } from '../../types/config'; +import type { Config } from '~/types/config'; + import { isLegacyClient } from '../config'; import { transformTypeKeyName } from '../type'; diff --git a/packages/openapi-ts/src/utils/cli.ts b/packages/openapi-ts/src/utils/cli.ts index af10deda1..d65a5b82d 100644 --- a/packages/openapi-ts/src/utils/cli.ts +++ b/packages/openapi-ts/src/utils/cli.ts @@ -1,6 +1,6 @@ import colors from 'ansi-colors'; -import { loadPackageJson } from '../generate/tsConfig'; +import { loadPackageJson } from '~/generate/tsConfig'; const textAscii = ` 888 | e 888~-_ 888 diff --git a/packages/openapi-ts/src/utils/config.ts b/packages/openapi-ts/src/utils/config.ts index dba4fa5af..304753734 100644 --- a/packages/openapi-ts/src/utils/config.ts +++ b/packages/openapi-ts/src/utils/config.ts @@ -1,5 +1,5 @@ -import { getClientPlugin } from '../plugins/@hey-api/client-core/utils'; -import type { Config } from '../types/config'; +import { getClientPlugin } from '~/plugins/@hey-api/client-core/utils'; +import type { Config } from '~/types/config'; let _config: Config; diff --git a/packages/openapi-ts/src/utils/enum.ts b/packages/openapi-ts/src/utils/enum.ts index 5d863b033..eef7f66df 100644 --- a/packages/openapi-ts/src/utils/enum.ts +++ b/packages/openapi-ts/src/utils/enum.ts @@ -1,5 +1,6 @@ -import type { Enum, Model } from '../openApi'; -import { ensureValidTypeScriptJavaScriptIdentifier } from '../openApi'; +import type { Enum, Model } from '~/openApi'; +import { ensureValidTypeScriptJavaScriptIdentifier } from '~/openApi'; + import { unescapeName } from './escape'; import { sort } from './sort'; import { unique } from './unique'; diff --git a/packages/openapi-ts/src/utils/getHttpRequestName.ts b/packages/openapi-ts/src/utils/getHttpRequestName.ts index c90fb64ce..8b4e74aa5 100644 --- a/packages/openapi-ts/src/utils/getHttpRequestName.ts +++ b/packages/openapi-ts/src/utils/getHttpRequestName.ts @@ -1,4 +1,4 @@ -import type { PluginClientNames } from '../plugins/types'; +import type { PluginClientNames } from '~/plugins/types'; /** * Generate the HttpRequest filename based on the selected client diff --git a/packages/openapi-ts/src/utils/handlebars.ts b/packages/openapi-ts/src/utils/handlebars.ts index c7d37ac24..b8eb52dc3 100644 --- a/packages/openapi-ts/src/utils/handlebars.ts +++ b/packages/openapi-ts/src/utils/handlebars.ts @@ -1,93 +1,94 @@ import Handlebars from 'handlebars'; // @ts-expect-error -import templateClient from '../legacy/handlebars/compiled/client.js'; +import templateClient from '~/legacy/handlebars/compiled/client'; // @ts-expect-error -import angularGetHeaders from '../legacy/handlebars/compiled/core/angular/getHeaders.js'; +import angularGetHeaders from '~/legacy/handlebars/compiled/core/angular/getHeaders'; // @ts-expect-error -import angularGetRequestBody from '../legacy/handlebars/compiled/core/angular/getRequestBody.js'; +import angularGetRequestBody from '~/legacy/handlebars/compiled/core/angular/getRequestBody'; // @ts-expect-error -import angularGetResponseBody from '../legacy/handlebars/compiled/core/angular/getResponseBody.js'; +import angularGetResponseBody from '~/legacy/handlebars/compiled/core/angular/getResponseBody'; // @ts-expect-error -import angularGetResponseHeader from '../legacy/handlebars/compiled/core/angular/getResponseHeader.js'; +import angularGetResponseHeader from '~/legacy/handlebars/compiled/core/angular/getResponseHeader'; // @ts-expect-error -import angularRequest from '../legacy/handlebars/compiled/core/angular/request.js'; +import angularRequest from '~/legacy/handlebars/compiled/core/angular/request'; // @ts-expect-error -import angularSendRequest from '../legacy/handlebars/compiled/core/angular/sendRequest.js'; +import angularSendRequest from '~/legacy/handlebars/compiled/core/angular/sendRequest'; // @ts-expect-error -import templateCoreApiError from '../legacy/handlebars/compiled/core/ApiError.js'; +import templateCoreApiError from '~/legacy/handlebars/compiled/core/ApiError'; // @ts-expect-error -import templateCoreApiRequestOptions from '../legacy/handlebars/compiled/core/ApiRequestOptions.js'; +import templateCoreApiRequestOptions from '~/legacy/handlebars/compiled/core/ApiRequestOptions'; // @ts-expect-error -import templateCoreApiResult from '../legacy/handlebars/compiled/core/ApiResult.js'; +import templateCoreApiResult from '~/legacy/handlebars/compiled/core/ApiResult'; // @ts-expect-error -import axiosGetHeaders from '../legacy/handlebars/compiled/core/axios/getHeaders.js'; +import axiosGetHeaders from '~/legacy/handlebars/compiled/core/axios/getHeaders'; // @ts-expect-error -import axiosGetRequestBody from '../legacy/handlebars/compiled/core/axios/getRequestBody.js'; +import axiosGetRequestBody from '~/legacy/handlebars/compiled/core/axios/getRequestBody'; // @ts-expect-error -import axiosGetResponseBody from '../legacy/handlebars/compiled/core/axios/getResponseBody.js'; +import axiosGetResponseBody from '~/legacy/handlebars/compiled/core/axios/getResponseBody'; // @ts-expect-error -import axiosGetResponseHeader from '../legacy/handlebars/compiled/core/axios/getResponseHeader.js'; +import axiosGetResponseHeader from '~/legacy/handlebars/compiled/core/axios/getResponseHeader'; // @ts-expect-error -import axiosRequest from '../legacy/handlebars/compiled/core/axios/request.js'; +import axiosRequest from '~/legacy/handlebars/compiled/core/axios/request'; // @ts-expect-error -import axiosSendRequest from '../legacy/handlebars/compiled/core/axios/sendRequest.js'; +import axiosSendRequest from '~/legacy/handlebars/compiled/core/axios/sendRequest'; // @ts-expect-error -import templateCoreBaseHttpRequest from '../legacy/handlebars/compiled/core/BaseHttpRequest.js'; +import templateCoreBaseHttpRequest from '~/legacy/handlebars/compiled/core/BaseHttpRequest'; // @ts-expect-error -import templateCancelablePromise from '../legacy/handlebars/compiled/core/CancelablePromise.js'; +import templateCancelablePromise from '~/legacy/handlebars/compiled/core/CancelablePromise'; // @ts-expect-error -import fetchGetHeaders from '../legacy/handlebars/compiled/core/fetch/getHeaders.js'; +import fetchGetHeaders from '~/legacy/handlebars/compiled/core/fetch/getHeaders'; // @ts-expect-error -import fetchGetRequestBody from '../legacy/handlebars/compiled/core/fetch/getRequestBody.js'; +import fetchGetRequestBody from '~/legacy/handlebars/compiled/core/fetch/getRequestBody'; // @ts-expect-error -import fetchGetResponseBody from '../legacy/handlebars/compiled/core/fetch/getResponseBody.js'; +import fetchGetResponseBody from '~/legacy/handlebars/compiled/core/fetch/getResponseBody'; // @ts-expect-error -import fetchGetResponseHeader from '../legacy/handlebars/compiled/core/fetch/getResponseHeader.js'; +import fetchGetResponseHeader from '~/legacy/handlebars/compiled/core/fetch/getResponseHeader'; // @ts-expect-error -import fetchRequest from '../legacy/handlebars/compiled/core/fetch/request.js'; +import fetchRequest from '~/legacy/handlebars/compiled/core/fetch/request'; // @ts-expect-error -import fetchSendRequest from '../legacy/handlebars/compiled/core/fetch/sendRequest.js'; +import fetchSendRequest from '~/legacy/handlebars/compiled/core/fetch/sendRequest'; // @ts-expect-error -import functionBase64 from '../legacy/handlebars/compiled/core/functions/base64.js'; +import functionBase64 from '~/legacy/handlebars/compiled/core/functions/base64'; // @ts-expect-error -import functionCatchErrorCodes from '../legacy/handlebars/compiled/core/functions/catchErrorCodes.js'; +import functionCatchErrorCodes from '~/legacy/handlebars/compiled/core/functions/catchErrorCodes'; // @ts-expect-error -import functionGetFormData from '../legacy/handlebars/compiled/core/functions/getFormData.js'; +import functionGetFormData from '~/legacy/handlebars/compiled/core/functions/getFormData'; // @ts-expect-error -import functionGetQueryString from '../legacy/handlebars/compiled/core/functions/getQueryString.js'; +import functionGetQueryString from '~/legacy/handlebars/compiled/core/functions/getQueryString'; // @ts-expect-error -import functionGetUrl from '../legacy/handlebars/compiled/core/functions/getUrl.js'; +import functionGetUrl from '~/legacy/handlebars/compiled/core/functions/getUrl'; // @ts-expect-error -import functionIsBlob from '../legacy/handlebars/compiled/core/functions/isBlob.js'; +import functionIsBlob from '~/legacy/handlebars/compiled/core/functions/isBlob'; // @ts-expect-error -import functionIsFormData from '../legacy/handlebars/compiled/core/functions/isFormData.js'; +import functionIsFormData from '~/legacy/handlebars/compiled/core/functions/isFormData'; // @ts-expect-error -import functionIsString from '../legacy/handlebars/compiled/core/functions/isString.js'; +import functionIsString from '~/legacy/handlebars/compiled/core/functions/isString'; // @ts-expect-error -import functionIsStringWithValue from '../legacy/handlebars/compiled/core/functions/isStringWithValue.js'; +import functionIsStringWithValue from '~/legacy/handlebars/compiled/core/functions/isStringWithValue'; // @ts-expect-error -import functionIsSuccess from '../legacy/handlebars/compiled/core/functions/isSuccess.js'; +import functionIsSuccess from '~/legacy/handlebars/compiled/core/functions/isSuccess'; // @ts-expect-error -import functionResolve from '../legacy/handlebars/compiled/core/functions/resolve.js'; +import functionResolve from '~/legacy/handlebars/compiled/core/functions/resolve'; // @ts-expect-error -import templateCoreHttpRequest from '../legacy/handlebars/compiled/core/HttpRequest.js'; +import templateCoreHttpRequest from '~/legacy/handlebars/compiled/core/HttpRequest'; // @ts-expect-error -import templateCoreSettings from '../legacy/handlebars/compiled/core/OpenAPI.js'; +import templateCoreSettings from '~/legacy/handlebars/compiled/core/OpenAPI'; // @ts-expect-error -import templateCoreRequest from '../legacy/handlebars/compiled/core/request.js'; +import templateCoreRequest from '~/legacy/handlebars/compiled/core/request'; // @ts-expect-error -import xhrGetHeaders from '../legacy/handlebars/compiled/core/xhr/getHeaders.js'; +import xhrGetHeaders from '~/legacy/handlebars/compiled/core/xhr/getHeaders'; // @ts-expect-error -import xhrGetRequestBody from '../legacy/handlebars/compiled/core/xhr/getRequestBody.js'; +import xhrGetRequestBody from '~/legacy/handlebars/compiled/core/xhr/getRequestBody'; // @ts-expect-error -import xhrGetResponseBody from '../legacy/handlebars/compiled/core/xhr/getResponseBody.js'; +import xhrGetResponseBody from '~/legacy/handlebars/compiled/core/xhr/getResponseBody'; // @ts-expect-error -import xhrGetResponseHeader from '../legacy/handlebars/compiled/core/xhr/getResponseHeader.js'; +import xhrGetResponseHeader from '~/legacy/handlebars/compiled/core/xhr/getResponseHeader'; // @ts-expect-error -import xhrRequest from '../legacy/handlebars/compiled/core/xhr/request.js'; +import xhrRequest from '~/legacy/handlebars/compiled/core/xhr/request'; // @ts-expect-error -import xhrSendRequest from '../legacy/handlebars/compiled/core/xhr/sendRequest.js'; +import xhrSendRequest from '~/legacy/handlebars/compiled/core/xhr/sendRequest'; + import { getConfig } from './config'; import { stringCase } from './stringCase'; import { transformClassName } from './transform'; diff --git a/packages/openapi-ts/src/utils/input/heyApi.ts b/packages/openapi-ts/src/utils/input/heyApi.ts index 08af7efc7..2f5b1ea7f 100644 --- a/packages/openapi-ts/src/utils/input/heyApi.ts +++ b/packages/openapi-ts/src/utils/input/heyApi.ts @@ -1,4 +1,4 @@ -import type { Input } from '../../types/input'; +import type { Input } from '~/types/input'; // Regular expression to match Hey API Registry input formats: // - {organization}/{project}?{queryParams} diff --git a/packages/openapi-ts/src/utils/input/index.ts b/packages/openapi-ts/src/utils/input/index.ts index c0b96b5c1..78bd3ced1 100644 --- a/packages/openapi-ts/src/utils/input/index.ts +++ b/packages/openapi-ts/src/utils/input/index.ts @@ -1,4 +1,5 @@ -import type { Input } from '../../types/input'; +import type { Input } from '~/types/input'; + import { heyApiRegistryBaseUrl, inputToHeyApiPath } from './heyApi'; import { inputToReadmePath } from './readme'; import { inputToScalarPath } from './scalar'; diff --git a/packages/openapi-ts/src/utils/input/readme.ts b/packages/openapi-ts/src/utils/input/readme.ts index 3ed5a5cdf..d5f10bdee 100644 --- a/packages/openapi-ts/src/utils/input/readme.ts +++ b/packages/openapi-ts/src/utils/input/readme.ts @@ -1,4 +1,4 @@ -import type { Input } from '../../types/input'; +import type { Input } from '~/types/input'; // Regular expression to match ReadMe API Registry input formats: // - @{organization}/{project}#{uuid} diff --git a/packages/openapi-ts/src/utils/input/scalar.ts b/packages/openapi-ts/src/utils/input/scalar.ts index 5686ae114..8e00aafb4 100644 --- a/packages/openapi-ts/src/utils/input/scalar.ts +++ b/packages/openapi-ts/src/utils/input/scalar.ts @@ -1,4 +1,4 @@ -import type { Input } from '../../types/input'; +import type { Input } from '~/types/input'; // Regular expression to match Scalar API Registry input formats: // - @{organization}/{project} diff --git a/packages/openapi-ts/src/utils/meta.ts b/packages/openapi-ts/src/utils/meta.ts index 5110278b1..af055a89e 100644 --- a/packages/openapi-ts/src/utils/meta.ts +++ b/packages/openapi-ts/src/utils/meta.ts @@ -1,4 +1,5 @@ -import { getType } from '../openApi'; +import { getType } from '~/openApi'; + import { refParametersPartial, refSchemasPartial } from './const'; import { reservedJavaScriptKeywordsRegExp } from './regexp'; import { cleanAndTransformTypeName } from './transform'; diff --git a/packages/openapi-ts/src/utils/postprocess.ts b/packages/openapi-ts/src/utils/postprocess.ts index c19937a2e..6e6b6b9aa 100644 --- a/packages/openapi-ts/src/utils/postprocess.ts +++ b/packages/openapi-ts/src/utils/postprocess.ts @@ -1,7 +1,8 @@ -import type { Client as ParserClient, Model } from '../openApi'; -import { sanitizeNamespaceIdentifier } from '../openApi'; -import type { Client, Operation, Service } from '../types/client'; -import type { Config } from '../types/config'; +import type { Client as ParserClient, Model } from '~/openApi'; +import { sanitizeNamespaceIdentifier } from '~/openApi'; +import type { Client, Operation, Service } from '~/types/client'; +import type { Config } from '~/types/config'; + import { getConfig, legacyNameFromConfig } from './config'; import { sort } from './sort'; import { stringCase } from './stringCase'; diff --git a/packages/openapi-ts/src/utils/stringCase.ts b/packages/openapi-ts/src/utils/stringCase.ts index 9b279a35a..d5acb3ba1 100644 --- a/packages/openapi-ts/src/utils/stringCase.ts +++ b/packages/openapi-ts/src/utils/stringCase.ts @@ -1,4 +1,4 @@ -import type { StringCase } from '../types/case'; +import type { StringCase } from '~/types/case'; const uppercaseRegExp = /[\p{Lu}]/u; const lowercaseRegExp = /[\p{Ll}]/u; diff --git a/packages/openapi-ts/src/utils/transform.ts b/packages/openapi-ts/src/utils/transform.ts index 8b68a18f1..a766161e6 100644 --- a/packages/openapi-ts/src/utils/transform.ts +++ b/packages/openapi-ts/src/utils/transform.ts @@ -1,5 +1,6 @@ -import { ensureValidTypeScriptJavaScriptIdentifier } from '../openApi'; -import type { Config } from '../types/config'; +import { ensureValidTypeScriptJavaScriptIdentifier } from '~/openApi'; +import type { Config } from '~/types/config'; + import { getConfig } from './config'; import { reservedJavaScriptKeywordsRegExp } from './regexp'; import { stringCase } from './stringCase'; diff --git a/packages/openapi-ts/src/utils/type.ts b/packages/openapi-ts/src/utils/type.ts index 351817a0e..04767ebb8 100644 --- a/packages/openapi-ts/src/utils/type.ts +++ b/packages/openapi-ts/src/utils/type.ts @@ -1,9 +1,10 @@ import type ts from 'typescript'; -import type { Model } from '../openApi'; -import { sanitizeOperationParameterName } from '../openApi'; -import { type Property, tsc } from '../tsc'; -import type { Client } from '../types/client'; +import type { Model } from '~/openApi'; +import { sanitizeOperationParameterName } from '~/openApi'; +import { type Property, tsc } from '~/tsc'; +import type { Client } from '~/types/client'; + import { getConfig, isLegacyClient } from './config'; import { refSchemasPartial } from './const'; import { enumValue } from './enum'; diff --git a/packages/openapi-ts/tsconfig.base.json b/packages/openapi-ts/tsconfig.base.json index 5afc381fb..43a2b88eb 100644 --- a/packages/openapi-ts/tsconfig.base.json +++ b/packages/openapi-ts/tsconfig.base.json @@ -11,6 +11,10 @@ "noUnusedParameters": true, "strict": true, "target": "ES2022", - "useUnknownInCatchVariables": false + "useUnknownInCatchVariables": false, + "baseUrl": "./", + "paths": { + "~/*": ["src/*"] + } } } diff --git a/packages/openapi-ts/vitest.config.ts b/packages/openapi-ts/vitest.config.ts index 1ae312607..21c403f8e 100644 --- a/packages/openapi-ts/vitest.config.ts +++ b/packages/openapi-ts/vitest.config.ts @@ -1,12 +1,19 @@ +import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createVitestConfig } from '@config/vite-base'; -export default createVitestConfig( - fileURLToPath(new URL('./', import.meta.url)), - { - test: { - setupFiles: './setupTests.ts', - }, +const rootDir = fileURLToPath(new URL('./', import.meta.url)); + +export default createVitestConfig(rootDir, { + resolve: { + alias: [ + // Support imports like `~/foo` and `~` -> maps to /src + { find: /^~\/(.*)/, replacement: path.resolve(rootDir, 'src/$1') }, + { find: '~', replacement: path.resolve(rootDir, 'src') }, + ], + }, + test: { + setupFiles: './setupTests.ts', }, -); +}); -- 2.51.2