@sdk-it/typescript 0.7.0 → 0.8.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/dist/index.js +507 -320
- package/dist/index.js.map +4 -4
- package/dist/lib/emitters/interface.d.ts +43 -0
- package/dist/lib/emitters/interface.d.ts.map +1 -0
- package/dist/lib/emitters/json-zod.d.ts +10 -0
- package/dist/lib/emitters/json-zod.d.ts.map +1 -0
- package/dist/lib/emitters/zod.d.ts +43 -0
- package/dist/lib/emitters/zod.d.ts.map +1 -0
- package/dist/lib/generate.d.ts.map +1 -1
- package/dist/lib/generator.d.ts.map +1 -1
- package/dist/lib/interface.d.ts +2 -0
- package/dist/lib/interface.d.ts.map +1 -0
- package/dist/lib/readme-generator.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +7 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { npmRunPathEnv } from "npm-run-path";
|
|
|
6
6
|
import ts, { TypeFlags, symbolName } from "typescript";
|
|
7
7
|
import debug from "debug";
|
|
8
8
|
import ts2 from "typescript";
|
|
9
|
+
import "lodash-es";
|
|
9
10
|
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
10
11
|
import { dirname as dirname2, isAbsolute, join as join2 } from "node:path";
|
|
11
12
|
var deriveSymbol = Symbol.for("serialize");
|
|
@@ -45,7 +46,7 @@ async function getFolderExports(folder, extensions = ["ts"]) {
|
|
|
45
46
|
const exports = [];
|
|
46
47
|
for (const file of files) {
|
|
47
48
|
if (file.isDirectory()) {
|
|
48
|
-
exports.push(`export * from './${file.name}';`);
|
|
49
|
+
exports.push(`export * from './${file.name}/index.ts';`);
|
|
49
50
|
} else if (file.name !== "index.ts" && extensions.includes(getExt(file.name))) {
|
|
50
51
|
exports.push(`export * from './${file.name}';`);
|
|
51
52
|
}
|
|
@@ -77,259 +78,8 @@ function toLitObject(obj, accessor = (value) => value) {
|
|
|
77
78
|
import { get as get2, merge } from "lodash-es";
|
|
78
79
|
import { camelcase as camelcase2, pascalcase as pascalcase2, spinalcase as spinalcase2 } from "stringcase";
|
|
79
80
|
|
|
80
|
-
// packages/typescript/src/lib/
|
|
81
|
+
// packages/typescript/src/lib/utils.ts
|
|
81
82
|
import { get } from "lodash-es";
|
|
82
|
-
function cleanRef(ref) {
|
|
83
|
-
return ref.replace(/^#\//, "");
|
|
84
|
-
}
|
|
85
|
-
function parseRef(ref) {
|
|
86
|
-
const parts = ref.split(ref);
|
|
87
|
-
const [model] = parts.splice(-1);
|
|
88
|
-
return { model, path: parts.join("/") };
|
|
89
|
-
}
|
|
90
|
-
function followRef(spec, ref) {
|
|
91
|
-
const pathParts = cleanRef(ref).split("/");
|
|
92
|
-
const entry = get(spec, pathParts);
|
|
93
|
-
if (entry && "$ref" in entry) {
|
|
94
|
-
return followRef(spec, entry.$ref);
|
|
95
|
-
}
|
|
96
|
-
return entry;
|
|
97
|
-
}
|
|
98
|
-
function jsonSchemaToZod(spec, schema, required = false, onRef, circularRefTracker = /* @__PURE__ */ new Set()) {
|
|
99
|
-
if ("$ref" in schema) {
|
|
100
|
-
const schemaName = cleanRef(schema.$ref).split("/").pop();
|
|
101
|
-
if (circularRefTracker.has(schemaName)) {
|
|
102
|
-
return schemaName;
|
|
103
|
-
}
|
|
104
|
-
circularRefTracker.add(schemaName);
|
|
105
|
-
onRef(
|
|
106
|
-
schemaName,
|
|
107
|
-
jsonSchemaToZod(
|
|
108
|
-
spec,
|
|
109
|
-
followRef(spec, schema.$ref),
|
|
110
|
-
required,
|
|
111
|
-
onRef,
|
|
112
|
-
circularRefTracker
|
|
113
|
-
)
|
|
114
|
-
);
|
|
115
|
-
circularRefTracker.delete(schemaName);
|
|
116
|
-
return schemaName;
|
|
117
|
-
}
|
|
118
|
-
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
119
|
-
const allOfSchemas = schema.allOf.map(
|
|
120
|
-
(sub) => jsonSchemaToZod(spec, sub, true, onRef, circularRefTracker)
|
|
121
|
-
);
|
|
122
|
-
return allOfSchemas.length ? `z.intersection(${allOfSchemas.join(", ")})` : allOfSchemas[0];
|
|
123
|
-
}
|
|
124
|
-
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
125
|
-
const anyOfSchemas = schema.anyOf.map(
|
|
126
|
-
(sub) => jsonSchemaToZod(spec, sub, false, onRef, circularRefTracker)
|
|
127
|
-
);
|
|
128
|
-
return anyOfSchemas.length > 1 ? `z.union([${anyOfSchemas.join(", ")}])${appendOptional(required)}` : (
|
|
129
|
-
// Handle an invalid anyOf with one schema
|
|
130
|
-
anyOfSchemas[0]
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
134
|
-
const oneOfSchemas = schema.oneOf.map((sub) => {
|
|
135
|
-
if ("$ref" in sub) {
|
|
136
|
-
const { model } = parseRef(sub.$ref);
|
|
137
|
-
if (circularRefTracker.has(model)) {
|
|
138
|
-
return model;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return jsonSchemaToZod(spec, sub, false, onRef, circularRefTracker);
|
|
142
|
-
});
|
|
143
|
-
return oneOfSchemas.length > 1 ? `z.union([${oneOfSchemas.join(", ")}])${appendOptional(required)}` : (
|
|
144
|
-
// Handle an invalid oneOf with one schema
|
|
145
|
-
oneOfSchemas[0]
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
if (schema.enum && Array.isArray(schema.enum)) {
|
|
149
|
-
const enumVals = schema.enum.map((val) => JSON.stringify(val)).join(", ");
|
|
150
|
-
return `z.enum([${enumVals}])${appendOptional(required)}`;
|
|
151
|
-
}
|
|
152
|
-
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
153
|
-
if (!types.length) {
|
|
154
|
-
return `z.unknown()${appendOptional(required)}`;
|
|
155
|
-
}
|
|
156
|
-
if (types.length > 1) {
|
|
157
|
-
const realTypes = types.filter((t) => t !== "null");
|
|
158
|
-
if (realTypes.length === 1 && types.includes("null")) {
|
|
159
|
-
const typeZod = basicTypeToZod(
|
|
160
|
-
realTypes[0],
|
|
161
|
-
schema,
|
|
162
|
-
spec,
|
|
163
|
-
false,
|
|
164
|
-
onRef,
|
|
165
|
-
circularRefTracker
|
|
166
|
-
);
|
|
167
|
-
return `${typeZod}.nullable()${appendOptional(required)}`;
|
|
168
|
-
}
|
|
169
|
-
const subSchemas = types.map(
|
|
170
|
-
(t) => basicTypeToZod(t, schema, spec, false, onRef, circularRefTracker)
|
|
171
|
-
);
|
|
172
|
-
return `z.union([${subSchemas.join(", ")}])${appendOptional(required)}`;
|
|
173
|
-
}
|
|
174
|
-
return basicTypeToZod(
|
|
175
|
-
types[0],
|
|
176
|
-
schema,
|
|
177
|
-
spec,
|
|
178
|
-
required,
|
|
179
|
-
onRef,
|
|
180
|
-
circularRefTracker
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
function basicTypeToZod(type, schema, spec, required = false, onRef, refProcessingStack) {
|
|
184
|
-
switch (type) {
|
|
185
|
-
case "string":
|
|
186
|
-
return handleString(schema, required);
|
|
187
|
-
case "number":
|
|
188
|
-
case "integer":
|
|
189
|
-
return handleNumber(schema, required);
|
|
190
|
-
case "boolean":
|
|
191
|
-
return `z.boolean()${appendDefault(schema.default)}${appendOptional(required)}`;
|
|
192
|
-
case "object":
|
|
193
|
-
return handleObject(schema, spec, required, onRef, refProcessingStack);
|
|
194
|
-
case "array":
|
|
195
|
-
return handleArray(schema, spec, required, onRef, refProcessingStack);
|
|
196
|
-
case "null":
|
|
197
|
-
return `z.null()${appendOptional(required)}`;
|
|
198
|
-
default:
|
|
199
|
-
return `z.unknown()${appendOptional(required)}`;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function handleString(schema, required) {
|
|
203
|
-
let base = "z.string()";
|
|
204
|
-
switch (schema.format) {
|
|
205
|
-
case "date-time":
|
|
206
|
-
case "datetime":
|
|
207
|
-
base = "z.coerce.date()";
|
|
208
|
-
break;
|
|
209
|
-
case "date":
|
|
210
|
-
base = "z.coerce.date() /* or z.string() if you want raw date strings */";
|
|
211
|
-
break;
|
|
212
|
-
case "time":
|
|
213
|
-
base = "z.string() /* optionally add .regex(...) for HH:MM:SS format */";
|
|
214
|
-
break;
|
|
215
|
-
case "email":
|
|
216
|
-
base = "z.string().email()";
|
|
217
|
-
break;
|
|
218
|
-
case "uuid":
|
|
219
|
-
base = "z.string().uuid()";
|
|
220
|
-
break;
|
|
221
|
-
case "url":
|
|
222
|
-
case "uri":
|
|
223
|
-
base = "z.string().url()";
|
|
224
|
-
break;
|
|
225
|
-
case "ipv4":
|
|
226
|
-
base = 'z.string().ip({version: "v4"})';
|
|
227
|
-
break;
|
|
228
|
-
case "ipv6":
|
|
229
|
-
base = 'z.string().ip({version: "v6"})';
|
|
230
|
-
break;
|
|
231
|
-
case "phone":
|
|
232
|
-
base = "z.string() /* or add .regex(...) for phone formats */";
|
|
233
|
-
break;
|
|
234
|
-
case "byte":
|
|
235
|
-
case "binary":
|
|
236
|
-
base = "z.instanceof(Blob) /* consider base64 check if needed */";
|
|
237
|
-
break;
|
|
238
|
-
case "int64":
|
|
239
|
-
base = "z.string() /* or z.bigint() if your app can handle it */";
|
|
240
|
-
break;
|
|
241
|
-
default:
|
|
242
|
-
break;
|
|
243
|
-
}
|
|
244
|
-
return `${base}${appendDefault(schema.default)}${appendOptional(required)}`;
|
|
245
|
-
}
|
|
246
|
-
function handleNumber(schema, required) {
|
|
247
|
-
let defaultValue = schema.default !== void 0 ? `.default(${schema.default})` : ``;
|
|
248
|
-
let base = "z.number()";
|
|
249
|
-
if (schema.format === "int64") {
|
|
250
|
-
base = "z.bigint()";
|
|
251
|
-
if (schema.default !== void 0) {
|
|
252
|
-
defaultValue = `.default(BigInt(${schema.default}))`;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (schema.format === "int32") {
|
|
256
|
-
base += ".int()";
|
|
257
|
-
}
|
|
258
|
-
if (typeof schema.exclusiveMinimum === "number") {
|
|
259
|
-
base += `.gt(${schema.exclusiveMinimum})`;
|
|
260
|
-
}
|
|
261
|
-
if (typeof schema.exclusiveMaximum === "number") {
|
|
262
|
-
base += `.lt(${schema.exclusiveMaximum})`;
|
|
263
|
-
}
|
|
264
|
-
if (typeof schema.minimum === "number") {
|
|
265
|
-
base += schema.format === "int64" ? `.min(BigInt(${schema.minimum}))` : `.min(${schema.minimum})`;
|
|
266
|
-
}
|
|
267
|
-
if (typeof schema.maximum === "number") {
|
|
268
|
-
base += schema.format === "int64" ? `.max(BigInt(${schema.maximum}))` : `.max(${schema.maximum})`;
|
|
269
|
-
}
|
|
270
|
-
if (typeof schema.multipleOf === "number") {
|
|
271
|
-
base += `.refine((val) => Number.isInteger(val / ${schema.multipleOf}), "Must be a multiple of ${schema.multipleOf}")`;
|
|
272
|
-
}
|
|
273
|
-
return `${base}${defaultValue}${appendOptional(required)}`;
|
|
274
|
-
}
|
|
275
|
-
function handleObject(schema, spec, required = false, onRef, refProcessingStack) {
|
|
276
|
-
const properties = schema.properties || {};
|
|
277
|
-
const propEntries = Object.entries(properties).map(([key, propSchema]) => {
|
|
278
|
-
const isRequired = (schema.required ?? []).includes(key);
|
|
279
|
-
const zodPart = jsonSchemaToZod(
|
|
280
|
-
spec,
|
|
281
|
-
propSchema,
|
|
282
|
-
isRequired,
|
|
283
|
-
onRef,
|
|
284
|
-
refProcessingStack
|
|
285
|
-
);
|
|
286
|
-
return `'${key}': ${zodPart}`;
|
|
287
|
-
});
|
|
288
|
-
let additionalProps = "";
|
|
289
|
-
if (schema.additionalProperties) {
|
|
290
|
-
if (typeof schema.additionalProperties === "object") {
|
|
291
|
-
const addPropZod = jsonSchemaToZod(
|
|
292
|
-
spec,
|
|
293
|
-
schema.additionalProperties,
|
|
294
|
-
true,
|
|
295
|
-
onRef,
|
|
296
|
-
refProcessingStack
|
|
297
|
-
);
|
|
298
|
-
additionalProps = `.catchall(${addPropZod})`;
|
|
299
|
-
} else if (schema.additionalProperties === true) {
|
|
300
|
-
additionalProps = `.catchall(z.unknown())`;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
const objectSchema = `z.object({${propEntries.join(", ")}})${additionalProps}`;
|
|
304
|
-
return `${objectSchema}${appendOptional(required)}`;
|
|
305
|
-
}
|
|
306
|
-
function handleArray(schema, spec, required = false, onRef, refProcessingStack) {
|
|
307
|
-
const { items } = schema;
|
|
308
|
-
if (!items) {
|
|
309
|
-
return `z.array(z.unknown())${appendOptional(required)}`;
|
|
310
|
-
}
|
|
311
|
-
if (Array.isArray(items)) {
|
|
312
|
-
const tupleItems = items.map(
|
|
313
|
-
(sub) => jsonSchemaToZod(spec, sub, true, onRef, refProcessingStack)
|
|
314
|
-
);
|
|
315
|
-
const base = `z.tuple([${tupleItems.join(", ")}])`;
|
|
316
|
-
return `${base}${appendOptional(required)}`;
|
|
317
|
-
}
|
|
318
|
-
const itemsSchema = jsonSchemaToZod(
|
|
319
|
-
spec,
|
|
320
|
-
items,
|
|
321
|
-
true,
|
|
322
|
-
onRef,
|
|
323
|
-
refProcessingStack
|
|
324
|
-
);
|
|
325
|
-
return `z.array(${itemsSchema})${appendOptional(required)}`;
|
|
326
|
-
}
|
|
327
|
-
function appendOptional(isRequired) {
|
|
328
|
-
return isRequired ? "" : ".optional()";
|
|
329
|
-
}
|
|
330
|
-
function appendDefault(defaultValue) {
|
|
331
|
-
return defaultValue !== void 0 ? `.default(${JSON.stringify(defaultValue)})` : "";
|
|
332
|
-
}
|
|
333
83
|
|
|
334
84
|
// packages/typescript/src/lib/sdk.ts
|
|
335
85
|
import { camelcase, pascalcase, spinalcase } from "stringcase";
|
|
@@ -506,7 +256,7 @@ function generateClientSdk(spec) {
|
|
|
506
256
|
const input = `z.infer<typeof ${schemaRef}>`;
|
|
507
257
|
const endpoint = `${operation.trigger.method.toUpperCase()} ${operation.trigger.path}`;
|
|
508
258
|
streamEmitter.addImport(
|
|
509
|
-
`import type {${pascalcase(operation.name)}} from './outputs/${spinalcase(operation.name)}';`
|
|
259
|
+
`import type {${pascalcase(operation.name)}} from './outputs/${spinalcase(operation.name)}.ts';`
|
|
510
260
|
);
|
|
511
261
|
streamEmitter.addEndpoint(
|
|
512
262
|
endpoint,
|
|
@@ -529,7 +279,7 @@ function generateClientSdk(spec) {
|
|
|
529
279
|
);
|
|
530
280
|
} else {
|
|
531
281
|
emitter.addImport(
|
|
532
|
-
`import type {${output.import}} from './outputs/${spinalcase(operation.name)}';`
|
|
282
|
+
`import type {${output.import}} from './outputs/${spinalcase(operation.name)}.ts';`
|
|
533
283
|
);
|
|
534
284
|
errors.push(...operation.errors ?? []);
|
|
535
285
|
const addTypeParser = Object.keys(operation.schemas).length > 1;
|
|
@@ -571,7 +321,9 @@ function generateClientSdk(spec) {
|
|
|
571
321
|
Object.entries(schemas).map(([key, value]) => [
|
|
572
322
|
`inputs/${key}.ts`,
|
|
573
323
|
[
|
|
574
|
-
schemasImports.length
|
|
324
|
+
// schemasImports.length
|
|
325
|
+
// ? `import {${removeDuplicates(schemasImports, (it) => it)}} from '../zod';`
|
|
326
|
+
// : '',
|
|
575
327
|
spec.commonZod ? 'import * as commonZod from "../zod.ts";' : "",
|
|
576
328
|
...value
|
|
577
329
|
].map((it) => it.trim()).filter(Boolean).join("\n") + "\n"
|
|
@@ -588,6 +340,22 @@ function generateClientSdk(spec) {
|
|
|
588
340
|
function isRef(obj) {
|
|
589
341
|
return "$ref" in obj;
|
|
590
342
|
}
|
|
343
|
+
function cleanRef(ref) {
|
|
344
|
+
return ref.replace(/^#\//, "");
|
|
345
|
+
}
|
|
346
|
+
function parseRef(ref) {
|
|
347
|
+
const parts = ref.split("/");
|
|
348
|
+
const [model] = parts.splice(-1);
|
|
349
|
+
return { model, path: parts.join("/") };
|
|
350
|
+
}
|
|
351
|
+
function followRef(spec, ref) {
|
|
352
|
+
const pathParts = cleanRef(ref).split("/");
|
|
353
|
+
const entry = get(spec, pathParts);
|
|
354
|
+
if (entry && "$ref" in entry) {
|
|
355
|
+
return followRef(spec, entry.$ref);
|
|
356
|
+
}
|
|
357
|
+
return entry;
|
|
358
|
+
}
|
|
591
359
|
function securityToOptions(security2, securitySchemas, staticIn) {
|
|
592
360
|
securitySchemas ??= {};
|
|
593
361
|
const options = {};
|
|
@@ -622,6 +390,418 @@ function securityToOptions(security2, securitySchemas, staticIn) {
|
|
|
622
390
|
return options;
|
|
623
391
|
}
|
|
624
392
|
|
|
393
|
+
// packages/typescript/src/lib/emitters/interface.ts
|
|
394
|
+
var TypeScriptDeserialzer = class {
|
|
395
|
+
circularRefTracker = /* @__PURE__ */ new Set();
|
|
396
|
+
#spec;
|
|
397
|
+
#onRef;
|
|
398
|
+
constructor(spec, onRef) {
|
|
399
|
+
this.#spec = spec;
|
|
400
|
+
this.#onRef = onRef;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Handle objects (properties)
|
|
404
|
+
*/
|
|
405
|
+
object(schema, required = false) {
|
|
406
|
+
const properties = schema.properties || {};
|
|
407
|
+
const propEntries = Object.entries(properties).map(([key, propSchema]) => {
|
|
408
|
+
const isRequired = (schema.required ?? []).includes(key);
|
|
409
|
+
const tsType = this.handle(propSchema, isRequired);
|
|
410
|
+
return `${key}: ${tsType}`;
|
|
411
|
+
});
|
|
412
|
+
if (schema.additionalProperties) {
|
|
413
|
+
if (typeof schema.additionalProperties === "object") {
|
|
414
|
+
const indexType = this.handle(schema.additionalProperties, true);
|
|
415
|
+
propEntries.push(`[key: string]: ${indexType}`);
|
|
416
|
+
} else if (schema.additionalProperties === true) {
|
|
417
|
+
propEntries.push("[key: string]: any");
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return `{ ${propEntries.join("; ")} }`;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Handle arrays (items could be a single schema or a tuple)
|
|
424
|
+
*/
|
|
425
|
+
array(schema, required = false) {
|
|
426
|
+
const { items } = schema;
|
|
427
|
+
if (!items) {
|
|
428
|
+
return "any[]";
|
|
429
|
+
}
|
|
430
|
+
if (Array.isArray(items)) {
|
|
431
|
+
const tupleItems = items.map((sub) => this.handle(sub, true));
|
|
432
|
+
return `[${tupleItems.join(", ")}]`;
|
|
433
|
+
}
|
|
434
|
+
const itemsType = this.handle(items, true);
|
|
435
|
+
return `${itemsType}[]`;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Convert a basic type (string | number | boolean | object | array, etc.) to TypeScript
|
|
439
|
+
*/
|
|
440
|
+
normal(type, schema, required = false) {
|
|
441
|
+
switch (type) {
|
|
442
|
+
case "string":
|
|
443
|
+
return this.string(schema, required);
|
|
444
|
+
case "number":
|
|
445
|
+
case "integer":
|
|
446
|
+
return this.number(schema, required);
|
|
447
|
+
case "boolean":
|
|
448
|
+
return appendOptional("boolean", required);
|
|
449
|
+
case "object":
|
|
450
|
+
return this.object(schema, required);
|
|
451
|
+
case "array":
|
|
452
|
+
return this.array(schema, required);
|
|
453
|
+
case "null":
|
|
454
|
+
return "null";
|
|
455
|
+
default:
|
|
456
|
+
return appendOptional("any", required);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
ref($ref, required) {
|
|
460
|
+
const schemaName = cleanRef($ref).split("/").pop();
|
|
461
|
+
if (this.circularRefTracker.has(schemaName)) {
|
|
462
|
+
return schemaName;
|
|
463
|
+
}
|
|
464
|
+
this.circularRefTracker.add(schemaName);
|
|
465
|
+
this.#onRef(schemaName, this.handle(followRef(this.#spec, $ref), true));
|
|
466
|
+
this.circularRefTracker.delete(schemaName);
|
|
467
|
+
return appendOptional(schemaName, required);
|
|
468
|
+
}
|
|
469
|
+
allOf(schemas) {
|
|
470
|
+
const allOfTypes = schemas.map((sub) => this.handle(sub, true));
|
|
471
|
+
return allOfTypes.length > 1 ? `${allOfTypes.join(" & ")}` : allOfTypes[0];
|
|
472
|
+
}
|
|
473
|
+
anyOf(schemas, required) {
|
|
474
|
+
const anyOfTypes = schemas.map((sub) => this.handle(sub, true));
|
|
475
|
+
return appendOptional(
|
|
476
|
+
anyOfTypes.length > 1 ? `${anyOfTypes.join(" | ")}` : anyOfTypes[0],
|
|
477
|
+
required
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
oneOf(schemas, required) {
|
|
481
|
+
const oneOfTypes = schemas.map((sub) => {
|
|
482
|
+
if (isRef(sub)) {
|
|
483
|
+
const { model } = parseRef(sub.$ref);
|
|
484
|
+
if (this.circularRefTracker.has(model)) {
|
|
485
|
+
return model;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return this.handle(sub, false);
|
|
489
|
+
});
|
|
490
|
+
return appendOptional(
|
|
491
|
+
oneOfTypes.length > 1 ? `${oneOfTypes.join(" | ")}` : oneOfTypes[0],
|
|
492
|
+
required
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
enum(values, required) {
|
|
496
|
+
const enumValues = values.map((val) => typeof val === "string" ? `'${val}'` : `${val}`).join(" | ");
|
|
497
|
+
return appendOptional(enumValues, required);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Handle string type with formats
|
|
501
|
+
*/
|
|
502
|
+
string(schema, required) {
|
|
503
|
+
let type;
|
|
504
|
+
switch (schema.format) {
|
|
505
|
+
case "date-time":
|
|
506
|
+
case "datetime":
|
|
507
|
+
case "date":
|
|
508
|
+
type = "Date";
|
|
509
|
+
break;
|
|
510
|
+
case "binary":
|
|
511
|
+
case "byte":
|
|
512
|
+
type = "Blob";
|
|
513
|
+
break;
|
|
514
|
+
case "int64":
|
|
515
|
+
type = "bigint";
|
|
516
|
+
break;
|
|
517
|
+
default:
|
|
518
|
+
type = "string";
|
|
519
|
+
}
|
|
520
|
+
return appendOptional(type, required);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Handle number/integer types with formats
|
|
524
|
+
*/
|
|
525
|
+
number(schema, required) {
|
|
526
|
+
const type = schema.format === "int64" ? "bigint" : "number";
|
|
527
|
+
return appendOptional(type, required);
|
|
528
|
+
}
|
|
529
|
+
handle(schema, required) {
|
|
530
|
+
if (isRef(schema)) {
|
|
531
|
+
return this.ref(schema.$ref, required);
|
|
532
|
+
}
|
|
533
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
534
|
+
return this.allOf(schema.allOf);
|
|
535
|
+
}
|
|
536
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
537
|
+
return this.anyOf(schema.anyOf, required);
|
|
538
|
+
}
|
|
539
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
540
|
+
return this.oneOf(schema.oneOf, required);
|
|
541
|
+
}
|
|
542
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
543
|
+
return this.enum(schema.enum, required);
|
|
544
|
+
}
|
|
545
|
+
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
546
|
+
if (!types.length) {
|
|
547
|
+
return appendOptional("any", required);
|
|
548
|
+
}
|
|
549
|
+
if (types.length > 1) {
|
|
550
|
+
const realTypes = types.filter((t) => t !== "null");
|
|
551
|
+
if (realTypes.length === 1 && types.includes("null")) {
|
|
552
|
+
const tsType = this.normal(realTypes[0], schema, false);
|
|
553
|
+
return appendOptional(`${tsType} | null`, required);
|
|
554
|
+
}
|
|
555
|
+
const typeResults = types.map((t) => this.normal(t, schema, false));
|
|
556
|
+
return appendOptional(typeResults.join(" | "), required);
|
|
557
|
+
}
|
|
558
|
+
return this.normal(types[0], schema, required);
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Generate an interface declaration
|
|
562
|
+
*/
|
|
563
|
+
generateInterface(name, schema) {
|
|
564
|
+
const content = this.handle(schema, true);
|
|
565
|
+
return `interface ${name} ${content}`;
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
function appendOptional(type, isRequired) {
|
|
569
|
+
return isRequired ? type : `${type} | undefined`;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// packages/typescript/src/lib/emitters/zod.ts
|
|
573
|
+
var ZodDeserialzer = class {
|
|
574
|
+
circularRefTracker = /* @__PURE__ */ new Set();
|
|
575
|
+
#spec;
|
|
576
|
+
#onRef;
|
|
577
|
+
constructor(spec, onRef) {
|
|
578
|
+
this.#spec = spec;
|
|
579
|
+
this.#onRef = onRef;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Handle objects (properties, additionalProperties).
|
|
583
|
+
*/
|
|
584
|
+
object(schema, required = false) {
|
|
585
|
+
const properties = schema.properties || {};
|
|
586
|
+
const propEntries = Object.entries(properties).map(([key, propSchema]) => {
|
|
587
|
+
const isRequired = (schema.required ?? []).includes(key);
|
|
588
|
+
const zodPart = this.handle(propSchema, isRequired);
|
|
589
|
+
return `'${key}': ${zodPart}`;
|
|
590
|
+
});
|
|
591
|
+
let additionalProps = "";
|
|
592
|
+
if (schema.additionalProperties) {
|
|
593
|
+
if (typeof schema.additionalProperties === "object") {
|
|
594
|
+
const addPropZod = this.handle(schema.additionalProperties, true);
|
|
595
|
+
additionalProps = `.catchall(${addPropZod})`;
|
|
596
|
+
} else if (schema.additionalProperties === true) {
|
|
597
|
+
additionalProps = `.catchall(z.unknown())`;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const objectSchema = `z.object({${propEntries.join(", ")}})${additionalProps}`;
|
|
601
|
+
return `${objectSchema}${appendOptional2(required)}`;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Handle arrays (items could be a single schema or a tuple (array of schemas)).
|
|
605
|
+
* In JSON Schema 2020-12, `items` can be an array → tuple style.
|
|
606
|
+
*/
|
|
607
|
+
array(schema, required = false) {
|
|
608
|
+
const { items } = schema;
|
|
609
|
+
if (!items) {
|
|
610
|
+
return `z.array(z.unknown())${appendOptional2(required)}`;
|
|
611
|
+
}
|
|
612
|
+
if (Array.isArray(items)) {
|
|
613
|
+
const tupleItems = items.map((sub) => this.handle(sub, true));
|
|
614
|
+
const base = `z.tuple([${tupleItems.join(", ")}])`;
|
|
615
|
+
return `${base}${appendOptional2(required)}`;
|
|
616
|
+
}
|
|
617
|
+
const itemsSchema = this.handle(items, true);
|
|
618
|
+
return `z.array(${itemsSchema})${appendOptional2(required)}`;
|
|
619
|
+
}
|
|
620
|
+
// oneOf() {}
|
|
621
|
+
// enum() {}
|
|
622
|
+
/**
|
|
623
|
+
* Convert a basic type (string | number | boolean | object | array, etc.) to Zod.
|
|
624
|
+
* We'll also handle .optional() if needed.
|
|
625
|
+
*/
|
|
626
|
+
normal(type, schema, required = false) {
|
|
627
|
+
switch (type) {
|
|
628
|
+
case "string":
|
|
629
|
+
return this.string(schema, required);
|
|
630
|
+
case "number":
|
|
631
|
+
case "integer":
|
|
632
|
+
return this.number(schema, required);
|
|
633
|
+
case "boolean":
|
|
634
|
+
return `z.boolean()${appendDefault(schema.default)}${appendOptional2(required)}`;
|
|
635
|
+
case "object":
|
|
636
|
+
return this.object(schema, required);
|
|
637
|
+
case "array":
|
|
638
|
+
return this.array(schema, required);
|
|
639
|
+
case "null":
|
|
640
|
+
return `z.null()${appendOptional2(required)}`;
|
|
641
|
+
default:
|
|
642
|
+
return `z.unknown()${appendOptional2(required)}`;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
ref($ref, required) {
|
|
646
|
+
const schemaName = cleanRef($ref).split("/").pop();
|
|
647
|
+
if (this.circularRefTracker.has(schemaName)) {
|
|
648
|
+
return schemaName;
|
|
649
|
+
}
|
|
650
|
+
this.circularRefTracker.add(schemaName);
|
|
651
|
+
this.#onRef(schemaName, this.handle(followRef(this.#spec, $ref), required));
|
|
652
|
+
this.circularRefTracker.delete(schemaName);
|
|
653
|
+
return schemaName;
|
|
654
|
+
}
|
|
655
|
+
allOf(schemas) {
|
|
656
|
+
const allOfSchemas = schemas.map((sub) => this.handle(sub, true));
|
|
657
|
+
return allOfSchemas.length ? `z.intersection(${allOfSchemas.join(", ")})` : allOfSchemas[0];
|
|
658
|
+
}
|
|
659
|
+
anyOf(schemas, required) {
|
|
660
|
+
const anyOfSchemas = schemas.map((sub) => this.handle(sub, false));
|
|
661
|
+
return anyOfSchemas.length > 1 ? `z.union([${anyOfSchemas.join(", ")}])${appendOptional2(required)}` : (
|
|
662
|
+
// Handle an invalid anyOf with one schema
|
|
663
|
+
anyOfSchemas[0]
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
oneOf(schemas, required) {
|
|
667
|
+
const oneOfSchemas = schemas.map((sub) => {
|
|
668
|
+
if ("$ref" in sub) {
|
|
669
|
+
const { model } = parseRef(sub.$ref);
|
|
670
|
+
if (this.circularRefTracker.has(model)) {
|
|
671
|
+
return model;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return this.handle(sub, false);
|
|
675
|
+
});
|
|
676
|
+
return oneOfSchemas.length > 1 ? `z.union([${oneOfSchemas.join(", ")}])${appendOptional2(required)}` : (
|
|
677
|
+
// Handle an invalid oneOf with one schema
|
|
678
|
+
oneOfSchemas[0]
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
enum(values, required) {
|
|
682
|
+
const enumVals = values.map((val) => JSON.stringify(val)).join(", ");
|
|
683
|
+
return `z.enum([${enumVals}])${appendOptional2(required)}`;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Handle a `string` schema with possible format keywords (JSON Schema).
|
|
687
|
+
*/
|
|
688
|
+
string(schema, required) {
|
|
689
|
+
let base = "z.string()";
|
|
690
|
+
switch (schema.format) {
|
|
691
|
+
case "date-time":
|
|
692
|
+
case "datetime":
|
|
693
|
+
base = "z.coerce.date()";
|
|
694
|
+
break;
|
|
695
|
+
case "date":
|
|
696
|
+
base = "z.coerce.date() /* or z.string() if you want raw date strings */";
|
|
697
|
+
break;
|
|
698
|
+
case "time":
|
|
699
|
+
base = "z.string() /* optionally add .regex(...) for HH:MM:SS format */";
|
|
700
|
+
break;
|
|
701
|
+
case "email":
|
|
702
|
+
base = "z.string().email()";
|
|
703
|
+
break;
|
|
704
|
+
case "uuid":
|
|
705
|
+
base = "z.string().uuid()";
|
|
706
|
+
break;
|
|
707
|
+
case "url":
|
|
708
|
+
case "uri":
|
|
709
|
+
base = "z.string().url()";
|
|
710
|
+
break;
|
|
711
|
+
case "ipv4":
|
|
712
|
+
base = 'z.string().ip({version: "v4"})';
|
|
713
|
+
break;
|
|
714
|
+
case "ipv6":
|
|
715
|
+
base = 'z.string().ip({version: "v6"})';
|
|
716
|
+
break;
|
|
717
|
+
case "phone":
|
|
718
|
+
base = "z.string() /* or add .regex(...) for phone formats */";
|
|
719
|
+
break;
|
|
720
|
+
case "byte":
|
|
721
|
+
case "binary":
|
|
722
|
+
base = "z.instanceof(Blob) /* consider base64 check if needed */";
|
|
723
|
+
break;
|
|
724
|
+
case "int64":
|
|
725
|
+
base = "z.string() /* or z.bigint() if your app can handle it */";
|
|
726
|
+
break;
|
|
727
|
+
default:
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
return `${base}${appendDefault(schema.default)}${appendOptional2(required)}`;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Handle number/integer constraints from OpenAPI/JSON Schema.
|
|
734
|
+
* In 3.1, exclusiveMinimum/Maximum hold the actual numeric threshold,
|
|
735
|
+
* rather than a boolean toggling `minimum`/`maximum`.
|
|
736
|
+
*/
|
|
737
|
+
number(schema, required) {
|
|
738
|
+
let defaultValue = schema.default !== void 0 ? `.default(${schema.default})` : ``;
|
|
739
|
+
let base = "z.number()";
|
|
740
|
+
if (schema.format === "int64") {
|
|
741
|
+
base = "z.bigint()";
|
|
742
|
+
if (schema.default !== void 0) {
|
|
743
|
+
defaultValue = `.default(BigInt(${schema.default}))`;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (schema.format === "int32") {
|
|
747
|
+
base += ".int()";
|
|
748
|
+
}
|
|
749
|
+
if (typeof schema.exclusiveMinimum === "number") {
|
|
750
|
+
base += `.gt(${schema.exclusiveMinimum})`;
|
|
751
|
+
}
|
|
752
|
+
if (typeof schema.exclusiveMaximum === "number") {
|
|
753
|
+
base += `.lt(${schema.exclusiveMaximum})`;
|
|
754
|
+
}
|
|
755
|
+
if (typeof schema.minimum === "number") {
|
|
756
|
+
base += schema.format === "int64" ? `.min(BigInt(${schema.minimum}))` : `.min(${schema.minimum})`;
|
|
757
|
+
}
|
|
758
|
+
if (typeof schema.maximum === "number") {
|
|
759
|
+
base += schema.format === "int64" ? `.max(BigInt(${schema.maximum}))` : `.max(${schema.maximum})`;
|
|
760
|
+
}
|
|
761
|
+
if (typeof schema.multipleOf === "number") {
|
|
762
|
+
base += `.refine((val) => Number.isInteger(val / ${schema.multipleOf}), "Must be a multiple of ${schema.multipleOf}")`;
|
|
763
|
+
}
|
|
764
|
+
return `${base}${defaultValue}${appendOptional2(required)}`;
|
|
765
|
+
}
|
|
766
|
+
handle(schema, required) {
|
|
767
|
+
if (isRef(schema)) {
|
|
768
|
+
return this.ref(schema.$ref, required);
|
|
769
|
+
}
|
|
770
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
771
|
+
return this.allOf(schema.allOf ?? []);
|
|
772
|
+
}
|
|
773
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
774
|
+
return this.anyOf(schema.anyOf ?? [], required);
|
|
775
|
+
}
|
|
776
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
777
|
+
return this.oneOf(schema.oneOf ?? [], required);
|
|
778
|
+
}
|
|
779
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
780
|
+
return this.enum(schema.enum, required);
|
|
781
|
+
}
|
|
782
|
+
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
783
|
+
if (!types.length) {
|
|
784
|
+
return `z.unknown()${appendOptional2(required)}`;
|
|
785
|
+
}
|
|
786
|
+
if (types.length > 1) {
|
|
787
|
+
const realTypes = types.filter((t) => t !== "null");
|
|
788
|
+
if (realTypes.length === 1 && types.includes("null")) {
|
|
789
|
+
const typeZod = this.normal(realTypes[0], schema, false);
|
|
790
|
+
return `${typeZod}.nullable()${appendOptional2(required)}`;
|
|
791
|
+
}
|
|
792
|
+
const subSchemas = types.map((t) => this.normal(t, schema, false));
|
|
793
|
+
return `z.union([${subSchemas.join(", ")}])${appendOptional2(required)}`;
|
|
794
|
+
}
|
|
795
|
+
return this.normal(types[0], schema, required);
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
function appendOptional2(isRequired) {
|
|
799
|
+
return isRequired ? "" : ".optional()";
|
|
800
|
+
}
|
|
801
|
+
function appendDefault(defaultValue) {
|
|
802
|
+
return defaultValue !== void 0 ? `.default(${JSON.stringify(defaultValue)})` : "";
|
|
803
|
+
}
|
|
804
|
+
|
|
625
805
|
// packages/typescript/src/lib/generator.ts
|
|
626
806
|
var responses = {
|
|
627
807
|
"400": "BadRequest",
|
|
@@ -653,6 +833,30 @@ var defaults = {
|
|
|
653
833
|
}
|
|
654
834
|
};
|
|
655
835
|
function generateCode(config) {
|
|
836
|
+
const imports = [];
|
|
837
|
+
const zodDeserialzer = new ZodDeserialzer(config.spec, (schemaName, zod) => {
|
|
838
|
+
commonSchemas[schemaName] = zod;
|
|
839
|
+
imports.push({
|
|
840
|
+
defaultImport: void 0,
|
|
841
|
+
isTypeOnly: false,
|
|
842
|
+
moduleSpecifier: `../models/${schemaName}.ts`,
|
|
843
|
+
namedImports: [{ isTypeOnly: false, name: schemaName }],
|
|
844
|
+
namespaceImport: void 0
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
const typeScriptDeserialzer = new TypeScriptDeserialzer(
|
|
848
|
+
config.spec,
|
|
849
|
+
(schemaName, zod) => {
|
|
850
|
+
commonSchemas[schemaName] = zod;
|
|
851
|
+
imports.push({
|
|
852
|
+
defaultImport: void 0,
|
|
853
|
+
isTypeOnly: true,
|
|
854
|
+
moduleSpecifier: `../models/${schemaName}.ts`,
|
|
855
|
+
namedImports: [{ isTypeOnly: true, name: schemaName }],
|
|
856
|
+
namespaceImport: void 0
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
);
|
|
656
860
|
const groups = {};
|
|
657
861
|
const commonSchemas = {};
|
|
658
862
|
const outputs = {};
|
|
@@ -664,7 +868,6 @@ function generateCode(config) {
|
|
|
664
868
|
const groupName = (operation.tags ?? ["unknown"])[0];
|
|
665
869
|
groups[groupName] ??= [];
|
|
666
870
|
const inputs = {};
|
|
667
|
-
const imports = [];
|
|
668
871
|
const additionalProperties = [];
|
|
669
872
|
for (const param of operation.parameters ?? []) {
|
|
670
873
|
if (isRef(param)) {
|
|
@@ -708,8 +911,7 @@ function generateCode(config) {
|
|
|
708
911
|
const content = isRef(operation.requestBody) ? get2(followRef(config.spec, operation.requestBody.$ref), ["content"]) : operation.requestBody.content;
|
|
709
912
|
for (const type in content) {
|
|
710
913
|
const schema = isRef(content[type].schema) ? followRef(config.spec, content[type].schema.$ref) : content[type].schema;
|
|
711
|
-
types[shortContenTypeMap[type]] =
|
|
712
|
-
config.spec,
|
|
914
|
+
types[shortContenTypeMap[type]] = zodDeserialzer.handle(
|
|
713
915
|
merge(schema, {
|
|
714
916
|
required: additionalProperties.filter((p) => p.required).map((p) => p.name),
|
|
715
917
|
properties: additionalProperties.reduce(
|
|
@@ -720,17 +922,7 @@ function generateCode(config) {
|
|
|
720
922
|
{}
|
|
721
923
|
)
|
|
722
924
|
}),
|
|
723
|
-
true
|
|
724
|
-
(schemaName, zod) => {
|
|
725
|
-
commonSchemas[schemaName] = zod;
|
|
726
|
-
imports.push({
|
|
727
|
-
defaultImport: void 0,
|
|
728
|
-
isTypeOnly: false,
|
|
729
|
-
moduleSpecifier: "../zod",
|
|
730
|
-
namedImports: [{ isTypeOnly: false, name: schemaName }],
|
|
731
|
-
namespaceImport: void 0
|
|
732
|
-
});
|
|
733
|
-
}
|
|
925
|
+
true
|
|
734
926
|
);
|
|
735
927
|
}
|
|
736
928
|
if (content["application/json"]) {
|
|
@@ -743,30 +935,20 @@ function generateCode(config) {
|
|
|
743
935
|
contentType = "json";
|
|
744
936
|
}
|
|
745
937
|
} else {
|
|
746
|
-
|
|
747
|
-
|
|
938
|
+
const properties = additionalProperties.reduce(
|
|
939
|
+
(acc, p) => ({
|
|
940
|
+
...acc,
|
|
941
|
+
[p.name]: p.schema
|
|
942
|
+
}),
|
|
943
|
+
{}
|
|
944
|
+
);
|
|
945
|
+
types[shortContenTypeMap["application/json"]] = zodDeserialzer.handle(
|
|
748
946
|
{
|
|
749
947
|
type: "object",
|
|
750
948
|
required: additionalProperties.filter((p) => p.required).map((p) => p.name),
|
|
751
|
-
properties
|
|
752
|
-
(acc, p) => ({
|
|
753
|
-
...acc,
|
|
754
|
-
[p.name]: p.schema
|
|
755
|
-
}),
|
|
756
|
-
{}
|
|
757
|
-
)
|
|
949
|
+
properties
|
|
758
950
|
},
|
|
759
|
-
true
|
|
760
|
-
(schemaName, zod) => {
|
|
761
|
-
commonSchemas[schemaName] = zod;
|
|
762
|
-
imports.push({
|
|
763
|
-
defaultImport: void 0,
|
|
764
|
-
isTypeOnly: false,
|
|
765
|
-
moduleSpecifier: "./zod",
|
|
766
|
-
namedImports: [{ isTypeOnly: false, name: schemaName }],
|
|
767
|
-
namespaceImport: void 0
|
|
768
|
-
});
|
|
769
|
-
}
|
|
951
|
+
true
|
|
770
952
|
);
|
|
771
953
|
}
|
|
772
954
|
const errors = [];
|
|
@@ -783,34 +965,23 @@ function generateCode(config) {
|
|
|
783
965
|
foundResponse = true;
|
|
784
966
|
const responseContent = get2(response, ["content"]);
|
|
785
967
|
const isJson = responseContent && responseContent["application/json"];
|
|
786
|
-
const responseSchema = isJson ?
|
|
787
|
-
config.spec,
|
|
968
|
+
const responseSchema = isJson ? typeScriptDeserialzer.handle(
|
|
788
969
|
responseContent["application/json"].schema,
|
|
789
|
-
true
|
|
790
|
-
|
|
791
|
-
commonSchemas[schemaName] = zod;
|
|
792
|
-
imports.push({
|
|
793
|
-
defaultImport: void 0,
|
|
794
|
-
isTypeOnly: false,
|
|
795
|
-
moduleSpecifier: "../zod",
|
|
796
|
-
namedImports: [{ isTypeOnly: false, name: schemaName }],
|
|
797
|
-
namespaceImport: void 0
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
) : "z.instanceof(ReadableStream)";
|
|
970
|
+
true
|
|
971
|
+
) : "ReadableStream";
|
|
801
972
|
output.push(
|
|
802
973
|
importsToString(mergeImports(Object.values(imports).flat())).join(
|
|
803
974
|
"\n"
|
|
804
975
|
)
|
|
805
976
|
);
|
|
806
977
|
output.push(
|
|
807
|
-
`export
|
|
978
|
+
`export type ${pascalcase2(operationName + " output")} = ${responseSchema}`
|
|
808
979
|
);
|
|
809
980
|
}
|
|
810
981
|
}
|
|
811
982
|
if (!foundResponse) {
|
|
812
983
|
output.push(
|
|
813
|
-
`export
|
|
984
|
+
`export type ${pascalcase2(operationName + " output")} = void`
|
|
814
985
|
);
|
|
815
986
|
}
|
|
816
987
|
outputs[`${spinalcase2(operationName)}.ts`] = output.join("\n");
|
|
@@ -824,7 +995,7 @@ function generateCode(config) {
|
|
|
824
995
|
schemas: types,
|
|
825
996
|
formatOutput: () => ({
|
|
826
997
|
import: pascalcase2(operationName + " output"),
|
|
827
|
-
use:
|
|
998
|
+
use: pascalcase2(operationName + " output")
|
|
828
999
|
}),
|
|
829
1000
|
trigger: {
|
|
830
1001
|
path,
|
|
@@ -851,17 +1022,17 @@ function mergeImports(imports) {
|
|
|
851
1022
|
return Object.values(merged);
|
|
852
1023
|
}
|
|
853
1024
|
function importsToString(imports) {
|
|
854
|
-
return imports.map((
|
|
855
|
-
if (
|
|
856
|
-
return `import ${
|
|
1025
|
+
return imports.map((it) => {
|
|
1026
|
+
if (it.defaultImport) {
|
|
1027
|
+
return `import ${it.defaultImport} from '${it.moduleSpecifier}'`;
|
|
857
1028
|
}
|
|
858
|
-
if (
|
|
859
|
-
return `import * as ${
|
|
1029
|
+
if (it.namespaceImport) {
|
|
1030
|
+
return `import * as ${it.namespaceImport} from '${it.moduleSpecifier}'`;
|
|
860
1031
|
}
|
|
861
|
-
if (
|
|
862
|
-
return `import {${removeDuplicates(
|
|
1032
|
+
if (it.namedImports) {
|
|
1033
|
+
return `import {${removeDuplicates(it.namedImports, (it2) => it2.name).map((n) => `${n.isTypeOnly ? "type" : ""} ${n.name}`).join(", ")}} from '${it.moduleSpecifier}'`;
|
|
863
1034
|
}
|
|
864
|
-
throw new Error(`Invalid import ${JSON.stringify(
|
|
1035
|
+
throw new Error(`Invalid import ${JSON.stringify(it)}`);
|
|
865
1036
|
});
|
|
866
1037
|
}
|
|
867
1038
|
|
|
@@ -928,22 +1099,35 @@ async function generate(spec, settings) {
|
|
|
928
1099
|
"request.ts": request_default
|
|
929
1100
|
});
|
|
930
1101
|
await writeFiles(join(output, "outputs"), outputs);
|
|
1102
|
+
const imports = Object.entries(commonSchemas).map(([name]) => name);
|
|
931
1103
|
await writeFiles(output, {
|
|
932
1104
|
...clientFiles,
|
|
933
|
-
|
|
934
|
-
|
|
1105
|
+
...Object.fromEntries(
|
|
1106
|
+
Object.entries(commonSchemas).map(([name, schema]) => [
|
|
1107
|
+
`models/${name}.ts`,
|
|
1108
|
+
[
|
|
1109
|
+
`import { z } from 'zod';`,
|
|
1110
|
+
...exclude(imports, [name]).map(
|
|
1111
|
+
(it) => `import type { ${it} } from './${it}.ts';`
|
|
1112
|
+
),
|
|
1113
|
+
`export type ${name} = ${schema};`
|
|
1114
|
+
].join("\n")
|
|
1115
|
+
])
|
|
1116
|
+
)
|
|
935
1117
|
});
|
|
936
|
-
const [index, outputIndex, inputsIndex, httpIndex] = await Promise.all([
|
|
1118
|
+
const [index, outputIndex, inputsIndex, httpIndex, modelsIndex] = await Promise.all([
|
|
937
1119
|
getFolderExports(output),
|
|
938
1120
|
getFolderExports(join(output, "outputs")),
|
|
939
1121
|
getFolderExports(join(output, "inputs")),
|
|
940
|
-
getFolderExports(join(output, "http"))
|
|
1122
|
+
getFolderExports(join(output, "http")),
|
|
1123
|
+
getFolderExports(join(output, "models"))
|
|
941
1124
|
]);
|
|
942
1125
|
await writeFiles(output, {
|
|
943
1126
|
"index.ts": index,
|
|
944
1127
|
"outputs/index.ts": outputIndex,
|
|
945
1128
|
"inputs/index.ts": inputsIndex,
|
|
946
|
-
"http/index.ts": httpIndex
|
|
1129
|
+
"http/index.ts": httpIndex,
|
|
1130
|
+
"models/index.ts": modelsIndex
|
|
947
1131
|
});
|
|
948
1132
|
if (settings.mode === "full") {
|
|
949
1133
|
await writeFiles(settings.output, {
|
|
@@ -958,6 +1142,9 @@ ${Object.entries(commonSchemas).map(([name, schema]) => `export const ${name} =
|
|
|
958
1142
|
env: npmRunPathEnv()
|
|
959
1143
|
});
|
|
960
1144
|
}
|
|
1145
|
+
function exclude(list, exclude2) {
|
|
1146
|
+
return list.filter((it) => !exclude2.includes(it));
|
|
1147
|
+
}
|
|
961
1148
|
|
|
962
1149
|
// packages/typescript/src/lib/watcher.ts
|
|
963
1150
|
import { watch as nodeWatch } from "node:fs/promises";
|