zenko 0.1.2 → 0.1.4
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 +153 -79
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +153 -79
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +153 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.mjs +153 -79
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/dist/cli.cjs
CHANGED
|
@@ -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,82 +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
|
-
|
|
413
|
-
output.push(`
|
|
390
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
391
|
+
output.push(`export const ${camelCaseOperationId} = {`);
|
|
392
|
+
output.push(` method: "${op.method}",`);
|
|
393
|
+
output.push(` path: paths.${camelCaseOperationId},`);
|
|
414
394
|
appendOperationField(output, "request", op.requestType);
|
|
415
395
|
appendOperationField(output, "response", op.responseType);
|
|
416
396
|
if (op.requestHeaders && op.requestHeaders.length > 0) {
|
|
417
|
-
output.push(` headers: headers.${
|
|
397
|
+
output.push(` headers: headers.${camelCaseOperationId},`);
|
|
418
398
|
}
|
|
419
399
|
if (op.errors && hasAnyErrors(op.errors)) {
|
|
420
400
|
output.push(" errors: {");
|
|
@@ -450,10 +430,57 @@ function hasAnyErrors(group) {
|
|
|
450
430
|
group.otherErrors
|
|
451
431
|
].some((bucket) => bucket && Object.keys(bucket).length > 0);
|
|
452
432
|
}
|
|
433
|
+
function isRequestMethod(method) {
|
|
434
|
+
switch (method) {
|
|
435
|
+
case "get":
|
|
436
|
+
case "put":
|
|
437
|
+
case "post":
|
|
438
|
+
case "delete":
|
|
439
|
+
case "options":
|
|
440
|
+
case "head":
|
|
441
|
+
case "patch":
|
|
442
|
+
case "trace":
|
|
443
|
+
return true;
|
|
444
|
+
default:
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
var CONTENT_TYPE_MAP = {
|
|
449
|
+
"application/json": "unknown",
|
|
450
|
+
// Will use schema when available
|
|
451
|
+
"text/csv": "string",
|
|
452
|
+
"text/plain": "string",
|
|
453
|
+
// Binary/ambiguous types default to unknown for cross-platform compatibility
|
|
454
|
+
"application/octet-stream": "unknown",
|
|
455
|
+
"application/pdf": "unknown"
|
|
456
|
+
};
|
|
457
|
+
function findContentType(content) {
|
|
458
|
+
const contentTypes = Object.keys(content);
|
|
459
|
+
if (contentTypes.includes("application/json")) {
|
|
460
|
+
return "application/json";
|
|
461
|
+
}
|
|
462
|
+
for (const contentType of contentTypes) {
|
|
463
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
464
|
+
return contentType;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return contentTypes[0] || "";
|
|
468
|
+
}
|
|
469
|
+
function inferResponseType(contentType, statusCode) {
|
|
470
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
471
|
+
return "undefined";
|
|
472
|
+
}
|
|
473
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
474
|
+
return CONTENT_TYPE_MAP[contentType];
|
|
475
|
+
}
|
|
476
|
+
return "unknown";
|
|
477
|
+
}
|
|
453
478
|
function parseOperations(spec) {
|
|
454
479
|
const operations = [];
|
|
455
480
|
for (const [path2, pathItem] of Object.entries(spec.paths)) {
|
|
456
481
|
for (const [method, operation] of Object.entries(pathItem)) {
|
|
482
|
+
const normalizedMethod = method.toLowerCase();
|
|
483
|
+
if (!isRequestMethod(normalizedMethod)) continue;
|
|
457
484
|
if (!operation.operationId) continue;
|
|
458
485
|
const pathParams = extractPathParams(path2);
|
|
459
486
|
const requestType = getRequestType(operation);
|
|
@@ -467,7 +494,7 @@ function parseOperations(spec) {
|
|
|
467
494
|
operations.push({
|
|
468
495
|
operationId: operation.operationId,
|
|
469
496
|
path: path2,
|
|
470
|
-
method:
|
|
497
|
+
method: normalizedMethod,
|
|
471
498
|
pathParams,
|
|
472
499
|
queryParams,
|
|
473
500
|
requestType,
|
|
@@ -503,20 +530,24 @@ function appendHelperTypesImport(buffer, config) {
|
|
|
503
530
|
buffer.push(
|
|
504
531
|
"type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string;"
|
|
505
532
|
);
|
|
533
|
+
buffer.push(
|
|
534
|
+
'type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";'
|
|
535
|
+
);
|
|
506
536
|
buffer.push(
|
|
507
537
|
"type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
|
|
508
538
|
);
|
|
509
539
|
buffer.push(
|
|
510
540
|
"type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
|
|
511
541
|
);
|
|
512
|
-
buffer.push(" clientErrors?:
|
|
513
|
-
buffer.push(" serverErrors?:
|
|
514
|
-
buffer.push(" defaultErrors?:
|
|
515
|
-
buffer.push(" otherErrors?:
|
|
542
|
+
buffer.push(" clientErrors?: TClient;");
|
|
543
|
+
buffer.push(" serverErrors?: TServer;");
|
|
544
|
+
buffer.push(" defaultErrors?: TDefault;");
|
|
545
|
+
buffer.push(" otherErrors?: TOther;");
|
|
516
546
|
buffer.push("};");
|
|
517
547
|
buffer.push(
|
|
518
|
-
"type OperationDefinition<TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends HeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
548
|
+
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends HeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
519
549
|
);
|
|
550
|
+
buffer.push(" method: TMethod;");
|
|
520
551
|
buffer.push(" path: TPath;");
|
|
521
552
|
buffer.push(" request?: TRequest;");
|
|
522
553
|
buffer.push(" response?: TResponse;");
|
|
@@ -530,14 +561,18 @@ function generateOperationTypes(buffer, operations, config) {
|
|
|
530
561
|
if (!config.emit) return;
|
|
531
562
|
buffer.push("// Operation Types");
|
|
532
563
|
for (const op of operations) {
|
|
533
|
-
const
|
|
564
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
565
|
+
const headerType = op.requestHeaders?.length ? isValidJSIdentifier(camelCaseOperationId) ? `typeof headers.${camelCaseOperationId}` : `(typeof headers)[${formatPropertyName(camelCaseOperationId)}]` : "undefined";
|
|
534
566
|
const requestType = wrapTypeReference(op.requestType);
|
|
535
567
|
const responseType = wrapTypeReference(op.responseType);
|
|
536
568
|
const errorsType = buildOperationErrorsType(op.errors);
|
|
537
569
|
buffer.push(
|
|
538
|
-
`export type ${capitalize(
|
|
570
|
+
`export type ${capitalize(camelCaseOperationId)}Operation = OperationDefinition<`
|
|
571
|
+
);
|
|
572
|
+
buffer.push(` "${op.method}",`);
|
|
573
|
+
buffer.push(
|
|
574
|
+
` ${isValidJSIdentifier(camelCaseOperationId) ? `typeof paths.${camelCaseOperationId}` : `(typeof paths)[${formatPropertyName(camelCaseOperationId)}]`},`
|
|
539
575
|
);
|
|
540
|
-
buffer.push(` typeof paths.${op.operationId},`);
|
|
541
576
|
buffer.push(` ${requestType},`);
|
|
542
577
|
buffer.push(` ${responseType},`);
|
|
543
578
|
buffer.push(` ${headerType},`);
|
|
@@ -660,13 +695,34 @@ function getResponseTypes(operation, operationId) {
|
|
|
660
695
|
const successCodes = /* @__PURE__ */ new Map();
|
|
661
696
|
const errorEntries = [];
|
|
662
697
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
663
|
-
const
|
|
664
|
-
if (!
|
|
698
|
+
const content = response?.content;
|
|
699
|
+
if (!content || Object.keys(content).length === 0) {
|
|
700
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
701
|
+
successCodes.set(statusCode, "undefined");
|
|
702
|
+
}
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
const contentType = findContentType(content);
|
|
706
|
+
const resolvedSchema = content[contentType]?.schema;
|
|
707
|
+
if (!resolvedSchema) {
|
|
708
|
+
const inferredType = inferResponseType(contentType, statusCode);
|
|
709
|
+
if (inferredType) {
|
|
710
|
+
if (isErrorStatus(statusCode)) {
|
|
711
|
+
errorEntries.push({
|
|
712
|
+
code: statusCode,
|
|
713
|
+
schema: inferredType
|
|
714
|
+
});
|
|
715
|
+
} else if (/^2\d\d$/.test(statusCode)) {
|
|
716
|
+
successCodes.set(statusCode, inferredType);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
665
721
|
if (isErrorStatus(statusCode)) {
|
|
666
722
|
errorEntries.push({ code: statusCode, schema: resolvedSchema });
|
|
667
723
|
continue;
|
|
668
724
|
}
|
|
669
|
-
if (/^2\d\d$/.test(statusCode)
|
|
725
|
+
if (/^2\d\d$/.test(statusCode)) {
|
|
670
726
|
successCodes.set(statusCode, resolvedSchema);
|
|
671
727
|
}
|
|
672
728
|
}
|
|
@@ -680,6 +736,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
680
736
|
for (const code of preferredOrder) {
|
|
681
737
|
const schema = responses.get(code);
|
|
682
738
|
if (schema) {
|
|
739
|
+
if (typeof schema === "string") {
|
|
740
|
+
return schema;
|
|
741
|
+
}
|
|
683
742
|
return resolveResponseType(
|
|
684
743
|
schema,
|
|
685
744
|
`${capitalize(operationId)}Response${code}`
|
|
@@ -688,6 +747,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
688
747
|
}
|
|
689
748
|
const [firstCode, firstSchema] = responses.entries().next().value ?? [];
|
|
690
749
|
if (!firstSchema) return void 0;
|
|
750
|
+
if (typeof firstSchema === "string") {
|
|
751
|
+
return firstSchema;
|
|
752
|
+
}
|
|
691
753
|
return resolveResponseType(
|
|
692
754
|
firstSchema,
|
|
693
755
|
`${capitalize(operationId)}Response${firstCode ?? "Default"}`
|
|
@@ -725,6 +787,9 @@ function buildErrorGroups(errors = [], operationId) {
|
|
|
725
787
|
return group;
|
|
726
788
|
}
|
|
727
789
|
function resolveResponseType(schema, fallbackName) {
|
|
790
|
+
if (typeof schema === "string") {
|
|
791
|
+
return schema;
|
|
792
|
+
}
|
|
728
793
|
if (schema.$ref) {
|
|
729
794
|
return extractRefName(schema.$ref);
|
|
730
795
|
}
|
|
@@ -758,16 +823,22 @@ function getQueryParams(parameters) {
|
|
|
758
823
|
}
|
|
759
824
|
return queryParams;
|
|
760
825
|
}
|
|
761
|
-
function
|
|
762
|
-
const
|
|
826
|
+
function mapHeaderToZodType(header) {
|
|
827
|
+
const schema = header.schema ?? {};
|
|
828
|
+
const schemaType = schema.type;
|
|
763
829
|
switch (schemaType) {
|
|
764
830
|
case "integer":
|
|
765
831
|
case "number":
|
|
766
|
-
return "number";
|
|
832
|
+
return "z.coerce.number()";
|
|
767
833
|
case "boolean":
|
|
768
|
-
return "boolean";
|
|
834
|
+
return "z.coerce.boolean()";
|
|
835
|
+
case "array": {
|
|
836
|
+
const items = schema.items ?? { type: "string" };
|
|
837
|
+
const itemType = items.type === "integer" || items.type === "number" ? "z.coerce.number()" : items.type === "boolean" ? "z.coerce.boolean()" : "z.string()";
|
|
838
|
+
return `z.array(${itemType})`;
|
|
839
|
+
}
|
|
769
840
|
default:
|
|
770
|
-
return "string";
|
|
841
|
+
return "z.string()";
|
|
771
842
|
}
|
|
772
843
|
}
|
|
773
844
|
function mapQueryType(param) {
|
|
@@ -971,6 +1042,9 @@ function applyNumericBounds(schema, builder) {
|
|
|
971
1042
|
}
|
|
972
1043
|
return builder;
|
|
973
1044
|
}
|
|
1045
|
+
function toCamelCase(str) {
|
|
1046
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1047
|
+
}
|
|
974
1048
|
function capitalize(str) {
|
|
975
1049
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
976
1050
|
}
|