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.mjs
CHANGED
|
@@ -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,82 +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
|
-
|
|
390
|
-
output.push(`
|
|
367
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
368
|
+
output.push(`export const ${camelCaseOperationId} = {`);
|
|
369
|
+
output.push(` method: "${op.method}",`);
|
|
370
|
+
output.push(` path: paths.${camelCaseOperationId},`);
|
|
391
371
|
appendOperationField(output, "request", op.requestType);
|
|
392
372
|
appendOperationField(output, "response", op.responseType);
|
|
393
373
|
if (op.requestHeaders && op.requestHeaders.length > 0) {
|
|
394
|
-
output.push(` headers: headers.${
|
|
374
|
+
output.push(` headers: headers.${camelCaseOperationId},`);
|
|
395
375
|
}
|
|
396
376
|
if (op.errors && hasAnyErrors(op.errors)) {
|
|
397
377
|
output.push(" errors: {");
|
|
@@ -427,10 +407,57 @@ function hasAnyErrors(group) {
|
|
|
427
407
|
group.otherErrors
|
|
428
408
|
].some((bucket) => bucket && Object.keys(bucket).length > 0);
|
|
429
409
|
}
|
|
410
|
+
function isRequestMethod(method) {
|
|
411
|
+
switch (method) {
|
|
412
|
+
case "get":
|
|
413
|
+
case "put":
|
|
414
|
+
case "post":
|
|
415
|
+
case "delete":
|
|
416
|
+
case "options":
|
|
417
|
+
case "head":
|
|
418
|
+
case "patch":
|
|
419
|
+
case "trace":
|
|
420
|
+
return true;
|
|
421
|
+
default:
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
var CONTENT_TYPE_MAP = {
|
|
426
|
+
"application/json": "unknown",
|
|
427
|
+
// Will use schema when available
|
|
428
|
+
"text/csv": "string",
|
|
429
|
+
"text/plain": "string",
|
|
430
|
+
// Binary/ambiguous types default to unknown for cross-platform compatibility
|
|
431
|
+
"application/octet-stream": "unknown",
|
|
432
|
+
"application/pdf": "unknown"
|
|
433
|
+
};
|
|
434
|
+
function findContentType(content) {
|
|
435
|
+
const contentTypes = Object.keys(content);
|
|
436
|
+
if (contentTypes.includes("application/json")) {
|
|
437
|
+
return "application/json";
|
|
438
|
+
}
|
|
439
|
+
for (const contentType of contentTypes) {
|
|
440
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
441
|
+
return contentType;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return contentTypes[0] || "";
|
|
445
|
+
}
|
|
446
|
+
function inferResponseType(contentType, statusCode) {
|
|
447
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
448
|
+
return "undefined";
|
|
449
|
+
}
|
|
450
|
+
if (contentType in CONTENT_TYPE_MAP) {
|
|
451
|
+
return CONTENT_TYPE_MAP[contentType];
|
|
452
|
+
}
|
|
453
|
+
return "unknown";
|
|
454
|
+
}
|
|
430
455
|
function parseOperations(spec) {
|
|
431
456
|
const operations = [];
|
|
432
457
|
for (const [path2, pathItem] of Object.entries(spec.paths)) {
|
|
433
458
|
for (const [method, operation] of Object.entries(pathItem)) {
|
|
459
|
+
const normalizedMethod = method.toLowerCase();
|
|
460
|
+
if (!isRequestMethod(normalizedMethod)) continue;
|
|
434
461
|
if (!operation.operationId) continue;
|
|
435
462
|
const pathParams = extractPathParams(path2);
|
|
436
463
|
const requestType = getRequestType(operation);
|
|
@@ -444,7 +471,7 @@ function parseOperations(spec) {
|
|
|
444
471
|
operations.push({
|
|
445
472
|
operationId: operation.operationId,
|
|
446
473
|
path: path2,
|
|
447
|
-
method:
|
|
474
|
+
method: normalizedMethod,
|
|
448
475
|
pathParams,
|
|
449
476
|
queryParams,
|
|
450
477
|
requestType,
|
|
@@ -480,20 +507,24 @@ function appendHelperTypesImport(buffer, config) {
|
|
|
480
507
|
buffer.push(
|
|
481
508
|
"type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string;"
|
|
482
509
|
);
|
|
510
|
+
buffer.push(
|
|
511
|
+
'type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";'
|
|
512
|
+
);
|
|
483
513
|
buffer.push(
|
|
484
514
|
"type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
|
|
485
515
|
);
|
|
486
516
|
buffer.push(
|
|
487
517
|
"type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
|
|
488
518
|
);
|
|
489
|
-
buffer.push(" clientErrors?:
|
|
490
|
-
buffer.push(" serverErrors?:
|
|
491
|
-
buffer.push(" defaultErrors?:
|
|
492
|
-
buffer.push(" otherErrors?:
|
|
519
|
+
buffer.push(" clientErrors?: TClient;");
|
|
520
|
+
buffer.push(" serverErrors?: TServer;");
|
|
521
|
+
buffer.push(" defaultErrors?: TDefault;");
|
|
522
|
+
buffer.push(" otherErrors?: TOther;");
|
|
493
523
|
buffer.push("};");
|
|
494
524
|
buffer.push(
|
|
495
|
-
"type OperationDefinition<TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends HeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
525
|
+
"type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends HeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
|
|
496
526
|
);
|
|
527
|
+
buffer.push(" method: TMethod;");
|
|
497
528
|
buffer.push(" path: TPath;");
|
|
498
529
|
buffer.push(" request?: TRequest;");
|
|
499
530
|
buffer.push(" response?: TResponse;");
|
|
@@ -507,14 +538,18 @@ function generateOperationTypes(buffer, operations, config) {
|
|
|
507
538
|
if (!config.emit) return;
|
|
508
539
|
buffer.push("// Operation Types");
|
|
509
540
|
for (const op of operations) {
|
|
510
|
-
const
|
|
541
|
+
const camelCaseOperationId = toCamelCase(op.operationId);
|
|
542
|
+
const headerType = op.requestHeaders?.length ? isValidJSIdentifier(camelCaseOperationId) ? `typeof headers.${camelCaseOperationId}` : `(typeof headers)[${formatPropertyName(camelCaseOperationId)}]` : "undefined";
|
|
511
543
|
const requestType = wrapTypeReference(op.requestType);
|
|
512
544
|
const responseType = wrapTypeReference(op.responseType);
|
|
513
545
|
const errorsType = buildOperationErrorsType(op.errors);
|
|
514
546
|
buffer.push(
|
|
515
|
-
`export type ${capitalize(
|
|
547
|
+
`export type ${capitalize(camelCaseOperationId)}Operation = OperationDefinition<`
|
|
548
|
+
);
|
|
549
|
+
buffer.push(` "${op.method}",`);
|
|
550
|
+
buffer.push(
|
|
551
|
+
` ${isValidJSIdentifier(camelCaseOperationId) ? `typeof paths.${camelCaseOperationId}` : `(typeof paths)[${formatPropertyName(camelCaseOperationId)}]`},`
|
|
516
552
|
);
|
|
517
|
-
buffer.push(` typeof paths.${op.operationId},`);
|
|
518
553
|
buffer.push(` ${requestType},`);
|
|
519
554
|
buffer.push(` ${responseType},`);
|
|
520
555
|
buffer.push(` ${headerType},`);
|
|
@@ -637,13 +672,34 @@ function getResponseTypes(operation, operationId) {
|
|
|
637
672
|
const successCodes = /* @__PURE__ */ new Map();
|
|
638
673
|
const errorEntries = [];
|
|
639
674
|
for (const [statusCode, response] of Object.entries(responses)) {
|
|
640
|
-
const
|
|
641
|
-
if (!
|
|
675
|
+
const content = response?.content;
|
|
676
|
+
if (!content || Object.keys(content).length === 0) {
|
|
677
|
+
if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
|
|
678
|
+
successCodes.set(statusCode, "undefined");
|
|
679
|
+
}
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
const contentType = findContentType(content);
|
|
683
|
+
const resolvedSchema = content[contentType]?.schema;
|
|
684
|
+
if (!resolvedSchema) {
|
|
685
|
+
const inferredType = inferResponseType(contentType, statusCode);
|
|
686
|
+
if (inferredType) {
|
|
687
|
+
if (isErrorStatus(statusCode)) {
|
|
688
|
+
errorEntries.push({
|
|
689
|
+
code: statusCode,
|
|
690
|
+
schema: inferredType
|
|
691
|
+
});
|
|
692
|
+
} else if (/^2\d\d$/.test(statusCode)) {
|
|
693
|
+
successCodes.set(statusCode, inferredType);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
642
698
|
if (isErrorStatus(statusCode)) {
|
|
643
699
|
errorEntries.push({ code: statusCode, schema: resolvedSchema });
|
|
644
700
|
continue;
|
|
645
701
|
}
|
|
646
|
-
if (/^2\d\d$/.test(statusCode)
|
|
702
|
+
if (/^2\d\d$/.test(statusCode)) {
|
|
647
703
|
successCodes.set(statusCode, resolvedSchema);
|
|
648
704
|
}
|
|
649
705
|
}
|
|
@@ -657,6 +713,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
657
713
|
for (const code of preferredOrder) {
|
|
658
714
|
const schema = responses.get(code);
|
|
659
715
|
if (schema) {
|
|
716
|
+
if (typeof schema === "string") {
|
|
717
|
+
return schema;
|
|
718
|
+
}
|
|
660
719
|
return resolveResponseType(
|
|
661
720
|
schema,
|
|
662
721
|
`${capitalize(operationId)}Response${code}`
|
|
@@ -665,6 +724,9 @@ function selectSuccessResponse(responses, operationId) {
|
|
|
665
724
|
}
|
|
666
725
|
const [firstCode, firstSchema] = responses.entries().next().value ?? [];
|
|
667
726
|
if (!firstSchema) return void 0;
|
|
727
|
+
if (typeof firstSchema === "string") {
|
|
728
|
+
return firstSchema;
|
|
729
|
+
}
|
|
668
730
|
return resolveResponseType(
|
|
669
731
|
firstSchema,
|
|
670
732
|
`${capitalize(operationId)}Response${firstCode ?? "Default"}`
|
|
@@ -702,6 +764,9 @@ function buildErrorGroups(errors = [], operationId) {
|
|
|
702
764
|
return group;
|
|
703
765
|
}
|
|
704
766
|
function resolveResponseType(schema, fallbackName) {
|
|
767
|
+
if (typeof schema === "string") {
|
|
768
|
+
return schema;
|
|
769
|
+
}
|
|
705
770
|
if (schema.$ref) {
|
|
706
771
|
return extractRefName(schema.$ref);
|
|
707
772
|
}
|
|
@@ -735,16 +800,22 @@ function getQueryParams(parameters) {
|
|
|
735
800
|
}
|
|
736
801
|
return queryParams;
|
|
737
802
|
}
|
|
738
|
-
function
|
|
739
|
-
const
|
|
803
|
+
function mapHeaderToZodType(header) {
|
|
804
|
+
const schema = header.schema ?? {};
|
|
805
|
+
const schemaType = schema.type;
|
|
740
806
|
switch (schemaType) {
|
|
741
807
|
case "integer":
|
|
742
808
|
case "number":
|
|
743
|
-
return "number";
|
|
809
|
+
return "z.coerce.number()";
|
|
744
810
|
case "boolean":
|
|
745
|
-
return "boolean";
|
|
811
|
+
return "z.coerce.boolean()";
|
|
812
|
+
case "array": {
|
|
813
|
+
const items = schema.items ?? { type: "string" };
|
|
814
|
+
const itemType = items.type === "integer" || items.type === "number" ? "z.coerce.number()" : items.type === "boolean" ? "z.coerce.boolean()" : "z.string()";
|
|
815
|
+
return `z.array(${itemType})`;
|
|
816
|
+
}
|
|
746
817
|
default:
|
|
747
|
-
return "string";
|
|
818
|
+
return "z.string()";
|
|
748
819
|
}
|
|
749
820
|
}
|
|
750
821
|
function mapQueryType(param) {
|
|
@@ -948,6 +1019,9 @@ function applyNumericBounds(schema, builder) {
|
|
|
948
1019
|
}
|
|
949
1020
|
return builder;
|
|
950
1021
|
}
|
|
1022
|
+
function toCamelCase(str) {
|
|
1023
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1024
|
+
}
|
|
951
1025
|
function capitalize(str) {
|
|
952
1026
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
953
1027
|
}
|