zod-codegen 1.6.2 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +50 -48
- package/.github/workflows/release.yml +13 -3
- package/.husky/commit-msg +1 -1
- package/.husky/pre-commit +1 -1
- package/.lintstagedrc.json +5 -1
- package/.nvmrc +1 -1
- package/.prettierrc.json +12 -5
- package/CHANGELOG.md +15 -0
- package/CONTRIBUTING.md +12 -12
- package/EXAMPLES.md +135 -57
- package/PERFORMANCE.md +4 -4
- package/README.md +87 -64
- package/SECURITY.md +1 -1
- package/dist/src/cli.js +11 -18
- package/dist/src/generator.d.ts +2 -2
- package/dist/src/generator.d.ts.map +1 -1
- package/dist/src/generator.js +5 -3
- package/dist/src/interfaces/code-generator.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.d.ts +24 -1
- package/dist/src/services/code-generator.service.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.js +385 -216
- package/dist/src/services/file-reader.service.d.ts.map +1 -1
- package/dist/src/services/file-reader.service.js +1 -1
- package/dist/src/services/file-writer.service.d.ts.map +1 -1
- package/dist/src/services/file-writer.service.js +2 -2
- package/dist/src/services/import-builder.service.d.ts.map +1 -1
- package/dist/src/services/import-builder.service.js +3 -3
- package/dist/src/services/type-builder.service.d.ts.map +1 -1
- package/dist/src/types/generator-options.d.ts.map +1 -1
- package/dist/src/types/openapi.d.ts.map +1 -1
- package/dist/src/types/openapi.js +20 -20
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.d.ts.map +1 -1
- package/dist/src/utils/naming-convention.js +6 -3
- package/dist/src/utils/signal-handler.d.ts.map +1 -1
- package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
- package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
- package/dist/tests/integration/cli-comprehensive.test.js +110 -0
- package/dist/tests/integration/cli.test.d.ts +2 -0
- package/dist/tests/integration/cli.test.d.ts.map +1 -0
- package/dist/tests/integration/cli.test.js +25 -0
- package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
- package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
- package/dist/tests/integration/error-scenarios.test.js +169 -0
- package/dist/tests/integration/snapshots.test.d.ts +2 -0
- package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
- package/dist/tests/integration/snapshots.test.js +100 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
- package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
- package/dist/tests/unit/code-generator.test.d.ts +2 -0
- package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
- package/dist/tests/unit/code-generator.test.js +1364 -0
- package/dist/tests/unit/file-reader.test.d.ts +2 -0
- package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
- package/dist/tests/unit/file-reader.test.js +125 -0
- package/dist/tests/unit/generator.test.d.ts +2 -0
- package/dist/tests/unit/generator.test.d.ts.map +1 -0
- package/dist/tests/unit/generator.test.js +119 -0
- package/dist/tests/unit/naming-convention.test.d.ts +2 -0
- package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
- package/dist/tests/unit/naming-convention.test.js +256 -0
- package/dist/tests/unit/reporter.test.d.ts +2 -0
- package/dist/tests/unit/reporter.test.d.ts.map +1 -0
- package/dist/tests/unit/reporter.test.js +44 -0
- package/dist/tests/unit/type-builder.test.d.ts +2 -0
- package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/type-builder.test.js +108 -0
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +10 -20
- package/eslint.config.mjs +38 -28
- package/examples/.gitkeep +1 -1
- package/examples/README.md +4 -2
- package/examples/petstore/README.md +18 -17
- package/examples/petstore/{type.ts → api.ts} +158 -74
- package/examples/petstore/authenticated-usage.ts +6 -4
- package/examples/petstore/basic-usage.ts +4 -3
- package/examples/petstore/error-handling-usage.ts +84 -0
- package/examples/petstore/retry-handler-usage.ts +11 -18
- package/examples/petstore/server-variables-usage.ts +10 -10
- package/examples/pokeapi/README.md +8 -8
- package/examples/pokeapi/api.ts +218 -0
- package/examples/pokeapi/basic-usage.ts +3 -2
- package/examples/pokeapi/custom-client.ts +5 -4
- package/package.json +17 -21
- package/src/cli.ts +20 -25
- package/src/generator.ts +13 -11
- package/src/interfaces/code-generator.ts +1 -1
- package/src/services/code-generator.service.ts +989 -1099
- package/src/services/file-reader.service.ts +6 -5
- package/src/services/file-writer.service.ts +7 -7
- package/src/services/import-builder.service.ts +9 -13
- package/src/services/type-builder.service.ts +8 -19
- package/src/types/generator-options.ts +1 -1
- package/src/types/openapi.ts +22 -22
- package/src/utils/error-handler.ts +2 -2
- package/src/utils/naming-convention.ts +13 -10
- package/src/utils/reporter.ts +2 -2
- package/src/utils/signal-handler.ts +7 -8
- package/tests/integration/cli-comprehensive.test.ts +38 -32
- package/tests/integration/cli.test.ts +5 -5
- package/tests/integration/error-scenarios.test.ts +20 -26
- package/tests/integration/snapshots.test.ts +19 -23
- package/tests/unit/code-generator-edge-cases.test.ts +133 -133
- package/tests/unit/code-generator.test.ts +674 -268
- package/tests/unit/file-reader.test.ts +14 -14
- package/tests/unit/generator.test.ts +30 -18
- package/tests/unit/naming-convention.test.ts +27 -27
- package/tests/unit/type-builder.test.ts +2 -2
- package/tsconfig.json +5 -3
- package/vitest.config.ts +11 -21
- package/dist/scripts/update-manifest.d.ts +0 -14
- package/dist/scripts/update-manifest.d.ts.map +0 -1
- package/dist/scripts/update-manifest.js +0 -33
- package/dist/src/assets/manifest.json +0 -5
- package/examples/pokeapi/type.ts +0 -109
- package/generated/type.ts +0 -326
- package/scripts/update-manifest.ts +0 -49
- package/src/assets/manifest.json +0 -5
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import jp from 'jsonpath';
|
|
2
1
|
import * as ts from 'typescript';
|
|
3
2
|
import { z } from 'zod';
|
|
4
3
|
import { MethodSchema, Reference, SchemaProperties } from '../types/openapi.js';
|
|
4
|
+
import { transformNamingConvention } from '../utils/naming-convention.js';
|
|
5
5
|
import { TypeScriptImportBuilderService } from './import-builder.service.js';
|
|
6
6
|
import { TypeScriptTypeBuilderService } from './type-builder.service.js';
|
|
7
|
-
import { transformNamingConvention, } from '../utils/naming-convention.js';
|
|
8
7
|
export class TypeScriptCodeGeneratorService {
|
|
9
8
|
typeBuilder = new TypeScriptTypeBuilderService();
|
|
10
9
|
importBuilder = new TypeScriptImportBuilderService();
|
|
@@ -20,10 +19,33 @@ export class TypeScriptCodeGeneratorService {
|
|
|
20
19
|
}
|
|
21
20
|
ZodAST = z.object({
|
|
22
21
|
type: z.enum(['string', 'number', 'boolean', 'object', 'array', 'unknown', 'record']),
|
|
23
|
-
args: z.array(z.unknown()).optional()
|
|
22
|
+
args: z.array(z.unknown()).optional()
|
|
24
23
|
});
|
|
24
|
+
extractSchemaReferences(schema) {
|
|
25
|
+
const references = new Set();
|
|
26
|
+
const walk = (node) => {
|
|
27
|
+
if (Array.isArray(node)) {
|
|
28
|
+
for (const item of node) {
|
|
29
|
+
walk(item);
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (node === null || typeof node !== 'object') {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const [key, value] of Object.entries(node)) {
|
|
37
|
+
if (key === '$ref' && typeof value === 'string' && value.startsWith('#/components/schemas/')) {
|
|
38
|
+
references.add(value.replace('#/components/schemas/', ''));
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
walk(value);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
walk(schema);
|
|
45
|
+
return [...references];
|
|
46
|
+
}
|
|
25
47
|
generate(spec) {
|
|
26
|
-
const file = ts.createSourceFile('
|
|
48
|
+
const file = ts.createSourceFile('api.ts', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
27
49
|
const nodes = this.buildAST(spec);
|
|
28
50
|
return this.printer.printList(ts.ListFormat.MultiLine, ts.factory.createNodeArray(nodes), file);
|
|
29
51
|
}
|
|
@@ -53,15 +75,19 @@ export class TypeScriptCodeGeneratorService {
|
|
|
53
75
|
const schemaTypeAliases = this.buildSchemaTypeAliases(schemas);
|
|
54
76
|
const serverConfig = this.buildServerConfiguration(openapi);
|
|
55
77
|
const clientClass = this.buildClientClass(openapi, schemas);
|
|
78
|
+
const explicitTypeDeclarations = this.buildExplicitTypeDeclarations(openapi);
|
|
56
79
|
return [
|
|
57
80
|
this.createComment('Imports'),
|
|
58
81
|
...imports,
|
|
82
|
+
this.createComment('Explicit type declarations'),
|
|
83
|
+
...explicitTypeDeclarations,
|
|
59
84
|
this.createComment('Components schemas'),
|
|
60
85
|
...Object.values(schemas),
|
|
61
86
|
...schemaTypeAliases,
|
|
62
87
|
...serverConfig,
|
|
88
|
+
this.buildResponseValidationErrorClass(),
|
|
63
89
|
this.createComment('Client class'),
|
|
64
|
-
clientClass
|
|
90
|
+
clientClass
|
|
65
91
|
];
|
|
66
92
|
}
|
|
67
93
|
buildSchemas(openapi) {
|
|
@@ -72,27 +98,195 @@ export class TypeScriptCodeGeneratorService {
|
|
|
72
98
|
const sortedSchemaNames = this.topologicalSort(schemasMap);
|
|
73
99
|
return sortedSchemaNames.reduce((schemaRegistered, name) => {
|
|
74
100
|
const schema = openapi.components?.schemas?.[name];
|
|
75
|
-
if (!schema)
|
|
101
|
+
if (!schema) {
|
|
76
102
|
return schemaRegistered;
|
|
103
|
+
}
|
|
77
104
|
// Set context for current schema being built
|
|
78
105
|
this.currentSchemaName = name;
|
|
79
106
|
const schemaExpression = this.buildSchema(schema);
|
|
80
107
|
// Clear context
|
|
81
108
|
this.currentSchemaName = null;
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
109
|
+
const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
|
|
110
|
+
// Add type annotation: z.ZodType<Name>
|
|
111
|
+
const typeAnnotation = ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('ZodType')), [
|
|
112
|
+
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedName), undefined)
|
|
113
|
+
]);
|
|
114
|
+
const variableStatement = ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(ts.factory.createIdentifier(sanitizedName), undefined, typeAnnotation, schemaExpression)], ts.NodeFlags.Const));
|
|
85
115
|
return {
|
|
86
116
|
...schemaRegistered,
|
|
87
|
-
[name]: variableStatement
|
|
117
|
+
[name]: variableStatement
|
|
88
118
|
};
|
|
89
119
|
}, {});
|
|
90
120
|
}
|
|
91
|
-
buildSchemaTypeAliases(
|
|
92
|
-
|
|
121
|
+
buildSchemaTypeAliases(_schemas) {
|
|
122
|
+
// Explicit type declarations are used instead of z.infer type exports
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Builds explicit TypeScript type declarations for all schemas.
|
|
127
|
+
* Returns interface declarations for object types and type aliases for other types.
|
|
128
|
+
*/
|
|
129
|
+
buildExplicitTypeDeclarations(openapi) {
|
|
130
|
+
const schemasEntries = Object.entries(openapi.components?.schemas ?? {});
|
|
131
|
+
const schemasMap = Object.fromEntries(schemasEntries);
|
|
132
|
+
const sortedSchemaNames = this.topologicalSort(schemasMap);
|
|
133
|
+
const statements = [];
|
|
134
|
+
for (const name of sortedSchemaNames) {
|
|
135
|
+
const schema = openapi.components?.schemas?.[name];
|
|
136
|
+
if (!schema) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
93
139
|
const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
|
|
94
|
-
|
|
140
|
+
const safeSchema = SchemaProperties.safeParse(schema);
|
|
141
|
+
if (!safeSchema.success) {
|
|
142
|
+
// Unknown schema type, create a type alias to unknown
|
|
143
|
+
statements.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(sanitizedName), undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)));
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const schemaData = safeSchema.data;
|
|
147
|
+
const typeNode = this.buildTypeNode(schemaData);
|
|
148
|
+
// For object types with properties, create an interface
|
|
149
|
+
if (schemaData['type'] === 'object' && schemaData['properties']) {
|
|
150
|
+
statements.push(this.buildInterfaceDeclaration(sanitizedName, schemaData));
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
// For all other types (enums, arrays, unions, etc.), create a type alias
|
|
154
|
+
statements.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(sanitizedName), undefined, typeNode));
|
|
155
|
+
}
|
|
156
|
+
return statements;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Converts an OpenAPI schema to a TypeScript type node.
|
|
160
|
+
*/
|
|
161
|
+
buildTypeNode(schema) {
|
|
162
|
+
const safeSchema = SchemaProperties.safeParse(schema);
|
|
163
|
+
if (!safeSchema.success) {
|
|
164
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
165
|
+
}
|
|
166
|
+
const prop = safeSchema.data;
|
|
167
|
+
// Handle $ref
|
|
168
|
+
if (this.isReference(prop)) {
|
|
169
|
+
const { $ref = '' } = Reference.parse(prop);
|
|
170
|
+
const refName = $ref.split('/').pop() ?? 'never';
|
|
171
|
+
const sanitizedRefName = this.typeBuilder.sanitizeIdentifier(refName);
|
|
172
|
+
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedRefName), undefined);
|
|
173
|
+
}
|
|
174
|
+
// Handle nullable
|
|
175
|
+
const isNullable = prop['nullable'] === true;
|
|
176
|
+
const baseTypeNode = this.buildBaseTypeNode(prop);
|
|
177
|
+
if (isNullable) {
|
|
178
|
+
return ts.factory.createUnionTypeNode([baseTypeNode, ts.factory.createLiteralTypeNode(ts.factory.createNull())]);
|
|
179
|
+
}
|
|
180
|
+
return baseTypeNode;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Builds the base type node without nullable handling.
|
|
184
|
+
*/
|
|
185
|
+
buildBaseTypeNode(prop) {
|
|
186
|
+
// Handle anyOf/oneOf (union types)
|
|
187
|
+
if (prop['anyOf'] && Array.isArray(prop['anyOf']) && prop['anyOf'].length > 0) {
|
|
188
|
+
const types = prop['anyOf'].map((s) => this.buildTypeNode(s));
|
|
189
|
+
return ts.factory.createUnionTypeNode(types);
|
|
190
|
+
}
|
|
191
|
+
if (prop['oneOf'] && Array.isArray(prop['oneOf']) && prop['oneOf'].length > 0) {
|
|
192
|
+
const types = prop['oneOf'].map((s) => this.buildTypeNode(s));
|
|
193
|
+
return ts.factory.createUnionTypeNode(types);
|
|
194
|
+
}
|
|
195
|
+
// Handle allOf (intersection types)
|
|
196
|
+
if (prop['allOf'] && Array.isArray(prop['allOf']) && prop['allOf'].length > 0) {
|
|
197
|
+
const types = prop['allOf'].map((s) => this.buildTypeNode(s));
|
|
198
|
+
return ts.factory.createIntersectionTypeNode(types);
|
|
199
|
+
}
|
|
200
|
+
// Handle enum
|
|
201
|
+
if (prop['enum'] && Array.isArray(prop['enum']) && prop['enum'].length > 0) {
|
|
202
|
+
const literalTypes = prop['enum'].map((val) => {
|
|
203
|
+
if (typeof val === 'string') {
|
|
204
|
+
return ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(val, true));
|
|
205
|
+
}
|
|
206
|
+
else if (typeof val === 'number') {
|
|
207
|
+
if (val < 0) {
|
|
208
|
+
return ts.factory.createLiteralTypeNode(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(String(Math.abs(val)))));
|
|
209
|
+
}
|
|
210
|
+
return ts.factory.createLiteralTypeNode(ts.factory.createNumericLiteral(String(val)));
|
|
211
|
+
}
|
|
212
|
+
else if (typeof val === 'boolean') {
|
|
213
|
+
return ts.factory.createLiteralTypeNode(val ? ts.factory.createTrue() : ts.factory.createFalse());
|
|
214
|
+
}
|
|
215
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
216
|
+
});
|
|
217
|
+
return ts.factory.createUnionTypeNode(literalTypes);
|
|
218
|
+
}
|
|
219
|
+
// Handle type-specific schemas
|
|
220
|
+
switch (prop['type']) {
|
|
221
|
+
case 'string':
|
|
222
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
223
|
+
case 'number':
|
|
224
|
+
case 'integer':
|
|
225
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
226
|
+
case 'boolean':
|
|
227
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
228
|
+
case 'array': {
|
|
229
|
+
const itemsType = prop['items'] ? this.buildTypeNode(prop['items']) : ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
230
|
+
return ts.factory.createArrayTypeNode(itemsType);
|
|
231
|
+
}
|
|
232
|
+
case 'object': {
|
|
233
|
+
const properties = (prop['properties'] ?? {});
|
|
234
|
+
const requiredProps = (prop['required'] ?? []);
|
|
235
|
+
if (Object.keys(properties).length > 0) {
|
|
236
|
+
return this.buildObjectTypeLiteral(properties, requiredProps);
|
|
237
|
+
}
|
|
238
|
+
// Empty object or additionalProperties - use Record<string, unknown>
|
|
239
|
+
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
|
|
240
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
241
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
|
|
242
|
+
]);
|
|
243
|
+
}
|
|
244
|
+
default:
|
|
245
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Builds a TypeScript type literal for an object schema.
|
|
250
|
+
*/
|
|
251
|
+
buildObjectTypeLiteral(properties, requiredProps) {
|
|
252
|
+
const members = Object.entries(properties).map(([name, propSchema]) => {
|
|
253
|
+
const isRequired = requiredProps.includes(name);
|
|
254
|
+
const typeNode = this.buildTypeNode(propSchema);
|
|
255
|
+
return ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier(name), isRequired ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), typeNode);
|
|
95
256
|
});
|
|
257
|
+
return ts.factory.createTypeLiteralNode(members);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Builds a TypeScript interface declaration for an object schema.
|
|
261
|
+
*/
|
|
262
|
+
buildInterfaceDeclaration(name, schema) {
|
|
263
|
+
const properties = (schema['properties'] ?? {});
|
|
264
|
+
const requiredProps = (schema['required'] ?? []);
|
|
265
|
+
const members = Object.entries(properties).map(([propName, propSchema]) => {
|
|
266
|
+
const isRequired = requiredProps.includes(propName);
|
|
267
|
+
const typeNode = this.buildTypeNode(propSchema);
|
|
268
|
+
return ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier(propName), isRequired ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), typeNode);
|
|
269
|
+
});
|
|
270
|
+
return ts.factory.createInterfaceDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(name), undefined, undefined, members);
|
|
271
|
+
}
|
|
272
|
+
buildResponseValidationErrorClass() {
|
|
273
|
+
const typeParamT = ts.factory.createTypeParameterDeclaration(undefined, 'T');
|
|
274
|
+
const responseProperty = ts.factory.createPropertyDeclaration([ts.factory.createToken(ts.SyntaxKind.ReadonlyKeyword)], 'response', undefined, ts.factory.createTypeReferenceNode('Response'), undefined);
|
|
275
|
+
const errorProperty = ts.factory.createPropertyDeclaration([ts.factory.createToken(ts.SyntaxKind.ReadonlyKeyword)], 'error', undefined, ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), 'ZodError'), [ts.factory.createTypeReferenceNode('T')]), undefined);
|
|
276
|
+
const constructorDecl = ts.factory.createConstructorDeclaration(undefined, [
|
|
277
|
+
ts.factory.createParameterDeclaration(undefined, undefined, 'message', undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),
|
|
278
|
+
ts.factory.createParameterDeclaration(undefined, undefined, 'response', undefined, ts.factory.createTypeReferenceNode('Response')),
|
|
279
|
+
ts.factory.createParameterDeclaration(undefined, undefined, 'error', undefined, ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), 'ZodError'), [ts.factory.createTypeReferenceNode('T')]))
|
|
280
|
+
], ts.factory.createBlock([
|
|
281
|
+
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createSuper(), undefined, [ts.factory.createIdentifier('message')])),
|
|
282
|
+
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'name'), ts.SyntaxKind.EqualsToken, ts.factory.createAsExpression(ts.factory.createStringLiteral('ResponseValidationError', true), ts.factory.createTypeReferenceNode('const')))),
|
|
283
|
+
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'response'), ts.SyntaxKind.EqualsToken, ts.factory.createIdentifier('response'))),
|
|
284
|
+
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'error'), ts.SyntaxKind.EqualsToken, ts.factory.createIdentifier('error')))
|
|
285
|
+
], true));
|
|
286
|
+
const dataGetter = ts.factory.createGetAccessorDeclaration(undefined, 'data', [], ts.factory.createTypeReferenceNode('T'), ts.factory.createBlock([
|
|
287
|
+
ts.factory.createReturnStatement(ts.factory.createAsExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'response'), 'json'), undefined, []), ts.factory.createTypeReferenceNode('T')))
|
|
288
|
+
], true));
|
|
289
|
+
return ts.factory.createClassDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], 'ResponseValidationError', [typeParamT], [ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier('Error'), undefined)])], [responseProperty, errorProperty, constructorDecl, dataGetter]);
|
|
96
290
|
}
|
|
97
291
|
buildClientClass(openapi, schemas) {
|
|
98
292
|
const clientName = this.generateClientName(openapi.info.title);
|
|
@@ -103,7 +297,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
103
297
|
this.buildGetBaseRequestOptionsMethod(),
|
|
104
298
|
this.buildHandleResponseMethod(),
|
|
105
299
|
this.buildHttpRequestMethod(),
|
|
106
|
-
...methods
|
|
300
|
+
...methods
|
|
107
301
|
]);
|
|
108
302
|
}
|
|
109
303
|
buildConstructor(openapi) {
|
|
@@ -111,24 +305,21 @@ export class TypeScriptCodeGeneratorService {
|
|
|
111
305
|
if (hasServers) {
|
|
112
306
|
// Options-based constructor
|
|
113
307
|
return ts.factory.createConstructorDeclaration(undefined, [
|
|
114
|
-
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('options'), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('ClientOptions'), undefined), undefined)
|
|
308
|
+
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('options'), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('ClientOptions'), undefined), undefined)
|
|
115
309
|
], ts.factory.createBlock([
|
|
116
310
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
117
311
|
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('resolvedUrl'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('baseUrl')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createNull()), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('baseUrl')), ts.factory.createToken(ts.SyntaxKind.ColonToken), ts.factory.createCallExpression(ts.factory.createIdentifier('resolveServerUrl'), undefined, [
|
|
118
312
|
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverIndex')),
|
|
119
|
-
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverVariables'))
|
|
120
|
-
])))
|
|
313
|
+
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverVariables'))
|
|
314
|
+
])))
|
|
121
315
|
], ts.NodeFlags.Const)),
|
|
122
|
-
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('resolvedUrl')))
|
|
316
|
+
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('resolvedUrl')))
|
|
123
317
|
], true));
|
|
124
318
|
}
|
|
125
319
|
else {
|
|
126
320
|
// Fallback: simple baseUrl parameter
|
|
127
|
-
return ts.factory.createConstructorDeclaration(undefined, [
|
|
128
|
-
|
|
129
|
-
this.typeBuilder.createParameter('_', 'unknown', undefined, true),
|
|
130
|
-
], ts.factory.createBlock([
|
|
131
|
-
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('baseUrl'))),
|
|
321
|
+
return ts.factory.createConstructorDeclaration(undefined, [this.typeBuilder.createParameter('baseUrl', 'string', ts.factory.createStringLiteral('/', true)), this.typeBuilder.createParameter('_', 'unknown', undefined, true)], ts.factory.createBlock([
|
|
322
|
+
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('baseUrl')))
|
|
132
323
|
], true));
|
|
133
324
|
}
|
|
134
325
|
}
|
|
@@ -138,9 +329,9 @@ export class TypeScriptCodeGeneratorService {
|
|
|
138
329
|
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('RequestInit'), undefined),
|
|
139
330
|
ts.factory.createUnionTypeNode([
|
|
140
331
|
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('method', true)),
|
|
141
|
-
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('body', true))
|
|
142
|
-
])
|
|
143
|
-
])
|
|
332
|
+
ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('body', true))
|
|
333
|
+
])
|
|
334
|
+
])
|
|
144
335
|
]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression([], false))], true));
|
|
145
336
|
}
|
|
146
337
|
buildHandleResponseMethod() {
|
|
@@ -148,76 +339,66 @@ export class TypeScriptCodeGeneratorService {
|
|
|
148
339
|
this.typeBuilder.createParameter('response', 'Response'),
|
|
149
340
|
this.typeBuilder.createParameter('method', 'string'),
|
|
150
341
|
this.typeBuilder.createParameter('path', 'string'),
|
|
151
|
-
this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}')
|
|
152
|
-
], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
153
|
-
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Response'), undefined),
|
|
154
|
-
]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createIdentifier('response'))], true));
|
|
342
|
+
this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}')
|
|
343
|
+
], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Response'), undefined)]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createIdentifier('response'))], true));
|
|
155
344
|
}
|
|
156
345
|
buildHttpRequestMethod() {
|
|
157
346
|
return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.ProtectedKeyword), ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier('makeRequest'), undefined, [this.typeBuilder.createGenericType('T')], [
|
|
158
347
|
this.typeBuilder.createParameter('method', 'string'),
|
|
159
348
|
this.typeBuilder.createParameter('path', 'string'),
|
|
160
|
-
this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}', ts.factory.createObjectLiteralExpression([], false), false)
|
|
161
|
-
], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
162
|
-
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined),
|
|
163
|
-
]), ts.factory.createBlock([
|
|
349
|
+
this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}', ts.factory.createObjectLiteralExpression([], false), false)
|
|
350
|
+
], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined)]), ts.factory.createBlock([
|
|
164
351
|
// Create initial URL object that we will use to build the final URL
|
|
165
352
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
166
353
|
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseUrl'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
|
|
167
354
|
ts.factory.createIdentifier('path'),
|
|
168
|
-
ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl'))
|
|
169
|
-
]))
|
|
355
|
+
ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl'))
|
|
356
|
+
]))
|
|
170
357
|
], ts.NodeFlags.Const)),
|
|
171
358
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
172
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('url'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [
|
|
173
|
-
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
|
|
174
|
-
]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('0'))), undefined, ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
|
|
175
|
-
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
|
|
176
|
-
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
|
|
177
|
-
]), ts.factory.createIdentifier('filter')), undefined, [
|
|
359
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('url'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params'))]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('0'))), undefined, ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
|
|
360
|
+
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params'))]), ts.factory.createIdentifier('filter')), undefined, [
|
|
178
361
|
ts.factory.createArrowFunction(undefined, undefined, [
|
|
179
362
|
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
|
|
180
363
|
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(''), undefined),
|
|
181
|
-
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
|
|
182
|
-
]), undefined, undefined)
|
|
183
|
-
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createIdentifier('value'), ts.SyntaxKind.ExclamationEqualsEqualsToken, ts.factory.createIdentifier('undefined')))
|
|
364
|
+
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
|
|
365
|
+
]), undefined, undefined)
|
|
366
|
+
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createIdentifier('value'), ts.SyntaxKind.ExclamationEqualsEqualsToken, ts.factory.createIdentifier('undefined')))
|
|
184
367
|
]), ts.factory.createIdentifier('forEach')), undefined, [
|
|
185
368
|
ts.factory.createArrowFunction(undefined, undefined, [
|
|
186
369
|
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
|
|
187
370
|
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('key'), undefined),
|
|
188
|
-
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
|
|
189
|
-
]), undefined, undefined)
|
|
371
|
+
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
|
|
372
|
+
]), undefined, undefined)
|
|
190
373
|
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
|
|
191
374
|
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('searchParams')), ts.factory.createIdentifier('set')), undefined, [
|
|
192
375
|
ts.factory.createIdentifier('key'),
|
|
193
|
-
ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')])
|
|
194
|
-
]))
|
|
195
|
-
], false))
|
|
376
|
+
ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')])
|
|
377
|
+
]))
|
|
378
|
+
], false))
|
|
196
379
|
])),
|
|
197
|
-
ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, []))
|
|
198
|
-
], true))), undefined, []), undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, [])))
|
|
380
|
+
ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, []))
|
|
381
|
+
], true))), undefined, []), undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, [])))
|
|
199
382
|
], ts.NodeFlags.Const)),
|
|
200
383
|
// Get base request options (headers, signal, credentials, etc.)
|
|
201
384
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
202
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseOptions'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('getBaseRequestOptions')), undefined, []))
|
|
385
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseOptions'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('getBaseRequestOptions')), undefined, []))
|
|
203
386
|
], ts.NodeFlags.Const)),
|
|
204
387
|
// Build Content-Type header
|
|
205
388
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
206
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('contentType'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('contentType')), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createStringLiteral('application/x-www-form-urlencoded', true)), undefined, ts.factory.createStringLiteral('application/x-www-form-urlencoded', true), undefined, ts.factory.createStringLiteral('application/json', true)))
|
|
389
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('contentType'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('contentType')), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createStringLiteral('application/x-www-form-urlencoded', true)), undefined, ts.factory.createStringLiteral('application/x-www-form-urlencoded', true), undefined, ts.factory.createStringLiteral('application/json', true)))
|
|
207
390
|
], ts.NodeFlags.Const)),
|
|
208
391
|
// Merge headers: base headers, Content-Type, and request-specific headers
|
|
209
392
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
210
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseHeaders'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false)))
|
|
393
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseHeaders'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false)))
|
|
211
394
|
], ts.NodeFlags.Const)),
|
|
212
395
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
213
396
|
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('headers'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('assign')), undefined, [
|
|
214
397
|
ts.factory.createObjectLiteralExpression([], false),
|
|
215
398
|
ts.factory.createIdentifier('baseHeaders'),
|
|
216
|
-
ts.factory.createObjectLiteralExpression([
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false)),
|
|
220
|
-
])),
|
|
399
|
+
ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment(ts.factory.createStringLiteral('Content-Type', true), ts.factory.createIdentifier('contentType'))], false),
|
|
400
|
+
ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false))
|
|
401
|
+
]))
|
|
221
402
|
], ts.NodeFlags.Const)),
|
|
222
403
|
// Build body with form-urlencoded support
|
|
223
404
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
@@ -225,29 +406,25 @@ export class TypeScriptCodeGeneratorService {
|
|
|
225
406
|
// Form-urlencoded: convert object to URLSearchParams
|
|
226
407
|
ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
|
|
227
408
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
228
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('params'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URLSearchParams'), undefined, []))
|
|
409
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('params'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URLSearchParams'), undefined, []))
|
|
229
410
|
], ts.NodeFlags.Const)),
|
|
230
|
-
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
|
|
231
|
-
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')),
|
|
232
|
-
]), ts.factory.createIdentifier('forEach')), undefined, [
|
|
411
|
+
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data'))]), ts.factory.createIdentifier('forEach')), undefined, [
|
|
233
412
|
ts.factory.createArrowFunction(undefined, undefined, [
|
|
234
413
|
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
|
|
235
414
|
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('key'), undefined),
|
|
236
|
-
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
|
|
237
|
-
]), undefined, undefined)
|
|
415
|
+
ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
|
|
416
|
+
]), undefined, undefined)
|
|
238
417
|
], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
|
|
239
418
|
ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('set')), undefined, [
|
|
240
419
|
ts.factory.createIdentifier('key'),
|
|
241
|
-
ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')])
|
|
242
|
-
]))
|
|
243
|
-
], false))
|
|
420
|
+
ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')])
|
|
421
|
+
]))
|
|
422
|
+
], false))
|
|
244
423
|
])),
|
|
245
|
-
ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('toString')), undefined, []))
|
|
424
|
+
ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('toString')), undefined, []))
|
|
246
425
|
], false))), undefined, []), undefined,
|
|
247
426
|
// JSON: stringify the data
|
|
248
|
-
ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JSON'), ts.factory.createIdentifier('stringify')), undefined, [
|
|
249
|
-
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')),
|
|
250
|
-
])), undefined, ts.factory.createNull())),
|
|
427
|
+
ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JSON'), ts.factory.createIdentifier('stringify')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data'))])), undefined, ts.factory.createNull()))
|
|
251
428
|
], ts.NodeFlags.Const)),
|
|
252
429
|
// Make fetch request: merge base options with method, headers, and body
|
|
253
430
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
@@ -259,10 +436,10 @@ export class TypeScriptCodeGeneratorService {
|
|
|
259
436
|
ts.factory.createObjectLiteralExpression([
|
|
260
437
|
ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier('method'), undefined),
|
|
261
438
|
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('headers'), ts.factory.createIdentifier('headers')),
|
|
262
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('body'), ts.factory.createIdentifier('body'))
|
|
263
|
-
], false)
|
|
264
|
-
])
|
|
265
|
-
])))
|
|
439
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('body'), ts.factory.createIdentifier('body'))
|
|
440
|
+
], false)
|
|
441
|
+
])
|
|
442
|
+
])))
|
|
266
443
|
], ts.NodeFlags.Const)),
|
|
267
444
|
// Handle response through hook (allows subclasses to intercept and modify response)
|
|
268
445
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
@@ -270,18 +447,18 @@ export class TypeScriptCodeGeneratorService {
|
|
|
270
447
|
ts.factory.createIdentifier('rawResponse'),
|
|
271
448
|
ts.factory.createIdentifier('method'),
|
|
272
449
|
ts.factory.createIdentifier('path'),
|
|
273
|
-
ts.factory.createIdentifier('options')
|
|
274
|
-
])))
|
|
450
|
+
ts.factory.createIdentifier('options')
|
|
451
|
+
])))
|
|
275
452
|
], ts.NodeFlags.Const)),
|
|
276
453
|
// Check response status
|
|
277
454
|
ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('ok'))), ts.factory.createThrowStatement(ts.factory.createNewExpression(ts.factory.createIdentifier('Error'), undefined, [
|
|
278
455
|
ts.factory.createTemplateExpression(ts.factory.createTemplateHead('HTTP ', 'HTTP '), [
|
|
279
456
|
ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('status')), ts.factory.createTemplateMiddle(': ', ': ')),
|
|
280
|
-
ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('statusText')), ts.factory.createTemplateTail('', ''))
|
|
281
|
-
])
|
|
457
|
+
ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('statusText')), ts.factory.createTemplateTail('', ''))
|
|
458
|
+
])
|
|
282
459
|
])), undefined),
|
|
283
460
|
// Return parsed JSON
|
|
284
|
-
ts.factory.createReturnStatement(ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('json')), undefined, [])))
|
|
461
|
+
ts.factory.createReturnStatement(ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('json')), undefined, [])))
|
|
285
462
|
], true));
|
|
286
463
|
}
|
|
287
464
|
buildClientMethods(openapi, schemas) {
|
|
@@ -327,7 +504,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
327
504
|
// This will be handled in transformOperationName
|
|
328
505
|
const modifiedSchema = {
|
|
329
506
|
...safeMethodSchema,
|
|
330
|
-
operationId: `${operationId}_${methodLower}
|
|
507
|
+
operationId: `${operationId}_${methodLower}`
|
|
331
508
|
};
|
|
332
509
|
return this.buildEndpointMethod(method, path, modifiedSchema, schemas);
|
|
333
510
|
}
|
|
@@ -352,7 +529,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
352
529
|
path,
|
|
353
530
|
...(schema.tags !== undefined && { tags: schema.tags }),
|
|
354
531
|
...(schema.summary !== undefined && { summary: schema.summary }),
|
|
355
|
-
...(schema.description !== undefined && { description: schema.description })
|
|
532
|
+
...(schema.description !== undefined && { description: schema.description })
|
|
356
533
|
};
|
|
357
534
|
transformed = this.operationNameTransformer(details);
|
|
358
535
|
}
|
|
@@ -391,9 +568,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
391
568
|
}), false)
|
|
392
569
|
: undefined;
|
|
393
570
|
// Build request body
|
|
394
|
-
const requestBodyExpression = hasRequestBody
|
|
395
|
-
? ts.factory.createIdentifier('body')
|
|
396
|
-
: undefined;
|
|
571
|
+
const requestBodyExpression = hasRequestBody ? ts.factory.createIdentifier('body') : undefined;
|
|
397
572
|
// Build options object for makeRequest
|
|
398
573
|
const optionsProps = [];
|
|
399
574
|
if (queryParamsExpression) {
|
|
@@ -409,10 +584,32 @@ export class TypeScriptCodeGeneratorService {
|
|
|
409
584
|
const optionsExpression = ts.factory.createObjectLiteralExpression(optionsProps, false);
|
|
410
585
|
// Call makeRequest
|
|
411
586
|
const makeRequestCall = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('makeRequest')), undefined, [ts.factory.createStringLiteral(method.toUpperCase(), true), pathExpression, optionsExpression]);
|
|
412
|
-
// Add Zod validation if we have a response schema
|
|
413
587
|
if (responseSchema) {
|
|
414
|
-
const
|
|
415
|
-
|
|
588
|
+
const schemaName = responseSchema.text;
|
|
589
|
+
const schemaNameLower = schemaName.charAt(0).toLowerCase() + schemaName.slice(1);
|
|
590
|
+
const parsedVarName = `parsed${schemaName}`;
|
|
591
|
+
// const response = await this.makeRequest(...)
|
|
592
|
+
statements.push(ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration('response', undefined, undefined, ts.factory.createAwaitExpression(makeRequestCall))], ts.NodeFlags.Const)));
|
|
593
|
+
// const parsed{Name} = {Schema}.safeParse(response)
|
|
594
|
+
statements.push(ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
595
|
+
ts.factory.createVariableDeclaration(parsedVarName, undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(responseSchema, ts.factory.createIdentifier('safeParse')), undefined, [
|
|
596
|
+
ts.factory.createIdentifier('response')
|
|
597
|
+
]))
|
|
598
|
+
], ts.NodeFlags.Const)));
|
|
599
|
+
// if (!parsed{Name}.success) { throw new ResponseValidationError<{Type}>(...) }
|
|
600
|
+
statements.push(ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'success')), ts.factory.createBlock([
|
|
601
|
+
ts.factory.createThrowStatement(ts.factory.createNewExpression(ts.factory.createIdentifier('ResponseValidationError'), [ts.factory.createTypeReferenceNode(schemaName)], [
|
|
602
|
+
ts.factory.createTemplateExpression(ts.factory.createTemplateHead(`Invalid ${schemaNameLower}: `), [
|
|
603
|
+
ts.factory.createTemplateSpan(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'error'), 'errors'), 'map'), undefined, [
|
|
604
|
+
ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'error')], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('error'), 'message'))
|
|
605
|
+
]), 'join'), undefined, [ts.factory.createStringLiteral(', ', true)]), ts.factory.createTemplateTail(''))
|
|
606
|
+
]),
|
|
607
|
+
ts.factory.createIdentifier('response'),
|
|
608
|
+
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'error')
|
|
609
|
+
]))
|
|
610
|
+
], true)));
|
|
611
|
+
// return parsed{Name}.data
|
|
612
|
+
statements.push(ts.factory.createReturnStatement(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'data')));
|
|
416
613
|
}
|
|
417
614
|
else {
|
|
418
615
|
statements.push(ts.factory.createReturnStatement(ts.factory.createAwaitExpression(makeRequestCall)));
|
|
@@ -441,7 +638,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
441
638
|
matches.push({
|
|
442
639
|
index: match.index,
|
|
443
640
|
length: match[0].length,
|
|
444
|
-
name: paramName
|
|
641
|
+
name: paramName
|
|
445
642
|
});
|
|
446
643
|
}
|
|
447
644
|
}
|
|
@@ -536,9 +733,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
536
733
|
parameters.push(this.typeBuilder.createParameter('body', bodyType, undefined, !schema.requestBody?.required));
|
|
537
734
|
}
|
|
538
735
|
// Determine content type for request body
|
|
539
|
-
const contentType = hasRequestBody && schema.requestBody?.content?.['application/x-www-form-urlencoded']
|
|
540
|
-
? 'application/x-www-form-urlencoded'
|
|
541
|
-
: 'application/json';
|
|
736
|
+
const contentType = hasRequestBody && schema.requestBody?.content?.['application/x-www-form-urlencoded'] ? 'application/x-www-form-urlencoded' : 'application/json';
|
|
542
737
|
return { parameters, pathParams, queryParams, hasRequestBody, contentType };
|
|
543
738
|
}
|
|
544
739
|
getParameterType(schema) {
|
|
@@ -632,9 +827,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
632
827
|
const responseDefault = schema.responses?.['default'];
|
|
633
828
|
const response = response200 ?? response201 ?? responseDefault;
|
|
634
829
|
if (!response?.content?.['application/json']?.schema) {
|
|
635
|
-
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
636
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
|
|
637
|
-
]);
|
|
830
|
+
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)]);
|
|
638
831
|
}
|
|
639
832
|
const responseSchema = response.content['application/json'].schema;
|
|
640
833
|
const typeName = this.getSchemaTypeName(responseSchema, schemas);
|
|
@@ -646,26 +839,22 @@ export class TypeScriptCodeGeneratorService {
|
|
|
646
839
|
if (schemas[sanitizedItemTypeName]) {
|
|
647
840
|
// Use the type alias directly (it already uses z.infer)
|
|
648
841
|
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
649
|
-
ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedItemTypeName), undefined))
|
|
842
|
+
ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedItemTypeName), undefined))
|
|
650
843
|
]);
|
|
651
844
|
}
|
|
652
845
|
// If it's a primitive array, use the type name as-is
|
|
653
|
-
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
654
|
-
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
|
|
655
|
-
]);
|
|
846
|
+
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined)]);
|
|
656
847
|
}
|
|
657
848
|
const sanitizedTypeName = this.typeBuilder.sanitizeIdentifier(typeName);
|
|
658
849
|
// Check if it's a custom schema type (we have a type alias for it)
|
|
659
850
|
if (schemas[sanitizedTypeName]) {
|
|
660
851
|
// Use the type name directly (we have a type alias that already uses z.infer)
|
|
661
852
|
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
662
|
-
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedTypeName), undefined)
|
|
853
|
+
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedTypeName), undefined)
|
|
663
854
|
]);
|
|
664
855
|
}
|
|
665
856
|
// For primitive types and Record types, use the type name directly
|
|
666
|
-
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
667
|
-
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
|
|
668
|
-
]);
|
|
857
|
+
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined)]);
|
|
669
858
|
}
|
|
670
859
|
buildServerConfiguration(openapi) {
|
|
671
860
|
const servers = openapi.servers;
|
|
@@ -675,17 +864,13 @@ export class TypeScriptCodeGeneratorService {
|
|
|
675
864
|
const statements = [];
|
|
676
865
|
// Build server configuration array
|
|
677
866
|
const serverConfigElements = servers.map((server) => {
|
|
678
|
-
const properties = [
|
|
679
|
-
ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
|
|
680
|
-
];
|
|
867
|
+
const properties = [ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true))];
|
|
681
868
|
if (server.description) {
|
|
682
869
|
properties.push(ts.factory.createPropertyAssignment('description', ts.factory.createStringLiteral(server.description, true)));
|
|
683
870
|
}
|
|
684
871
|
if (server.variables && Object.keys(server.variables).length > 0) {
|
|
685
872
|
const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
|
|
686
|
-
const varProps = [
|
|
687
|
-
ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
|
|
688
|
-
];
|
|
873
|
+
const varProps = [ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true))];
|
|
689
874
|
if (varDef.enum && varDef.enum.length > 0) {
|
|
690
875
|
varProps.push(ts.factory.createPropertyAssignment('enum', ts.factory.createArrayLiteralExpression(varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)), false)));
|
|
691
876
|
}
|
|
@@ -700,22 +885,20 @@ export class TypeScriptCodeGeneratorService {
|
|
|
700
885
|
});
|
|
701
886
|
// Export server configuration
|
|
702
887
|
statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
|
|
703
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('serverConfigurations'), undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false))
|
|
888
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('serverConfigurations'), undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false))
|
|
704
889
|
], ts.NodeFlags.Const)));
|
|
705
890
|
// Export default base URL (first server with default variables)
|
|
706
891
|
const firstServer = servers[0];
|
|
707
892
|
const defaultBaseUrl = firstServer ? this.resolveServerUrl(firstServer, {}) : '/';
|
|
708
|
-
statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
|
|
709
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('defaultBaseUrl'), undefined, undefined, ts.factory.createStringLiteral(defaultBaseUrl, true)),
|
|
710
|
-
], ts.NodeFlags.Const)));
|
|
893
|
+
statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(ts.factory.createIdentifier('defaultBaseUrl'), undefined, undefined, ts.factory.createStringLiteral(defaultBaseUrl, true))], ts.NodeFlags.Const)));
|
|
711
894
|
// Build ClientOptions type
|
|
712
895
|
const optionProperties = [
|
|
713
896
|
ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('baseUrl'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),
|
|
714
897
|
ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
|
|
715
898
|
ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('serverVariables'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
|
|
716
899
|
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
717
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
|
718
|
-
]))
|
|
900
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
|
901
|
+
]))
|
|
719
902
|
];
|
|
720
903
|
statements.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier('ClientOptions'), undefined, ts.factory.createTypeLiteralNode(optionProperties)));
|
|
721
904
|
// Build resolveServerUrl helper function
|
|
@@ -735,14 +918,10 @@ export class TypeScriptCodeGeneratorService {
|
|
|
735
918
|
buildResolveServerUrlFunction(servers) {
|
|
736
919
|
// Build server configs array inline
|
|
737
920
|
const serverConfigElements = servers.map((server) => {
|
|
738
|
-
const properties = [
|
|
739
|
-
ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
|
|
740
|
-
];
|
|
921
|
+
const properties = [ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true))];
|
|
741
922
|
if (server.variables && Object.keys(server.variables).length > 0) {
|
|
742
923
|
const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
|
|
743
|
-
const varProps = [
|
|
744
|
-
ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
|
|
745
|
-
];
|
|
924
|
+
const varProps = [ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true))];
|
|
746
925
|
if (varDef.enum && varDef.enum.length > 0) {
|
|
747
926
|
varProps.push(ts.factory.createPropertyAssignment('enum', ts.factory.createArrayLiteralExpression(varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)), false)));
|
|
748
927
|
}
|
|
@@ -761,56 +940,47 @@ export class TypeScriptCodeGeneratorService {
|
|
|
761
940
|
const value = ts.factory.createIdentifier('value');
|
|
762
941
|
const bodyStatements = [
|
|
763
942
|
// const configs = [...]
|
|
764
|
-
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
765
|
-
ts.factory.createVariableDeclaration(configs, undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false)),
|
|
766
|
-
], ts.NodeFlags.Const)),
|
|
943
|
+
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(configs, undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false))], ts.NodeFlags.Const)),
|
|
767
944
|
// const idx = serverIndex ?? 0
|
|
768
945
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
769
|
-
ts.factory.createVariableDeclaration(idx, undefined, undefined, ts.factory.createBinaryExpression(ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createNumericLiteral('0')))
|
|
946
|
+
ts.factory.createVariableDeclaration(idx, undefined, undefined, ts.factory.createBinaryExpression(ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createNumericLiteral('0')))
|
|
770
947
|
], ts.NodeFlags.Const)),
|
|
771
948
|
// if (idx < configs.length) { ... }
|
|
772
949
|
ts.factory.createIfStatement(ts.factory.createBinaryExpression(idx, ts.factory.createToken(ts.SyntaxKind.LessThanToken), ts.factory.createPropertyAccessExpression(configs, ts.factory.createIdentifier('length'))), ts.factory.createBlock([
|
|
773
950
|
// const config = configs[idx]
|
|
774
|
-
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
775
|
-
ts.factory.createVariableDeclaration(config, undefined, undefined, ts.factory.createElementAccessExpression(configs, idx)),
|
|
776
|
-
], ts.NodeFlags.Const)),
|
|
951
|
+
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(config, undefined, undefined, ts.factory.createElementAccessExpression(configs, idx))], ts.NodeFlags.Const)),
|
|
777
952
|
// let url = config.url
|
|
778
|
-
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
779
|
-
ts.factory.createVariableDeclaration(url, undefined, undefined, ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('url'))),
|
|
780
|
-
], ts.NodeFlags.Let)),
|
|
953
|
+
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(url, undefined, undefined, ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('url')))], ts.NodeFlags.Let)),
|
|
781
954
|
// if (config.variables && serverVariables) { ... }
|
|
782
955
|
ts.factory.createIfStatement(ts.factory.createLogicalAnd(ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('variables')), ts.factory.createIdentifier('serverVariables')), ts.factory.createBlock([
|
|
783
956
|
// for (const [key, value] of Object.entries(serverVariables)) { url = url.replace(...) }
|
|
784
957
|
ts.factory.createForOfStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
785
958
|
ts.factory.createVariableDeclaration(ts.factory.createArrayBindingPattern([
|
|
786
959
|
ts.factory.createBindingElement(undefined, undefined, key),
|
|
787
|
-
ts.factory.createBindingElement(undefined, undefined, value)
|
|
788
|
-
]), undefined, undefined)
|
|
960
|
+
ts.factory.createBindingElement(undefined, undefined, value)
|
|
961
|
+
]), undefined, undefined)
|
|
789
962
|
], ts.NodeFlags.Const), ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [ts.factory.createIdentifier('serverVariables')]), ts.factory.createBlock([
|
|
790
963
|
ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(url, ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(url, ts.factory.createIdentifier('replace')), undefined, [
|
|
791
964
|
ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
|
|
792
965
|
ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createStringLiteral('\\{'), ts.factory.createToken(ts.SyntaxKind.PlusToken), key), ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createStringLiteral('\\}')),
|
|
793
|
-
ts.factory.createStringLiteral('g')
|
|
966
|
+
ts.factory.createStringLiteral('g')
|
|
794
967
|
]),
|
|
795
|
-
value
|
|
796
|
-
])))
|
|
797
|
-
], true))
|
|
968
|
+
value
|
|
969
|
+
])))
|
|
970
|
+
], true))
|
|
798
971
|
], true)),
|
|
799
972
|
// return url
|
|
800
|
-
ts.factory.createReturnStatement(url)
|
|
973
|
+
ts.factory.createReturnStatement(url)
|
|
801
974
|
], true)),
|
|
802
975
|
// return default (first server with defaults)
|
|
803
|
-
ts.factory.createReturnStatement(ts.factory.createStringLiteral(servers[0] ? this.resolveServerUrl(servers[0], {}) : '/', true))
|
|
976
|
+
ts.factory.createReturnStatement(ts.factory.createStringLiteral(servers[0] ? this.resolveServerUrl(servers[0], {}) : '/', true))
|
|
804
977
|
];
|
|
805
978
|
return ts.factory.createFunctionDeclaration(undefined, undefined, ts.factory.createIdentifier('resolveServerUrl'), undefined, [
|
|
806
|
-
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createUnionTypeNode([
|
|
807
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
|
|
808
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
|
|
809
|
-
]), undefined),
|
|
979
|
+
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createUnionTypeNode([ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)]), undefined),
|
|
810
980
|
ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverVariables'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
|
|
811
981
|
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
812
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
|
813
|
-
]), ts.factory.createObjectLiteralExpression([], false))
|
|
982
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
|
983
|
+
]), ts.factory.createObjectLiteralExpression([], false))
|
|
814
984
|
], ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), ts.factory.createBlock(bodyStatements, true));
|
|
815
985
|
}
|
|
816
986
|
generateClientName(title) {
|
|
@@ -914,9 +1084,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
914
1084
|
const prop = safeProperty.data;
|
|
915
1085
|
if (this.isReference(prop)) {
|
|
916
1086
|
const refSchema = this.buildFromReference(prop);
|
|
917
|
-
return required
|
|
918
|
-
? refSchema
|
|
919
|
-
: ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(refSchema, ts.factory.createIdentifier('optional')), undefined, []);
|
|
1087
|
+
return required ? refSchema : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(refSchema, ts.factory.createIdentifier('optional')), undefined, []);
|
|
920
1088
|
}
|
|
921
1089
|
if (prop['anyOf'] && Array.isArray(prop['anyOf']) && prop['anyOf'].length > 0) {
|
|
922
1090
|
return this.handleLogicalOperator('anyOf', prop['anyOf'], required);
|
|
@@ -964,7 +1132,9 @@ export class TypeScriptCodeGeneratorService {
|
|
|
964
1132
|
else {
|
|
965
1133
|
literalValue = ts.factory.createStringLiteral(String(val), true);
|
|
966
1134
|
}
|
|
967
|
-
return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('literal')), undefined, [
|
|
1135
|
+
return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('literal')), undefined, [
|
|
1136
|
+
literalValue
|
|
1137
|
+
]);
|
|
968
1138
|
});
|
|
969
1139
|
const unionExpression = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('union')), undefined, [ts.factory.createArrayLiteralExpression(literalSchemas, false)]);
|
|
970
1140
|
return required
|
|
@@ -978,15 +1148,19 @@ export class TypeScriptCodeGeneratorService {
|
|
|
978
1148
|
let arraySchema = this.buildZodAST([
|
|
979
1149
|
{
|
|
980
1150
|
type: 'array',
|
|
981
|
-
args: [itemsSchema]
|
|
982
|
-
}
|
|
1151
|
+
args: [itemsSchema]
|
|
1152
|
+
}
|
|
983
1153
|
]);
|
|
984
1154
|
// Apply array constraints
|
|
985
1155
|
if (typeof prop['minItems'] === 'number') {
|
|
986
|
-
arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('min')), undefined, [
|
|
1156
|
+
arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('min')), undefined, [
|
|
1157
|
+
ts.factory.createNumericLiteral(String(prop['minItems']))
|
|
1158
|
+
]);
|
|
987
1159
|
}
|
|
988
1160
|
if (typeof prop['maxItems'] === 'number') {
|
|
989
|
-
arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('max')), undefined, [
|
|
1161
|
+
arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('max')), undefined, [
|
|
1162
|
+
ts.factory.createNumericLiteral(String(prop['maxItems']))
|
|
1163
|
+
]);
|
|
990
1164
|
}
|
|
991
1165
|
return required
|
|
992
1166
|
? arraySchema
|
|
@@ -1004,9 +1178,9 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1004
1178
|
args: [
|
|
1005
1179
|
ts.factory.createObjectLiteralExpression(propertiesEntries.map(([name, propValue]) => {
|
|
1006
1180
|
return ts.factory.createPropertyAssignment(ts.factory.createIdentifier(name), this.buildProperty(propValue, propRequired.includes(name)));
|
|
1007
|
-
}), true)
|
|
1008
|
-
]
|
|
1009
|
-
}
|
|
1181
|
+
}), true)
|
|
1182
|
+
]
|
|
1183
|
+
}
|
|
1010
1184
|
]);
|
|
1011
1185
|
// Apply object constraints
|
|
1012
1186
|
let constrainedSchema = objectSchema;
|
|
@@ -1014,16 +1188,16 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1014
1188
|
constrainedSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')), undefined, [
|
|
1015
1189
|
ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createIdentifier('obj')]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanEqualsToken), ts.factory.createNumericLiteral(String(prop['minProperties'])))),
|
|
1016
1190
|
ts.factory.createObjectLiteralExpression([
|
|
1017
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at least ${String(prop['minProperties'])} properties`))
|
|
1018
|
-
])
|
|
1191
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at least ${String(prop['minProperties'])} properties`))
|
|
1192
|
+
])
|
|
1019
1193
|
]);
|
|
1020
1194
|
}
|
|
1021
1195
|
if (typeof prop['maxProperties'] === 'number') {
|
|
1022
1196
|
constrainedSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')), undefined, [
|
|
1023
1197
|
ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createIdentifier('obj')]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.LessThanEqualsToken), ts.factory.createNumericLiteral(String(prop['maxProperties'])))),
|
|
1024
1198
|
ts.factory.createObjectLiteralExpression([
|
|
1025
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at most ${String(prop['maxProperties'])} properties`))
|
|
1026
|
-
])
|
|
1199
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at most ${String(prop['maxProperties'])} properties`))
|
|
1200
|
+
])
|
|
1027
1201
|
]);
|
|
1028
1202
|
}
|
|
1029
1203
|
return required
|
|
@@ -1033,31 +1207,30 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1033
1207
|
return this.buildZodAST([
|
|
1034
1208
|
{
|
|
1035
1209
|
type: 'record',
|
|
1036
|
-
args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])]
|
|
1037
|
-
}
|
|
1210
|
+
args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])]
|
|
1211
|
+
}
|
|
1038
1212
|
]);
|
|
1039
1213
|
}
|
|
1040
1214
|
case 'integer': {
|
|
1041
1215
|
let numberSchema = this.buildZodAST(['number', 'int']);
|
|
1042
1216
|
// Apply number constraints
|
|
1043
1217
|
if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
|
|
1044
|
-
const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
|
|
1045
|
-
? prop['minimum'] + 1
|
|
1046
|
-
: prop['minimum'];
|
|
1218
|
+
const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? prop['minimum'] + 1 : prop['minimum'];
|
|
1047
1219
|
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte')), undefined, [ts.factory.createNumericLiteral(String(minValue))]);
|
|
1048
1220
|
}
|
|
1049
1221
|
if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
|
|
1050
|
-
const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
|
|
1051
|
-
? prop['maximum'] - 1
|
|
1052
|
-
: prop['maximum'];
|
|
1222
|
+
const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean' ? prop['maximum'] - 1 : prop['maximum'];
|
|
1053
1223
|
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte')), undefined, [ts.factory.createNumericLiteral(String(maxValue))]);
|
|
1054
1224
|
}
|
|
1055
1225
|
if (typeof prop['multipleOf'] === 'number') {
|
|
1056
1226
|
const refineFunction = ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createIdentifier('val'), ts.factory.createToken(ts.SyntaxKind.PercentToken), ts.factory.createNumericLiteral(String(prop['multipleOf']))), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createNumericLiteral('0')));
|
|
1057
1227
|
const refineOptions = ts.factory.createObjectLiteralExpression([
|
|
1058
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`))
|
|
1228
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`))
|
|
1229
|
+
]);
|
|
1230
|
+
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [
|
|
1231
|
+
refineFunction,
|
|
1232
|
+
refineOptions
|
|
1059
1233
|
]);
|
|
1060
|
-
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [refineFunction, refineOptions]);
|
|
1061
1234
|
}
|
|
1062
1235
|
return required
|
|
1063
1236
|
? numberSchema
|
|
@@ -1067,23 +1240,22 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1067
1240
|
let numberSchema = this.buildZodAST(['number']);
|
|
1068
1241
|
// Apply number constraints
|
|
1069
1242
|
if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
|
|
1070
|
-
const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
|
|
1071
|
-
? prop['minimum'] + 1
|
|
1072
|
-
: prop['minimum'];
|
|
1243
|
+
const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? prop['minimum'] + 1 : prop['minimum'];
|
|
1073
1244
|
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte')), undefined, [ts.factory.createNumericLiteral(String(minValue))]);
|
|
1074
1245
|
}
|
|
1075
1246
|
if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
|
|
1076
|
-
const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
|
|
1077
|
-
? prop['maximum'] - 1
|
|
1078
|
-
: prop['maximum'];
|
|
1247
|
+
const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean' ? prop['maximum'] - 1 : prop['maximum'];
|
|
1079
1248
|
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte')), undefined, [ts.factory.createNumericLiteral(String(maxValue))]);
|
|
1080
1249
|
}
|
|
1081
1250
|
if (typeof prop['multipleOf'] === 'number') {
|
|
1082
1251
|
const refineFunction = ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createIdentifier('val'), ts.factory.createToken(ts.SyntaxKind.PercentToken), ts.factory.createNumericLiteral(String(prop['multipleOf']))), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createNumericLiteral('0')));
|
|
1083
1252
|
const refineOptions = ts.factory.createObjectLiteralExpression([
|
|
1084
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`))
|
|
1253
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`))
|
|
1254
|
+
]);
|
|
1255
|
+
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [
|
|
1256
|
+
refineFunction,
|
|
1257
|
+
refineOptions
|
|
1085
1258
|
]);
|
|
1086
|
-
numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [refineFunction, refineOptions]);
|
|
1087
1259
|
}
|
|
1088
1260
|
return required
|
|
1089
1261
|
? numberSchema
|
|
@@ -1118,22 +1290,26 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1118
1290
|
}
|
|
1119
1291
|
// Apply string constraints
|
|
1120
1292
|
if (typeof prop['minLength'] === 'number') {
|
|
1121
|
-
stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('min')), undefined, [
|
|
1293
|
+
stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('min')), undefined, [
|
|
1294
|
+
ts.factory.createNumericLiteral(String(prop['minLength']))
|
|
1295
|
+
]);
|
|
1122
1296
|
}
|
|
1123
1297
|
if (typeof prop['maxLength'] === 'number') {
|
|
1124
|
-
stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('max')), undefined, [
|
|
1298
|
+
stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('max')), undefined, [
|
|
1299
|
+
ts.factory.createNumericLiteral(String(prop['maxLength']))
|
|
1300
|
+
]);
|
|
1125
1301
|
}
|
|
1126
1302
|
if (prop['pattern'] && typeof prop['pattern'] === 'string') {
|
|
1127
1303
|
stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('regex')), undefined, [
|
|
1128
|
-
ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
|
|
1129
|
-
ts.factory.createStringLiteral(prop['pattern'], true),
|
|
1130
|
-
]),
|
|
1304
|
+
ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [ts.factory.createStringLiteral(prop['pattern'], true)])
|
|
1131
1305
|
]);
|
|
1132
1306
|
}
|
|
1133
1307
|
// Apply default value if not required
|
|
1134
1308
|
if (!required && prop['default'] !== undefined) {
|
|
1135
1309
|
const defaultValue = this.buildDefaultValue(prop['default']);
|
|
1136
|
-
stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('default')), undefined, [
|
|
1310
|
+
stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('default')), undefined, [
|
|
1311
|
+
defaultValue
|
|
1312
|
+
]);
|
|
1137
1313
|
}
|
|
1138
1314
|
return required
|
|
1139
1315
|
? stringSchema
|
|
@@ -1143,12 +1319,10 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1143
1319
|
let booleanSchema = this.buildZodAST(['boolean']);
|
|
1144
1320
|
// Apply default value if not required
|
|
1145
1321
|
if (!required && prop['default'] !== undefined) {
|
|
1146
|
-
const defaultValue = typeof prop['default'] === 'boolean'
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
: ts.factory.createFalse();
|
|
1151
|
-
booleanSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('default')), undefined, [defaultValue]);
|
|
1322
|
+
const defaultValue = typeof prop['default'] === 'boolean' ? (prop['default'] ? ts.factory.createTrue() : ts.factory.createFalse()) : ts.factory.createFalse();
|
|
1323
|
+
booleanSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('default')), undefined, [
|
|
1324
|
+
defaultValue
|
|
1325
|
+
]);
|
|
1152
1326
|
}
|
|
1153
1327
|
return required
|
|
1154
1328
|
? booleanSchema
|
|
@@ -1200,7 +1374,9 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1200
1374
|
case 'anyOf':
|
|
1201
1375
|
case 'oneOf': {
|
|
1202
1376
|
const unionSchemas = schemas.map((schema) => this.buildSchemaFromLogicalOperator(schema));
|
|
1203
|
-
return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('union')), undefined, [
|
|
1377
|
+
return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('union')), undefined, [
|
|
1378
|
+
ts.factory.createArrayLiteralExpression(unionSchemas, false)
|
|
1379
|
+
]);
|
|
1204
1380
|
}
|
|
1205
1381
|
case 'allOf': {
|
|
1206
1382
|
if (schemas.length === 0) {
|
|
@@ -1215,10 +1391,12 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1215
1391
|
case 'not': {
|
|
1216
1392
|
const notSchema = this.buildSchemaFromLogicalOperator(schemas[0]);
|
|
1217
1393
|
return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('any')), ts.factory.createIdentifier('refine')), undefined, [
|
|
1218
|
-
ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(notSchema, ts.factory.createIdentifier('safeParse')), undefined, [
|
|
1394
|
+
ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(notSchema, ts.factory.createIdentifier('safeParse')), undefined, [
|
|
1395
|
+
ts.factory.createIdentifier('val')
|
|
1396
|
+
]))),
|
|
1219
1397
|
ts.factory.createObjectLiteralExpression([
|
|
1220
|
-
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral('Value must not match the excluded schema'))
|
|
1221
|
-
])
|
|
1398
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral('Value must not match the excluded schema'))
|
|
1399
|
+
])
|
|
1222
1400
|
]);
|
|
1223
1401
|
}
|
|
1224
1402
|
default:
|
|
@@ -1267,16 +1445,16 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1267
1445
|
args: [
|
|
1268
1446
|
ts.factory.createObjectLiteralExpression(properties.map(([name, property]) => {
|
|
1269
1447
|
return ts.factory.createPropertyAssignment(ts.factory.createIdentifier(name), this.buildSchemaFromLogicalOperator(property));
|
|
1270
|
-
}), true)
|
|
1271
|
-
]
|
|
1272
|
-
}
|
|
1448
|
+
}), true)
|
|
1449
|
+
]
|
|
1450
|
+
}
|
|
1273
1451
|
]);
|
|
1274
1452
|
}
|
|
1275
1453
|
return this.buildZodAST([
|
|
1276
1454
|
{
|
|
1277
1455
|
type: 'record',
|
|
1278
|
-
args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])]
|
|
1279
|
-
}
|
|
1456
|
+
args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])]
|
|
1457
|
+
}
|
|
1280
1458
|
]);
|
|
1281
1459
|
}
|
|
1282
1460
|
buildArrayTypeFromSchema(schemaObj) {
|
|
@@ -1284,8 +1462,8 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1284
1462
|
return this.buildZodAST([
|
|
1285
1463
|
{
|
|
1286
1464
|
type: 'array',
|
|
1287
|
-
args: [this.buildSchemaFromLogicalOperator(schemaObj.items)]
|
|
1288
|
-
}
|
|
1465
|
+
args: [this.buildSchemaFromLogicalOperator(schemaObj.items)]
|
|
1466
|
+
}
|
|
1289
1467
|
]);
|
|
1290
1468
|
}
|
|
1291
1469
|
return this.buildZodAST(['array']);
|
|
@@ -1305,7 +1483,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1305
1483
|
if (this.isCircularReference(refName)) {
|
|
1306
1484
|
// Generate: z.lazy(() => RefSchema)
|
|
1307
1485
|
return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('lazy')), undefined, [
|
|
1308
|
-
ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createIdentifier(sanitizedRefName))
|
|
1486
|
+
ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createIdentifier(sanitizedRefName))
|
|
1309
1487
|
]);
|
|
1310
1488
|
}
|
|
1311
1489
|
return ts.factory.createIdentifier(sanitizedRefName);
|
|
@@ -1323,9 +1501,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1323
1501
|
}
|
|
1324
1502
|
// Case 2: Reference to a schema that's part of a circular dependency chain
|
|
1325
1503
|
// and we're currently building a schema that's also in that chain
|
|
1326
|
-
if (this.circularSchemas.has(refName) &&
|
|
1327
|
-
this.currentSchemaName !== null &&
|
|
1328
|
-
this.circularSchemas.has(this.currentSchemaName)) {
|
|
1504
|
+
if (this.circularSchemas.has(refName) && this.currentSchemaName !== null && this.circularSchemas.has(this.currentSchemaName)) {
|
|
1329
1505
|
return true;
|
|
1330
1506
|
}
|
|
1331
1507
|
return false;
|
|
@@ -1340,11 +1516,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1340
1516
|
// Build dependency graph
|
|
1341
1517
|
const graph = new Map();
|
|
1342
1518
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
1343
|
-
const dependencies =
|
|
1344
|
-
.query(schema, '$..["$ref"]')
|
|
1345
|
-
.filter((ref) => ref.startsWith('#/components/schemas/'))
|
|
1346
|
-
.map((ref) => ref.replace('#/components/schemas/', ''))
|
|
1347
|
-
.filter((dep) => dep in schemas);
|
|
1519
|
+
const dependencies = this.extractSchemaReferences(schema).filter((dep) => dep in schemas);
|
|
1348
1520
|
graph.set(name, dependencies);
|
|
1349
1521
|
}
|
|
1350
1522
|
// Tarjan's algorithm for finding SCCs
|
|
@@ -1422,10 +1594,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
1422
1594
|
}
|
|
1423
1595
|
visiting.add(name);
|
|
1424
1596
|
const schema = schemas[name];
|
|
1425
|
-
const dependencies =
|
|
1426
|
-
.query(schema, '$..["$ref"]')
|
|
1427
|
-
.filter((ref) => ref.startsWith('#/components/schemas/'))
|
|
1428
|
-
.map((ref) => ref.replace('#/components/schemas/', ''));
|
|
1597
|
+
const dependencies = this.extractSchemaReferences(schema);
|
|
1429
1598
|
for (const dep of dependencies) {
|
|
1430
1599
|
if (schemas[dep]) {
|
|
1431
1600
|
visit(dep);
|