zenko 0.1.12 → 0.1.13

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