zenko 0.1.6 → 0.1.7

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 CHANGED
@@ -158,6 +158,14 @@ function formatPropertyName(name) {
158
158
  return isValidJSIdentifier(name) ? name : `"${name}"`;
159
159
  }
160
160
 
161
+ // src/utils/string-utils.ts
162
+ function toCamelCase(str) {
163
+ return str.replace(/-([a-zA-Z])/g, (_, letter) => letter.toUpperCase()).replace(/-+$/, "");
164
+ }
165
+ function capitalize(str) {
166
+ return str.charAt(0).toUpperCase() + str.slice(1);
167
+ }
168
+
161
169
  // src/utils/http-status.ts
162
170
  var statusNameMap = {
163
171
  "400": "badRequest",
@@ -217,14 +225,6 @@ function mapStatusToIdentifier(status) {
217
225
  if (!candidate) return "unknownError";
218
226
  return /^[a-zA-Z_$]/.test(candidate) ? candidate : `status${candidate.charAt(0).toUpperCase()}${candidate.slice(1)}`;
219
227
  }
220
- function getStatusCategory(status) {
221
- if (status === "default") return "default";
222
- const code = Number(status);
223
- if (!Number.isInteger(code)) return "unknown";
224
- if (code >= 400 && code <= 499) return "client";
225
- if (code >= 500 && code <= 599) return "server";
226
- return "unknown";
227
- }
228
228
  function isErrorStatus(status) {
229
229
  if (status === "default") return true;
230
230
  const code = Number(status);
@@ -232,6 +232,169 @@ function isErrorStatus(status) {
232
232
  return code >= 400;
233
233
  }
234
234
 
235
+ // src/utils/tree-shaking.ts
236
+ function analyzeZenkoUsage(operations) {
237
+ const usage = {
238
+ usesHeaderFn: false,
239
+ usesOperationDefinition: false,
240
+ usesOperationErrors: false
241
+ };
242
+ if (operations.length > 0) {
243
+ usage.usesOperationDefinition = true;
244
+ }
245
+ for (const op of operations) {
246
+ if (op.requestHeaders && op.requestHeaders.length > 0) {
247
+ usage.usesHeaderFn = true;
248
+ }
249
+ if (op.errors && hasAnyErrors(op.errors)) {
250
+ usage.usesOperationErrors = true;
251
+ }
252
+ }
253
+ if (operations.length > 0 && !usage.usesOperationErrors) {
254
+ const hasDefaultErrors = operations.some(
255
+ (op) => !op.errors || !hasAnyErrors(op.errors)
256
+ );
257
+ if (hasDefaultErrors) {
258
+ usage.usesOperationErrors = true;
259
+ }
260
+ }
261
+ return usage;
262
+ }
263
+ function generateZenkoImport(usage, mode, helpersOutput) {
264
+ const types = [];
265
+ if (usage.usesHeaderFn) types.push("HeaderFn");
266
+ if (usage.usesOperationDefinition) types.push("OperationDefinition");
267
+ if (usage.usesOperationErrors) types.push("OperationErrors");
268
+ if (types.length === 0) {
269
+ return "";
270
+ }
271
+ const importSource = mode === "package" ? '"zenko"' : `"${helpersOutput}"`;
272
+ return `import type { ${types.join(", ")} } from ${importSource};`;
273
+ }
274
+ function hasAnyErrors(errors) {
275
+ return Boolean(errors && Object.keys(errors).length > 0);
276
+ }
277
+
278
+ // src/utils/collect-inline-types.ts
279
+ function collectInlineRequestTypes(operations, spec) {
280
+ const requestTypesToGenerate = /* @__PURE__ */ new Map();
281
+ const operationLookup = /* @__PURE__ */ new Map();
282
+ for (const [, pathItem] of Object.entries(spec.paths || {})) {
283
+ for (const [, operation] of Object.entries(pathItem)) {
284
+ const op = operation;
285
+ if (op.operationId) {
286
+ operationLookup.set(op.operationId, op);
287
+ }
288
+ }
289
+ }
290
+ for (const [, pathItem] of Object.entries(spec.webhooks || {})) {
291
+ for (const [, operation] of Object.entries(pathItem)) {
292
+ const op = operation;
293
+ if (op.operationId) {
294
+ operationLookup.set(op.operationId, op);
295
+ }
296
+ }
297
+ }
298
+ for (const op of operations) {
299
+ const operation = operationLookup.get(op.operationId);
300
+ if (!operation) continue;
301
+ const requestBody = operation.requestBody;
302
+ if (requestBody && requestBody.content) {
303
+ const content = requestBody.content;
304
+ const jsonContent = content["application/json"];
305
+ if (jsonContent && jsonContent.schema) {
306
+ const schema = jsonContent.schema;
307
+ const typeName = `${capitalize(toCamelCase(op.operationId))}Request`;
308
+ if (!schema.$ref || schema.allOf || schema.oneOf || schema.anyOf) {
309
+ requestTypesToGenerate.set(typeName, schema);
310
+ }
311
+ }
312
+ }
313
+ }
314
+ return requestTypesToGenerate;
315
+ }
316
+ function collectInlineResponseTypes(operations, spec) {
317
+ const responseTypesToGenerate = /* @__PURE__ */ new Map();
318
+ const operationLookup = /* @__PURE__ */ new Map();
319
+ for (const [, pathItem] of Object.entries(spec.paths || {})) {
320
+ for (const [, operation] of Object.entries(pathItem)) {
321
+ const op = operation;
322
+ if (op.operationId) {
323
+ operationLookup.set(op.operationId, op);
324
+ }
325
+ }
326
+ }
327
+ for (const [, pathItem] of Object.entries(spec.webhooks || {})) {
328
+ for (const [, operation] of Object.entries(pathItem)) {
329
+ const op = operation;
330
+ if (op.operationId) {
331
+ operationLookup.set(op.operationId, op);
332
+ }
333
+ }
334
+ }
335
+ for (const op of operations) {
336
+ const operation = operationLookup.get(op.operationId);
337
+ if (!operation) continue;
338
+ const responses = operation.responses || {};
339
+ for (const [statusCode, response] of Object.entries(responses)) {
340
+ if (/^2\d\d$/.test(statusCode) && response.content) {
341
+ const content = response.content;
342
+ const jsonContent = content["application/json"];
343
+ if (jsonContent && jsonContent.schema) {
344
+ const schema = jsonContent.schema;
345
+ const typeName = `${capitalize(toCamelCase(op.operationId))}Response`;
346
+ if (!schema.$ref || schema.allOf || schema.oneOf || schema.anyOf) {
347
+ responseTypesToGenerate.set(typeName, schema);
348
+ }
349
+ }
350
+ }
351
+ }
352
+ }
353
+ return responseTypesToGenerate;
354
+ }
355
+
356
+ // src/utils/generate-helper-file.ts
357
+ function generateHelperFile() {
358
+ const output = [];
359
+ output.push("// Generated helper types for Zenko");
360
+ output.push(
361
+ "// This file provides type definitions for operation objects and path functions"
362
+ );
363
+ output.push("");
364
+ output.push(
365
+ "export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
366
+ );
367
+ output.push("");
368
+ output.push(
369
+ 'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
370
+ );
371
+ output.push("");
372
+ output.push(
373
+ "export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
374
+ );
375
+ output.push("");
376
+ output.push(
377
+ "export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
378
+ );
379
+ output.push("");
380
+ output.push(
381
+ "export type OperationErrors<TError = unknown> = TError extends Record<string, unknown> ? TError : Record<string, TError>;"
382
+ );
383
+ output.push("");
384
+ output.push(
385
+ "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> = {"
386
+ );
387
+ output.push(" method: TMethod");
388
+ output.push(" path: TPath");
389
+ output.push(" request?: TRequest");
390
+ output.push(" response?: TResponse");
391
+ output.push(" headers?: THeaders");
392
+ output.push(" errors?: TErrors");
393
+ output.push("}");
394
+ output.push("");
395
+ return output.join("\n");
396
+ }
397
+
235
398
  // src/zenko.ts
236
399
  function generateWithMetadata(spec, options = {}) {
237
400
  const output = [];
@@ -243,7 +406,14 @@ function generateWithMetadata(spec, options = {}) {
243
406
  strictNumeric
244
407
  };
245
408
  output.push('import { z } from "zod";');
246
- appendHelperTypesImport(output, typesConfig);
409
+ const nameMap = /* @__PURE__ */ new Map();
410
+ if (spec.components?.schemas) {
411
+ for (const name of Object.keys(spec.components.schemas)) {
412
+ nameMap.set(name, toCamelCase(name));
413
+ }
414
+ }
415
+ const operations = parseOperations(spec, nameMap);
416
+ appendHelperTypesImport(output, typesConfig, operations);
247
417
  output.push("");
248
418
  if (spec.components?.schemas) {
249
419
  output.push("// Generated Zod Schemas");
@@ -251,15 +421,23 @@ function generateWithMetadata(spec, options = {}) {
251
421
  const sortedSchemas = topologicalSort(spec.components.schemas);
252
422
  for (const name of sortedSchemas) {
253
423
  const schema = spec.components.schemas[name];
424
+ const sanitizedName = nameMap.get(name);
254
425
  output.push(
255
- generateZodSchema(name, schema, generatedTypes, schemaOptions)
426
+ generateZodSchema(
427
+ sanitizedName,
428
+ schema,
429
+ generatedTypes,
430
+ schemaOptions,
431
+ nameMap
432
+ )
256
433
  );
257
434
  output.push("");
258
- output.push(`export type ${name} = z.infer<typeof ${name}>;`);
435
+ output.push(
436
+ `export type ${sanitizedName} = z.infer<typeof ${sanitizedName}>;`
437
+ );
259
438
  output.push("");
260
439
  }
261
440
  }
262
- const operations = parseOperations(spec);
263
441
  output.push("// Path Functions");
264
442
  output.push("export const paths = {");
265
443
  for (const op of operations) {
@@ -273,24 +451,37 @@ function generateWithMetadata(spec, options = {}) {
273
451
  );
274
452
  continue;
275
453
  }
276
- const allParamNames = [
277
- ...pathParamNames,
278
- ...op.queryParams.map((p) => p.name)
279
- ];
280
- const signaturePieces = [];
454
+ const alias = (n) => {
455
+ if (isValidJSIdentifier(n)) return n;
456
+ let aliased = toCamelCase(n);
457
+ if (!isValidJSIdentifier(aliased)) {
458
+ aliased = `_${aliased}`;
459
+ }
460
+ return aliased;
461
+ };
462
+ const destructPieces = [];
463
+ const typePieces = [];
281
464
  for (const param of op.pathParams) {
282
- signaturePieces.push(`${param.name}: string`);
465
+ destructPieces.push(
466
+ isValidJSIdentifier(param.name) ? param.name : `${formatPropertyName(param.name)}: ${alias(param.name)}`
467
+ );
468
+ typePieces.push(`${formatPropertyName(param.name)}: string`);
283
469
  }
284
470
  for (const param of op.queryParams) {
285
- signaturePieces.push(
286
- `${param.name}${param.required ? "" : "?"}: ${mapQueryType(param)}`
471
+ destructPieces.push(
472
+ isValidJSIdentifier(param.name) ? param.name : `${formatPropertyName(param.name)}: ${alias(param.name)}`
473
+ );
474
+ typePieces.push(
475
+ `${formatPropertyName(param.name)}${param.required ? "" : "?"}: ${mapQueryType(param)}`
287
476
  );
288
477
  }
289
- const signatureParams = signaturePieces.join(", ");
290
478
  const needsDefaultObject = !hasPathParams && hasQueryParams && op.queryParams.every((param) => !param.required);
291
- const signatureArgs = allParamNames.length ? `{ ${allParamNames.join(", ")} }` : "{}";
292
- const signature = `${signatureArgs}: { ${signatureParams} }${needsDefaultObject ? " = {}" : ""}`;
293
- const pathWithParams = op.path.replace(/{([^}]+)}/g, "${$1}");
479
+ const signatureArgs = destructPieces.length ? `{ ${destructPieces.join(", ")} }` : "{}";
480
+ const signature = `${signatureArgs}: { ${typePieces.join(", ")} }${needsDefaultObject ? " = {}" : ""}`;
481
+ const pathWithParams = op.path.replace(
482
+ /{([^}]+)}/g,
483
+ (_m, n) => `\${${alias(n)}}`
484
+ );
294
485
  if (!hasQueryParams) {
295
486
  output.push(
296
487
  ` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => \`${pathWithParams}\`,`
@@ -302,8 +493,7 @@ function generateWithMetadata(spec, options = {}) {
302
493
  );
303
494
  output.push(" const params = new URLSearchParams()");
304
495
  for (const param of op.queryParams) {
305
- const propertyKey = formatPropertyName(param.name);
306
- const accessor = isValidJSIdentifier(param.name) ? param.name : propertyKey;
496
+ const accessor = isValidJSIdentifier(param.name) ? param.name : alias(toCamelCase(param.name));
307
497
  const schema = param.schema ?? {};
308
498
  if (schema?.type === "array") {
309
499
  const itemValueExpression = convertQueryParamValue(
@@ -385,6 +575,8 @@ function generateWithMetadata(spec, options = {}) {
385
575
  }
386
576
  output.push("} as const;");
387
577
  output.push("");
578
+ generateRequestTypes(output, operations, spec, nameMap, schemaOptions);
579
+ generateResponseTypes(output, operations, spec, nameMap, schemaOptions);
388
580
  generateOperationTypes(output, operations, typesConfig);
389
581
  output.push("// Operation Objects");
390
582
  for (const op of operations) {
@@ -398,13 +590,8 @@ function generateWithMetadata(spec, options = {}) {
398
590
  if (op.requestHeaders && op.requestHeaders.length > 0) {
399
591
  output.push(` headers: headers.${camelCaseOperationId},`);
400
592
  }
401
- if (op.errors && hasAnyErrors(op.errors)) {
402
- output.push(" errors: {");
403
- appendErrorGroup(output, "clientErrors", op.errors.clientErrors);
404
- appendErrorGroup(output, "serverErrors", op.errors.serverErrors);
405
- appendErrorGroup(output, "defaultErrors", op.errors.defaultErrors);
406
- appendErrorGroup(output, "otherErrors", op.errors.otherErrors);
407
- output.push(" },");
593
+ if (op.errors && hasAnyErrors2(op.errors)) {
594
+ appendErrorGroup(output, "errors", op.errors);
408
595
  }
409
596
  output.push("} as const;");
410
597
  output.push("");
@@ -420,6 +607,46 @@ function generateWithMetadata(spec, options = {}) {
420
607
  }
421
608
  return result;
422
609
  }
610
+ function generateRequestTypes(output, operations, spec, nameMap, schemaOptions) {
611
+ const requestTypesToGenerate = collectInlineRequestTypes(operations, spec);
612
+ if (requestTypesToGenerate.size > 0) {
613
+ output.push("// Generated Request Types");
614
+ output.push("");
615
+ for (const [typeName, schema] of requestTypesToGenerate) {
616
+ const generatedSchema = generateZodSchema(
617
+ typeName,
618
+ schema,
619
+ /* @__PURE__ */ new Set(),
620
+ schemaOptions,
621
+ nameMap
622
+ );
623
+ output.push(generatedSchema);
624
+ output.push("");
625
+ output.push(`export type ${typeName} = z.infer<typeof ${typeName}>;`);
626
+ output.push("");
627
+ }
628
+ }
629
+ }
630
+ function generateResponseTypes(output, operations, spec, nameMap, schemaOptions) {
631
+ const responseTypesToGenerate = collectInlineResponseTypes(operations, spec);
632
+ if (responseTypesToGenerate.size > 0) {
633
+ output.push("// Generated Response Types");
634
+ output.push("");
635
+ for (const [typeName, schema] of responseTypesToGenerate) {
636
+ const generatedSchema = generateZodSchema(
637
+ typeName,
638
+ schema,
639
+ /* @__PURE__ */ new Set(),
640
+ schemaOptions,
641
+ nameMap
642
+ );
643
+ output.push(generatedSchema);
644
+ output.push("");
645
+ output.push(`export type ${typeName} = z.infer<typeof ${typeName}>;`);
646
+ output.push("");
647
+ }
648
+ }
649
+ }
423
650
  function appendOperationField(buffer, key, value) {
424
651
  if (!value) return;
425
652
  buffer.push(` ${key}: ${value},`);
@@ -432,13 +659,8 @@ function appendErrorGroup(buffer, label, errors) {
432
659
  }
433
660
  buffer.push(" },");
434
661
  }
435
- function hasAnyErrors(group) {
436
- return [
437
- group.clientErrors,
438
- group.serverErrors,
439
- group.defaultErrors,
440
- group.otherErrors
441
- ].some((bucket) => bucket && Object.keys(bucket).length > 0);
662
+ function hasAnyErrors2(group) {
663
+ return Boolean(group && Object.keys(group).length > 0);
442
664
  }
443
665
  function isRequestMethod(method) {
444
666
  switch (method) {
@@ -485,33 +707,70 @@ function inferResponseType(contentType, statusCode) {
485
707
  }
486
708
  return "unknown";
487
709
  }
488
- function parseOperations(spec) {
710
+ function parseOperations(spec, nameMap) {
489
711
  const operations = [];
490
- for (const [path2, pathItem] of Object.entries(spec.paths)) {
491
- for (const [method, operation] of Object.entries(pathItem)) {
492
- const normalizedMethod = method.toLowerCase();
493
- if (!isRequestMethod(normalizedMethod)) continue;
494
- if (!operation.operationId) continue;
495
- const pathParams = extractPathParams(path2);
496
- const requestType = getRequestType(operation);
497
- const { successResponse, errors } = getResponseTypes(
498
- operation,
499
- operation.operationId
500
- );
501
- const resolvedParameters = collectParameters(pathItem, operation, spec);
502
- const requestHeaders = getRequestHeaders(resolvedParameters);
503
- const queryParams = getQueryParams(resolvedParameters);
504
- operations.push({
505
- operationId: operation.operationId,
506
- path: path2,
507
- method: normalizedMethod,
508
- pathParams,
509
- queryParams,
510
- requestType,
511
- responseType: successResponse,
512
- requestHeaders,
513
- errors
514
- });
712
+ if (spec.paths) {
713
+ for (const [path2, pathItem] of Object.entries(spec.paths)) {
714
+ for (const [method, operation] of Object.entries(pathItem)) {
715
+ const normalizedMethod = method.toLowerCase();
716
+ if (!isRequestMethod(normalizedMethod)) continue;
717
+ if (!operation.operationId) continue;
718
+ const pathParams = extractPathParams(path2);
719
+ const requestType = getRequestType(operation);
720
+ const { successResponse, errors } = getResponseTypes(
721
+ operation,
722
+ operation.operationId,
723
+ nameMap
724
+ );
725
+ const resolvedParameters = collectParameters(pathItem, operation, spec);
726
+ const requestHeaders = getRequestHeaders(resolvedParameters);
727
+ const queryParams = getQueryParams(resolvedParameters);
728
+ operations.push({
729
+ operationId: operation.operationId,
730
+ path: path2,
731
+ method: normalizedMethod,
732
+ pathParams,
733
+ queryParams,
734
+ requestType,
735
+ responseType: successResponse,
736
+ requestHeaders,
737
+ errors
738
+ });
739
+ }
740
+ }
741
+ }
742
+ if (spec.webhooks) {
743
+ for (const [webhookName, webhookItem] of Object.entries(spec.webhooks)) {
744
+ for (const [method, operation] of Object.entries(webhookItem)) {
745
+ const normalizedMethod = method.toLowerCase();
746
+ if (!isRequestMethod(normalizedMethod)) continue;
747
+ if (!operation.operationId) continue;
748
+ const path2 = webhookName;
749
+ const pathParams = extractPathParams(path2);
750
+ const requestType = getRequestType(operation);
751
+ const { successResponse, errors } = getResponseTypes(
752
+ operation,
753
+ operation.operationId
754
+ );
755
+ const resolvedParameters = collectParameters(
756
+ webhookItem,
757
+ operation,
758
+ spec
759
+ );
760
+ const requestHeaders = getRequestHeaders(resolvedParameters);
761
+ const queryParams = getQueryParams(resolvedParameters);
762
+ operations.push({
763
+ operationId: operation.operationId,
764
+ path: path2,
765
+ method: normalizedMethod,
766
+ pathParams,
767
+ queryParams,
768
+ requestType,
769
+ responseType: successResponse,
770
+ requestHeaders,
771
+ errors
772
+ });
773
+ }
515
774
  }
516
775
  }
517
776
  return operations;
@@ -520,21 +779,42 @@ function normalizeTypesConfig(config) {
520
779
  return {
521
780
  emit: config?.emit ?? true,
522
781
  helpers: config?.helpers ?? "package",
523
- helpersOutput: config?.helpersOutput ?? "./zenko-types"
782
+ helpersOutput: config?.helpersOutput ?? "./zenko-types",
783
+ treeShake: config?.treeShake ?? true
524
784
  };
525
785
  }
526
- function appendHelperTypesImport(buffer, config) {
786
+ function appendHelperTypesImport(buffer, config, operations) {
527
787
  if (!config.emit) return;
528
788
  switch (config.helpers) {
529
789
  case "package":
530
- buffer.push(
531
- 'import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "zenko";'
532
- );
790
+ if (config.treeShake) {
791
+ const usage = analyzeZenkoUsage(operations);
792
+ const importStatement = generateZenkoImport(usage, "package");
793
+ if (importStatement) {
794
+ buffer.push(importStatement);
795
+ }
796
+ } else {
797
+ buffer.push(
798
+ 'import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "zenko";'
799
+ );
800
+ }
533
801
  return;
534
802
  case "file":
535
- buffer.push(
536
- `import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "${config.helpersOutput}";`
537
- );
803
+ if (config.treeShake) {
804
+ const usage = analyzeZenkoUsage(operations);
805
+ const importStatement = generateZenkoImport(
806
+ usage,
807
+ "file",
808
+ config.helpersOutput
809
+ );
810
+ if (importStatement) {
811
+ buffer.push(importStatement);
812
+ }
813
+ } else {
814
+ buffer.push(
815
+ `import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "${config.helpersOutput}";`
816
+ );
817
+ }
538
818
  return;
539
819
  case "inline":
540
820
  buffer.push(
@@ -550,24 +830,19 @@ function appendHelperTypesImport(buffer, config) {
550
830
  "type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown);"
551
831
  );
552
832
  buffer.push(
553
- "type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
833
+ "type OperationErrors<TError = unknown> = TError extends Record<string, unknown> ? TError : Record<string, TError>;"
554
834
  );
555
- buffer.push(" clientErrors?: TClient;");
556
- buffer.push(" serverErrors?: TServer;");
557
- buffer.push(" defaultErrors?: TDefault;");
558
- buffer.push(" otherErrors?: TOther;");
559
- buffer.push("};");
560
835
  buffer.push(
561
836
  "type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends AnyHeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
562
837
  );
563
- buffer.push(" method: TMethod;");
564
- buffer.push(" path: TPath;");
565
- buffer.push(" request?: TRequest;");
566
- buffer.push(" response?: TResponse;");
567
- buffer.push(" headers?: THeaders;");
568
- buffer.push(" errors?: TErrors;");
569
- buffer.push("};");
570
- return;
838
+ buffer.push(" method: TMethod");
839
+ buffer.push(" path: TPath");
840
+ buffer.push(" request?: TRequest");
841
+ buffer.push(" response?: TResponse");
842
+ buffer.push(" headers?: THeaders");
843
+ buffer.push(" errors?: TErrors");
844
+ buffer.push("}");
845
+ buffer.push("");
571
846
  }
572
847
  }
573
848
  function generateOperationTypes(buffer, operations, config) {
@@ -595,14 +870,11 @@ function generateOperationTypes(buffer, operations, config) {
595
870
  }
596
871
  }
597
872
  function buildOperationErrorsType(errors) {
598
- if (!errors || !hasAnyErrors(errors)) {
873
+ if (!errors || !hasAnyErrors2(errors)) {
599
874
  return "OperationErrors";
600
875
  }
601
- const client = buildErrorBucket(errors.clientErrors);
602
- const server = buildErrorBucket(errors.serverErrors);
603
- const fallback = buildErrorBucket(errors.defaultErrors);
604
- const other = buildErrorBucket(errors.otherErrors);
605
- return `OperationErrors<${client}, ${server}, ${fallback}, ${other}>`;
876
+ const errorBucket = buildErrorBucket(errors);
877
+ return `OperationErrors<${errorBucket}>`;
606
878
  }
607
879
  function buildErrorBucket(bucket) {
608
880
  if (!bucket || Object.keys(bucket).length === 0) {
@@ -709,10 +981,10 @@ function getRequestType(operation) {
709
981
  if (requestBody.$ref) {
710
982
  return extractRefName(requestBody.$ref);
711
983
  }
712
- const typeName = `${capitalize(operation.operationId)}Request`;
984
+ const typeName = `${capitalize(toCamelCase(operation.operationId))}Request`;
713
985
  return typeName;
714
986
  }
715
- function getResponseTypes(operation, operationId) {
987
+ function getResponseTypes(operation, operationId, nameMap) {
716
988
  const responses = operation.responses ?? {};
717
989
  const successCodes = /* @__PURE__ */ new Map();
718
990
  const errorEntries = [];
@@ -753,11 +1025,15 @@ function getResponseTypes(operation, operationId) {
753
1025
  successCodes.set(statusCode, resolvedSchema);
754
1026
  }
755
1027
  }
756
- const successResponse = selectSuccessResponse(successCodes, operationId);
757
- const errors = buildErrorGroups(errorEntries, operationId);
1028
+ const successResponse = selectSuccessResponse(
1029
+ successCodes,
1030
+ operationId,
1031
+ nameMap
1032
+ );
1033
+ const errors = buildErrorGroups(errorEntries, operationId, nameMap);
758
1034
  return { successResponse, errors };
759
1035
  }
760
- function selectSuccessResponse(responses, operationId) {
1036
+ function selectSuccessResponse(responses, operationId, nameMap) {
761
1037
  if (responses.size === 0) return void 0;
762
1038
  const preferredOrder = ["200", "201", "204"];
763
1039
  for (const code of preferredOrder) {
@@ -768,61 +1044,51 @@ function selectSuccessResponse(responses, operationId) {
768
1044
  }
769
1045
  return resolveResponseType(
770
1046
  schema,
771
- `${capitalize(operationId)}Response${code}`
1047
+ `${capitalize(toCamelCase(operationId))}Response`,
1048
+ nameMap
772
1049
  );
773
1050
  }
774
1051
  }
775
- const [firstCode, firstSchema] = responses.entries().next().value ?? [];
1052
+ const [, firstSchema] = responses.entries().next().value ?? [];
776
1053
  if (!firstSchema) return void 0;
777
1054
  if (typeof firstSchema === "string") {
778
1055
  return firstSchema;
779
1056
  }
780
1057
  return resolveResponseType(
781
1058
  firstSchema,
782
- `${capitalize(operationId)}Response${firstCode ?? "Default"}`
1059
+ `${capitalize(toCamelCase(operationId))}Response`,
1060
+ nameMap
783
1061
  );
784
1062
  }
785
- function buildErrorGroups(errors = [], operationId) {
1063
+ function buildErrorGroups(errors = [], operationId, nameMap) {
786
1064
  if (!errors.length) return void 0;
787
1065
  const group = {};
788
1066
  for (const { code, schema } of errors) {
789
- const category = getStatusCategory(code);
790
1067
  const identifier = mapStatusToIdentifier(code);
791
1068
  const typeName = resolveResponseType(
792
1069
  schema,
793
- `${capitalize(operationId)}${capitalize(identifier)}`
1070
+ `${capitalize(toCamelCase(operationId))}${capitalize(identifier)}`,
1071
+ nameMap
794
1072
  );
795
- switch (category) {
796
- case "client":
797
- group.clientErrors ??= {};
798
- group.clientErrors[identifier] = typeName;
799
- break;
800
- case "server":
801
- group.serverErrors ??= {};
802
- group.serverErrors[identifier] = typeName;
803
- break;
804
- case "default":
805
- group.defaultErrors ??= {};
806
- group.defaultErrors[identifier] = typeName;
807
- break;
808
- default:
809
- group.otherErrors ??= {};
810
- group.otherErrors[identifier] = typeName;
811
- break;
812
- }
1073
+ group[identifier] = typeName;
813
1074
  }
814
1075
  return group;
815
1076
  }
816
- function resolveResponseType(schema, fallbackName) {
1077
+ function resolveResponseType(schema, fallbackName, nameMap) {
817
1078
  if (typeof schema === "string") {
818
1079
  return schema;
819
1080
  }
820
1081
  if (schema.$ref) {
821
- return extractRefName(schema.$ref);
1082
+ const refName = extractRefName(schema.$ref);
1083
+ return nameMap?.get(refName) || refName;
822
1084
  }
823
1085
  if (schema.type === "array" && schema.items?.$ref) {
824
1086
  const itemRef = extractRefName(schema.items.$ref);
825
- return `z.array(${itemRef})`;
1087
+ const sanitizedItemRef = nameMap?.get(itemRef) || itemRef;
1088
+ return `z.array(${sanitizedItemRef})`;
1089
+ }
1090
+ if (schema.allOf && Array.isArray(schema.allOf)) {
1091
+ return fallbackName;
826
1092
  }
827
1093
  return fallbackName;
828
1094
  }
@@ -905,19 +1171,30 @@ function convertQueryParamValue(schema, accessor) {
905
1171
  return `String(${accessor})`;
906
1172
  }
907
1173
  }
908
- function generateZodSchema(name, schema, generatedTypes, options) {
1174
+ function generateZodSchema(name, schema, generatedTypes, options, nameMap) {
909
1175
  if (generatedTypes.has(name)) return "";
910
1176
  generatedTypes.add(name);
911
1177
  if (schema.enum) {
912
1178
  const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
913
1179
  return `export const ${name} = z.enum([${enumValues}]);`;
914
1180
  }
1181
+ if (schema.allOf && Array.isArray(schema.allOf)) {
1182
+ const allOfParts = schema.allOf.map(
1183
+ (part) => getZodTypeFromSchema(part, options, nameMap)
1184
+ );
1185
+ if (allOfParts.length === 0) return `export const ${name} = z.object({});`;
1186
+ if (allOfParts.length === 1)
1187
+ return `export const ${name} = ${allOfParts[0]};`;
1188
+ const first = allOfParts[0];
1189
+ const rest = allOfParts.slice(1).map((part) => `.and(${part})`).join("");
1190
+ return `export const ${name} = ${first}${rest};`;
1191
+ }
915
1192
  if (schema.type === "object" || schema.properties) {
916
- return `export const ${name} = ${buildZodObject(schema, options)};`;
1193
+ return `export const ${name} = ${buildZodObject(schema, options, nameMap)};`;
917
1194
  }
918
1195
  if (schema.type === "array") {
919
1196
  const itemSchema = schema.items ?? { type: "unknown" };
920
- const itemType = getZodTypeFromSchema(itemSchema, options);
1197
+ const itemType = getZodTypeFromSchema(itemSchema, options, nameMap);
921
1198
  const builder = applyStrictArrayBounds(
922
1199
  schema,
923
1200
  `z.array(${itemType})`,
@@ -926,18 +1203,29 @@ function generateZodSchema(name, schema, generatedTypes, options) {
926
1203
  );
927
1204
  return `export const ${name} = ${builder};`;
928
1205
  }
929
- return `export const ${name} = ${getZodTypeFromSchema(schema, options)};`;
1206
+ return `export const ${name} = ${getZodTypeFromSchema(schema, options, nameMap)};`;
930
1207
  }
931
- function getZodTypeFromSchema(schema, options) {
1208
+ function getZodTypeFromSchema(schema, options, nameMap) {
932
1209
  if (schema.$ref) {
933
- return extractRefName(schema.$ref);
1210
+ const refName = extractRefName(schema.$ref);
1211
+ return nameMap?.get(refName) || refName;
934
1212
  }
935
1213
  if (schema.enum) {
936
1214
  const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
937
1215
  return `z.enum([${enumValues}])`;
938
1216
  }
939
- if (schema.type === "object" || schema.properties) {
940
- return buildZodObject(schema, options);
1217
+ if (schema.allOf && Array.isArray(schema.allOf)) {
1218
+ const allOfParts = schema.allOf.map(
1219
+ (part) => getZodTypeFromSchema(part, options, nameMap)
1220
+ );
1221
+ if (allOfParts.length === 0) return "z.object({})";
1222
+ if (allOfParts.length === 1) return allOfParts[0];
1223
+ const first = allOfParts[0];
1224
+ const rest = allOfParts.slice(1).map((part) => `.and(${part})`).join("");
1225
+ return `${first}${rest}`;
1226
+ }
1227
+ if (schema.type === "object" || schema.properties || schema.allOf || schema.oneOf || schema.anyOf) {
1228
+ return buildZodObject(schema, options, nameMap);
941
1229
  }
942
1230
  switch (schema.type) {
943
1231
  case "string":
@@ -947,7 +1235,8 @@ function getZodTypeFromSchema(schema, options) {
947
1235
  case "array":
948
1236
  return `z.array(${getZodTypeFromSchema(
949
1237
  schema.items ?? { type: "unknown" },
950
- options
1238
+ options,
1239
+ nameMap
951
1240
  )})`;
952
1241
  case "null":
953
1242
  return "z.null()";
@@ -959,13 +1248,13 @@ function getZodTypeFromSchema(schema, options) {
959
1248
  return "z.unknown()";
960
1249
  }
961
1250
  }
962
- function buildZodObject(schema, options) {
1251
+ function buildZodObject(schema, options, nameMap) {
963
1252
  const properties = [];
964
1253
  for (const [propName, propSchema] of Object.entries(
965
1254
  schema.properties || {}
966
1255
  )) {
967
1256
  const isRequired = schema.required?.includes(propName) ?? false;
968
- const zodType = getZodTypeFromSchema(propSchema, options);
1257
+ const zodType = getZodTypeFromSchema(propSchema, options, nameMap);
969
1258
  const finalType = isRequired ? zodType : `${zodType}.optional()`;
970
1259
  properties.push(` ${formatPropertyName(propName)}: ${finalType},`);
971
1260
  }
@@ -1073,57 +1362,6 @@ function applyNumericBounds(schema, builder) {
1073
1362
  }
1074
1363
  return builder;
1075
1364
  }
1076
- function generateHelperFile() {
1077
- const output = [];
1078
- output.push("// Generated helper types for Zenko");
1079
- output.push(
1080
- "// This file provides type definitions for operation objects and path functions"
1081
- );
1082
- output.push("");
1083
- output.push(
1084
- "export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
1085
- );
1086
- output.push("");
1087
- output.push(
1088
- 'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
1089
- );
1090
- output.push("");
1091
- output.push(
1092
- "export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
1093
- );
1094
- output.push("");
1095
- output.push(
1096
- "export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
1097
- );
1098
- output.push("");
1099
- output.push(
1100
- "export type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
1101
- );
1102
- output.push(" clientErrors?: TClient");
1103
- output.push(" serverErrors?: TServer");
1104
- output.push(" defaultErrors?: TDefault");
1105
- output.push(" otherErrors?: TOther");
1106
- output.push("}");
1107
- output.push("");
1108
- output.push(
1109
- "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> = {"
1110
- );
1111
- output.push(" method: TMethod");
1112
- output.push(" path: TPath");
1113
- output.push(" request?: TRequest");
1114
- output.push(" response?: TResponse");
1115
- output.push(" headers?: THeaders");
1116
- output.push(" errors?: TErrors");
1117
- output.push("}");
1118
- output.push("");
1119
- return output.join("\n");
1120
- }
1121
- function toCamelCase(str) {
1122
- return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
1123
- }
1124
- function capitalize(str) {
1125
- return str.charAt(0).toUpperCase() + str.slice(1);
1126
- }
1127
1365
 
1128
1366
  // src/cli.ts
1129
1367
  async function main() {
@@ -1290,7 +1528,10 @@ async function generateSingle(options) {
1290
1528
  fs.mkdirSync(path.dirname(resolvedOutput), { recursive: true });
1291
1529
  fs.writeFileSync(resolvedOutput, result.output);
1292
1530
  console.log(`\u2705 Generated TypeScript types in ${resolvedOutput}`);
1293
- console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths).length} paths`);
1531
+ console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths || {}).length} paths`);
1532
+ if (spec.webhooks) {
1533
+ console.log(`\u{1FA9D} Processed ${Object.keys(spec.webhooks).length} webhooks`);
1534
+ }
1294
1535
  if (result.helperFile) {
1295
1536
  const helperPath = path.isAbsolute(result.helperFile.path) ? result.helperFile.path : path.resolve(path.dirname(resolvedOutput), result.helperFile.path);
1296
1537
  const absoluteResolvedOutput = path.resolve(resolvedOutput);