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.mjs CHANGED
@@ -135,6 +135,14 @@ function formatPropertyName(name) {
135
135
  return isValidJSIdentifier(name) ? name : `"${name}"`;
136
136
  }
137
137
 
138
+ // src/utils/string-utils.ts
139
+ function toCamelCase(str) {
140
+ return str.replace(/-([a-zA-Z])/g, (_, letter) => letter.toUpperCase()).replace(/-+$/, "");
141
+ }
142
+ function capitalize(str) {
143
+ return str.charAt(0).toUpperCase() + str.slice(1);
144
+ }
145
+
138
146
  // src/utils/http-status.ts
139
147
  var statusNameMap = {
140
148
  "400": "badRequest",
@@ -194,14 +202,6 @@ function mapStatusToIdentifier(status) {
194
202
  if (!candidate) return "unknownError";
195
203
  return /^[a-zA-Z_$]/.test(candidate) ? candidate : `status${candidate.charAt(0).toUpperCase()}${candidate.slice(1)}`;
196
204
  }
197
- function getStatusCategory(status) {
198
- if (status === "default") return "default";
199
- const code = Number(status);
200
- if (!Number.isInteger(code)) return "unknown";
201
- if (code >= 400 && code <= 499) return "client";
202
- if (code >= 500 && code <= 599) return "server";
203
- return "unknown";
204
- }
205
205
  function isErrorStatus(status) {
206
206
  if (status === "default") return true;
207
207
  const code = Number(status);
@@ -209,6 +209,169 @@ function isErrorStatus(status) {
209
209
  return code >= 400;
210
210
  }
211
211
 
212
+ // src/utils/tree-shaking.ts
213
+ function analyzeZenkoUsage(operations) {
214
+ const usage = {
215
+ usesHeaderFn: false,
216
+ usesOperationDefinition: false,
217
+ usesOperationErrors: false
218
+ };
219
+ if (operations.length > 0) {
220
+ usage.usesOperationDefinition = true;
221
+ }
222
+ for (const op of operations) {
223
+ if (op.requestHeaders && op.requestHeaders.length > 0) {
224
+ usage.usesHeaderFn = true;
225
+ }
226
+ if (op.errors && hasAnyErrors(op.errors)) {
227
+ usage.usesOperationErrors = true;
228
+ }
229
+ }
230
+ if (operations.length > 0 && !usage.usesOperationErrors) {
231
+ const hasDefaultErrors = operations.some(
232
+ (op) => !op.errors || !hasAnyErrors(op.errors)
233
+ );
234
+ if (hasDefaultErrors) {
235
+ usage.usesOperationErrors = true;
236
+ }
237
+ }
238
+ return usage;
239
+ }
240
+ function generateZenkoImport(usage, mode, helpersOutput) {
241
+ const types = [];
242
+ if (usage.usesHeaderFn) types.push("HeaderFn");
243
+ if (usage.usesOperationDefinition) types.push("OperationDefinition");
244
+ if (usage.usesOperationErrors) types.push("OperationErrors");
245
+ if (types.length === 0) {
246
+ return "";
247
+ }
248
+ const importSource = mode === "package" ? '"zenko"' : `"${helpersOutput}"`;
249
+ return `import type { ${types.join(", ")} } from ${importSource};`;
250
+ }
251
+ function hasAnyErrors(errors) {
252
+ return Boolean(errors && Object.keys(errors).length > 0);
253
+ }
254
+
255
+ // src/utils/collect-inline-types.ts
256
+ function collectInlineRequestTypes(operations, spec) {
257
+ const requestTypesToGenerate = /* @__PURE__ */ new Map();
258
+ const operationLookup = /* @__PURE__ */ new Map();
259
+ for (const [, pathItem] of Object.entries(spec.paths || {})) {
260
+ for (const [, operation] of Object.entries(pathItem)) {
261
+ const op = operation;
262
+ if (op.operationId) {
263
+ operationLookup.set(op.operationId, op);
264
+ }
265
+ }
266
+ }
267
+ for (const [, pathItem] of Object.entries(spec.webhooks || {})) {
268
+ for (const [, operation] of Object.entries(pathItem)) {
269
+ const op = operation;
270
+ if (op.operationId) {
271
+ operationLookup.set(op.operationId, op);
272
+ }
273
+ }
274
+ }
275
+ for (const op of operations) {
276
+ const operation = operationLookup.get(op.operationId);
277
+ if (!operation) continue;
278
+ const requestBody = operation.requestBody;
279
+ if (requestBody && requestBody.content) {
280
+ const content = requestBody.content;
281
+ const jsonContent = content["application/json"];
282
+ if (jsonContent && jsonContent.schema) {
283
+ const schema = jsonContent.schema;
284
+ const typeName = `${capitalize(toCamelCase(op.operationId))}Request`;
285
+ if (!schema.$ref || schema.allOf || schema.oneOf || schema.anyOf) {
286
+ requestTypesToGenerate.set(typeName, schema);
287
+ }
288
+ }
289
+ }
290
+ }
291
+ return requestTypesToGenerate;
292
+ }
293
+ function collectInlineResponseTypes(operations, spec) {
294
+ const responseTypesToGenerate = /* @__PURE__ */ new Map();
295
+ const operationLookup = /* @__PURE__ */ new Map();
296
+ for (const [, pathItem] of Object.entries(spec.paths || {})) {
297
+ for (const [, operation] of Object.entries(pathItem)) {
298
+ const op = operation;
299
+ if (op.operationId) {
300
+ operationLookup.set(op.operationId, op);
301
+ }
302
+ }
303
+ }
304
+ for (const [, pathItem] of Object.entries(spec.webhooks || {})) {
305
+ for (const [, operation] of Object.entries(pathItem)) {
306
+ const op = operation;
307
+ if (op.operationId) {
308
+ operationLookup.set(op.operationId, op);
309
+ }
310
+ }
311
+ }
312
+ for (const op of operations) {
313
+ const operation = operationLookup.get(op.operationId);
314
+ if (!operation) continue;
315
+ const responses = operation.responses || {};
316
+ for (const [statusCode, response] of Object.entries(responses)) {
317
+ if (/^2\d\d$/.test(statusCode) && response.content) {
318
+ const content = response.content;
319
+ const jsonContent = content["application/json"];
320
+ if (jsonContent && jsonContent.schema) {
321
+ const schema = jsonContent.schema;
322
+ const typeName = `${capitalize(toCamelCase(op.operationId))}Response`;
323
+ if (!schema.$ref || schema.allOf || schema.oneOf || schema.anyOf) {
324
+ responseTypesToGenerate.set(typeName, schema);
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+ return responseTypesToGenerate;
331
+ }
332
+
333
+ // src/utils/generate-helper-file.ts
334
+ function generateHelperFile() {
335
+ const output = [];
336
+ output.push("// Generated helper types for Zenko");
337
+ output.push(
338
+ "// This file provides type definitions for operation objects and path functions"
339
+ );
340
+ output.push("");
341
+ output.push(
342
+ "export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
343
+ );
344
+ output.push("");
345
+ output.push(
346
+ 'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
347
+ );
348
+ output.push("");
349
+ output.push(
350
+ "export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
351
+ );
352
+ output.push("");
353
+ output.push(
354
+ "export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
355
+ );
356
+ output.push("");
357
+ output.push(
358
+ "export type OperationErrors<TError = unknown> = TError extends Record<string, unknown> ? TError : Record<string, TError>;"
359
+ );
360
+ output.push("");
361
+ output.push(
362
+ "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> = {"
363
+ );
364
+ output.push(" method: TMethod");
365
+ output.push(" path: TPath");
366
+ output.push(" request?: TRequest");
367
+ output.push(" response?: TResponse");
368
+ output.push(" headers?: THeaders");
369
+ output.push(" errors?: TErrors");
370
+ output.push("}");
371
+ output.push("");
372
+ return output.join("\n");
373
+ }
374
+
212
375
  // src/zenko.ts
213
376
  function generateWithMetadata(spec, options = {}) {
214
377
  const output = [];
@@ -220,7 +383,14 @@ function generateWithMetadata(spec, options = {}) {
220
383
  strictNumeric
221
384
  };
222
385
  output.push('import { z } from "zod";');
223
- appendHelperTypesImport(output, typesConfig);
386
+ const nameMap = /* @__PURE__ */ new Map();
387
+ if (spec.components?.schemas) {
388
+ for (const name of Object.keys(spec.components.schemas)) {
389
+ nameMap.set(name, toCamelCase(name));
390
+ }
391
+ }
392
+ const operations = parseOperations(spec, nameMap);
393
+ appendHelperTypesImport(output, typesConfig, operations);
224
394
  output.push("");
225
395
  if (spec.components?.schemas) {
226
396
  output.push("// Generated Zod Schemas");
@@ -228,15 +398,23 @@ function generateWithMetadata(spec, options = {}) {
228
398
  const sortedSchemas = topologicalSort(spec.components.schemas);
229
399
  for (const name of sortedSchemas) {
230
400
  const schema = spec.components.schemas[name];
401
+ const sanitizedName = nameMap.get(name);
231
402
  output.push(
232
- generateZodSchema(name, schema, generatedTypes, schemaOptions)
403
+ generateZodSchema(
404
+ sanitizedName,
405
+ schema,
406
+ generatedTypes,
407
+ schemaOptions,
408
+ nameMap
409
+ )
233
410
  );
234
411
  output.push("");
235
- output.push(`export type ${name} = z.infer<typeof ${name}>;`);
412
+ output.push(
413
+ `export type ${sanitizedName} = z.infer<typeof ${sanitizedName}>;`
414
+ );
236
415
  output.push("");
237
416
  }
238
417
  }
239
- const operations = parseOperations(spec);
240
418
  output.push("// Path Functions");
241
419
  output.push("export const paths = {");
242
420
  for (const op of operations) {
@@ -250,24 +428,37 @@ function generateWithMetadata(spec, options = {}) {
250
428
  );
251
429
  continue;
252
430
  }
253
- const allParamNames = [
254
- ...pathParamNames,
255
- ...op.queryParams.map((p) => p.name)
256
- ];
257
- const signaturePieces = [];
431
+ const alias = (n) => {
432
+ if (isValidJSIdentifier(n)) return n;
433
+ let aliased = toCamelCase(n);
434
+ if (!isValidJSIdentifier(aliased)) {
435
+ aliased = `_${aliased}`;
436
+ }
437
+ return aliased;
438
+ };
439
+ const destructPieces = [];
440
+ const typePieces = [];
258
441
  for (const param of op.pathParams) {
259
- signaturePieces.push(`${param.name}: string`);
442
+ destructPieces.push(
443
+ isValidJSIdentifier(param.name) ? param.name : `${formatPropertyName(param.name)}: ${alias(param.name)}`
444
+ );
445
+ typePieces.push(`${formatPropertyName(param.name)}: string`);
260
446
  }
261
447
  for (const param of op.queryParams) {
262
- signaturePieces.push(
263
- `${param.name}${param.required ? "" : "?"}: ${mapQueryType(param)}`
448
+ destructPieces.push(
449
+ isValidJSIdentifier(param.name) ? param.name : `${formatPropertyName(param.name)}: ${alias(param.name)}`
450
+ );
451
+ typePieces.push(
452
+ `${formatPropertyName(param.name)}${param.required ? "" : "?"}: ${mapQueryType(param)}`
264
453
  );
265
454
  }
266
- const signatureParams = signaturePieces.join(", ");
267
455
  const needsDefaultObject = !hasPathParams && hasQueryParams && op.queryParams.every((param) => !param.required);
268
- const signatureArgs = allParamNames.length ? `{ ${allParamNames.join(", ")} }` : "{}";
269
- const signature = `${signatureArgs}: { ${signatureParams} }${needsDefaultObject ? " = {}" : ""}`;
270
- const pathWithParams = op.path.replace(/{([^}]+)}/g, "${$1}");
456
+ const signatureArgs = destructPieces.length ? `{ ${destructPieces.join(", ")} }` : "{}";
457
+ const signature = `${signatureArgs}: { ${typePieces.join(", ")} }${needsDefaultObject ? " = {}" : ""}`;
458
+ const pathWithParams = op.path.replace(
459
+ /{([^}]+)}/g,
460
+ (_m, n) => `\${${alias(n)}}`
461
+ );
271
462
  if (!hasQueryParams) {
272
463
  output.push(
273
464
  ` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => \`${pathWithParams}\`,`
@@ -279,8 +470,7 @@ function generateWithMetadata(spec, options = {}) {
279
470
  );
280
471
  output.push(" const params = new URLSearchParams()");
281
472
  for (const param of op.queryParams) {
282
- const propertyKey = formatPropertyName(param.name);
283
- const accessor = isValidJSIdentifier(param.name) ? param.name : propertyKey;
473
+ const accessor = isValidJSIdentifier(param.name) ? param.name : alias(toCamelCase(param.name));
284
474
  const schema = param.schema ?? {};
285
475
  if (schema?.type === "array") {
286
476
  const itemValueExpression = convertQueryParamValue(
@@ -362,6 +552,8 @@ function generateWithMetadata(spec, options = {}) {
362
552
  }
363
553
  output.push("} as const;");
364
554
  output.push("");
555
+ generateRequestTypes(output, operations, spec, nameMap, schemaOptions);
556
+ generateResponseTypes(output, operations, spec, nameMap, schemaOptions);
365
557
  generateOperationTypes(output, operations, typesConfig);
366
558
  output.push("// Operation Objects");
367
559
  for (const op of operations) {
@@ -375,13 +567,8 @@ function generateWithMetadata(spec, options = {}) {
375
567
  if (op.requestHeaders && op.requestHeaders.length > 0) {
376
568
  output.push(` headers: headers.${camelCaseOperationId},`);
377
569
  }
378
- if (op.errors && hasAnyErrors(op.errors)) {
379
- output.push(" errors: {");
380
- appendErrorGroup(output, "clientErrors", op.errors.clientErrors);
381
- appendErrorGroup(output, "serverErrors", op.errors.serverErrors);
382
- appendErrorGroup(output, "defaultErrors", op.errors.defaultErrors);
383
- appendErrorGroup(output, "otherErrors", op.errors.otherErrors);
384
- output.push(" },");
570
+ if (op.errors && hasAnyErrors2(op.errors)) {
571
+ appendErrorGroup(output, "errors", op.errors);
385
572
  }
386
573
  output.push("} as const;");
387
574
  output.push("");
@@ -397,6 +584,46 @@ function generateWithMetadata(spec, options = {}) {
397
584
  }
398
585
  return result;
399
586
  }
587
+ function generateRequestTypes(output, operations, spec, nameMap, schemaOptions) {
588
+ const requestTypesToGenerate = collectInlineRequestTypes(operations, spec);
589
+ if (requestTypesToGenerate.size > 0) {
590
+ output.push("// Generated Request Types");
591
+ output.push("");
592
+ for (const [typeName, schema] of requestTypesToGenerate) {
593
+ const generatedSchema = generateZodSchema(
594
+ typeName,
595
+ schema,
596
+ /* @__PURE__ */ new Set(),
597
+ schemaOptions,
598
+ nameMap
599
+ );
600
+ output.push(generatedSchema);
601
+ output.push("");
602
+ output.push(`export type ${typeName} = z.infer<typeof ${typeName}>;`);
603
+ output.push("");
604
+ }
605
+ }
606
+ }
607
+ function generateResponseTypes(output, operations, spec, nameMap, schemaOptions) {
608
+ const responseTypesToGenerate = collectInlineResponseTypes(operations, spec);
609
+ if (responseTypesToGenerate.size > 0) {
610
+ output.push("// Generated Response Types");
611
+ output.push("");
612
+ for (const [typeName, schema] of responseTypesToGenerate) {
613
+ const generatedSchema = generateZodSchema(
614
+ typeName,
615
+ schema,
616
+ /* @__PURE__ */ new Set(),
617
+ schemaOptions,
618
+ nameMap
619
+ );
620
+ output.push(generatedSchema);
621
+ output.push("");
622
+ output.push(`export type ${typeName} = z.infer<typeof ${typeName}>;`);
623
+ output.push("");
624
+ }
625
+ }
626
+ }
400
627
  function appendOperationField(buffer, key, value) {
401
628
  if (!value) return;
402
629
  buffer.push(` ${key}: ${value},`);
@@ -409,13 +636,8 @@ function appendErrorGroup(buffer, label, errors) {
409
636
  }
410
637
  buffer.push(" },");
411
638
  }
412
- function hasAnyErrors(group) {
413
- return [
414
- group.clientErrors,
415
- group.serverErrors,
416
- group.defaultErrors,
417
- group.otherErrors
418
- ].some((bucket) => bucket && Object.keys(bucket).length > 0);
639
+ function hasAnyErrors2(group) {
640
+ return Boolean(group && Object.keys(group).length > 0);
419
641
  }
420
642
  function isRequestMethod(method) {
421
643
  switch (method) {
@@ -462,33 +684,70 @@ function inferResponseType(contentType, statusCode) {
462
684
  }
463
685
  return "unknown";
464
686
  }
465
- function parseOperations(spec) {
687
+ function parseOperations(spec, nameMap) {
466
688
  const operations = [];
467
- for (const [path2, pathItem] of Object.entries(spec.paths)) {
468
- for (const [method, operation] of Object.entries(pathItem)) {
469
- const normalizedMethod = method.toLowerCase();
470
- if (!isRequestMethod(normalizedMethod)) continue;
471
- if (!operation.operationId) continue;
472
- const pathParams = extractPathParams(path2);
473
- const requestType = getRequestType(operation);
474
- const { successResponse, errors } = getResponseTypes(
475
- operation,
476
- operation.operationId
477
- );
478
- const resolvedParameters = collectParameters(pathItem, operation, spec);
479
- const requestHeaders = getRequestHeaders(resolvedParameters);
480
- const queryParams = getQueryParams(resolvedParameters);
481
- operations.push({
482
- operationId: operation.operationId,
483
- path: path2,
484
- method: normalizedMethod,
485
- pathParams,
486
- queryParams,
487
- requestType,
488
- responseType: successResponse,
489
- requestHeaders,
490
- errors
491
- });
689
+ if (spec.paths) {
690
+ for (const [path2, pathItem] of Object.entries(spec.paths)) {
691
+ for (const [method, operation] of Object.entries(pathItem)) {
692
+ const normalizedMethod = method.toLowerCase();
693
+ if (!isRequestMethod(normalizedMethod)) continue;
694
+ if (!operation.operationId) continue;
695
+ const pathParams = extractPathParams(path2);
696
+ const requestType = getRequestType(operation);
697
+ const { successResponse, errors } = getResponseTypes(
698
+ operation,
699
+ operation.operationId,
700
+ nameMap
701
+ );
702
+ const resolvedParameters = collectParameters(pathItem, operation, spec);
703
+ const requestHeaders = getRequestHeaders(resolvedParameters);
704
+ const queryParams = getQueryParams(resolvedParameters);
705
+ operations.push({
706
+ operationId: operation.operationId,
707
+ path: path2,
708
+ method: normalizedMethod,
709
+ pathParams,
710
+ queryParams,
711
+ requestType,
712
+ responseType: successResponse,
713
+ requestHeaders,
714
+ errors
715
+ });
716
+ }
717
+ }
718
+ }
719
+ if (spec.webhooks) {
720
+ for (const [webhookName, webhookItem] of Object.entries(spec.webhooks)) {
721
+ for (const [method, operation] of Object.entries(webhookItem)) {
722
+ const normalizedMethod = method.toLowerCase();
723
+ if (!isRequestMethod(normalizedMethod)) continue;
724
+ if (!operation.operationId) continue;
725
+ const path2 = webhookName;
726
+ const pathParams = extractPathParams(path2);
727
+ const requestType = getRequestType(operation);
728
+ const { successResponse, errors } = getResponseTypes(
729
+ operation,
730
+ operation.operationId
731
+ );
732
+ const resolvedParameters = collectParameters(
733
+ webhookItem,
734
+ operation,
735
+ spec
736
+ );
737
+ const requestHeaders = getRequestHeaders(resolvedParameters);
738
+ const queryParams = getQueryParams(resolvedParameters);
739
+ operations.push({
740
+ operationId: operation.operationId,
741
+ path: path2,
742
+ method: normalizedMethod,
743
+ pathParams,
744
+ queryParams,
745
+ requestType,
746
+ responseType: successResponse,
747
+ requestHeaders,
748
+ errors
749
+ });
750
+ }
492
751
  }
493
752
  }
494
753
  return operations;
@@ -497,21 +756,42 @@ function normalizeTypesConfig(config) {
497
756
  return {
498
757
  emit: config?.emit ?? true,
499
758
  helpers: config?.helpers ?? "package",
500
- helpersOutput: config?.helpersOutput ?? "./zenko-types"
759
+ helpersOutput: config?.helpersOutput ?? "./zenko-types",
760
+ treeShake: config?.treeShake ?? true
501
761
  };
502
762
  }
503
- function appendHelperTypesImport(buffer, config) {
763
+ function appendHelperTypesImport(buffer, config, operations) {
504
764
  if (!config.emit) return;
505
765
  switch (config.helpers) {
506
766
  case "package":
507
- buffer.push(
508
- 'import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "zenko";'
509
- );
767
+ if (config.treeShake) {
768
+ const usage = analyzeZenkoUsage(operations);
769
+ const importStatement = generateZenkoImport(usage, "package");
770
+ if (importStatement) {
771
+ buffer.push(importStatement);
772
+ }
773
+ } else {
774
+ buffer.push(
775
+ 'import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "zenko";'
776
+ );
777
+ }
510
778
  return;
511
779
  case "file":
512
- buffer.push(
513
- `import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "${config.helpersOutput}";`
514
- );
780
+ if (config.treeShake) {
781
+ const usage = analyzeZenkoUsage(operations);
782
+ const importStatement = generateZenkoImport(
783
+ usage,
784
+ "file",
785
+ config.helpersOutput
786
+ );
787
+ if (importStatement) {
788
+ buffer.push(importStatement);
789
+ }
790
+ } else {
791
+ buffer.push(
792
+ `import type { PathFn, HeaderFn, OperationDefinition, OperationErrors } from "${config.helpersOutput}";`
793
+ );
794
+ }
515
795
  return;
516
796
  case "inline":
517
797
  buffer.push(
@@ -527,24 +807,19 @@ function appendHelperTypesImport(buffer, config) {
527
807
  "type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown);"
528
808
  );
529
809
  buffer.push(
530
- "type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
810
+ "type OperationErrors<TError = unknown> = TError extends Record<string, unknown> ? TError : Record<string, TError>;"
531
811
  );
532
- buffer.push(" clientErrors?: TClient;");
533
- buffer.push(" serverErrors?: TServer;");
534
- buffer.push(" defaultErrors?: TDefault;");
535
- buffer.push(" otherErrors?: TOther;");
536
- buffer.push("};");
537
812
  buffer.push(
538
813
  "type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends AnyHeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
539
814
  );
540
- buffer.push(" method: TMethod;");
541
- buffer.push(" path: TPath;");
542
- buffer.push(" request?: TRequest;");
543
- buffer.push(" response?: TResponse;");
544
- buffer.push(" headers?: THeaders;");
545
- buffer.push(" errors?: TErrors;");
546
- buffer.push("};");
547
- return;
815
+ buffer.push(" method: TMethod");
816
+ buffer.push(" path: TPath");
817
+ buffer.push(" request?: TRequest");
818
+ buffer.push(" response?: TResponse");
819
+ buffer.push(" headers?: THeaders");
820
+ buffer.push(" errors?: TErrors");
821
+ buffer.push("}");
822
+ buffer.push("");
548
823
  }
549
824
  }
550
825
  function generateOperationTypes(buffer, operations, config) {
@@ -572,14 +847,11 @@ function generateOperationTypes(buffer, operations, config) {
572
847
  }
573
848
  }
574
849
  function buildOperationErrorsType(errors) {
575
- if (!errors || !hasAnyErrors(errors)) {
850
+ if (!errors || !hasAnyErrors2(errors)) {
576
851
  return "OperationErrors";
577
852
  }
578
- const client = buildErrorBucket(errors.clientErrors);
579
- const server = buildErrorBucket(errors.serverErrors);
580
- const fallback = buildErrorBucket(errors.defaultErrors);
581
- const other = buildErrorBucket(errors.otherErrors);
582
- return `OperationErrors<${client}, ${server}, ${fallback}, ${other}>`;
853
+ const errorBucket = buildErrorBucket(errors);
854
+ return `OperationErrors<${errorBucket}>`;
583
855
  }
584
856
  function buildErrorBucket(bucket) {
585
857
  if (!bucket || Object.keys(bucket).length === 0) {
@@ -686,10 +958,10 @@ function getRequestType(operation) {
686
958
  if (requestBody.$ref) {
687
959
  return extractRefName(requestBody.$ref);
688
960
  }
689
- const typeName = `${capitalize(operation.operationId)}Request`;
961
+ const typeName = `${capitalize(toCamelCase(operation.operationId))}Request`;
690
962
  return typeName;
691
963
  }
692
- function getResponseTypes(operation, operationId) {
964
+ function getResponseTypes(operation, operationId, nameMap) {
693
965
  const responses = operation.responses ?? {};
694
966
  const successCodes = /* @__PURE__ */ new Map();
695
967
  const errorEntries = [];
@@ -730,11 +1002,15 @@ function getResponseTypes(operation, operationId) {
730
1002
  successCodes.set(statusCode, resolvedSchema);
731
1003
  }
732
1004
  }
733
- const successResponse = selectSuccessResponse(successCodes, operationId);
734
- const errors = buildErrorGroups(errorEntries, operationId);
1005
+ const successResponse = selectSuccessResponse(
1006
+ successCodes,
1007
+ operationId,
1008
+ nameMap
1009
+ );
1010
+ const errors = buildErrorGroups(errorEntries, operationId, nameMap);
735
1011
  return { successResponse, errors };
736
1012
  }
737
- function selectSuccessResponse(responses, operationId) {
1013
+ function selectSuccessResponse(responses, operationId, nameMap) {
738
1014
  if (responses.size === 0) return void 0;
739
1015
  const preferredOrder = ["200", "201", "204"];
740
1016
  for (const code of preferredOrder) {
@@ -745,61 +1021,51 @@ function selectSuccessResponse(responses, operationId) {
745
1021
  }
746
1022
  return resolveResponseType(
747
1023
  schema,
748
- `${capitalize(operationId)}Response${code}`
1024
+ `${capitalize(toCamelCase(operationId))}Response`,
1025
+ nameMap
749
1026
  );
750
1027
  }
751
1028
  }
752
- const [firstCode, firstSchema] = responses.entries().next().value ?? [];
1029
+ const [, firstSchema] = responses.entries().next().value ?? [];
753
1030
  if (!firstSchema) return void 0;
754
1031
  if (typeof firstSchema === "string") {
755
1032
  return firstSchema;
756
1033
  }
757
1034
  return resolveResponseType(
758
1035
  firstSchema,
759
- `${capitalize(operationId)}Response${firstCode ?? "Default"}`
1036
+ `${capitalize(toCamelCase(operationId))}Response`,
1037
+ nameMap
760
1038
  );
761
1039
  }
762
- function buildErrorGroups(errors = [], operationId) {
1040
+ function buildErrorGroups(errors = [], operationId, nameMap) {
763
1041
  if (!errors.length) return void 0;
764
1042
  const group = {};
765
1043
  for (const { code, schema } of errors) {
766
- const category = getStatusCategory(code);
767
1044
  const identifier = mapStatusToIdentifier(code);
768
1045
  const typeName = resolveResponseType(
769
1046
  schema,
770
- `${capitalize(operationId)}${capitalize(identifier)}`
1047
+ `${capitalize(toCamelCase(operationId))}${capitalize(identifier)}`,
1048
+ nameMap
771
1049
  );
772
- switch (category) {
773
- case "client":
774
- group.clientErrors ??= {};
775
- group.clientErrors[identifier] = typeName;
776
- break;
777
- case "server":
778
- group.serverErrors ??= {};
779
- group.serverErrors[identifier] = typeName;
780
- break;
781
- case "default":
782
- group.defaultErrors ??= {};
783
- group.defaultErrors[identifier] = typeName;
784
- break;
785
- default:
786
- group.otherErrors ??= {};
787
- group.otherErrors[identifier] = typeName;
788
- break;
789
- }
1050
+ group[identifier] = typeName;
790
1051
  }
791
1052
  return group;
792
1053
  }
793
- function resolveResponseType(schema, fallbackName) {
1054
+ function resolveResponseType(schema, fallbackName, nameMap) {
794
1055
  if (typeof schema === "string") {
795
1056
  return schema;
796
1057
  }
797
1058
  if (schema.$ref) {
798
- return extractRefName(schema.$ref);
1059
+ const refName = extractRefName(schema.$ref);
1060
+ return nameMap?.get(refName) || refName;
799
1061
  }
800
1062
  if (schema.type === "array" && schema.items?.$ref) {
801
1063
  const itemRef = extractRefName(schema.items.$ref);
802
- return `z.array(${itemRef})`;
1064
+ const sanitizedItemRef = nameMap?.get(itemRef) || itemRef;
1065
+ return `z.array(${sanitizedItemRef})`;
1066
+ }
1067
+ if (schema.allOf && Array.isArray(schema.allOf)) {
1068
+ return fallbackName;
803
1069
  }
804
1070
  return fallbackName;
805
1071
  }
@@ -882,19 +1148,30 @@ function convertQueryParamValue(schema, accessor) {
882
1148
  return `String(${accessor})`;
883
1149
  }
884
1150
  }
885
- function generateZodSchema(name, schema, generatedTypes, options) {
1151
+ function generateZodSchema(name, schema, generatedTypes, options, nameMap) {
886
1152
  if (generatedTypes.has(name)) return "";
887
1153
  generatedTypes.add(name);
888
1154
  if (schema.enum) {
889
1155
  const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
890
1156
  return `export const ${name} = z.enum([${enumValues}]);`;
891
1157
  }
1158
+ if (schema.allOf && Array.isArray(schema.allOf)) {
1159
+ const allOfParts = schema.allOf.map(
1160
+ (part) => getZodTypeFromSchema(part, options, nameMap)
1161
+ );
1162
+ if (allOfParts.length === 0) return `export const ${name} = z.object({});`;
1163
+ if (allOfParts.length === 1)
1164
+ return `export const ${name} = ${allOfParts[0]};`;
1165
+ const first = allOfParts[0];
1166
+ const rest = allOfParts.slice(1).map((part) => `.and(${part})`).join("");
1167
+ return `export const ${name} = ${first}${rest};`;
1168
+ }
892
1169
  if (schema.type === "object" || schema.properties) {
893
- return `export const ${name} = ${buildZodObject(schema, options)};`;
1170
+ return `export const ${name} = ${buildZodObject(schema, options, nameMap)};`;
894
1171
  }
895
1172
  if (schema.type === "array") {
896
1173
  const itemSchema = schema.items ?? { type: "unknown" };
897
- const itemType = getZodTypeFromSchema(itemSchema, options);
1174
+ const itemType = getZodTypeFromSchema(itemSchema, options, nameMap);
898
1175
  const builder = applyStrictArrayBounds(
899
1176
  schema,
900
1177
  `z.array(${itemType})`,
@@ -903,18 +1180,29 @@ function generateZodSchema(name, schema, generatedTypes, options) {
903
1180
  );
904
1181
  return `export const ${name} = ${builder};`;
905
1182
  }
906
- return `export const ${name} = ${getZodTypeFromSchema(schema, options)};`;
1183
+ return `export const ${name} = ${getZodTypeFromSchema(schema, options, nameMap)};`;
907
1184
  }
908
- function getZodTypeFromSchema(schema, options) {
1185
+ function getZodTypeFromSchema(schema, options, nameMap) {
909
1186
  if (schema.$ref) {
910
- return extractRefName(schema.$ref);
1187
+ const refName = extractRefName(schema.$ref);
1188
+ return nameMap?.get(refName) || refName;
911
1189
  }
912
1190
  if (schema.enum) {
913
1191
  const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
914
1192
  return `z.enum([${enumValues}])`;
915
1193
  }
916
- if (schema.type === "object" || schema.properties) {
917
- return buildZodObject(schema, options);
1194
+ if (schema.allOf && Array.isArray(schema.allOf)) {
1195
+ const allOfParts = schema.allOf.map(
1196
+ (part) => getZodTypeFromSchema(part, options, nameMap)
1197
+ );
1198
+ if (allOfParts.length === 0) return "z.object({})";
1199
+ if (allOfParts.length === 1) return allOfParts[0];
1200
+ const first = allOfParts[0];
1201
+ const rest = allOfParts.slice(1).map((part) => `.and(${part})`).join("");
1202
+ return `${first}${rest}`;
1203
+ }
1204
+ if (schema.type === "object" || schema.properties || schema.allOf || schema.oneOf || schema.anyOf) {
1205
+ return buildZodObject(schema, options, nameMap);
918
1206
  }
919
1207
  switch (schema.type) {
920
1208
  case "string":
@@ -924,7 +1212,8 @@ function getZodTypeFromSchema(schema, options) {
924
1212
  case "array":
925
1213
  return `z.array(${getZodTypeFromSchema(
926
1214
  schema.items ?? { type: "unknown" },
927
- options
1215
+ options,
1216
+ nameMap
928
1217
  )})`;
929
1218
  case "null":
930
1219
  return "z.null()";
@@ -936,13 +1225,13 @@ function getZodTypeFromSchema(schema, options) {
936
1225
  return "z.unknown()";
937
1226
  }
938
1227
  }
939
- function buildZodObject(schema, options) {
1228
+ function buildZodObject(schema, options, nameMap) {
940
1229
  const properties = [];
941
1230
  for (const [propName, propSchema] of Object.entries(
942
1231
  schema.properties || {}
943
1232
  )) {
944
1233
  const isRequired = schema.required?.includes(propName) ?? false;
945
- const zodType = getZodTypeFromSchema(propSchema, options);
1234
+ const zodType = getZodTypeFromSchema(propSchema, options, nameMap);
946
1235
  const finalType = isRequired ? zodType : `${zodType}.optional()`;
947
1236
  properties.push(` ${formatPropertyName(propName)}: ${finalType},`);
948
1237
  }
@@ -1050,57 +1339,6 @@ function applyNumericBounds(schema, builder) {
1050
1339
  }
1051
1340
  return builder;
1052
1341
  }
1053
- function generateHelperFile() {
1054
- const output = [];
1055
- output.push("// Generated helper types for Zenko");
1056
- output.push(
1057
- "// This file provides type definitions for operation objects and path functions"
1058
- );
1059
- output.push("");
1060
- output.push(
1061
- "export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
1062
- );
1063
- output.push("");
1064
- output.push(
1065
- 'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
1066
- );
1067
- output.push("");
1068
- output.push(
1069
- "export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
1070
- );
1071
- output.push("");
1072
- output.push(
1073
- "export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
1074
- );
1075
- output.push("");
1076
- output.push(
1077
- "export type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
1078
- );
1079
- output.push(" clientErrors?: TClient");
1080
- output.push(" serverErrors?: TServer");
1081
- output.push(" defaultErrors?: TDefault");
1082
- output.push(" otherErrors?: TOther");
1083
- output.push("}");
1084
- output.push("");
1085
- output.push(
1086
- "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> = {"
1087
- );
1088
- output.push(" method: TMethod");
1089
- output.push(" path: TPath");
1090
- output.push(" request?: TRequest");
1091
- output.push(" response?: TResponse");
1092
- output.push(" headers?: THeaders");
1093
- output.push(" errors?: TErrors");
1094
- output.push("}");
1095
- output.push("");
1096
- return output.join("\n");
1097
- }
1098
- function toCamelCase(str) {
1099
- return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
1100
- }
1101
- function capitalize(str) {
1102
- return str.charAt(0).toUpperCase() + str.slice(1);
1103
- }
1104
1342
 
1105
1343
  // src/cli.ts
1106
1344
  async function main() {
@@ -1267,7 +1505,10 @@ async function generateSingle(options) {
1267
1505
  fs.mkdirSync(path.dirname(resolvedOutput), { recursive: true });
1268
1506
  fs.writeFileSync(resolvedOutput, result.output);
1269
1507
  console.log(`\u2705 Generated TypeScript types in ${resolvedOutput}`);
1270
- console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths).length} paths`);
1508
+ console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths || {}).length} paths`);
1509
+ if (spec.webhooks) {
1510
+ console.log(`\u{1FA9D} Processed ${Object.keys(spec.webhooks).length} webhooks`);
1511
+ }
1271
1512
  if (result.helperFile) {
1272
1513
  const helperPath = path.isAbsolute(result.helperFile.path) ? result.helperFile.path : path.resolve(path.dirname(resolvedOutput), result.helperFile.path);
1273
1514
  const absoluteResolvedOutput = path.resolve(resolvedOutput);