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.cjs
CHANGED
|
@@ -233,7 +233,7 @@ function isErrorStatus(status) {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
// src/zenko.ts
|
|
236
|
-
function
|
|
236
|
+
function generateWithMetadata(spec, options = {}) {
|
|
237
237
|
const output = [];
|
|
238
238
|
const generatedTypes = /* @__PURE__ */ new Set();
|
|
239
239
|
const { strictDates = false, strictNumeric = false } = options;
|
|
@@ -266,8 +266,11 @@ function generate(spec, options = {}) {
|
|
|
266
266
|
const pathParamNames = op.pathParams.map((p) => p.name);
|
|
267
267
|
const hasPathParams = pathParamNames.length > 0;
|
|
268
268
|
const hasQueryParams = op.queryParams.length > 0;
|
|
269
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
269
270
|
if (!hasPathParams && !hasQueryParams) {
|
|
270
|
-
output.push(
|
|
271
|
+
output.push(
|
|
272
|
+
` ${formatPropertyName(camelCaseOperationId)}: () => "${op.path}",`
|
|
273
|
+
);
|
|
271
274
|
continue;
|
|
272
275
|
}
|
|
273
276
|
const allParamNames = [
|
|
@@ -290,11 +293,13 @@ function generate(spec, options = {}) {
|
|
|
290
293
|
const pathWithParams = op.path.replace(/{([^}]+)}/g, "${$1}");
|
|
291
294
|
if (!hasQueryParams) {
|
|
292
295
|
output.push(
|
|
293
|
-
` ${
|
|
296
|
+
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => \`${pathWithParams}\`,`
|
|
294
297
|
);
|
|
295
298
|
continue;
|
|
296
299
|
}
|
|
297
|
-
output.push(
|
|
300
|
+
output.push(
|
|
301
|
+
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => {`
|
|
302
|
+
);
|
|
298
303
|
output.push(" const params = new URLSearchParams()");
|
|
299
304
|
for (const param of op.queryParams) {
|
|
300
305
|
const propertyKey = formatPropertyName(param.name);
|
|
@@ -339,83 +344,57 @@ function generate(spec, options = {}) {
|
|
|
339
344
|
}
|
|
340
345
|
output.push("} as const;");
|
|
341
346
|
output.push("");
|
|
342
|
-
output.push("// Header
|
|
343
|
-
output.push("export const
|
|
347
|
+
output.push("// Header Schemas");
|
|
348
|
+
output.push("export const headerSchemas = {");
|
|
344
349
|
for (const op of operations) {
|
|
350
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
345
351
|
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
346
|
-
output.push(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const typeEntries = op.requestHeaders.map(
|
|
350
|
-
(header) => `${formatPropertyName(header.name)}${header.required ? "" : "?"}: ${mapHeaderType(
|
|
351
|
-
header
|
|
352
|
-
)}`
|
|
353
|
-
).join(", ");
|
|
354
|
-
const requiredHeaders = op.requestHeaders.filter(
|
|
355
|
-
(header) => header.required
|
|
356
|
-
);
|
|
357
|
-
const optionalHeaders = op.requestHeaders.filter(
|
|
358
|
-
(header) => !header.required
|
|
359
|
-
);
|
|
360
|
-
const hasRequired = requiredHeaders.length > 0;
|
|
361
|
-
const signature = hasRequired ? `(params: { ${typeEntries} })` : `(params: { ${typeEntries} } = {})`;
|
|
362
|
-
if (optionalHeaders.length === 0) {
|
|
363
|
-
output.push(` ${op.operationId}: ${signature} => ({`);
|
|
364
|
-
for (const header of requiredHeaders) {
|
|
365
|
-
const propertyKey = formatPropertyName(header.name);
|
|
366
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
367
|
-
output.push(` ${propertyKey}: ${accessor},`);
|
|
368
|
-
}
|
|
369
|
-
output.push(" }),");
|
|
352
|
+
output.push(
|
|
353
|
+
` ${formatPropertyName(camelCaseOperationId)}: z.object({}),`
|
|
354
|
+
);
|
|
370
355
|
continue;
|
|
371
356
|
}
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
357
|
+
const schemaFields = op.requestHeaders.map((header) => {
|
|
358
|
+
const zodType = mapHeaderToZodType(header);
|
|
359
|
+
const optional = header.required ? "" : ".optional()";
|
|
360
|
+
return ` ${formatPropertyName(header.name)}: ${zodType}${optional},`;
|
|
361
|
+
}).join("\n");
|
|
362
|
+
output.push(` ${formatPropertyName(camelCaseOperationId)}: z.object({`);
|
|
363
|
+
output.push(schemaFields);
|
|
364
|
+
output.push(" }),");
|
|
365
|
+
}
|
|
366
|
+
output.push("} as const;");
|
|
367
|
+
output.push("");
|
|
368
|
+
output.push("// Header Functions");
|
|
369
|
+
output.push("export const headers = {");
|
|
370
|
+
for (const op of operations) {
|
|
371
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
372
|
+
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
377
373
|
output.push(
|
|
378
|
-
`
|
|
374
|
+
` ${formatPropertyName(camelCaseOperationId)}: () => ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse({}),`
|
|
379
375
|
);
|
|
380
376
|
continue;
|
|
381
377
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
)
|
|
385
|
-
output.push(
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
for (const header of requiredHeaders) {
|
|
389
|
-
const propertyKey = formatPropertyName(header.name);
|
|
390
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
391
|
-
output.push(` ${propertyKey}: ${accessor},`);
|
|
392
|
-
}
|
|
393
|
-
output.push(" }");
|
|
394
|
-
} else {
|
|
395
|
-
output.push(` const headers: Record<string, ${valueTypes}> = {}`);
|
|
396
|
-
}
|
|
397
|
-
for (const header of optionalHeaders) {
|
|
398
|
-
const propertyKey = formatPropertyName(header.name);
|
|
399
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
400
|
-
const assignment = isValidJSIdentifier(header.name) ? `headers.${header.name}` : `headers[${propertyKey}]`;
|
|
401
|
-
output.push(` if (${accessor} !== undefined) {`);
|
|
402
|
-
output.push(` ${assignment} = ${accessor}`);
|
|
403
|
-
output.push(" }");
|
|
404
|
-
}
|
|
405
|
-
output.push(" return headers");
|
|
378
|
+
output.push(
|
|
379
|
+
` ${formatPropertyName(camelCaseOperationId)}: (params: z.input<${isValidJSIdentifier(camelCaseOperationId) ? `typeof headerSchemas.${camelCaseOperationId}` : `(typeof headerSchemas)[${formatPropertyName(camelCaseOperationId)}]`}>) => {`
|
|
380
|
+
);
|
|
381
|
+
output.push(
|
|
382
|
+
` return ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse(params)`
|
|
383
|
+
);
|
|
406
384
|
output.push(" },");
|
|
407
385
|
}
|
|
408
386
|
output.push("} as const;");
|
|
409
387
|
output.push("");
|
|
410
388
|
output.push("// Operation Objects");
|
|
411
389
|
for (const op of operations) {
|
|
412
|
-
|
|
390
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
391
|
+
output.push(`export const ${camelCaseOperationId} = {`);
|
|
413
392
|
output.push(` method: "${op.method}",`);
|
|
414
|
-
output.push(` path: paths.${
|
|
393
|
+
output.push(` path: paths.${camelCaseOperationId},`);
|
|
415
394
|
appendOperationField(output, "request", op.requestType);
|
|
416
395
|
appendOperationField(output, "response", op.responseType);
|
|
417
396
|
if (op.requestHeaders && op.requestHeaders.length > 0) {
|
|
418
|
-
output.push(` headers: headers.${
|
|
397
|
+
output.push(` headers: headers.${camelCaseOperationId},`);
|
|
419
398
|
}
|
|
420
399
|
if (op.errors && hasAnyErrors(op.errors)) {
|
|
421
400
|
output.push(" errors: {");
|
|
@@ -429,7 +408,16 @@ function generate(spec, options = {}) {
|
|
|
429
408
|
output.push("");
|
|
430
409
|
}
|
|
431
410
|
generateOperationTypes(output, operations, typesConfig);
|
|
432
|
-
|
|
411
|
+
const result = {
|
|
412
|
+
output: output.join("\n")
|
|
413
|
+
};
|
|
414
|
+
if (typesConfig.emit && typesConfig.helpers === "file" && typesConfig.helpersOutput) {
|
|
415
|
+
result.helperFile = {
|
|
416
|
+
path: typesConfig.helpersOutput,
|
|
417
|
+
content: generateHelperFile()
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return result;
|
|
433
421
|
}
|
|
434
422
|
function appendOperationField(buffer, key, value) {
|
|
435
423
|
if (!value) return;
|
|
@@ -466,6 +454,36 @@ function isRequestMethod(method) {
|
|
|
466
454
|
return false;
|
|
467
455
|
}
|
|
468
456
|
}
|
|
457
|
+
var CONTENT_TYPE_MAP = {
|
|
458
|
+
"application/json": "unknown",
|
|
459
|
+
// Will use schema when available
|
|
460
|
+
"text/csv": "string",
|
|
461
|
+
"text/plain": "string",
|
|
462
|
+
// Binary/ambiguous types default to unknown for cross-platform compatibility
|
|
463
|
+
"application/octet-stream": "unknown",
|
|
464
|
+
"application/pdf": "unknown"
|
|
465
|
+
};
|
|
466
|
+
function findContentType(content) {
|
|
467
|
+
const contentTypes = Object.keys(content);
|
|
468
|
+
if (contentTypes.includes("application/json")) {
|
|
469
|
+
return "application/json";
|
|
470
|
+
}
|
|
471
|
+
for (const contentType of contentTypes) {
|
|
472
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
473
|
+
return contentType;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return contentTypes[0] || "";
|
|
477
|
+
}
|
|
478
|
+
function inferResponseType(contentType, statusCode) {
|
|
479
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
480
|
+
return "undefined";
|
|
481
|
+
}
|
|
482
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
483
|
+
return CONTENT_TYPE_MAP[contentType];
|
|
484
|
+
}
|
|
485
|
+
return "unknown";
|
|
486
|
+
}
|
|
469
487
|
function parseOperations(spec) {
|
|
470
488
|
const operations = [];
|
|
471
489
|
for (const [path2, pathItem] of Object.entries(spec.paths)) {
|
|
@@ -527,6 +545,9 @@ function appendHelperTypesImport(buffer, config) {
|
|
|
527
545
|
buffer.push(
|
|
528
546
|
"type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
|
|
529
547
|
);
|
|
548
|
+
buffer.push(
|
|
549
|
+
"type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown);"
|
|
550
|
+
);
|
|
530
551
|
buffer.push(
|
|
531
552
|
"type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
|
|
532
553
|
);
|
|
@@ -536,7 +557,7 @@ function appendHelperTypesImport(buffer, config) {
|
|
|
536
557
|
buffer.push(" otherErrors?: TOther;");
|
|
537
558
|
buffer.push("};");
|
|
538
559
|
buffer.push(
|
|
539
|
-
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends
|
|
560
|
+
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends AnyHeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
540
561
|
);
|
|
541
562
|
buffer.push(" method: TMethod;");
|
|
542
563
|
buffer.push(" path: TPath;");
|
|
@@ -552,15 +573,18 @@ function generateOperationTypes(buffer, operations, config) {
|
|
|
552
573
|
if (!config.emit) return;
|
|
553
574
|
buffer.push("// Operation Types");
|
|
554
575
|
for (const op of operations) {
|
|
555
|
-
const
|
|
576
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
577
|
+
const headerType = op.requestHeaders?.length ? isValidJSIdentifier(camelCaseOperationId) ? `typeof headers.${camelCaseOperationId}` : `(typeof headers)[${formatPropertyName(camelCaseOperationId)}]` : "undefined";
|
|
556
578
|
const requestType = wrapTypeReference(op.requestType);
|
|
557
579
|
const responseType = wrapTypeReference(op.responseType);
|
|
558
580
|
const errorsType = buildOperationErrorsType(op.errors);
|
|
559
581
|
buffer.push(
|
|
560
|
-
`export type ${capitalize(
|
|
582
|
+
`export type ${capitalize(camelCaseOperationId)}Operation = OperationDefinition<`
|
|
561
583
|
);
|
|
562
584
|
buffer.push(` "${op.method}",`);
|
|
563
|
-
buffer.push(
|
|
585
|
+
buffer.push(
|
|
586
|
+
` ${isValidJSIdentifier(camelCaseOperationId) ? `typeof paths.${camelCaseOperationId}` : `(typeof paths)[${formatPropertyName(camelCaseOperationId)}]`},`
|
|
587
|
+
);
|
|
564
588
|
buffer.push(` ${requestType},`);
|
|
565
589
|
buffer.push(` ${responseType},`);
|
|
566
590
|
buffer.push(` ${headerType},`);
|
|
@@ -604,26 +628,35 @@ var TYPE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
604
628
|
"bigint",
|
|
605
629
|
"symbol"
|
|
606
630
|
]);
|
|
631
|
+
var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
607
632
|
function wrapTypeReference(typeName) {
|
|
608
633
|
if (!typeName) return "undefined";
|
|
609
|
-
|
|
610
|
-
if (
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
634
|
+
const normalized = typeName.trim();
|
|
635
|
+
if (normalized === "undefined") return "undefined";
|
|
636
|
+
if (TYPE_KEYWORDS.has(normalized)) return normalized;
|
|
637
|
+
if (normalized.startsWith("typeof ")) return normalized;
|
|
638
|
+
const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
|
|
639
|
+
if (arrayMatch) {
|
|
640
|
+
return `z.ZodArray<${wrapTypeReference(arrayMatch[1])}>`;
|
|
615
641
|
}
|
|
616
|
-
|
|
642
|
+
if (IDENTIFIER_PATTERN.test(normalized)) {
|
|
643
|
+
return `typeof ${normalized}`;
|
|
644
|
+
}
|
|
645
|
+
return normalized;
|
|
617
646
|
}
|
|
618
647
|
function wrapErrorValueType(typeName) {
|
|
619
648
|
if (!typeName) return "unknown";
|
|
620
|
-
|
|
621
|
-
if (
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
649
|
+
const normalized = typeName.trim();
|
|
650
|
+
if (TYPE_KEYWORDS.has(normalized)) return normalized;
|
|
651
|
+
if (normalized.startsWith("typeof ")) return normalized;
|
|
652
|
+
const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
|
|
653
|
+
if (arrayMatch) {
|
|
654
|
+
return `z.ZodArray<${wrapErrorValueType(arrayMatch[1])}>`;
|
|
625
655
|
}
|
|
626
|
-
|
|
656
|
+
if (IDENTIFIER_PATTERN.test(normalized)) {
|
|
657
|
+
return `typeof ${normalized}`;
|
|
658
|
+
}
|
|
659
|
+
return normalized;
|
|
627
660
|
}
|
|
628
661
|
function collectParameters(pathItem, operation, spec) {
|
|
629
662
|
const parametersMap = /* @__PURE__ */ new Map();
|
|
@@ -683,13 +716,39 @@ function getResponseTypes(operation, operationId) {
|
|
|
683
716
|
const successCodes = /* @__PURE__ */ new Map();
|
|
684
717
|
const errorEntries = [];
|
|
685
718
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
686
|
-
const
|
|
687
|
-
if (!
|
|
719
|
+
const content = response?.content;
|
|
720
|
+
if (!content || Object.keys(content).length === 0) {
|
|
721
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
722
|
+
successCodes.set(statusCode, "undefined");
|
|
723
|
+
} else if (isErrorStatus(statusCode)) {
|
|
724
|
+
errorEntries.push({
|
|
725
|
+
code: statusCode,
|
|
726
|
+
schema: "undefined"
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
const contentType = findContentType(content);
|
|
732
|
+
const resolvedSchema = content[contentType]?.schema;
|
|
733
|
+
if (!resolvedSchema) {
|
|
734
|
+
const inferredType = inferResponseType(contentType, statusCode);
|
|
735
|
+
if (inferredType) {
|
|
736
|
+
if (isErrorStatus(statusCode)) {
|
|
737
|
+
errorEntries.push({
|
|
738
|
+
code: statusCode,
|
|
739
|
+
schema: inferredType
|
|
740
|
+
});
|
|
741
|
+
} else if (/^2\d\d$/.test(statusCode)) {
|
|
742
|
+
successCodes.set(statusCode, inferredType);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
688
747
|
if (isErrorStatus(statusCode)) {
|
|
689
748
|
errorEntries.push({ code: statusCode, schema: resolvedSchema });
|
|
690
749
|
continue;
|
|
691
750
|
}
|
|
692
|
-
if (/^2\d\d$/.test(statusCode)
|
|
751
|
+
if (/^2\d\d$/.test(statusCode)) {
|
|
693
752
|
successCodes.set(statusCode, resolvedSchema);
|
|
694
753
|
}
|
|
695
754
|
}
|
|
@@ -703,6 +762,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
703
762
|
for (const code of preferredOrder) {
|
|
704
763
|
const schema = responses.get(code);
|
|
705
764
|
if (schema) {
|
|
765
|
+
if (typeof schema === "string") {
|
|
766
|
+
return schema;
|
|
767
|
+
}
|
|
706
768
|
return resolveResponseType(
|
|
707
769
|
schema,
|
|
708
770
|
`${capitalize(operationId)}Response${code}`
|
|
@@ -711,6 +773,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
711
773
|
}
|
|
712
774
|
const [firstCode, firstSchema] = responses.entries().next().value ?? [];
|
|
713
775
|
if (!firstSchema) return void 0;
|
|
776
|
+
if (typeof firstSchema === "string") {
|
|
777
|
+
return firstSchema;
|
|
778
|
+
}
|
|
714
779
|
return resolveResponseType(
|
|
715
780
|
firstSchema,
|
|
716
781
|
`${capitalize(operationId)}Response${firstCode ?? "Default"}`
|
|
@@ -748,9 +813,16 @@ function buildErrorGroups(errors = [], operationId) {
|
|
|
748
813
|
return group;
|
|
749
814
|
}
|
|
750
815
|
function resolveResponseType(schema, fallbackName) {
|
|
816
|
+
if (typeof schema === "string") {
|
|
817
|
+
return schema;
|
|
818
|
+
}
|
|
751
819
|
if (schema.$ref) {
|
|
752
820
|
return extractRefName(schema.$ref);
|
|
753
821
|
}
|
|
822
|
+
if (schema.type === "array" && schema.items?.$ref) {
|
|
823
|
+
const itemRef = extractRefName(schema.items.$ref);
|
|
824
|
+
return `z.array(${itemRef})`;
|
|
825
|
+
}
|
|
754
826
|
return fallbackName;
|
|
755
827
|
}
|
|
756
828
|
function getRequestHeaders(parameters) {
|
|
@@ -781,16 +853,22 @@ function getQueryParams(parameters) {
|
|
|
781
853
|
}
|
|
782
854
|
return queryParams;
|
|
783
855
|
}
|
|
784
|
-
function
|
|
785
|
-
const
|
|
856
|
+
function mapHeaderToZodType(header) {
|
|
857
|
+
const schema = header.schema ?? {};
|
|
858
|
+
const schemaType = schema.type;
|
|
786
859
|
switch (schemaType) {
|
|
787
860
|
case "integer":
|
|
788
861
|
case "number":
|
|
789
|
-
return "number";
|
|
862
|
+
return "z.coerce.number()";
|
|
790
863
|
case "boolean":
|
|
791
|
-
return "boolean";
|
|
864
|
+
return "z.coerce.boolean()";
|
|
865
|
+
case "array": {
|
|
866
|
+
const items = schema.items ?? { type: "string" };
|
|
867
|
+
const itemType = items.type === "integer" || items.type === "number" ? "z.coerce.number()" : items.type === "boolean" ? "z.coerce.boolean()" : "z.string()";
|
|
868
|
+
return `z.array(${itemType})`;
|
|
869
|
+
}
|
|
792
870
|
default:
|
|
793
|
-
return "string";
|
|
871
|
+
return "z.string()";
|
|
794
872
|
}
|
|
795
873
|
}
|
|
796
874
|
function mapQueryType(param) {
|
|
@@ -994,6 +1072,54 @@ function applyNumericBounds(schema, builder) {
|
|
|
994
1072
|
}
|
|
995
1073
|
return builder;
|
|
996
1074
|
}
|
|
1075
|
+
function generateHelperFile() {
|
|
1076
|
+
const output = [];
|
|
1077
|
+
output.push("// Generated helper types for Zenko");
|
|
1078
|
+
output.push(
|
|
1079
|
+
"// This file provides type definitions for operation objects and path functions"
|
|
1080
|
+
);
|
|
1081
|
+
output.push("");
|
|
1082
|
+
output.push(
|
|
1083
|
+
"export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
|
|
1084
|
+
);
|
|
1085
|
+
output.push("");
|
|
1086
|
+
output.push(
|
|
1087
|
+
'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
|
|
1088
|
+
);
|
|
1089
|
+
output.push("");
|
|
1090
|
+
output.push(
|
|
1091
|
+
"export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
|
|
1092
|
+
);
|
|
1093
|
+
output.push("");
|
|
1094
|
+
output.push(
|
|
1095
|
+
"export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
|
|
1096
|
+
);
|
|
1097
|
+
output.push("");
|
|
1098
|
+
output.push(
|
|
1099
|
+
"export type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
|
|
1100
|
+
);
|
|
1101
|
+
output.push(" clientErrors?: TClient");
|
|
1102
|
+
output.push(" serverErrors?: TServer");
|
|
1103
|
+
output.push(" defaultErrors?: TDefault");
|
|
1104
|
+
output.push(" otherErrors?: TOther");
|
|
1105
|
+
output.push("}");
|
|
1106
|
+
output.push("");
|
|
1107
|
+
output.push(
|
|
1108
|
+
"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> = {"
|
|
1109
|
+
);
|
|
1110
|
+
output.push(" method: TMethod");
|
|
1111
|
+
output.push(" path: TPath");
|
|
1112
|
+
output.push(" request?: TRequest");
|
|
1113
|
+
output.push(" response?: TResponse");
|
|
1114
|
+
output.push(" headers?: THeaders");
|
|
1115
|
+
output.push(" errors?: TErrors");
|
|
1116
|
+
output.push("}");
|
|
1117
|
+
output.push("");
|
|
1118
|
+
return output.join("\n");
|
|
1119
|
+
}
|
|
1120
|
+
function toCamelCase(str) {
|
|
1121
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1122
|
+
}
|
|
997
1123
|
function capitalize(str) {
|
|
998
1124
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
999
1125
|
}
|
|
@@ -1155,15 +1281,31 @@ async function generateSingle(options) {
|
|
|
1155
1281
|
const resolvedInput = path.resolve(inputFile);
|
|
1156
1282
|
const resolvedOutput = path.resolve(outputFile);
|
|
1157
1283
|
const spec = readSpec(resolvedInput);
|
|
1158
|
-
const
|
|
1284
|
+
const result = generateWithMetadata(spec, {
|
|
1159
1285
|
strictDates,
|
|
1160
1286
|
strictNumeric,
|
|
1161
1287
|
types: typesConfig
|
|
1162
1288
|
});
|
|
1163
1289
|
fs.mkdirSync(path.dirname(resolvedOutput), { recursive: true });
|
|
1164
|
-
fs.writeFileSync(resolvedOutput, output);
|
|
1290
|
+
fs.writeFileSync(resolvedOutput, result.output);
|
|
1165
1291
|
console.log(`\u2705 Generated TypeScript types in ${resolvedOutput}`);
|
|
1166
1292
|
console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths).length} paths`);
|
|
1293
|
+
if (result.helperFile) {
|
|
1294
|
+
const helperPath = path.isAbsolute(result.helperFile.path) ? result.helperFile.path : path.resolve(path.dirname(resolvedOutput), result.helperFile.path);
|
|
1295
|
+
const absoluteResolvedOutput = path.resolve(resolvedOutput);
|
|
1296
|
+
const absoluteHelperPath = path.resolve(helperPath);
|
|
1297
|
+
if (absoluteResolvedOutput === absoluteHelperPath) {
|
|
1298
|
+
console.warn(
|
|
1299
|
+
`\u26A0\uFE0F Skipping helper file generation: would overwrite main output at ${absoluteResolvedOutput}`
|
|
1300
|
+
);
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
fs.mkdirSync(path.dirname(helperPath), { recursive: true });
|
|
1304
|
+
fs.writeFileSync(helperPath, result.helperFile.content, {
|
|
1305
|
+
encoding: "utf8"
|
|
1306
|
+
});
|
|
1307
|
+
console.log(`\u{1F4E6} Generated helper types in ${helperPath}`);
|
|
1308
|
+
}
|
|
1167
1309
|
}
|
|
1168
1310
|
function readSpec(filePath) {
|
|
1169
1311
|
if (!fs.existsSync(filePath)) {
|