zenko 0.1.8 → 0.1.10-beta.1
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/README.md +11 -21
- package/dist/cli.cjs +90 -1386
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.mjs +89 -1385
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +8 -1336
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -46
- package/dist/index.d.ts +1 -46
- package/dist/index.mjs +11 -1334
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -4
package/dist/cli.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
"use strict";
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -24,1340 +24,76 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var
|
|
27
|
+
var path2 = __toESM(require("path"), 1);
|
|
28
|
+
var import_promises = require("fs/promises");
|
|
29
|
+
var import_core = require("@zenko/core");
|
|
30
|
+
|
|
31
|
+
// src/loader.ts
|
|
28
32
|
var path = __toESM(require("path"), 1);
|
|
29
33
|
var import_url = require("url");
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const result = [];
|
|
37
|
-
const visit = (name) => {
|
|
38
|
-
if (visited.has(name)) return;
|
|
39
|
-
if (visiting.has(name)) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
visiting.add(name);
|
|
43
|
-
const schema = schemas[name];
|
|
44
|
-
const dependencies = extractDependencies(schema);
|
|
45
|
-
for (const dep of dependencies) {
|
|
46
|
-
if (schemas[dep]) {
|
|
47
|
-
visit(dep);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
visiting.delete(name);
|
|
51
|
-
visited.add(name);
|
|
52
|
-
result.push(name);
|
|
53
|
-
};
|
|
54
|
-
for (const name of Object.keys(schemas)) {
|
|
55
|
-
visit(name);
|
|
56
|
-
}
|
|
57
|
-
return result;
|
|
58
|
-
}
|
|
59
|
-
function extractDependencies(schema) {
|
|
60
|
-
const dependencies = [];
|
|
61
|
-
const traverse = (obj) => {
|
|
62
|
-
if (typeof obj !== "object" || obj === null) return;
|
|
63
|
-
if (obj.$ref && typeof obj.$ref === "string") {
|
|
64
|
-
const refName = extractRefName(obj.$ref);
|
|
65
|
-
dependencies.push(refName);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (Array.isArray(obj)) {
|
|
69
|
-
obj.forEach(traverse);
|
|
70
|
-
} else {
|
|
71
|
-
Object.values(obj).forEach(traverse);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
traverse(schema);
|
|
75
|
-
return [...new Set(dependencies)];
|
|
76
|
-
}
|
|
77
|
-
function extractRefName(ref) {
|
|
78
|
-
return ref.split("/").pop() || "Unknown";
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// src/utils/property-name.ts
|
|
82
|
-
function isValidJSIdentifier(name) {
|
|
83
|
-
if (!name) return false;
|
|
84
|
-
const firstChar = name.at(0);
|
|
85
|
-
if (firstChar === void 0) return false;
|
|
86
|
-
if (!/[a-zA-Z_$]/.test(firstChar)) return false;
|
|
87
|
-
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) return false;
|
|
88
|
-
const reservedWords = /* @__PURE__ */ new Set([
|
|
89
|
-
"abstract",
|
|
90
|
-
"arguments",
|
|
91
|
-
"await",
|
|
92
|
-
"boolean",
|
|
93
|
-
"break",
|
|
94
|
-
"byte",
|
|
95
|
-
"case",
|
|
96
|
-
"catch",
|
|
97
|
-
"char",
|
|
98
|
-
"class",
|
|
99
|
-
"const",
|
|
100
|
-
"continue",
|
|
101
|
-
"debugger",
|
|
102
|
-
"default",
|
|
103
|
-
"delete",
|
|
104
|
-
"do",
|
|
105
|
-
"double",
|
|
106
|
-
"else",
|
|
107
|
-
"enum",
|
|
108
|
-
"eval",
|
|
109
|
-
"export",
|
|
110
|
-
"extends",
|
|
111
|
-
"false",
|
|
112
|
-
"final",
|
|
113
|
-
"finally",
|
|
114
|
-
"float",
|
|
115
|
-
"for",
|
|
116
|
-
"function",
|
|
117
|
-
"goto",
|
|
118
|
-
"if",
|
|
119
|
-
"implements",
|
|
120
|
-
"import",
|
|
121
|
-
"in",
|
|
122
|
-
"instanceof",
|
|
123
|
-
"int",
|
|
124
|
-
"interface",
|
|
125
|
-
"let",
|
|
126
|
-
"long",
|
|
127
|
-
"native",
|
|
128
|
-
"new",
|
|
129
|
-
"null",
|
|
130
|
-
"package",
|
|
131
|
-
"private",
|
|
132
|
-
"protected",
|
|
133
|
-
"public",
|
|
134
|
-
"return",
|
|
135
|
-
"short",
|
|
136
|
-
"static",
|
|
137
|
-
"super",
|
|
138
|
-
"switch",
|
|
139
|
-
"synchronized",
|
|
140
|
-
"this",
|
|
141
|
-
"throw",
|
|
142
|
-
"throws",
|
|
143
|
-
"transient",
|
|
144
|
-
"true",
|
|
145
|
-
"try",
|
|
146
|
-
"typeof",
|
|
147
|
-
"var",
|
|
148
|
-
"void",
|
|
149
|
-
"volatile",
|
|
150
|
-
"while",
|
|
151
|
-
"with",
|
|
152
|
-
"yield",
|
|
153
|
-
"async"
|
|
154
|
-
]);
|
|
155
|
-
return !reservedWords.has(name);
|
|
156
|
-
}
|
|
157
|
-
function formatPropertyName(name) {
|
|
158
|
-
return isValidJSIdentifier(name) ? name : `"${name}"`;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// src/utils/string-utils.ts
|
|
162
|
-
function toCamelCase(str) {
|
|
163
|
-
return str.replace(/-([a-zA-Z])/g, (_, letter) => letter.toUpperCase()).replace(/-+$/, "");
|
|
164
|
-
}
|
|
165
|
-
function capitalize(str) {
|
|
166
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// src/utils/http-status.ts
|
|
170
|
-
var statusNameMap = {
|
|
171
|
-
"400": "badRequest",
|
|
172
|
-
"401": "unauthorized",
|
|
173
|
-
"402": "paymentRequired",
|
|
174
|
-
"403": "forbidden",
|
|
175
|
-
"404": "notFound",
|
|
176
|
-
"405": "methodNotAllowed",
|
|
177
|
-
"406": "notAcceptable",
|
|
178
|
-
"407": "proxyAuthenticationRequired",
|
|
179
|
-
"408": "requestTimeout",
|
|
180
|
-
"409": "conflict",
|
|
181
|
-
"410": "gone",
|
|
182
|
-
"411": "lengthRequired",
|
|
183
|
-
"412": "preconditionFailed",
|
|
184
|
-
"413": "payloadTooLarge",
|
|
185
|
-
"414": "uriTooLong",
|
|
186
|
-
"415": "unsupportedMediaType",
|
|
187
|
-
"416": "rangeNotSatisfiable",
|
|
188
|
-
"417": "expectationFailed",
|
|
189
|
-
"418": "imATeapot",
|
|
190
|
-
"421": "misdirectedRequest",
|
|
191
|
-
"422": "unprocessableEntity",
|
|
192
|
-
"423": "locked",
|
|
193
|
-
"424": "failedDependency",
|
|
194
|
-
"425": "tooEarly",
|
|
195
|
-
"426": "upgradeRequired",
|
|
196
|
-
"428": "preconditionRequired",
|
|
197
|
-
"429": "tooManyRequests",
|
|
198
|
-
"431": "requestHeaderFieldsTooLarge",
|
|
199
|
-
"451": "unavailableForLegalReasons",
|
|
200
|
-
"500": "internalServerError",
|
|
201
|
-
"501": "notImplemented",
|
|
202
|
-
"502": "badGateway",
|
|
203
|
-
"503": "serviceUnavailable",
|
|
204
|
-
"504": "gatewayTimeout",
|
|
205
|
-
"505": "httpVersionNotSupported",
|
|
206
|
-
"506": "variantAlsoNegotiates",
|
|
207
|
-
"507": "insufficientStorage",
|
|
208
|
-
"508": "loopDetected",
|
|
209
|
-
"510": "notExtended",
|
|
210
|
-
"511": "networkAuthenticationRequired"
|
|
211
|
-
};
|
|
212
|
-
function mapStatusToIdentifier(status) {
|
|
213
|
-
if (status === "default") return "defaultError";
|
|
214
|
-
const trimmed = status.trim();
|
|
215
|
-
const mapped = statusNameMap[trimmed];
|
|
216
|
-
if (mapped) return mapped;
|
|
217
|
-
if (/^\d{3}$/.test(trimmed)) {
|
|
218
|
-
return `status${trimmed}`;
|
|
219
|
-
}
|
|
220
|
-
const sanitized = trimmed.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
221
|
-
if (!sanitized) return "unknownError";
|
|
222
|
-
const parts = sanitized.split(/\s+/);
|
|
223
|
-
const [first, ...rest] = parts;
|
|
224
|
-
const candidate = first + rest.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
|
|
225
|
-
if (!candidate) return "unknownError";
|
|
226
|
-
return /^[a-zA-Z_$]/.test(candidate) ? candidate : `status${candidate.charAt(0).toUpperCase()}${candidate.slice(1)}`;
|
|
227
|
-
}
|
|
228
|
-
function isErrorStatus(status) {
|
|
229
|
-
if (status === "default") return true;
|
|
230
|
-
const code = Number(status);
|
|
231
|
-
if (!Number.isInteger(code)) return false;
|
|
232
|
-
return code >= 400;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// src/utils/tree-shaking.ts
|
|
236
|
-
function analyzeZenkoUsage(operations) {
|
|
237
|
-
const usage = {
|
|
238
|
-
usesHeaderFn: false,
|
|
239
|
-
usesOperationDefinition: false,
|
|
240
|
-
usesOperationErrors: false
|
|
241
|
-
};
|
|
242
|
-
if (operations.length > 0) {
|
|
243
|
-
usage.usesOperationDefinition = true;
|
|
244
|
-
}
|
|
245
|
-
for (const op of operations) {
|
|
246
|
-
if (op.errors && hasAnyErrors(op.errors)) {
|
|
247
|
-
usage.usesOperationErrors = true;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
if (operations.length > 0 && !usage.usesOperationErrors) {
|
|
251
|
-
const hasDefaultErrors = operations.some(
|
|
252
|
-
(op) => !op.errors || !hasAnyErrors(op.errors)
|
|
253
|
-
);
|
|
254
|
-
if (hasDefaultErrors) {
|
|
255
|
-
usage.usesOperationErrors = true;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return usage;
|
|
259
|
-
}
|
|
260
|
-
function generateZenkoImport(usage, mode, helpersOutput) {
|
|
261
|
-
const types = [];
|
|
262
|
-
if (usage.usesHeaderFn) types.push("HeaderFn");
|
|
263
|
-
if (usage.usesOperationDefinition) types.push("OperationDefinition");
|
|
264
|
-
if (usage.usesOperationErrors) types.push("OperationErrors");
|
|
265
|
-
if (types.length === 0) {
|
|
266
|
-
return "";
|
|
34
|
+
var YAML_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml"]);
|
|
35
|
+
var JSON_EXTENSIONS = /* @__PURE__ */ new Set([".json"]);
|
|
36
|
+
async function readFileText(filePath) {
|
|
37
|
+
const file = Bun.file(filePath);
|
|
38
|
+
if (!await file.exists()) {
|
|
39
|
+
throw new Error(`File not found: ${filePath}`);
|
|
267
40
|
}
|
|
268
|
-
|
|
269
|
-
return `import type { ${types.join(", ")} } from ${importSource};`;
|
|
41
|
+
return await file.text();
|
|
270
42
|
}
|
|
271
|
-
function
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
function collectInlineRequestTypes(operations, spec) {
|
|
277
|
-
const requestTypesToGenerate = /* @__PURE__ */ new Map();
|
|
278
|
-
const operationLookup = /* @__PURE__ */ new Map();
|
|
279
|
-
for (const [, pathItem] of Object.entries(spec.paths || {})) {
|
|
280
|
-
for (const [, operation] of Object.entries(pathItem)) {
|
|
281
|
-
const op = operation;
|
|
282
|
-
if (op.operationId) {
|
|
283
|
-
operationLookup.set(op.operationId, op);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
for (const [, pathItem] of Object.entries(spec.webhooks || {})) {
|
|
288
|
-
for (const [, operation] of Object.entries(pathItem)) {
|
|
289
|
-
const op = operation;
|
|
290
|
-
if (op.operationId) {
|
|
291
|
-
operationLookup.set(op.operationId, op);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
for (const op of operations) {
|
|
296
|
-
const operation = operationLookup.get(op.operationId);
|
|
297
|
-
if (!operation) continue;
|
|
298
|
-
const requestBody = operation.requestBody;
|
|
299
|
-
if (requestBody && requestBody.content) {
|
|
300
|
-
const content = requestBody.content;
|
|
301
|
-
const jsonContent = content["application/json"];
|
|
302
|
-
if (jsonContent && jsonContent.schema) {
|
|
303
|
-
const schema = jsonContent.schema;
|
|
304
|
-
const typeName = `${capitalize(toCamelCase(op.operationId))}Request`;
|
|
305
|
-
if (!schema.$ref || schema.allOf || schema.oneOf || schema.anyOf) {
|
|
306
|
-
requestTypesToGenerate.set(typeName, schema);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return requestTypesToGenerate;
|
|
312
|
-
}
|
|
313
|
-
function collectInlineResponseTypes(operations, spec) {
|
|
314
|
-
const responseTypesToGenerate = /* @__PURE__ */ new Map();
|
|
315
|
-
const operationLookup = /* @__PURE__ */ new Map();
|
|
316
|
-
for (const [, pathItem] of Object.entries(spec.paths || {})) {
|
|
317
|
-
for (const [, operation] of Object.entries(pathItem)) {
|
|
318
|
-
const op = operation;
|
|
319
|
-
if (op.operationId) {
|
|
320
|
-
operationLookup.set(op.operationId, op);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
for (const [, pathItem] of Object.entries(spec.webhooks || {})) {
|
|
325
|
-
for (const [, operation] of Object.entries(pathItem)) {
|
|
326
|
-
const op = operation;
|
|
327
|
-
if (op.operationId) {
|
|
328
|
-
operationLookup.set(op.operationId, op);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
for (const op of operations) {
|
|
333
|
-
const operation = operationLookup.get(op.operationId);
|
|
334
|
-
if (!operation) continue;
|
|
335
|
-
const responses = operation.responses || {};
|
|
336
|
-
for (const [statusCode, response] of Object.entries(responses)) {
|
|
337
|
-
if (/^2\d\d$/.test(statusCode) && response.content) {
|
|
338
|
-
const content = response.content;
|
|
339
|
-
const jsonContent = content["application/json"];
|
|
340
|
-
if (jsonContent && jsonContent.schema) {
|
|
341
|
-
const schema = jsonContent.schema;
|
|
342
|
-
const typeName = `${capitalize(toCamelCase(op.operationId))}Response`;
|
|
343
|
-
if (!schema.$ref || schema.allOf || schema.oneOf || schema.anyOf) {
|
|
344
|
-
responseTypesToGenerate.set(typeName, schema);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return responseTypesToGenerate;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// src/utils/generate-helper-file.ts
|
|
354
|
-
function generateHelperFile() {
|
|
355
|
-
const output = [];
|
|
356
|
-
output.push("// Generated helper types for Zenko");
|
|
357
|
-
output.push(
|
|
358
|
-
"// This file provides type definitions for operation objects and path functions"
|
|
359
|
-
);
|
|
360
|
-
output.push("");
|
|
361
|
-
output.push(
|
|
362
|
-
"export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
|
|
363
|
-
);
|
|
364
|
-
output.push("");
|
|
365
|
-
output.push(
|
|
366
|
-
'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
|
|
367
|
-
);
|
|
368
|
-
output.push("");
|
|
369
|
-
output.push(
|
|
370
|
-
"export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
|
|
371
|
-
);
|
|
372
|
-
output.push("");
|
|
373
|
-
output.push(
|
|
374
|
-
"export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
|
|
375
|
-
);
|
|
376
|
-
output.push("");
|
|
377
|
-
output.push(
|
|
378
|
-
"export type OperationErrors<TError = unknown> = TError extends Record<string, unknown> ? TError : Record<string, TError>;"
|
|
379
|
-
);
|
|
380
|
-
output.push("");
|
|
381
|
-
output.push(
|
|
382
|
-
"export type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends AnyHeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
383
|
-
);
|
|
384
|
-
output.push(" method: TMethod");
|
|
385
|
-
output.push(" path: TPath");
|
|
386
|
-
output.push(" request?: TRequest");
|
|
387
|
-
output.push(" response?: TResponse");
|
|
388
|
-
output.push(" headers?: THeaders");
|
|
389
|
-
output.push(" errors?: TErrors");
|
|
390
|
-
output.push("}");
|
|
391
|
-
output.push("");
|
|
392
|
-
return output.join("\n");
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// src/zenko.ts
|
|
396
|
-
function generateWithMetadata(spec, options = {}) {
|
|
397
|
-
const output = [];
|
|
398
|
-
const generatedTypes = /* @__PURE__ */ new Set();
|
|
399
|
-
const { strictDates = false, strictNumeric = false } = options;
|
|
400
|
-
const typesConfig = normalizeTypesConfig(options.types);
|
|
401
|
-
const schemaOptions = {
|
|
402
|
-
strictDates,
|
|
403
|
-
strictNumeric
|
|
404
|
-
};
|
|
405
|
-
output.push('import { z } from "zod";');
|
|
406
|
-
const nameMap = /* @__PURE__ */ new Map();
|
|
407
|
-
if (spec.components?.schemas) {
|
|
408
|
-
for (const name of Object.keys(spec.components.schemas)) {
|
|
409
|
-
nameMap.set(name, toCamelCase(name));
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
const operations = parseOperations(spec, nameMap);
|
|
413
|
-
appendHelperTypesImport(output, typesConfig, operations);
|
|
414
|
-
output.push("");
|
|
415
|
-
if (spec.components?.schemas) {
|
|
416
|
-
output.push("// Generated Zod Schemas");
|
|
417
|
-
output.push("");
|
|
418
|
-
const sortedSchemas = topologicalSort(spec.components.schemas);
|
|
419
|
-
for (const name of sortedSchemas) {
|
|
420
|
-
const schema = spec.components.schemas[name];
|
|
421
|
-
const sanitizedName = nameMap.get(name);
|
|
422
|
-
output.push(
|
|
423
|
-
generateZodSchema(
|
|
424
|
-
sanitizedName,
|
|
425
|
-
schema,
|
|
426
|
-
generatedTypes,
|
|
427
|
-
schemaOptions,
|
|
428
|
-
nameMap
|
|
429
|
-
)
|
|
430
|
-
);
|
|
431
|
-
output.push("");
|
|
432
|
-
output.push(
|
|
433
|
-
`export type ${sanitizedName} = z.infer<typeof ${sanitizedName}>;`
|
|
434
|
-
);
|
|
435
|
-
output.push("");
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
output.push("// Path Functions");
|
|
439
|
-
output.push("export const paths = {");
|
|
440
|
-
for (const op of operations) {
|
|
441
|
-
const pathParamNames = op.pathParams.map((p) => p.name);
|
|
442
|
-
const hasPathParams = pathParamNames.length > 0;
|
|
443
|
-
const hasQueryParams = op.queryParams.length > 0;
|
|
444
|
-
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
445
|
-
if (!hasPathParams && !hasQueryParams) {
|
|
446
|
-
output.push(
|
|
447
|
-
` ${formatPropertyName(camelCaseOperationId)}: () => "${op.path}",`
|
|
448
|
-
);
|
|
449
|
-
continue;
|
|
450
|
-
}
|
|
451
|
-
const alias = (n) => {
|
|
452
|
-
if (isValidJSIdentifier(n)) return n;
|
|
453
|
-
let aliased = toCamelCase(n);
|
|
454
|
-
if (!isValidJSIdentifier(aliased)) {
|
|
455
|
-
aliased = `_${aliased}`;
|
|
456
|
-
}
|
|
457
|
-
return aliased;
|
|
458
|
-
};
|
|
459
|
-
const destructPieces = [];
|
|
460
|
-
const typePieces = [];
|
|
461
|
-
for (const param of op.pathParams) {
|
|
462
|
-
destructPieces.push(
|
|
463
|
-
isValidJSIdentifier(param.name) ? param.name : `${formatPropertyName(param.name)}: ${alias(param.name)}`
|
|
464
|
-
);
|
|
465
|
-
typePieces.push(`${formatPropertyName(param.name)}: string`);
|
|
466
|
-
}
|
|
467
|
-
for (const param of op.queryParams) {
|
|
468
|
-
destructPieces.push(
|
|
469
|
-
isValidJSIdentifier(param.name) ? param.name : `${formatPropertyName(param.name)}: ${alias(param.name)}`
|
|
470
|
-
);
|
|
471
|
-
typePieces.push(
|
|
472
|
-
`${formatPropertyName(param.name)}${param.required ? "" : "?"}: ${mapQueryType(param)}`
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
const needsDefaultObject = !hasPathParams && hasQueryParams && op.queryParams.every((param) => !param.required);
|
|
476
|
-
const signatureArgs = destructPieces.length ? `{ ${destructPieces.join(", ")} }` : "{}";
|
|
477
|
-
const signature = `${signatureArgs}: { ${typePieces.join(", ")} }${needsDefaultObject ? " = {}" : ""}`;
|
|
478
|
-
const pathWithParams = op.path.replace(
|
|
479
|
-
/{([^}]+)}/g,
|
|
480
|
-
(_m, n) => `\${${alias(n)}}`
|
|
481
|
-
);
|
|
482
|
-
if (!hasQueryParams) {
|
|
483
|
-
output.push(
|
|
484
|
-
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => \`${pathWithParams}\`,`
|
|
485
|
-
);
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
output.push(
|
|
489
|
-
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => {`
|
|
490
|
-
);
|
|
491
|
-
output.push(" const params = new URLSearchParams()");
|
|
492
|
-
for (const param of op.queryParams) {
|
|
493
|
-
const accessor = isValidJSIdentifier(param.name) ? param.name : alias(toCamelCase(param.name));
|
|
494
|
-
const schema = param.schema ?? {};
|
|
495
|
-
if (schema?.type === "array") {
|
|
496
|
-
const itemValueExpression = convertQueryParamValue(
|
|
497
|
-
schema.items ?? {},
|
|
498
|
-
"value"
|
|
499
|
-
);
|
|
500
|
-
if (param.required) {
|
|
501
|
-
output.push(` for (const value of ${accessor}) {`);
|
|
502
|
-
output.push(
|
|
503
|
-
` params.append("${param.name}", ${itemValueExpression})`
|
|
504
|
-
);
|
|
505
|
-
output.push(" }");
|
|
506
|
-
} else {
|
|
507
|
-
output.push(` if (${accessor} !== undefined) {`);
|
|
508
|
-
output.push(` for (const value of ${accessor}) {`);
|
|
509
|
-
output.push(
|
|
510
|
-
` params.append("${param.name}", ${itemValueExpression})`
|
|
511
|
-
);
|
|
512
|
-
output.push(" }");
|
|
513
|
-
output.push(" }");
|
|
514
|
-
}
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
const valueExpression = convertQueryParamValue(schema, accessor);
|
|
518
|
-
if (param.required) {
|
|
519
|
-
output.push(` params.set("${param.name}", ${valueExpression})`);
|
|
520
|
-
} else {
|
|
521
|
-
output.push(` if (${accessor} !== undefined) {`);
|
|
522
|
-
output.push(` params.set("${param.name}", ${valueExpression})`);
|
|
523
|
-
output.push(" }");
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
output.push(" const _searchParams = params.toString()");
|
|
527
|
-
output.push(
|
|
528
|
-
` return \`${pathWithParams}\${_searchParams ? \`?\${_searchParams}\` : ""}\``
|
|
529
|
-
);
|
|
530
|
-
output.push(" },");
|
|
531
|
-
}
|
|
532
|
-
output.push("} as const;");
|
|
533
|
-
output.push("");
|
|
534
|
-
output.push("// Header Schemas");
|
|
535
|
-
output.push("export const headerSchemas = {");
|
|
536
|
-
for (const op of operations) {
|
|
537
|
-
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
538
|
-
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
539
|
-
output.push(
|
|
540
|
-
` ${formatPropertyName(camelCaseOperationId)}: z.object({}),`
|
|
541
|
-
);
|
|
542
|
-
continue;
|
|
543
|
-
}
|
|
544
|
-
const schemaFields = op.requestHeaders.map((header) => {
|
|
545
|
-
const zodType = mapHeaderToZodType(header);
|
|
546
|
-
const optional = header.required ? "" : ".optional()";
|
|
547
|
-
return ` ${formatPropertyName(header.name)}: ${zodType}${optional},`;
|
|
548
|
-
}).join("\n");
|
|
549
|
-
output.push(` ${formatPropertyName(camelCaseOperationId)}: z.object({`);
|
|
550
|
-
output.push(schemaFields);
|
|
551
|
-
output.push(" }),");
|
|
552
|
-
}
|
|
553
|
-
output.push("} as const;");
|
|
554
|
-
output.push("");
|
|
555
|
-
output.push("// Header Functions");
|
|
556
|
-
output.push("export const headers = {");
|
|
557
|
-
for (const op of operations) {
|
|
558
|
-
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
559
|
-
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
560
|
-
output.push(
|
|
561
|
-
` ${formatPropertyName(camelCaseOperationId)}: () => ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse({}),`
|
|
562
|
-
);
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
output.push(
|
|
566
|
-
` ${formatPropertyName(camelCaseOperationId)}: (params: z.input<${isValidJSIdentifier(camelCaseOperationId) ? `typeof headerSchemas.${camelCaseOperationId}` : `(typeof headerSchemas)[${formatPropertyName(camelCaseOperationId)}]`}>) => {`
|
|
567
|
-
);
|
|
568
|
-
output.push(
|
|
569
|
-
` return ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse(params)`
|
|
570
|
-
);
|
|
571
|
-
output.push(" },");
|
|
572
|
-
}
|
|
573
|
-
output.push("} as const;");
|
|
574
|
-
output.push("");
|
|
575
|
-
generateRequestTypes(output, operations, spec, nameMap, schemaOptions);
|
|
576
|
-
generateResponseTypes(output, operations, spec, nameMap, schemaOptions);
|
|
577
|
-
generateOperationTypes(output, operations, typesConfig);
|
|
578
|
-
output.push("// Operation Objects");
|
|
579
|
-
for (const op of operations) {
|
|
580
|
-
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
581
|
-
const typeAnnotation = typesConfig.emit ? `: ${capitalize(camelCaseOperationId)}Operation` : "";
|
|
582
|
-
output.push(`export const ${camelCaseOperationId}${typeAnnotation} = {`);
|
|
583
|
-
output.push(` method: "${op.method}",`);
|
|
584
|
-
output.push(` path: paths.${camelCaseOperationId},`);
|
|
585
|
-
appendOperationField(output, "request", op.requestType);
|
|
586
|
-
appendOperationField(output, "response", op.responseType);
|
|
587
|
-
if (op.requestHeaders && op.requestHeaders.length > 0) {
|
|
588
|
-
output.push(` headers: headers.${camelCaseOperationId},`);
|
|
589
|
-
}
|
|
590
|
-
if (op.errors && hasAnyErrors2(op.errors)) {
|
|
591
|
-
appendErrorGroup(output, "errors", op.errors);
|
|
592
|
-
}
|
|
593
|
-
output.push("} as const;");
|
|
594
|
-
output.push("");
|
|
595
|
-
}
|
|
596
|
-
const result = {
|
|
597
|
-
output: output.join("\n")
|
|
598
|
-
};
|
|
599
|
-
if (typesConfig.emit && typesConfig.helpers === "file" && typesConfig.helpersOutput) {
|
|
600
|
-
result.helperFile = {
|
|
601
|
-
path: typesConfig.helpersOutput,
|
|
602
|
-
content: generateHelperFile()
|
|
603
|
-
};
|
|
43
|
+
async function loadConfig(filePath) {
|
|
44
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
45
|
+
if (JSON_EXTENSIONS.has(extension)) {
|
|
46
|
+
const content = await readFileText(filePath);
|
|
47
|
+
return JSON.parse(content);
|
|
604
48
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
const requestTypesToGenerate = collectInlineRequestTypes(operations, spec);
|
|
609
|
-
if (requestTypesToGenerate.size > 0) {
|
|
610
|
-
output.push("// Generated Request Types");
|
|
611
|
-
output.push("");
|
|
612
|
-
for (const [typeName, schema] of requestTypesToGenerate) {
|
|
613
|
-
const generatedSchema = generateZodSchema(
|
|
614
|
-
typeName,
|
|
615
|
-
schema,
|
|
616
|
-
/* @__PURE__ */ new Set(),
|
|
617
|
-
schemaOptions,
|
|
618
|
-
nameMap
|
|
619
|
-
);
|
|
620
|
-
output.push(generatedSchema);
|
|
621
|
-
output.push("");
|
|
622
|
-
output.push(`export type ${typeName} = z.infer<typeof ${typeName}>;`);
|
|
623
|
-
output.push("");
|
|
624
|
-
}
|
|
49
|
+
if (YAML_EXTENSIONS.has(extension)) {
|
|
50
|
+
const content = await readFileText(filePath);
|
|
51
|
+
return Bun.YAML.parse(content);
|
|
625
52
|
}
|
|
53
|
+
const fileUrl = (0, import_url.pathToFileURL)(filePath).href;
|
|
54
|
+
const module2 = await import(fileUrl);
|
|
55
|
+
return module2.default ?? module2.config ?? module2;
|
|
626
56
|
}
|
|
627
|
-
function
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
nameMap
|
|
57
|
+
async function loadSpec(filePath) {
|
|
58
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
59
|
+
const content = await readFileText(filePath);
|
|
60
|
+
if (YAML_EXTENSIONS.has(extension)) {
|
|
61
|
+
const parsed = Bun.YAML.parse(content);
|
|
62
|
+
if (!parsed || typeof parsed !== "object") {
|
|
63
|
+
throw new Error(`YAML spec did not resolve to an object: ${filePath}`);
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(parsed)) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`YAML spec produced multiple documents; provide a single document in ${filePath}`
|
|
639
68
|
);
|
|
640
|
-
output.push(generatedSchema);
|
|
641
|
-
output.push("");
|
|
642
|
-
output.push(`export type ${typeName} = z.infer<typeof ${typeName}>;`);
|
|
643
|
-
output.push("");
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
function appendOperationField(buffer, key, value) {
|
|
648
|
-
if (!value) return;
|
|
649
|
-
buffer.push(` ${key}: ${value},`);
|
|
650
|
-
}
|
|
651
|
-
function appendErrorGroup(buffer, label, errors) {
|
|
652
|
-
if (!errors || Object.keys(errors).length === 0) return;
|
|
653
|
-
buffer.push(` ${label}: {`);
|
|
654
|
-
for (const [name, typeName] of Object.entries(errors)) {
|
|
655
|
-
buffer.push(` ${formatPropertyName(name)}: ${typeName},`);
|
|
656
|
-
}
|
|
657
|
-
buffer.push(" },");
|
|
658
|
-
}
|
|
659
|
-
function hasAnyErrors2(group) {
|
|
660
|
-
return Boolean(group && Object.keys(group).length > 0);
|
|
661
|
-
}
|
|
662
|
-
function isRequestMethod(method) {
|
|
663
|
-
switch (method) {
|
|
664
|
-
case "get":
|
|
665
|
-
case "put":
|
|
666
|
-
case "post":
|
|
667
|
-
case "delete":
|
|
668
|
-
case "options":
|
|
669
|
-
case "head":
|
|
670
|
-
case "patch":
|
|
671
|
-
case "trace":
|
|
672
|
-
return true;
|
|
673
|
-
default:
|
|
674
|
-
return false;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
var CONTENT_TYPE_MAP = {
|
|
678
|
-
"application/json": "unknown",
|
|
679
|
-
// Will use schema when available
|
|
680
|
-
"text/csv": "string",
|
|
681
|
-
"text/plain": "string",
|
|
682
|
-
// Binary/ambiguous types default to unknown for cross-platform compatibility
|
|
683
|
-
"application/octet-stream": "unknown",
|
|
684
|
-
"application/pdf": "unknown"
|
|
685
|
-
};
|
|
686
|
-
function findContentType(content) {
|
|
687
|
-
const contentTypes = Object.keys(content);
|
|
688
|
-
if (contentTypes.includes("application/json")) {
|
|
689
|
-
return "application/json";
|
|
690
|
-
}
|
|
691
|
-
for (const contentType of contentTypes) {
|
|
692
|
-
if (contentType in CONTENT_TYPE_MAP) {
|
|
693
|
-
return contentType;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
return contentTypes[0] || "";
|
|
697
|
-
}
|
|
698
|
-
function inferResponseType(contentType, statusCode) {
|
|
699
|
-
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
700
|
-
return "undefined";
|
|
701
|
-
}
|
|
702
|
-
if (contentType in CONTENT_TYPE_MAP) {
|
|
703
|
-
return CONTENT_TYPE_MAP[contentType];
|
|
704
|
-
}
|
|
705
|
-
return "unknown";
|
|
706
|
-
}
|
|
707
|
-
function parseOperations(spec, nameMap) {
|
|
708
|
-
const operations = [];
|
|
709
|
-
if (spec.paths) {
|
|
710
|
-
for (const [path2, pathItem] of Object.entries(spec.paths)) {
|
|
711
|
-
for (const [method, operation] of Object.entries(pathItem)) {
|
|
712
|
-
const normalizedMethod = method.toLowerCase();
|
|
713
|
-
if (!isRequestMethod(normalizedMethod)) continue;
|
|
714
|
-
if (!operation.operationId) continue;
|
|
715
|
-
const pathParams = extractPathParams(path2);
|
|
716
|
-
const requestType = getRequestType(operation);
|
|
717
|
-
const { successResponse, errors } = getResponseTypes(
|
|
718
|
-
operation,
|
|
719
|
-
operation.operationId,
|
|
720
|
-
nameMap
|
|
721
|
-
);
|
|
722
|
-
const resolvedParameters = collectParameters(pathItem, operation, spec);
|
|
723
|
-
const requestHeaders = getRequestHeaders(resolvedParameters);
|
|
724
|
-
const queryParams = getQueryParams(resolvedParameters);
|
|
725
|
-
operations.push({
|
|
726
|
-
operationId: operation.operationId,
|
|
727
|
-
path: path2,
|
|
728
|
-
method: normalizedMethod,
|
|
729
|
-
pathParams,
|
|
730
|
-
queryParams,
|
|
731
|
-
requestType,
|
|
732
|
-
responseType: successResponse,
|
|
733
|
-
requestHeaders,
|
|
734
|
-
errors
|
|
735
|
-
});
|
|
736
|
-
}
|
|
737
69
|
}
|
|
70
|
+
return parsed;
|
|
738
71
|
}
|
|
739
|
-
if (
|
|
740
|
-
|
|
741
|
-
for (const [method, operation] of Object.entries(webhookItem)) {
|
|
742
|
-
const normalizedMethod = method.toLowerCase();
|
|
743
|
-
if (!isRequestMethod(normalizedMethod)) continue;
|
|
744
|
-
if (!operation.operationId) continue;
|
|
745
|
-
const path2 = webhookName;
|
|
746
|
-
const pathParams = extractPathParams(path2);
|
|
747
|
-
const requestType = getRequestType(operation);
|
|
748
|
-
const { successResponse, errors } = getResponseTypes(
|
|
749
|
-
operation,
|
|
750
|
-
operation.operationId
|
|
751
|
-
);
|
|
752
|
-
const resolvedParameters = collectParameters(
|
|
753
|
-
webhookItem,
|
|
754
|
-
operation,
|
|
755
|
-
spec
|
|
756
|
-
);
|
|
757
|
-
const requestHeaders = getRequestHeaders(resolvedParameters);
|
|
758
|
-
const queryParams = getQueryParams(resolvedParameters);
|
|
759
|
-
operations.push({
|
|
760
|
-
operationId: operation.operationId,
|
|
761
|
-
path: path2,
|
|
762
|
-
method: normalizedMethod,
|
|
763
|
-
pathParams,
|
|
764
|
-
queryParams,
|
|
765
|
-
requestType,
|
|
766
|
-
responseType: successResponse,
|
|
767
|
-
requestHeaders,
|
|
768
|
-
errors
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
}
|
|
72
|
+
if (JSON_EXTENSIONS.has(extension)) {
|
|
73
|
+
return JSON.parse(content);
|
|
772
74
|
}
|
|
773
|
-
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Unsupported specification format for ${filePath}. Expected .yaml, .yml, or .json`
|
|
77
|
+
);
|
|
774
78
|
}
|
|
775
|
-
function
|
|
79
|
+
function normalizeGenerationOptions(entry, baseDir, defaults) {
|
|
80
|
+
const resolvedInput = path.isAbsolute(entry.input) ? entry.input : path.join(baseDir, entry.input);
|
|
81
|
+
const resolvedOutput = path.isAbsolute(entry.output) ? entry.output : path.join(baseDir, entry.output);
|
|
776
82
|
return {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
83
|
+
resolvedInput,
|
|
84
|
+
resolvedOutput,
|
|
85
|
+
strictDates: entry.strictDates ?? defaults.strictDates,
|
|
86
|
+
strictNumeric: entry.strictNumeric ?? defaults.strictNumeric,
|
|
87
|
+
types: mergeTypesConfig(defaults.types, entry.types),
|
|
88
|
+
operationIds: entry.operationIds ?? defaults.operationIds
|
|
781
89
|
};
|
|
782
90
|
}
|
|
783
|
-
function
|
|
784
|
-
if (!
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
const usage = analyzeZenkoUsage(operations);
|
|
789
|
-
const importStatement = generateZenkoImport(usage, "package");
|
|
790
|
-
if (importStatement) {
|
|
791
|
-
buffer.push(importStatement);
|
|
792
|
-
}
|
|
793
|
-
} else {
|
|
794
|
-
buffer.push(
|
|
795
|
-
'import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "zenko";'
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
return;
|
|
799
|
-
case "file":
|
|
800
|
-
if (config.treeShake) {
|
|
801
|
-
const usage = analyzeZenkoUsage(operations);
|
|
802
|
-
const importStatement = generateZenkoImport(
|
|
803
|
-
usage,
|
|
804
|
-
"file",
|
|
805
|
-
config.helpersOutput
|
|
806
|
-
);
|
|
807
|
-
if (importStatement) {
|
|
808
|
-
buffer.push(importStatement);
|
|
809
|
-
}
|
|
810
|
-
} else {
|
|
811
|
-
buffer.push(
|
|
812
|
-
`import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "${config.helpersOutput}";`
|
|
813
|
-
);
|
|
814
|
-
}
|
|
815
|
-
return;
|
|
816
|
-
case "inline":
|
|
817
|
-
buffer.push(
|
|
818
|
-
"type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string;"
|
|
819
|
-
);
|
|
820
|
-
buffer.push(
|
|
821
|
-
'type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";'
|
|
822
|
-
);
|
|
823
|
-
buffer.push(
|
|
824
|
-
"type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
|
|
825
|
-
);
|
|
826
|
-
buffer.push(
|
|
827
|
-
"type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown);"
|
|
828
|
-
);
|
|
829
|
-
buffer.push(
|
|
830
|
-
"type OperationErrors<TError = unknown> = TError extends Record<string, unknown> ? TError : Record<string, TError>;"
|
|
831
|
-
);
|
|
832
|
-
buffer.push(
|
|
833
|
-
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends AnyHeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
834
|
-
);
|
|
835
|
-
buffer.push(" method: TMethod");
|
|
836
|
-
buffer.push(" path: TPath");
|
|
837
|
-
buffer.push(" request?: TRequest");
|
|
838
|
-
buffer.push(" response?: TResponse");
|
|
839
|
-
buffer.push(" headers?: THeaders");
|
|
840
|
-
buffer.push(" errors?: TErrors");
|
|
841
|
-
buffer.push("}");
|
|
842
|
-
buffer.push("");
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
function generateOperationTypes(buffer, operations, config) {
|
|
846
|
-
if (!config.emit) return;
|
|
847
|
-
buffer.push("// Operation Types");
|
|
848
|
-
for (const op of operations) {
|
|
849
|
-
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
850
|
-
const headerType = op.requestHeaders?.length ? isValidJSIdentifier(camelCaseOperationId) ? `typeof headers.${camelCaseOperationId}` : `(typeof headers)[${formatPropertyName(camelCaseOperationId)}]` : "undefined";
|
|
851
|
-
const requestType = wrapTypeReference(op.requestType);
|
|
852
|
-
const responseType = wrapTypeReference(op.responseType);
|
|
853
|
-
const errorsType = buildOperationErrorsType(op.errors);
|
|
854
|
-
buffer.push(
|
|
855
|
-
`export type ${capitalize(camelCaseOperationId)}Operation = OperationDefinition<`
|
|
856
|
-
);
|
|
857
|
-
buffer.push(` "${op.method}",`);
|
|
858
|
-
buffer.push(
|
|
859
|
-
` ${isValidJSIdentifier(camelCaseOperationId) ? `typeof paths.${camelCaseOperationId}` : `(typeof paths)[${formatPropertyName(camelCaseOperationId)}]`},`
|
|
860
|
-
);
|
|
861
|
-
buffer.push(` ${requestType},`);
|
|
862
|
-
buffer.push(` ${responseType},`);
|
|
863
|
-
buffer.push(` ${headerType},`);
|
|
864
|
-
buffer.push(` ${errorsType}`);
|
|
865
|
-
buffer.push(`>;`);
|
|
866
|
-
buffer.push("");
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
function buildOperationErrorsType(errors) {
|
|
870
|
-
if (!errors || !hasAnyErrors2(errors)) {
|
|
871
|
-
return "OperationErrors";
|
|
872
|
-
}
|
|
873
|
-
const errorBucket = buildErrorBucket(errors);
|
|
874
|
-
return `OperationErrors<${errorBucket}>`;
|
|
875
|
-
}
|
|
876
|
-
function buildErrorBucket(bucket) {
|
|
877
|
-
if (!bucket || Object.keys(bucket).length === 0) {
|
|
878
|
-
return "unknown";
|
|
879
|
-
}
|
|
880
|
-
const entries = Object.entries(bucket);
|
|
881
|
-
const accessibleEntries = entries.map(([name, type]) => {
|
|
882
|
-
const propertyKey = formatPropertyName(name);
|
|
883
|
-
const valueType = wrapErrorValueType(type);
|
|
884
|
-
return `${propertyKey}: ${valueType}`;
|
|
885
|
-
});
|
|
886
|
-
return `{ ${accessibleEntries.join("; ")} }`;
|
|
887
|
-
}
|
|
888
|
-
var TYPE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
889
|
-
"any",
|
|
890
|
-
"unknown",
|
|
891
|
-
"never",
|
|
892
|
-
"void",
|
|
893
|
-
"null",
|
|
894
|
-
"undefined",
|
|
895
|
-
"string",
|
|
896
|
-
"number",
|
|
897
|
-
"boolean",
|
|
898
|
-
"bigint",
|
|
899
|
-
"symbol"
|
|
900
|
-
]);
|
|
901
|
-
var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
902
|
-
function wrapTypeReference(typeName) {
|
|
903
|
-
if (!typeName) return "undefined";
|
|
904
|
-
const normalized = typeName.trim();
|
|
905
|
-
if (normalized === "undefined") return "undefined";
|
|
906
|
-
if (TYPE_KEYWORDS.has(normalized)) return normalized;
|
|
907
|
-
if (normalized.startsWith("typeof ")) return normalized;
|
|
908
|
-
const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
|
|
909
|
-
if (arrayMatch) {
|
|
910
|
-
return `z.ZodArray<${wrapTypeReference(arrayMatch[1])}>`;
|
|
911
|
-
}
|
|
912
|
-
if (IDENTIFIER_PATTERN.test(normalized)) {
|
|
913
|
-
return `typeof ${normalized}`;
|
|
914
|
-
}
|
|
915
|
-
return normalized;
|
|
916
|
-
}
|
|
917
|
-
function wrapErrorValueType(typeName) {
|
|
918
|
-
if (!typeName) return "unknown";
|
|
919
|
-
const normalized = typeName.trim();
|
|
920
|
-
if (TYPE_KEYWORDS.has(normalized)) return normalized;
|
|
921
|
-
if (normalized.startsWith("typeof ")) return normalized;
|
|
922
|
-
const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
|
|
923
|
-
if (arrayMatch) {
|
|
924
|
-
return `z.ZodArray<${wrapErrorValueType(arrayMatch[1])}>`;
|
|
925
|
-
}
|
|
926
|
-
if (IDENTIFIER_PATTERN.test(normalized)) {
|
|
927
|
-
return `typeof ${normalized}`;
|
|
928
|
-
}
|
|
929
|
-
return normalized;
|
|
930
|
-
}
|
|
931
|
-
function collectParameters(pathItem, operation, spec) {
|
|
932
|
-
const parametersMap = /* @__PURE__ */ new Map();
|
|
933
|
-
const addParameters = (params) => {
|
|
934
|
-
if (!Array.isArray(params)) return;
|
|
935
|
-
for (const param of params) {
|
|
936
|
-
const resolved = resolveParameter(param, spec);
|
|
937
|
-
if (!resolved) continue;
|
|
938
|
-
const key = `${resolved.in}:${resolved.name}`;
|
|
939
|
-
parametersMap.set(key, resolved);
|
|
940
|
-
}
|
|
91
|
+
function mergeTypesConfig(baseConfig, entryConfig) {
|
|
92
|
+
if (!baseConfig && !entryConfig) return void 0;
|
|
93
|
+
return {
|
|
94
|
+
...baseConfig,
|
|
95
|
+
...entryConfig
|
|
941
96
|
};
|
|
942
|
-
addParameters(pathItem.parameters);
|
|
943
|
-
addParameters(operation.parameters);
|
|
944
|
-
return Array.from(parametersMap.values());
|
|
945
|
-
}
|
|
946
|
-
function resolveParameter(parameter, spec) {
|
|
947
|
-
if (!parameter) return void 0;
|
|
948
|
-
if (parameter.$ref) {
|
|
949
|
-
const refName = extractRefName(parameter.$ref);
|
|
950
|
-
const resolved = spec.components?.parameters?.[refName];
|
|
951
|
-
if (!resolved) return void 0;
|
|
952
|
-
const { $ref, ...overrides } = parameter;
|
|
953
|
-
return {
|
|
954
|
-
...resolved,
|
|
955
|
-
...overrides
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
return parameter;
|
|
959
|
-
}
|
|
960
|
-
function extractPathParams(path2) {
|
|
961
|
-
const params = [];
|
|
962
|
-
const matches = path2.match(/{([^}]+)}/g);
|
|
963
|
-
if (matches) {
|
|
964
|
-
for (const match of matches) {
|
|
965
|
-
const paramName = match.slice(1, -1);
|
|
966
|
-
params.push({
|
|
967
|
-
name: paramName,
|
|
968
|
-
type: "string"
|
|
969
|
-
// OpenAPI path params are always strings
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
return params;
|
|
974
|
-
}
|
|
975
|
-
function getRequestType(operation) {
|
|
976
|
-
const requestBody = operation.requestBody?.content?.["application/json"]?.schema;
|
|
977
|
-
if (!requestBody) return void 0;
|
|
978
|
-
if (requestBody.$ref) {
|
|
979
|
-
return extractRefName(requestBody.$ref);
|
|
980
|
-
}
|
|
981
|
-
const typeName = `${capitalize(toCamelCase(operation.operationId))}Request`;
|
|
982
|
-
return typeName;
|
|
983
|
-
}
|
|
984
|
-
function getResponseTypes(operation, operationId, nameMap) {
|
|
985
|
-
const responses = operation.responses ?? {};
|
|
986
|
-
const successCodes = /* @__PURE__ */ new Map();
|
|
987
|
-
const errorEntries = [];
|
|
988
|
-
for (const [statusCode, response] of Object.entries(responses)) {
|
|
989
|
-
const content = response?.content;
|
|
990
|
-
if (!content || Object.keys(content).length === 0) {
|
|
991
|
-
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
992
|
-
successCodes.set(statusCode, "undefined");
|
|
993
|
-
} else if (isErrorStatus(statusCode)) {
|
|
994
|
-
errorEntries.push({
|
|
995
|
-
code: statusCode,
|
|
996
|
-
schema: "undefined"
|
|
997
|
-
});
|
|
998
|
-
}
|
|
999
|
-
continue;
|
|
1000
|
-
}
|
|
1001
|
-
const contentType = findContentType(content);
|
|
1002
|
-
const resolvedSchema = content[contentType]?.schema;
|
|
1003
|
-
if (!resolvedSchema) {
|
|
1004
|
-
const inferredType = inferResponseType(contentType, statusCode);
|
|
1005
|
-
if (inferredType) {
|
|
1006
|
-
if (isErrorStatus(statusCode)) {
|
|
1007
|
-
errorEntries.push({
|
|
1008
|
-
code: statusCode,
|
|
1009
|
-
schema: inferredType
|
|
1010
|
-
});
|
|
1011
|
-
} else if (/^2\d\d$/.test(statusCode)) {
|
|
1012
|
-
successCodes.set(statusCode, inferredType);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
continue;
|
|
1016
|
-
}
|
|
1017
|
-
if (isErrorStatus(statusCode)) {
|
|
1018
|
-
errorEntries.push({ code: statusCode, schema: resolvedSchema });
|
|
1019
|
-
continue;
|
|
1020
|
-
}
|
|
1021
|
-
if (/^2\d\d$/.test(statusCode)) {
|
|
1022
|
-
successCodes.set(statusCode, resolvedSchema);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
const successResponse = selectSuccessResponse(
|
|
1026
|
-
successCodes,
|
|
1027
|
-
operationId,
|
|
1028
|
-
nameMap
|
|
1029
|
-
);
|
|
1030
|
-
const errors = buildErrorGroups(errorEntries, operationId, nameMap);
|
|
1031
|
-
return { successResponse, errors };
|
|
1032
|
-
}
|
|
1033
|
-
function selectSuccessResponse(responses, operationId, nameMap) {
|
|
1034
|
-
if (responses.size === 0) return void 0;
|
|
1035
|
-
const preferredOrder = ["200", "201", "204"];
|
|
1036
|
-
for (const code of preferredOrder) {
|
|
1037
|
-
const schema = responses.get(code);
|
|
1038
|
-
if (schema) {
|
|
1039
|
-
if (typeof schema === "string") {
|
|
1040
|
-
return schema;
|
|
1041
|
-
}
|
|
1042
|
-
return resolveResponseType(
|
|
1043
|
-
schema,
|
|
1044
|
-
`${capitalize(toCamelCase(operationId))}Response`,
|
|
1045
|
-
nameMap
|
|
1046
|
-
);
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
const [, firstSchema] = responses.entries().next().value ?? [];
|
|
1050
|
-
if (!firstSchema) return void 0;
|
|
1051
|
-
if (typeof firstSchema === "string") {
|
|
1052
|
-
return firstSchema;
|
|
1053
|
-
}
|
|
1054
|
-
return resolveResponseType(
|
|
1055
|
-
firstSchema,
|
|
1056
|
-
`${capitalize(toCamelCase(operationId))}Response`,
|
|
1057
|
-
nameMap
|
|
1058
|
-
);
|
|
1059
|
-
}
|
|
1060
|
-
function buildErrorGroups(errors = [], operationId, nameMap) {
|
|
1061
|
-
if (!errors.length) return void 0;
|
|
1062
|
-
const group = {};
|
|
1063
|
-
for (const { code, schema } of errors) {
|
|
1064
|
-
const identifier = mapStatusToIdentifier(code);
|
|
1065
|
-
const typeName = resolveResponseType(
|
|
1066
|
-
schema,
|
|
1067
|
-
`${capitalize(toCamelCase(operationId))}${capitalize(identifier)}`,
|
|
1068
|
-
nameMap
|
|
1069
|
-
);
|
|
1070
|
-
group[identifier] = typeName;
|
|
1071
|
-
}
|
|
1072
|
-
return group;
|
|
1073
|
-
}
|
|
1074
|
-
function resolveResponseType(schema, fallbackName, nameMap) {
|
|
1075
|
-
if (typeof schema === "string") {
|
|
1076
|
-
return schema;
|
|
1077
|
-
}
|
|
1078
|
-
if (schema.$ref) {
|
|
1079
|
-
const refName = extractRefName(schema.$ref);
|
|
1080
|
-
return nameMap?.get(refName) || refName;
|
|
1081
|
-
}
|
|
1082
|
-
if (schema.type === "array" && schema.items?.$ref) {
|
|
1083
|
-
const itemRef = extractRefName(schema.items.$ref);
|
|
1084
|
-
const sanitizedItemRef = nameMap?.get(itemRef) || itemRef;
|
|
1085
|
-
return `z.array(${sanitizedItemRef})`;
|
|
1086
|
-
}
|
|
1087
|
-
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
1088
|
-
return fallbackName;
|
|
1089
|
-
}
|
|
1090
|
-
return fallbackName;
|
|
1091
|
-
}
|
|
1092
|
-
function getRequestHeaders(parameters) {
|
|
1093
|
-
const headers = [];
|
|
1094
|
-
for (const param of parameters ?? []) {
|
|
1095
|
-
if (param.in === "header") {
|
|
1096
|
-
headers.push({
|
|
1097
|
-
name: param.name,
|
|
1098
|
-
description: param.description,
|
|
1099
|
-
schema: param.schema,
|
|
1100
|
-
required: param.required
|
|
1101
|
-
});
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
return headers;
|
|
1105
|
-
}
|
|
1106
|
-
function getQueryParams(parameters) {
|
|
1107
|
-
const queryParams = [];
|
|
1108
|
-
for (const param of parameters ?? []) {
|
|
1109
|
-
if (param.in === "query") {
|
|
1110
|
-
queryParams.push({
|
|
1111
|
-
name: param.name,
|
|
1112
|
-
description: param.description,
|
|
1113
|
-
schema: param.schema,
|
|
1114
|
-
required: param.required
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
return queryParams;
|
|
1119
|
-
}
|
|
1120
|
-
function mapHeaderToZodType(header) {
|
|
1121
|
-
const schema = header.schema ?? {};
|
|
1122
|
-
const schemaType = schema.type;
|
|
1123
|
-
switch (schemaType) {
|
|
1124
|
-
case "integer":
|
|
1125
|
-
case "number":
|
|
1126
|
-
return "z.coerce.number()";
|
|
1127
|
-
case "boolean":
|
|
1128
|
-
return "z.coerce.boolean()";
|
|
1129
|
-
case "array": {
|
|
1130
|
-
const items = schema.items ?? { type: "string" };
|
|
1131
|
-
const itemType = items.type === "integer" || items.type === "number" ? "z.coerce.number()" : items.type === "boolean" ? "z.coerce.boolean()" : "z.string()";
|
|
1132
|
-
return `z.array(${itemType})`;
|
|
1133
|
-
}
|
|
1134
|
-
default:
|
|
1135
|
-
return "z.string()";
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
function mapQueryType(param) {
|
|
1139
|
-
return mapQuerySchemaType(param.schema);
|
|
1140
|
-
}
|
|
1141
|
-
function mapQuerySchemaType(schema) {
|
|
1142
|
-
if (!schema) return "string";
|
|
1143
|
-
if (schema.type === "array") {
|
|
1144
|
-
const itemType = mapQuerySchemaType(schema.items);
|
|
1145
|
-
return `Array<${itemType}>`;
|
|
1146
|
-
}
|
|
1147
|
-
switch (schema.type) {
|
|
1148
|
-
case "integer":
|
|
1149
|
-
case "number":
|
|
1150
|
-
return "number";
|
|
1151
|
-
case "boolean":
|
|
1152
|
-
return "boolean";
|
|
1153
|
-
default:
|
|
1154
|
-
return "string";
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
function convertQueryParamValue(schema, accessor) {
|
|
1158
|
-
if (!schema) {
|
|
1159
|
-
return `String(${accessor})`;
|
|
1160
|
-
}
|
|
1161
|
-
switch (schema.type) {
|
|
1162
|
-
case "integer":
|
|
1163
|
-
case "number":
|
|
1164
|
-
return `String(${accessor})`;
|
|
1165
|
-
case "boolean":
|
|
1166
|
-
return `${accessor} ? "true" : "false"`;
|
|
1167
|
-
default:
|
|
1168
|
-
return `String(${accessor})`;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
function generateZodSchema(name, schema, generatedTypes, options, nameMap) {
|
|
1172
|
-
if (generatedTypes.has(name)) return "";
|
|
1173
|
-
generatedTypes.add(name);
|
|
1174
|
-
if (schema.enum) {
|
|
1175
|
-
const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
|
|
1176
|
-
return `export const ${name} = z.enum([${enumValues}]);`;
|
|
1177
|
-
}
|
|
1178
|
-
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
1179
|
-
const allOfParts = schema.allOf.map(
|
|
1180
|
-
(part) => getZodTypeFromSchema(part, options, nameMap)
|
|
1181
|
-
);
|
|
1182
|
-
if (allOfParts.length === 0) return `export const ${name} = z.object({});`;
|
|
1183
|
-
if (allOfParts.length === 1)
|
|
1184
|
-
return `export const ${name} = ${allOfParts[0]};`;
|
|
1185
|
-
const first = allOfParts[0];
|
|
1186
|
-
const rest = allOfParts.slice(1).map((part) => `.and(${part})`).join("");
|
|
1187
|
-
return `export const ${name} = ${first}${rest};`;
|
|
1188
|
-
}
|
|
1189
|
-
if (schema.type === "object" || schema.properties) {
|
|
1190
|
-
return `export const ${name} = ${buildZodObject(schema, options, nameMap)};`;
|
|
1191
|
-
}
|
|
1192
|
-
if (schema.type === "array") {
|
|
1193
|
-
const itemSchema = schema.items ?? { type: "unknown" };
|
|
1194
|
-
const itemType = getZodTypeFromSchema(itemSchema, options, nameMap);
|
|
1195
|
-
const builder = applyStrictArrayBounds(
|
|
1196
|
-
schema,
|
|
1197
|
-
`z.array(${itemType})`,
|
|
1198
|
-
itemSchema,
|
|
1199
|
-
options.strictNumeric
|
|
1200
|
-
);
|
|
1201
|
-
return `export const ${name} = ${builder};`;
|
|
1202
|
-
}
|
|
1203
|
-
return `export const ${name} = ${getZodTypeFromSchema(schema, options, nameMap)};`;
|
|
1204
|
-
}
|
|
1205
|
-
function getZodTypeFromSchema(schema, options, nameMap) {
|
|
1206
|
-
if (schema.$ref) {
|
|
1207
|
-
const refName = extractRefName(schema.$ref);
|
|
1208
|
-
return nameMap?.get(refName) || refName;
|
|
1209
|
-
}
|
|
1210
|
-
if (schema.enum) {
|
|
1211
|
-
const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
|
|
1212
|
-
return `z.enum([${enumValues}])`;
|
|
1213
|
-
}
|
|
1214
|
-
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
1215
|
-
const allOfParts = schema.allOf.map(
|
|
1216
|
-
(part) => getZodTypeFromSchema(part, options, nameMap)
|
|
1217
|
-
);
|
|
1218
|
-
if (allOfParts.length === 0) return "z.object({})";
|
|
1219
|
-
if (allOfParts.length === 1) return allOfParts[0];
|
|
1220
|
-
const first = allOfParts[0];
|
|
1221
|
-
const rest = allOfParts.slice(1).map((part) => `.and(${part})`).join("");
|
|
1222
|
-
return `${first}${rest}`;
|
|
1223
|
-
}
|
|
1224
|
-
if (schema.type === "object" || schema.properties || schema.allOf || schema.oneOf || schema.anyOf) {
|
|
1225
|
-
return buildZodObject(schema, options, nameMap);
|
|
1226
|
-
}
|
|
1227
|
-
switch (schema.type) {
|
|
1228
|
-
case "string":
|
|
1229
|
-
return buildString(schema, options);
|
|
1230
|
-
case "boolean":
|
|
1231
|
-
return "z.boolean()";
|
|
1232
|
-
case "array":
|
|
1233
|
-
return `z.array(${getZodTypeFromSchema(
|
|
1234
|
-
schema.items ?? { type: "unknown" },
|
|
1235
|
-
options,
|
|
1236
|
-
nameMap
|
|
1237
|
-
)})`;
|
|
1238
|
-
case "null":
|
|
1239
|
-
return "z.null()";
|
|
1240
|
-
case "number":
|
|
1241
|
-
return buildNumber(schema, options);
|
|
1242
|
-
case "integer":
|
|
1243
|
-
return buildInteger(schema, options);
|
|
1244
|
-
default:
|
|
1245
|
-
return "z.unknown()";
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
function buildZodObject(schema, options, nameMap) {
|
|
1249
|
-
const properties = [];
|
|
1250
|
-
for (const [propName, propSchema] of Object.entries(
|
|
1251
|
-
schema.properties || {}
|
|
1252
|
-
)) {
|
|
1253
|
-
const isRequired = schema.required?.includes(propName) ?? false;
|
|
1254
|
-
const zodType = getZodTypeFromSchema(propSchema, options, nameMap);
|
|
1255
|
-
const finalType = isRequired ? zodType : `${zodType}.optional()`;
|
|
1256
|
-
properties.push(` ${formatPropertyName(propName)}: ${finalType},`);
|
|
1257
|
-
}
|
|
1258
|
-
if (properties.length === 0) {
|
|
1259
|
-
return "z.object({})";
|
|
1260
|
-
}
|
|
1261
|
-
return `z.object({
|
|
1262
|
-
${properties.join("\n")}
|
|
1263
|
-
})`;
|
|
1264
|
-
}
|
|
1265
|
-
function buildString(schema, options) {
|
|
1266
|
-
if (options.strictDates) {
|
|
1267
|
-
switch (schema.format) {
|
|
1268
|
-
case "date-time":
|
|
1269
|
-
return "z.string().datetime()";
|
|
1270
|
-
case "date":
|
|
1271
|
-
return "z.string().date()";
|
|
1272
|
-
case "time":
|
|
1273
|
-
return "z.string().time()";
|
|
1274
|
-
case "duration":
|
|
1275
|
-
return "z.string().duration()";
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
let builder = "z.string()";
|
|
1279
|
-
if (options.strictNumeric) {
|
|
1280
|
-
if (typeof schema.minLength === "number") {
|
|
1281
|
-
builder += `.min(${schema.minLength})`;
|
|
1282
|
-
}
|
|
1283
|
-
if (typeof schema.maxLength === "number") {
|
|
1284
|
-
builder += `.max(${schema.maxLength})`;
|
|
1285
|
-
}
|
|
1286
|
-
if (schema.pattern) {
|
|
1287
|
-
builder += `.regex(new RegExp(${JSON.stringify(schema.pattern)}))`;
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
switch (schema.format) {
|
|
1291
|
-
case "uuid":
|
|
1292
|
-
return `${builder}.uuid()`;
|
|
1293
|
-
case "email":
|
|
1294
|
-
return `${builder}.email()`;
|
|
1295
|
-
case "uri":
|
|
1296
|
-
case "url":
|
|
1297
|
-
return `${builder}.url()`;
|
|
1298
|
-
case "ipv4":
|
|
1299
|
-
return `${builder}.ip({ version: "v4" })`;
|
|
1300
|
-
case "ipv6":
|
|
1301
|
-
return `${builder}.ip({ version: "v6" })`;
|
|
1302
|
-
default:
|
|
1303
|
-
return builder;
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
function buildNumber(schema, options) {
|
|
1307
|
-
let builder = "z.number()";
|
|
1308
|
-
if (options.strictNumeric) {
|
|
1309
|
-
builder = applyNumericBounds(schema, builder);
|
|
1310
|
-
if (typeof schema.multipleOf === "number" && schema.multipleOf !== 0) {
|
|
1311
|
-
builder += `.refine((value) => Math.abs(value / ${schema.multipleOf} - Math.round(value / ${schema.multipleOf})) < Number.EPSILON, { message: "Must be a multiple of ${schema.multipleOf}" })`;
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
return builder;
|
|
1315
|
-
}
|
|
1316
|
-
function buildInteger(schema, options) {
|
|
1317
|
-
let builder = buildNumber(schema, options);
|
|
1318
|
-
builder += ".int()";
|
|
1319
|
-
return builder;
|
|
1320
|
-
}
|
|
1321
|
-
function applyStrictArrayBounds(schema, builder, itemSchema, enforceBounds) {
|
|
1322
|
-
if (!enforceBounds) {
|
|
1323
|
-
return builder;
|
|
1324
|
-
}
|
|
1325
|
-
if (typeof schema.minItems === "number") {
|
|
1326
|
-
builder += `.min(${schema.minItems})`;
|
|
1327
|
-
}
|
|
1328
|
-
if (typeof schema.maxItems === "number") {
|
|
1329
|
-
builder += `.max(${schema.maxItems})`;
|
|
1330
|
-
}
|
|
1331
|
-
if (schema.uniqueItems && isPrimitiveLike(itemSchema)) {
|
|
1332
|
-
builder += '.refine((items) => new Set(items).size === items.length, { message: "Items must be unique" })';
|
|
1333
|
-
}
|
|
1334
|
-
return builder;
|
|
1335
|
-
}
|
|
1336
|
-
function isPrimitiveLike(schema) {
|
|
1337
|
-
if (schema?.$ref) return false;
|
|
1338
|
-
const primitiveTypes = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean"]);
|
|
1339
|
-
return primitiveTypes.has(schema?.type);
|
|
1340
|
-
}
|
|
1341
|
-
function applyNumericBounds(schema, builder) {
|
|
1342
|
-
if (typeof schema.minimum === "number") {
|
|
1343
|
-
if (schema.exclusiveMinimum === true) {
|
|
1344
|
-
builder += `.gt(${schema.minimum})`;
|
|
1345
|
-
} else {
|
|
1346
|
-
builder += `.min(${schema.minimum})`;
|
|
1347
|
-
}
|
|
1348
|
-
} else if (typeof schema.exclusiveMinimum === "number") {
|
|
1349
|
-
builder += `.gt(${schema.exclusiveMinimum})`;
|
|
1350
|
-
}
|
|
1351
|
-
if (typeof schema.maximum === "number") {
|
|
1352
|
-
if (schema.exclusiveMaximum === true) {
|
|
1353
|
-
builder += `.lt(${schema.maximum})`;
|
|
1354
|
-
} else {
|
|
1355
|
-
builder += `.max(${schema.maximum})`;
|
|
1356
|
-
}
|
|
1357
|
-
} else if (typeof schema.exclusiveMaximum === "number") {
|
|
1358
|
-
builder += `.lt(${schema.exclusiveMaximum})`;
|
|
1359
|
-
}
|
|
1360
|
-
return builder;
|
|
1361
97
|
}
|
|
1362
98
|
|
|
1363
99
|
// src/cli.ts
|
|
@@ -1385,8 +121,8 @@ async function main() {
|
|
|
1385
121
|
return;
|
|
1386
122
|
}
|
|
1387
123
|
await generateSingle({
|
|
1388
|
-
inputFile,
|
|
1389
|
-
outputFile,
|
|
124
|
+
resolvedInput: path2.resolve(inputFile),
|
|
125
|
+
resolvedOutput: path2.resolve(outputFile),
|
|
1390
126
|
strictDates: parsed.strictDates,
|
|
1391
127
|
strictNumeric: parsed.strictNumeric
|
|
1392
128
|
});
|
|
@@ -1449,42 +185,26 @@ function printHelp() {
|
|
|
1449
185
|
console.log("");
|
|
1450
186
|
console.log("Config file format:");
|
|
1451
187
|
console.log(
|
|
1452
|
-
' {"types"?: { emit?, helpers?, helpersOutput? }, "schemas": [{ input, output, strictDates?, strictNumeric?, types? }] }'
|
|
188
|
+
' {"types"?: { emit?, helpers?, helpersOutput?, optionalType?, treeShake? }, "schemas": [{ input, output, strictDates?, strictNumeric?, types? }] }'
|
|
1453
189
|
);
|
|
1454
190
|
}
|
|
1455
191
|
async function runFromConfig(parsed) {
|
|
1456
192
|
const configPath = parsed.configPath;
|
|
1457
|
-
const resolvedConfigPath =
|
|
1458
|
-
const
|
|
1459
|
-
validateConfig(
|
|
1460
|
-
const
|
|
1461
|
-
const
|
|
193
|
+
const resolvedConfigPath = path2.resolve(configPath);
|
|
194
|
+
const configDocument = await loadConfig(resolvedConfigPath);
|
|
195
|
+
validateConfig(configDocument);
|
|
196
|
+
const config = configDocument;
|
|
197
|
+
const baseDir = path2.dirname(resolvedConfigPath);
|
|
198
|
+
const defaults = {
|
|
199
|
+
strictDates: parsed.strictDates,
|
|
200
|
+
strictNumeric: parsed.strictNumeric,
|
|
201
|
+
types: config.types,
|
|
202
|
+
operationIds: void 0
|
|
203
|
+
};
|
|
1462
204
|
for (const entry of config.schemas) {
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
const typesConfig = resolveTypesConfig(baseTypesConfig, entry.types);
|
|
1466
|
-
await generateSingle({
|
|
1467
|
-
inputFile,
|
|
1468
|
-
outputFile,
|
|
1469
|
-
strictDates: entry.strictDates ?? parsed.strictDates,
|
|
1470
|
-
strictNumeric: entry.strictNumeric ?? parsed.strictNumeric,
|
|
1471
|
-
typesConfig
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
async function loadConfig(filePath) {
|
|
1476
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
1477
|
-
if (extension === ".json") {
|
|
1478
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
1479
|
-
return JSON.parse(content);
|
|
205
|
+
const options = normalizeGenerationOptions(entry, baseDir, defaults);
|
|
206
|
+
await generateSingle(options);
|
|
1480
207
|
}
|
|
1481
|
-
if (extension === ".yaml" || extension === ".yml") {
|
|
1482
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
1483
|
-
return (0, import_js_yaml.load)(content);
|
|
1484
|
-
}
|
|
1485
|
-
const fileUrl = (0, import_url.pathToFileURL)(filePath).href;
|
|
1486
|
-
const module2 = await import(fileUrl);
|
|
1487
|
-
return module2.default ?? module2.config ?? module2;
|
|
1488
208
|
}
|
|
1489
209
|
function validateConfig(config) {
|
|
1490
210
|
if (!config || typeof config !== "object") {
|
|
@@ -1502,60 +222,44 @@ function validateConfig(config) {
|
|
|
1502
222
|
}
|
|
1503
223
|
}
|
|
1504
224
|
}
|
|
1505
|
-
function resolvePath(filePath, baseDir) {
|
|
1506
|
-
return path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);
|
|
1507
|
-
}
|
|
1508
|
-
function resolveTypesConfig(baseConfig, entryConfig) {
|
|
1509
|
-
if (!baseConfig && !entryConfig) return void 0;
|
|
1510
|
-
return {
|
|
1511
|
-
...baseConfig,
|
|
1512
|
-
...entryConfig
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
225
|
async function generateSingle(options) {
|
|
1516
|
-
const {
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
226
|
+
const {
|
|
227
|
+
resolvedInput,
|
|
228
|
+
resolvedOutput,
|
|
229
|
+
strictDates = false,
|
|
230
|
+
strictNumeric = false,
|
|
231
|
+
types,
|
|
232
|
+
operationIds
|
|
233
|
+
} = options;
|
|
234
|
+
const spec = await loadSpec(resolvedInput);
|
|
235
|
+
const result = (0, import_core.generateFromDocument)(spec, {
|
|
1521
236
|
strictDates,
|
|
1522
237
|
strictNumeric,
|
|
1523
|
-
types
|
|
238
|
+
types,
|
|
239
|
+
operationIds
|
|
1524
240
|
});
|
|
1525
|
-
|
|
1526
|
-
|
|
241
|
+
await (0, import_promises.mkdir)(path2.dirname(resolvedOutput), { recursive: true });
|
|
242
|
+
await Bun.write(resolvedOutput, result.output);
|
|
1527
243
|
console.log(`\u2705 Generated TypeScript types in ${resolvedOutput}`);
|
|
1528
244
|
console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths || {}).length} paths`);
|
|
1529
245
|
if (spec.webhooks) {
|
|
1530
246
|
console.log(`\u{1FA9D} Processed ${Object.keys(spec.webhooks).length} webhooks`);
|
|
1531
247
|
}
|
|
1532
248
|
if (result.helperFile) {
|
|
1533
|
-
const helperPath =
|
|
1534
|
-
const absoluteResolvedOutput =
|
|
1535
|
-
const absoluteHelperPath =
|
|
249
|
+
const helperPath = path2.isAbsolute(result.helperFile.path) ? result.helperFile.path : path2.resolve(path2.dirname(resolvedOutput), result.helperFile.path);
|
|
250
|
+
const absoluteResolvedOutput = path2.resolve(resolvedOutput);
|
|
251
|
+
const absoluteHelperPath = path2.resolve(helperPath);
|
|
1536
252
|
if (absoluteResolvedOutput === absoluteHelperPath) {
|
|
1537
253
|
console.warn(
|
|
1538
254
|
`\u26A0\uFE0F Skipping helper file generation: would overwrite main output at ${absoluteResolvedOutput}`
|
|
1539
255
|
);
|
|
1540
256
|
return;
|
|
1541
257
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
encoding: "utf8"
|
|
1545
|
-
});
|
|
258
|
+
await (0, import_promises.mkdir)(path2.dirname(helperPath), { recursive: true });
|
|
259
|
+
await Bun.write(helperPath, result.helperFile.content);
|
|
1546
260
|
console.log(`\u{1F4E6} Generated helper types in ${helperPath}`);
|
|
1547
261
|
}
|
|
1548
262
|
}
|
|
1549
|
-
function readSpec(filePath) {
|
|
1550
|
-
if (!fs.existsSync(filePath)) {
|
|
1551
|
-
throw new Error(`Input file not found: ${filePath}`);
|
|
1552
|
-
}
|
|
1553
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
1554
|
-
if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
|
|
1555
|
-
return (0, import_js_yaml.load)(content);
|
|
1556
|
-
}
|
|
1557
|
-
return JSON.parse(content);
|
|
1558
|
-
}
|
|
1559
263
|
main().catch((error) => {
|
|
1560
264
|
console.error("\u274C Error:", error);
|
|
1561
265
|
process.exit(1);
|