zenko 0.1.3 → 0.1.5
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 +233 -91
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +233 -91
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +218 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +218 -89
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -210,7 +210,7 @@ function isErrorStatus(status) {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
// src/zenko.ts
|
|
213
|
-
function
|
|
213
|
+
function generateWithMetadata(spec, options = {}) {
|
|
214
214
|
const output = [];
|
|
215
215
|
const generatedTypes = /* @__PURE__ */ new Set();
|
|
216
216
|
const { strictDates = false, strictNumeric = false } = options;
|
|
@@ -243,8 +243,11 @@ function generate(spec, options = {}) {
|
|
|
243
243
|
const pathParamNames = op.pathParams.map((p) => p.name);
|
|
244
244
|
const hasPathParams = pathParamNames.length > 0;
|
|
245
245
|
const hasQueryParams = op.queryParams.length > 0;
|
|
246
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
246
247
|
if (!hasPathParams && !hasQueryParams) {
|
|
247
|
-
output.push(
|
|
248
|
+
output.push(
|
|
249
|
+
` ${formatPropertyName(camelCaseOperationId)}: () => "${op.path}",`
|
|
250
|
+
);
|
|
248
251
|
continue;
|
|
249
252
|
}
|
|
250
253
|
const allParamNames = [
|
|
@@ -267,11 +270,13 @@ function generate(spec, options = {}) {
|
|
|
267
270
|
const pathWithParams = op.path.replace(/{([^}]+)}/g, "${$1}");
|
|
268
271
|
if (!hasQueryParams) {
|
|
269
272
|
output.push(
|
|
270
|
-
` ${
|
|
273
|
+
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => \`${pathWithParams}\`,`
|
|
271
274
|
);
|
|
272
275
|
continue;
|
|
273
276
|
}
|
|
274
|
-
output.push(
|
|
277
|
+
output.push(
|
|
278
|
+
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => {`
|
|
279
|
+
);
|
|
275
280
|
output.push(" const params = new URLSearchParams()");
|
|
276
281
|
for (const param of op.queryParams) {
|
|
277
282
|
const propertyKey = formatPropertyName(param.name);
|
|
@@ -316,83 +321,57 @@ function generate(spec, options = {}) {
|
|
|
316
321
|
}
|
|
317
322
|
output.push("} as const;");
|
|
318
323
|
output.push("");
|
|
319
|
-
output.push("// Header
|
|
320
|
-
output.push("export const
|
|
324
|
+
output.push("// Header Schemas");
|
|
325
|
+
output.push("export const headerSchemas = {");
|
|
321
326
|
for (const op of operations) {
|
|
327
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
322
328
|
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
323
|
-
output.push(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const typeEntries = op.requestHeaders.map(
|
|
327
|
-
(header) => `${formatPropertyName(header.name)}${header.required ? "" : "?"}: ${mapHeaderType(
|
|
328
|
-
header
|
|
329
|
-
)}`
|
|
330
|
-
).join(", ");
|
|
331
|
-
const requiredHeaders = op.requestHeaders.filter(
|
|
332
|
-
(header) => header.required
|
|
333
|
-
);
|
|
334
|
-
const optionalHeaders = op.requestHeaders.filter(
|
|
335
|
-
(header) => !header.required
|
|
336
|
-
);
|
|
337
|
-
const hasRequired = requiredHeaders.length > 0;
|
|
338
|
-
const signature = hasRequired ? `(params: { ${typeEntries} })` : `(params: { ${typeEntries} } = {})`;
|
|
339
|
-
if (optionalHeaders.length === 0) {
|
|
340
|
-
output.push(` ${op.operationId}: ${signature} => ({`);
|
|
341
|
-
for (const header of requiredHeaders) {
|
|
342
|
-
const propertyKey = formatPropertyName(header.name);
|
|
343
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
344
|
-
output.push(` ${propertyKey}: ${accessor},`);
|
|
345
|
-
}
|
|
346
|
-
output.push(" }),");
|
|
329
|
+
output.push(
|
|
330
|
+
` ${formatPropertyName(camelCaseOperationId)}: z.object({}),`
|
|
331
|
+
);
|
|
347
332
|
continue;
|
|
348
333
|
}
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
334
|
+
const schemaFields = op.requestHeaders.map((header) => {
|
|
335
|
+
const zodType = mapHeaderToZodType(header);
|
|
336
|
+
const optional = header.required ? "" : ".optional()";
|
|
337
|
+
return ` ${formatPropertyName(header.name)}: ${zodType}${optional},`;
|
|
338
|
+
}).join("\n");
|
|
339
|
+
output.push(` ${formatPropertyName(camelCaseOperationId)}: z.object({`);
|
|
340
|
+
output.push(schemaFields);
|
|
341
|
+
output.push(" }),");
|
|
342
|
+
}
|
|
343
|
+
output.push("} as const;");
|
|
344
|
+
output.push("");
|
|
345
|
+
output.push("// Header Functions");
|
|
346
|
+
output.push("export const headers = {");
|
|
347
|
+
for (const op of operations) {
|
|
348
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
349
|
+
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
354
350
|
output.push(
|
|
355
|
-
`
|
|
351
|
+
` ${formatPropertyName(camelCaseOperationId)}: () => ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse({}),`
|
|
356
352
|
);
|
|
357
353
|
continue;
|
|
358
354
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
)
|
|
362
|
-
output.push(
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
for (const header of requiredHeaders) {
|
|
366
|
-
const propertyKey = formatPropertyName(header.name);
|
|
367
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
368
|
-
output.push(` ${propertyKey}: ${accessor},`);
|
|
369
|
-
}
|
|
370
|
-
output.push(" }");
|
|
371
|
-
} else {
|
|
372
|
-
output.push(` const headers: Record<string, ${valueTypes}> = {}`);
|
|
373
|
-
}
|
|
374
|
-
for (const header of optionalHeaders) {
|
|
375
|
-
const propertyKey = formatPropertyName(header.name);
|
|
376
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
377
|
-
const assignment = isValidJSIdentifier(header.name) ? `headers.${header.name}` : `headers[${propertyKey}]`;
|
|
378
|
-
output.push(` if (${accessor} !== undefined) {`);
|
|
379
|
-
output.push(` ${assignment} = ${accessor}`);
|
|
380
|
-
output.push(" }");
|
|
381
|
-
}
|
|
382
|
-
output.push(" return headers");
|
|
355
|
+
output.push(
|
|
356
|
+
` ${formatPropertyName(camelCaseOperationId)}: (params: z.input<${isValidJSIdentifier(camelCaseOperationId) ? `typeof headerSchemas.${camelCaseOperationId}` : `(typeof headerSchemas)[${formatPropertyName(camelCaseOperationId)}]`}>) => {`
|
|
357
|
+
);
|
|
358
|
+
output.push(
|
|
359
|
+
` return ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse(params)`
|
|
360
|
+
);
|
|
383
361
|
output.push(" },");
|
|
384
362
|
}
|
|
385
363
|
output.push("} as const;");
|
|
386
364
|
output.push("");
|
|
387
365
|
output.push("// Operation Objects");
|
|
388
366
|
for (const op of operations) {
|
|
389
|
-
|
|
367
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
368
|
+
output.push(`export const ${camelCaseOperationId} = {`);
|
|
390
369
|
output.push(` method: "${op.method}",`);
|
|
391
|
-
output.push(` path: paths.${
|
|
370
|
+
output.push(` path: paths.${camelCaseOperationId},`);
|
|
392
371
|
appendOperationField(output, "request", op.requestType);
|
|
393
372
|
appendOperationField(output, "response", op.responseType);
|
|
394
373
|
if (op.requestHeaders && op.requestHeaders.length > 0) {
|
|
395
|
-
output.push(` headers: headers.${
|
|
374
|
+
output.push(` headers: headers.${camelCaseOperationId},`);
|
|
396
375
|
}
|
|
397
376
|
if (op.errors && hasAnyErrors(op.errors)) {
|
|
398
377
|
output.push(" errors: {");
|
|
@@ -406,7 +385,16 @@ function generate(spec, options = {}) {
|
|
|
406
385
|
output.push("");
|
|
407
386
|
}
|
|
408
387
|
generateOperationTypes(output, operations, typesConfig);
|
|
409
|
-
|
|
388
|
+
const result = {
|
|
389
|
+
output: output.join("\n")
|
|
390
|
+
};
|
|
391
|
+
if (typesConfig.emit && typesConfig.helpers === "file" && typesConfig.helpersOutput) {
|
|
392
|
+
result.helperFile = {
|
|
393
|
+
path: typesConfig.helpersOutput,
|
|
394
|
+
content: generateHelperFile()
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
return result;
|
|
410
398
|
}
|
|
411
399
|
function appendOperationField(buffer, key, value) {
|
|
412
400
|
if (!value) return;
|
|
@@ -443,6 +431,36 @@ function isRequestMethod(method) {
|
|
|
443
431
|
return false;
|
|
444
432
|
}
|
|
445
433
|
}
|
|
434
|
+
var CONTENT_TYPE_MAP = {
|
|
435
|
+
"application/json": "unknown",
|
|
436
|
+
// Will use schema when available
|
|
437
|
+
"text/csv": "string",
|
|
438
|
+
"text/plain": "string",
|
|
439
|
+
// Binary/ambiguous types default to unknown for cross-platform compatibility
|
|
440
|
+
"application/octet-stream": "unknown",
|
|
441
|
+
"application/pdf": "unknown"
|
|
442
|
+
};
|
|
443
|
+
function findContentType(content) {
|
|
444
|
+
const contentTypes = Object.keys(content);
|
|
445
|
+
if (contentTypes.includes("application/json")) {
|
|
446
|
+
return "application/json";
|
|
447
|
+
}
|
|
448
|
+
for (const contentType of contentTypes) {
|
|
449
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
450
|
+
return contentType;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return contentTypes[0] || "";
|
|
454
|
+
}
|
|
455
|
+
function inferResponseType(contentType, statusCode) {
|
|
456
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
457
|
+
return "undefined";
|
|
458
|
+
}
|
|
459
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
460
|
+
return CONTENT_TYPE_MAP[contentType];
|
|
461
|
+
}
|
|
462
|
+
return "unknown";
|
|
463
|
+
}
|
|
446
464
|
function parseOperations(spec) {
|
|
447
465
|
const operations = [];
|
|
448
466
|
for (const [path2, pathItem] of Object.entries(spec.paths)) {
|
|
@@ -504,6 +522,9 @@ function appendHelperTypesImport(buffer, config) {
|
|
|
504
522
|
buffer.push(
|
|
505
523
|
"type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
|
|
506
524
|
);
|
|
525
|
+
buffer.push(
|
|
526
|
+
"type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown);"
|
|
527
|
+
);
|
|
507
528
|
buffer.push(
|
|
508
529
|
"type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
|
|
509
530
|
);
|
|
@@ -513,7 +534,7 @@ function appendHelperTypesImport(buffer, config) {
|
|
|
513
534
|
buffer.push(" otherErrors?: TOther;");
|
|
514
535
|
buffer.push("};");
|
|
515
536
|
buffer.push(
|
|
516
|
-
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends
|
|
537
|
+
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends AnyHeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
517
538
|
);
|
|
518
539
|
buffer.push(" method: TMethod;");
|
|
519
540
|
buffer.push(" path: TPath;");
|
|
@@ -529,15 +550,18 @@ function generateOperationTypes(buffer, operations, config) {
|
|
|
529
550
|
if (!config.emit) return;
|
|
530
551
|
buffer.push("// Operation Types");
|
|
531
552
|
for (const op of operations) {
|
|
532
|
-
const
|
|
553
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
554
|
+
const headerType = op.requestHeaders?.length ? isValidJSIdentifier(camelCaseOperationId) ? `typeof headers.${camelCaseOperationId}` : `(typeof headers)[${formatPropertyName(camelCaseOperationId)}]` : "undefined";
|
|
533
555
|
const requestType = wrapTypeReference(op.requestType);
|
|
534
556
|
const responseType = wrapTypeReference(op.responseType);
|
|
535
557
|
const errorsType = buildOperationErrorsType(op.errors);
|
|
536
558
|
buffer.push(
|
|
537
|
-
`export type ${capitalize(
|
|
559
|
+
`export type ${capitalize(camelCaseOperationId)}Operation = OperationDefinition<`
|
|
538
560
|
);
|
|
539
561
|
buffer.push(` "${op.method}",`);
|
|
540
|
-
buffer.push(
|
|
562
|
+
buffer.push(
|
|
563
|
+
` ${isValidJSIdentifier(camelCaseOperationId) ? `typeof paths.${camelCaseOperationId}` : `(typeof paths)[${formatPropertyName(camelCaseOperationId)}]`},`
|
|
564
|
+
);
|
|
541
565
|
buffer.push(` ${requestType},`);
|
|
542
566
|
buffer.push(` ${responseType},`);
|
|
543
567
|
buffer.push(` ${headerType},`);
|
|
@@ -581,26 +605,35 @@ var TYPE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
581
605
|
"bigint",
|
|
582
606
|
"symbol"
|
|
583
607
|
]);
|
|
608
|
+
var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
584
609
|
function wrapTypeReference(typeName) {
|
|
585
610
|
if (!typeName) return "undefined";
|
|
586
|
-
|
|
587
|
-
if (
|
|
588
|
-
if (
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
611
|
+
const normalized = typeName.trim();
|
|
612
|
+
if (normalized === "undefined") return "undefined";
|
|
613
|
+
if (TYPE_KEYWORDS.has(normalized)) return normalized;
|
|
614
|
+
if (normalized.startsWith("typeof ")) return normalized;
|
|
615
|
+
const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
|
|
616
|
+
if (arrayMatch) {
|
|
617
|
+
return `z.ZodArray<${wrapTypeReference(arrayMatch[1])}>`;
|
|
592
618
|
}
|
|
593
|
-
|
|
619
|
+
if (IDENTIFIER_PATTERN.test(normalized)) {
|
|
620
|
+
return `typeof ${normalized}`;
|
|
621
|
+
}
|
|
622
|
+
return normalized;
|
|
594
623
|
}
|
|
595
624
|
function wrapErrorValueType(typeName) {
|
|
596
625
|
if (!typeName) return "unknown";
|
|
597
|
-
|
|
598
|
-
if (
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
626
|
+
const normalized = typeName.trim();
|
|
627
|
+
if (TYPE_KEYWORDS.has(normalized)) return normalized;
|
|
628
|
+
if (normalized.startsWith("typeof ")) return normalized;
|
|
629
|
+
const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
|
|
630
|
+
if (arrayMatch) {
|
|
631
|
+
return `z.ZodArray<${wrapErrorValueType(arrayMatch[1])}>`;
|
|
602
632
|
}
|
|
603
|
-
|
|
633
|
+
if (IDENTIFIER_PATTERN.test(normalized)) {
|
|
634
|
+
return `typeof ${normalized}`;
|
|
635
|
+
}
|
|
636
|
+
return normalized;
|
|
604
637
|
}
|
|
605
638
|
function collectParameters(pathItem, operation, spec) {
|
|
606
639
|
const parametersMap = /* @__PURE__ */ new Map();
|
|
@@ -660,13 +693,39 @@ function getResponseTypes(operation, operationId) {
|
|
|
660
693
|
const successCodes = /* @__PURE__ */ new Map();
|
|
661
694
|
const errorEntries = [];
|
|
662
695
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
663
|
-
const
|
|
664
|
-
if (!
|
|
696
|
+
const content = response?.content;
|
|
697
|
+
if (!content || Object.keys(content).length === 0) {
|
|
698
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
699
|
+
successCodes.set(statusCode, "undefined");
|
|
700
|
+
} else if (isErrorStatus(statusCode)) {
|
|
701
|
+
errorEntries.push({
|
|
702
|
+
code: statusCode,
|
|
703
|
+
schema: "undefined"
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
const contentType = findContentType(content);
|
|
709
|
+
const resolvedSchema = content[contentType]?.schema;
|
|
710
|
+
if (!resolvedSchema) {
|
|
711
|
+
const inferredType = inferResponseType(contentType, statusCode);
|
|
712
|
+
if (inferredType) {
|
|
713
|
+
if (isErrorStatus(statusCode)) {
|
|
714
|
+
errorEntries.push({
|
|
715
|
+
code: statusCode,
|
|
716
|
+
schema: inferredType
|
|
717
|
+
});
|
|
718
|
+
} else if (/^2\d\d$/.test(statusCode)) {
|
|
719
|
+
successCodes.set(statusCode, inferredType);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
665
724
|
if (isErrorStatus(statusCode)) {
|
|
666
725
|
errorEntries.push({ code: statusCode, schema: resolvedSchema });
|
|
667
726
|
continue;
|
|
668
727
|
}
|
|
669
|
-
if (/^2\d\d$/.test(statusCode)
|
|
728
|
+
if (/^2\d\d$/.test(statusCode)) {
|
|
670
729
|
successCodes.set(statusCode, resolvedSchema);
|
|
671
730
|
}
|
|
672
731
|
}
|
|
@@ -680,6 +739,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
680
739
|
for (const code of preferredOrder) {
|
|
681
740
|
const schema = responses.get(code);
|
|
682
741
|
if (schema) {
|
|
742
|
+
if (typeof schema === "string") {
|
|
743
|
+
return schema;
|
|
744
|
+
}
|
|
683
745
|
return resolveResponseType(
|
|
684
746
|
schema,
|
|
685
747
|
`${capitalize(operationId)}Response${code}`
|
|
@@ -688,6 +750,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
688
750
|
}
|
|
689
751
|
const [firstCode, firstSchema] = responses.entries().next().value ?? [];
|
|
690
752
|
if (!firstSchema) return void 0;
|
|
753
|
+
if (typeof firstSchema === "string") {
|
|
754
|
+
return firstSchema;
|
|
755
|
+
}
|
|
691
756
|
return resolveResponseType(
|
|
692
757
|
firstSchema,
|
|
693
758
|
`${capitalize(operationId)}Response${firstCode ?? "Default"}`
|
|
@@ -725,9 +790,16 @@ function buildErrorGroups(errors = [], operationId) {
|
|
|
725
790
|
return group;
|
|
726
791
|
}
|
|
727
792
|
function resolveResponseType(schema, fallbackName) {
|
|
793
|
+
if (typeof schema === "string") {
|
|
794
|
+
return schema;
|
|
795
|
+
}
|
|
728
796
|
if (schema.$ref) {
|
|
729
797
|
return extractRefName(schema.$ref);
|
|
730
798
|
}
|
|
799
|
+
if (schema.type === "array" && schema.items?.$ref) {
|
|
800
|
+
const itemRef = extractRefName(schema.items.$ref);
|
|
801
|
+
return `z.array(${itemRef})`;
|
|
802
|
+
}
|
|
731
803
|
return fallbackName;
|
|
732
804
|
}
|
|
733
805
|
function getRequestHeaders(parameters) {
|
|
@@ -758,16 +830,22 @@ function getQueryParams(parameters) {
|
|
|
758
830
|
}
|
|
759
831
|
return queryParams;
|
|
760
832
|
}
|
|
761
|
-
function
|
|
762
|
-
const
|
|
833
|
+
function mapHeaderToZodType(header) {
|
|
834
|
+
const schema = header.schema ?? {};
|
|
835
|
+
const schemaType = schema.type;
|
|
763
836
|
switch (schemaType) {
|
|
764
837
|
case "integer":
|
|
765
838
|
case "number":
|
|
766
|
-
return "number";
|
|
839
|
+
return "z.coerce.number()";
|
|
767
840
|
case "boolean":
|
|
768
|
-
return "boolean";
|
|
841
|
+
return "z.coerce.boolean()";
|
|
842
|
+
case "array": {
|
|
843
|
+
const items = schema.items ?? { type: "string" };
|
|
844
|
+
const itemType = items.type === "integer" || items.type === "number" ? "z.coerce.number()" : items.type === "boolean" ? "z.coerce.boolean()" : "z.string()";
|
|
845
|
+
return `z.array(${itemType})`;
|
|
846
|
+
}
|
|
769
847
|
default:
|
|
770
|
-
return "string";
|
|
848
|
+
return "z.string()";
|
|
771
849
|
}
|
|
772
850
|
}
|
|
773
851
|
function mapQueryType(param) {
|
|
@@ -971,6 +1049,54 @@ function applyNumericBounds(schema, builder) {
|
|
|
971
1049
|
}
|
|
972
1050
|
return builder;
|
|
973
1051
|
}
|
|
1052
|
+
function generateHelperFile() {
|
|
1053
|
+
const output = [];
|
|
1054
|
+
output.push("// Generated helper types for Zenko");
|
|
1055
|
+
output.push(
|
|
1056
|
+
"// This file provides type definitions for operation objects and path functions"
|
|
1057
|
+
);
|
|
1058
|
+
output.push("");
|
|
1059
|
+
output.push(
|
|
1060
|
+
"export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
|
|
1061
|
+
);
|
|
1062
|
+
output.push("");
|
|
1063
|
+
output.push(
|
|
1064
|
+
'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
|
|
1065
|
+
);
|
|
1066
|
+
output.push("");
|
|
1067
|
+
output.push(
|
|
1068
|
+
"export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
|
|
1069
|
+
);
|
|
1070
|
+
output.push("");
|
|
1071
|
+
output.push(
|
|
1072
|
+
"export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
|
|
1073
|
+
);
|
|
1074
|
+
output.push("");
|
|
1075
|
+
output.push(
|
|
1076
|
+
"export type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
|
|
1077
|
+
);
|
|
1078
|
+
output.push(" clientErrors?: TClient");
|
|
1079
|
+
output.push(" serverErrors?: TServer");
|
|
1080
|
+
output.push(" defaultErrors?: TDefault");
|
|
1081
|
+
output.push(" otherErrors?: TOther");
|
|
1082
|
+
output.push("}");
|
|
1083
|
+
output.push("");
|
|
1084
|
+
output.push(
|
|
1085
|
+
"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> = {"
|
|
1086
|
+
);
|
|
1087
|
+
output.push(" method: TMethod");
|
|
1088
|
+
output.push(" path: TPath");
|
|
1089
|
+
output.push(" request?: TRequest");
|
|
1090
|
+
output.push(" response?: TResponse");
|
|
1091
|
+
output.push(" headers?: THeaders");
|
|
1092
|
+
output.push(" errors?: TErrors");
|
|
1093
|
+
output.push("}");
|
|
1094
|
+
output.push("");
|
|
1095
|
+
return output.join("\n");
|
|
1096
|
+
}
|
|
1097
|
+
function toCamelCase(str) {
|
|
1098
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1099
|
+
}
|
|
974
1100
|
function capitalize(str) {
|
|
975
1101
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
976
1102
|
}
|
|
@@ -1132,15 +1258,31 @@ async function generateSingle(options) {
|
|
|
1132
1258
|
const resolvedInput = path.resolve(inputFile);
|
|
1133
1259
|
const resolvedOutput = path.resolve(outputFile);
|
|
1134
1260
|
const spec = readSpec(resolvedInput);
|
|
1135
|
-
const
|
|
1261
|
+
const result = generateWithMetadata(spec, {
|
|
1136
1262
|
strictDates,
|
|
1137
1263
|
strictNumeric,
|
|
1138
1264
|
types: typesConfig
|
|
1139
1265
|
});
|
|
1140
1266
|
fs.mkdirSync(path.dirname(resolvedOutput), { recursive: true });
|
|
1141
|
-
fs.writeFileSync(resolvedOutput, output);
|
|
1267
|
+
fs.writeFileSync(resolvedOutput, result.output);
|
|
1142
1268
|
console.log(`\u2705 Generated TypeScript types in ${resolvedOutput}`);
|
|
1143
1269
|
console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths).length} paths`);
|
|
1270
|
+
if (result.helperFile) {
|
|
1271
|
+
const helperPath = path.isAbsolute(result.helperFile.path) ? result.helperFile.path : path.resolve(path.dirname(resolvedOutput), result.helperFile.path);
|
|
1272
|
+
const absoluteResolvedOutput = path.resolve(resolvedOutput);
|
|
1273
|
+
const absoluteHelperPath = path.resolve(helperPath);
|
|
1274
|
+
if (absoluteResolvedOutput === absoluteHelperPath) {
|
|
1275
|
+
console.warn(
|
|
1276
|
+
`\u26A0\uFE0F Skipping helper file generation: would overwrite main output at ${absoluteResolvedOutput}`
|
|
1277
|
+
);
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
fs.mkdirSync(path.dirname(helperPath), { recursive: true });
|
|
1281
|
+
fs.writeFileSync(helperPath, result.helperFile.content, {
|
|
1282
|
+
encoding: "utf8"
|
|
1283
|
+
});
|
|
1284
|
+
console.log(`\u{1F4E6} Generated helper types in ${helperPath}`);
|
|
1285
|
+
}
|
|
1144
1286
|
}
|
|
1145
1287
|
function readSpec(filePath) {
|
|
1146
1288
|
if (!fs.existsSync(filePath)) {
|