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/index.mjs
CHANGED
|
@@ -235,8 +235,11 @@ function generate(spec, options = {}) {
|
|
|
235
235
|
const pathParamNames = op.pathParams.map((p) => p.name);
|
|
236
236
|
const hasPathParams = pathParamNames.length > 0;
|
|
237
237
|
const hasQueryParams = op.queryParams.length > 0;
|
|
238
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
238
239
|
if (!hasPathParams && !hasQueryParams) {
|
|
239
|
-
output.push(
|
|
240
|
+
output.push(
|
|
241
|
+
` ${formatPropertyName(camelCaseOperationId)}: () => "${op.path}",`
|
|
242
|
+
);
|
|
240
243
|
continue;
|
|
241
244
|
}
|
|
242
245
|
const allParamNames = [
|
|
@@ -259,11 +262,13 @@ function generate(spec, options = {}) {
|
|
|
259
262
|
const pathWithParams = op.path.replace(/{([^}]+)}/g, "${$1}");
|
|
260
263
|
if (!hasQueryParams) {
|
|
261
264
|
output.push(
|
|
262
|
-
` ${
|
|
265
|
+
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => \`${pathWithParams}\`,`
|
|
263
266
|
);
|
|
264
267
|
continue;
|
|
265
268
|
}
|
|
266
|
-
output.push(
|
|
269
|
+
output.push(
|
|
270
|
+
` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => {`
|
|
271
|
+
);
|
|
267
272
|
output.push(" const params = new URLSearchParams()");
|
|
268
273
|
for (const param of op.queryParams) {
|
|
269
274
|
const propertyKey = formatPropertyName(param.name);
|
|
@@ -308,82 +313,57 @@ function generate(spec, options = {}) {
|
|
|
308
313
|
}
|
|
309
314
|
output.push("} as const;");
|
|
310
315
|
output.push("");
|
|
311
|
-
output.push("// Header
|
|
312
|
-
output.push("export const
|
|
316
|
+
output.push("// Header Schemas");
|
|
317
|
+
output.push("export const headerSchemas = {");
|
|
313
318
|
for (const op of operations) {
|
|
319
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
314
320
|
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
315
|
-
output.push(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const typeEntries = op.requestHeaders.map(
|
|
319
|
-
(header) => `${formatPropertyName(header.name)}${header.required ? "" : "?"}: ${mapHeaderType(
|
|
320
|
-
header
|
|
321
|
-
)}`
|
|
322
|
-
).join(", ");
|
|
323
|
-
const requiredHeaders = op.requestHeaders.filter(
|
|
324
|
-
(header) => header.required
|
|
325
|
-
);
|
|
326
|
-
const optionalHeaders = op.requestHeaders.filter(
|
|
327
|
-
(header) => !header.required
|
|
328
|
-
);
|
|
329
|
-
const hasRequired = requiredHeaders.length > 0;
|
|
330
|
-
const signature = hasRequired ? `(params: { ${typeEntries} })` : `(params: { ${typeEntries} } = {})`;
|
|
331
|
-
if (optionalHeaders.length === 0) {
|
|
332
|
-
output.push(` ${op.operationId}: ${signature} => ({`);
|
|
333
|
-
for (const header of requiredHeaders) {
|
|
334
|
-
const propertyKey = formatPropertyName(header.name);
|
|
335
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
336
|
-
output.push(` ${propertyKey}: ${accessor},`);
|
|
337
|
-
}
|
|
338
|
-
output.push(" }),");
|
|
321
|
+
output.push(
|
|
322
|
+
` ${formatPropertyName(camelCaseOperationId)}: z.object({}),`
|
|
323
|
+
);
|
|
339
324
|
continue;
|
|
340
325
|
}
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
326
|
+
const schemaFields = op.requestHeaders.map((header) => {
|
|
327
|
+
const zodType = mapHeaderToZodType(header);
|
|
328
|
+
const optional = header.required ? "" : ".optional()";
|
|
329
|
+
return ` ${formatPropertyName(header.name)}: ${zodType}${optional},`;
|
|
330
|
+
}).join("\n");
|
|
331
|
+
output.push(` ${formatPropertyName(camelCaseOperationId)}: z.object({`);
|
|
332
|
+
output.push(schemaFields);
|
|
333
|
+
output.push(" }),");
|
|
334
|
+
}
|
|
335
|
+
output.push("} as const;");
|
|
336
|
+
output.push("");
|
|
337
|
+
output.push("// Header Functions");
|
|
338
|
+
output.push("export const headers = {");
|
|
339
|
+
for (const op of operations) {
|
|
340
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
341
|
+
if (!op.requestHeaders || op.requestHeaders.length === 0) {
|
|
346
342
|
output.push(
|
|
347
|
-
`
|
|
343
|
+
` ${formatPropertyName(camelCaseOperationId)}: () => ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse({}),`
|
|
348
344
|
);
|
|
349
345
|
continue;
|
|
350
346
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
)
|
|
354
|
-
output.push(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
for (const header of requiredHeaders) {
|
|
358
|
-
const propertyKey = formatPropertyName(header.name);
|
|
359
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
360
|
-
output.push(` ${propertyKey}: ${accessor},`);
|
|
361
|
-
}
|
|
362
|
-
output.push(" }");
|
|
363
|
-
} else {
|
|
364
|
-
output.push(` const headers: Record<string, ${valueTypes}> = {}`);
|
|
365
|
-
}
|
|
366
|
-
for (const header of optionalHeaders) {
|
|
367
|
-
const propertyKey = formatPropertyName(header.name);
|
|
368
|
-
const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
|
|
369
|
-
const assignment = isValidJSIdentifier(header.name) ? `headers.${header.name}` : `headers[${propertyKey}]`;
|
|
370
|
-
output.push(` if (${accessor} !== undefined) {`);
|
|
371
|
-
output.push(` ${assignment} = ${accessor}`);
|
|
372
|
-
output.push(" }");
|
|
373
|
-
}
|
|
374
|
-
output.push(" return headers");
|
|
347
|
+
output.push(
|
|
348
|
+
` ${formatPropertyName(camelCaseOperationId)}: (params: z.input<${isValidJSIdentifier(camelCaseOperationId) ? `typeof headerSchemas.${camelCaseOperationId}` : `(typeof headerSchemas)[${formatPropertyName(camelCaseOperationId)}]`}>) => {`
|
|
349
|
+
);
|
|
350
|
+
output.push(
|
|
351
|
+
` return ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse(params)`
|
|
352
|
+
);
|
|
375
353
|
output.push(" },");
|
|
376
354
|
}
|
|
377
355
|
output.push("} as const;");
|
|
378
356
|
output.push("");
|
|
379
357
|
output.push("// Operation Objects");
|
|
380
358
|
for (const op of operations) {
|
|
381
|
-
|
|
382
|
-
output.push(`
|
|
359
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
360
|
+
output.push(`export const ${camelCaseOperationId} = {`);
|
|
361
|
+
output.push(` method: "${op.method}",`);
|
|
362
|
+
output.push(` path: paths.${camelCaseOperationId},`);
|
|
383
363
|
appendOperationField(output, "request", op.requestType);
|
|
384
364
|
appendOperationField(output, "response", op.responseType);
|
|
385
365
|
if (op.requestHeaders && op.requestHeaders.length > 0) {
|
|
386
|
-
output.push(` headers: headers.${
|
|
366
|
+
output.push(` headers: headers.${camelCaseOperationId},`);
|
|
387
367
|
}
|
|
388
368
|
if (op.errors && hasAnyErrors(op.errors)) {
|
|
389
369
|
output.push(" errors: {");
|
|
@@ -419,10 +399,57 @@ function hasAnyErrors(group) {
|
|
|
419
399
|
group.otherErrors
|
|
420
400
|
].some((bucket) => bucket && Object.keys(bucket).length > 0);
|
|
421
401
|
}
|
|
402
|
+
function isRequestMethod(method) {
|
|
403
|
+
switch (method) {
|
|
404
|
+
case "get":
|
|
405
|
+
case "put":
|
|
406
|
+
case "post":
|
|
407
|
+
case "delete":
|
|
408
|
+
case "options":
|
|
409
|
+
case "head":
|
|
410
|
+
case "patch":
|
|
411
|
+
case "trace":
|
|
412
|
+
return true;
|
|
413
|
+
default:
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
var CONTENT_TYPE_MAP = {
|
|
418
|
+
"application/json": "unknown",
|
|
419
|
+
// Will use schema when available
|
|
420
|
+
"text/csv": "string",
|
|
421
|
+
"text/plain": "string",
|
|
422
|
+
// Binary/ambiguous types default to unknown for cross-platform compatibility
|
|
423
|
+
"application/octet-stream": "unknown",
|
|
424
|
+
"application/pdf": "unknown"
|
|
425
|
+
};
|
|
426
|
+
function findContentType(content) {
|
|
427
|
+
const contentTypes = Object.keys(content);
|
|
428
|
+
if (contentTypes.includes("application/json")) {
|
|
429
|
+
return "application/json";
|
|
430
|
+
}
|
|
431
|
+
for (const contentType of contentTypes) {
|
|
432
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
433
|
+
return contentType;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return contentTypes[0] || "";
|
|
437
|
+
}
|
|
438
|
+
function inferResponseType(contentType, statusCode) {
|
|
439
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
440
|
+
return "undefined";
|
|
441
|
+
}
|
|
442
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
443
|
+
return CONTENT_TYPE_MAP[contentType];
|
|
444
|
+
}
|
|
445
|
+
return "unknown";
|
|
446
|
+
}
|
|
422
447
|
function parseOperations(spec) {
|
|
423
448
|
const operations = [];
|
|
424
449
|
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
425
450
|
for (const [method, operation] of Object.entries(pathItem)) {
|
|
451
|
+
const normalizedMethod = method.toLowerCase();
|
|
452
|
+
if (!isRequestMethod(normalizedMethod)) continue;
|
|
426
453
|
if (!operation.operationId) continue;
|
|
427
454
|
const pathParams = extractPathParams(path);
|
|
428
455
|
const requestType = getRequestType(operation);
|
|
@@ -436,7 +463,7 @@ function parseOperations(spec) {
|
|
|
436
463
|
operations.push({
|
|
437
464
|
operationId: operation.operationId,
|
|
438
465
|
path,
|
|
439
|
-
method:
|
|
466
|
+
method: normalizedMethod,
|
|
440
467
|
pathParams,
|
|
441
468
|
queryParams,
|
|
442
469
|
requestType,
|
|
@@ -472,20 +499,24 @@ function appendHelperTypesImport(buffer, config) {
|
|
|
472
499
|
buffer.push(
|
|
473
500
|
"type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string;"
|
|
474
501
|
);
|
|
502
|
+
buffer.push(
|
|
503
|
+
'type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";'
|
|
504
|
+
);
|
|
475
505
|
buffer.push(
|
|
476
506
|
"type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
|
|
477
507
|
);
|
|
478
508
|
buffer.push(
|
|
479
509
|
"type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
|
|
480
510
|
);
|
|
481
|
-
buffer.push(" clientErrors?:
|
|
482
|
-
buffer.push(" serverErrors?:
|
|
483
|
-
buffer.push(" defaultErrors?:
|
|
484
|
-
buffer.push(" otherErrors?:
|
|
511
|
+
buffer.push(" clientErrors?: TClient;");
|
|
512
|
+
buffer.push(" serverErrors?: TServer;");
|
|
513
|
+
buffer.push(" defaultErrors?: TDefault;");
|
|
514
|
+
buffer.push(" otherErrors?: TOther;");
|
|
485
515
|
buffer.push("};");
|
|
486
516
|
buffer.push(
|
|
487
|
-
"type OperationDefinition<TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends HeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
517
|
+
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends HeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
488
518
|
);
|
|
519
|
+
buffer.push(" method: TMethod;");
|
|
489
520
|
buffer.push(" path: TPath;");
|
|
490
521
|
buffer.push(" request?: TRequest;");
|
|
491
522
|
buffer.push(" response?: TResponse;");
|
|
@@ -499,14 +530,18 @@ function generateOperationTypes(buffer, operations, config) {
|
|
|
499
530
|
if (!config.emit) return;
|
|
500
531
|
buffer.push("// Operation Types");
|
|
501
532
|
for (const op of operations) {
|
|
502
|
-
const
|
|
533
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
534
|
+
const headerType = op.requestHeaders?.length ? isValidJSIdentifier(camelCaseOperationId) ? `typeof headers.${camelCaseOperationId}` : `(typeof headers)[${formatPropertyName(camelCaseOperationId)}]` : "undefined";
|
|
503
535
|
const requestType = wrapTypeReference(op.requestType);
|
|
504
536
|
const responseType = wrapTypeReference(op.responseType);
|
|
505
537
|
const errorsType = buildOperationErrorsType(op.errors);
|
|
506
538
|
buffer.push(
|
|
507
|
-
`export type ${capitalize(
|
|
539
|
+
`export type ${capitalize(camelCaseOperationId)}Operation = OperationDefinition<`
|
|
540
|
+
);
|
|
541
|
+
buffer.push(` "${op.method}",`);
|
|
542
|
+
buffer.push(
|
|
543
|
+
` ${isValidJSIdentifier(camelCaseOperationId) ? `typeof paths.${camelCaseOperationId}` : `(typeof paths)[${formatPropertyName(camelCaseOperationId)}]`},`
|
|
508
544
|
);
|
|
509
|
-
buffer.push(` typeof paths.${op.operationId},`);
|
|
510
545
|
buffer.push(` ${requestType},`);
|
|
511
546
|
buffer.push(` ${responseType},`);
|
|
512
547
|
buffer.push(` ${headerType},`);
|
|
@@ -629,13 +664,34 @@ function getResponseTypes(operation, operationId) {
|
|
|
629
664
|
const successCodes = /* @__PURE__ */ new Map();
|
|
630
665
|
const errorEntries = [];
|
|
631
666
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
632
|
-
const
|
|
633
|
-
if (!
|
|
667
|
+
const content = response?.content;
|
|
668
|
+
if (!content || Object.keys(content).length === 0) {
|
|
669
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
670
|
+
successCodes.set(statusCode, "undefined");
|
|
671
|
+
}
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
const contentType = findContentType(content);
|
|
675
|
+
const resolvedSchema = content[contentType]?.schema;
|
|
676
|
+
if (!resolvedSchema) {
|
|
677
|
+
const inferredType = inferResponseType(contentType, statusCode);
|
|
678
|
+
if (inferredType) {
|
|
679
|
+
if (isErrorStatus(statusCode)) {
|
|
680
|
+
errorEntries.push({
|
|
681
|
+
code: statusCode,
|
|
682
|
+
schema: inferredType
|
|
683
|
+
});
|
|
684
|
+
} else if (/^2\d\d$/.test(statusCode)) {
|
|
685
|
+
successCodes.set(statusCode, inferredType);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
634
690
|
if (isErrorStatus(statusCode)) {
|
|
635
691
|
errorEntries.push({ code: statusCode, schema: resolvedSchema });
|
|
636
692
|
continue;
|
|
637
693
|
}
|
|
638
|
-
if (/^2\d\d$/.test(statusCode)
|
|
694
|
+
if (/^2\d\d$/.test(statusCode)) {
|
|
639
695
|
successCodes.set(statusCode, resolvedSchema);
|
|
640
696
|
}
|
|
641
697
|
}
|
|
@@ -649,6 +705,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
649
705
|
for (const code of preferredOrder) {
|
|
650
706
|
const schema = responses.get(code);
|
|
651
707
|
if (schema) {
|
|
708
|
+
if (typeof schema === "string") {
|
|
709
|
+
return schema;
|
|
710
|
+
}
|
|
652
711
|
return resolveResponseType(
|
|
653
712
|
schema,
|
|
654
713
|
`${capitalize(operationId)}Response${code}`
|
|
@@ -657,6 +716,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
657
716
|
}
|
|
658
717
|
const [firstCode, firstSchema] = responses.entries().next().value ?? [];
|
|
659
718
|
if (!firstSchema) return void 0;
|
|
719
|
+
if (typeof firstSchema === "string") {
|
|
720
|
+
return firstSchema;
|
|
721
|
+
}
|
|
660
722
|
return resolveResponseType(
|
|
661
723
|
firstSchema,
|
|
662
724
|
`${capitalize(operationId)}Response${firstCode ?? "Default"}`
|
|
@@ -694,6 +756,9 @@ function buildErrorGroups(errors = [], operationId) {
|
|
|
694
756
|
return group;
|
|
695
757
|
}
|
|
696
758
|
function resolveResponseType(schema, fallbackName) {
|
|
759
|
+
if (typeof schema === "string") {
|
|
760
|
+
return schema;
|
|
761
|
+
}
|
|
697
762
|
if (schema.$ref) {
|
|
698
763
|
return extractRefName(schema.$ref);
|
|
699
764
|
}
|
|
@@ -727,16 +792,22 @@ function getQueryParams(parameters) {
|
|
|
727
792
|
}
|
|
728
793
|
return queryParams;
|
|
729
794
|
}
|
|
730
|
-
function
|
|
731
|
-
const
|
|
795
|
+
function mapHeaderToZodType(header) {
|
|
796
|
+
const schema = header.schema ?? {};
|
|
797
|
+
const schemaType = schema.type;
|
|
732
798
|
switch (schemaType) {
|
|
733
799
|
case "integer":
|
|
734
800
|
case "number":
|
|
735
|
-
return "number";
|
|
801
|
+
return "z.coerce.number()";
|
|
736
802
|
case "boolean":
|
|
737
|
-
return "boolean";
|
|
803
|
+
return "z.coerce.boolean()";
|
|
804
|
+
case "array": {
|
|
805
|
+
const items = schema.items ?? { type: "string" };
|
|
806
|
+
const itemType = items.type === "integer" || items.type === "number" ? "z.coerce.number()" : items.type === "boolean" ? "z.coerce.boolean()" : "z.string()";
|
|
807
|
+
return `z.array(${itemType})`;
|
|
808
|
+
}
|
|
738
809
|
default:
|
|
739
|
-
return "string";
|
|
810
|
+
return "z.string()";
|
|
740
811
|
}
|
|
741
812
|
}
|
|
742
813
|
function mapQueryType(param) {
|
|
@@ -940,6 +1011,9 @@ function applyNumericBounds(schema, builder) {
|
|
|
940
1011
|
}
|
|
941
1012
|
return builder;
|
|
942
1013
|
}
|
|
1014
|
+
function toCamelCase(str) {
|
|
1015
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1016
|
+
}
|
|
943
1017
|
function capitalize(str) {
|
|
944
1018
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
945
1019
|
}
|