zenko 0.1.4 → 0.1.6-beta.1

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
@@ -29,7 +29,7 @@ var path = __toESM(require("path"), 1);
29
29
  var import_url = require("url");
30
30
  var import_js_yaml = require("js-yaml");
31
31
 
32
- // src/utils/topological-sort.ts
32
+ // ../zenko-core/src/utils/topological-sort.ts
33
33
  function topologicalSort(schemas) {
34
34
  const visited = /* @__PURE__ */ new Set();
35
35
  const visiting = /* @__PURE__ */ new Set();
@@ -78,7 +78,7 @@ function extractRefName(ref) {
78
78
  return ref.split("/").pop() || "Unknown";
79
79
  }
80
80
 
81
- // src/utils/property-name.ts
81
+ // ../zenko-core/src/utils/property-name.ts
82
82
  function isValidJSIdentifier(name) {
83
83
  if (!name) return false;
84
84
  const firstChar = name.at(0);
@@ -158,7 +158,7 @@ function formatPropertyName(name) {
158
158
  return isValidJSIdentifier(name) ? name : `"${name}"`;
159
159
  }
160
160
 
161
- // src/utils/http-status.ts
161
+ // ../zenko-core/src/utils/http-status.ts
162
162
  var statusNameMap = {
163
163
  "400": "badRequest",
164
164
  "401": "unauthorized",
@@ -232,8 +232,8 @@ function isErrorStatus(status) {
232
232
  return code >= 400;
233
233
  }
234
234
 
235
- // src/zenko.ts
236
- function generate(spec, options = {}) {
235
+ // ../zenko-core/src/zenko.ts
236
+ function generateWithMetadata(spec, options = {}) {
237
237
  const output = [];
238
238
  const generatedTypes = /* @__PURE__ */ new Set();
239
239
  const { strictDates = false, strictNumeric = false } = options;
@@ -408,7 +408,16 @@ function generate(spec, options = {}) {
408
408
  output.push("");
409
409
  }
410
410
  generateOperationTypes(output, operations, typesConfig);
411
- return output.join("\n");
411
+ const result = {
412
+ output: output.join("\n")
413
+ };
414
+ if (typesConfig.emit && typesConfig.helpers === "file" && typesConfig.helpersOutput) {
415
+ result.helperFile = {
416
+ path: typesConfig.helpersOutput,
417
+ content: generateHelperFile()
418
+ };
419
+ }
420
+ return result;
412
421
  }
413
422
  function appendOperationField(buffer, key, value) {
414
423
  if (!value) return;
@@ -536,6 +545,9 @@ function appendHelperTypesImport(buffer, config) {
536
545
  buffer.push(
537
546
  "type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
538
547
  );
548
+ buffer.push(
549
+ "type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown);"
550
+ );
539
551
  buffer.push(
540
552
  "type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
541
553
  );
@@ -545,7 +557,7 @@ function appendHelperTypesImport(buffer, config) {
545
557
  buffer.push(" otherErrors?: TOther;");
546
558
  buffer.push("};");
547
559
  buffer.push(
548
- "type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends HeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
560
+ "type OperationDefinition<TMethod extends RequestMethod, TPath extends (...args: any[]) => string, TRequest = undefined, TResponse = undefined, THeaders extends AnyHeaderFn | undefined = undefined, TErrors extends OperationErrors | undefined = undefined> = {"
549
561
  );
550
562
  buffer.push(" method: TMethod;");
551
563
  buffer.push(" path: TPath;");
@@ -616,26 +628,35 @@ var TYPE_KEYWORDS = /* @__PURE__ */ new Set([
616
628
  "bigint",
617
629
  "symbol"
618
630
  ]);
631
+ var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
619
632
  function wrapTypeReference(typeName) {
620
633
  if (!typeName) return "undefined";
621
- if (typeName === "undefined") return "undefined";
622
- if (TYPE_KEYWORDS.has(typeName)) return typeName;
623
- if (typeName.startsWith("typeof ")) return typeName;
624
- const identifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
625
- if (identifierPattern.test(typeName)) {
626
- return `typeof ${typeName}`;
627
- }
628
- return typeName;
634
+ const normalized = typeName.trim();
635
+ if (normalized === "undefined") return "undefined";
636
+ if (TYPE_KEYWORDS.has(normalized)) return normalized;
637
+ if (normalized.startsWith("typeof ")) return normalized;
638
+ const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
639
+ if (arrayMatch) {
640
+ return `z.ZodArray<${wrapTypeReference(arrayMatch[1])}>`;
641
+ }
642
+ if (IDENTIFIER_PATTERN.test(normalized)) {
643
+ return `typeof ${normalized}`;
644
+ }
645
+ return normalized;
629
646
  }
630
647
  function wrapErrorValueType(typeName) {
631
648
  if (!typeName) return "unknown";
632
- if (TYPE_KEYWORDS.has(typeName)) return typeName;
633
- if (typeName.startsWith("typeof ")) return typeName;
634
- const identifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
635
- if (identifierPattern.test(typeName)) {
636
- return `typeof ${typeName}`;
649
+ const normalized = typeName.trim();
650
+ if (TYPE_KEYWORDS.has(normalized)) return normalized;
651
+ if (normalized.startsWith("typeof ")) return normalized;
652
+ const arrayMatch = normalized.match(/^z\.array\((.+)\)$/);
653
+ if (arrayMatch) {
654
+ return `z.ZodArray<${wrapErrorValueType(arrayMatch[1])}>`;
637
655
  }
638
- return typeName;
656
+ if (IDENTIFIER_PATTERN.test(normalized)) {
657
+ return `typeof ${normalized}`;
658
+ }
659
+ return normalized;
639
660
  }
640
661
  function collectParameters(pathItem, operation, spec) {
641
662
  const parametersMap = /* @__PURE__ */ new Map();
@@ -699,6 +720,11 @@ function getResponseTypes(operation, operationId) {
699
720
  if (!content || Object.keys(content).length === 0) {
700
721
  if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
701
722
  successCodes.set(statusCode, "undefined");
723
+ } else if (isErrorStatus(statusCode)) {
724
+ errorEntries.push({
725
+ code: statusCode,
726
+ schema: "undefined"
727
+ });
702
728
  }
703
729
  continue;
704
730
  }
@@ -793,6 +819,10 @@ function resolveResponseType(schema, fallbackName) {
793
819
  if (schema.$ref) {
794
820
  return extractRefName(schema.$ref);
795
821
  }
822
+ if (schema.type === "array" && schema.items?.$ref) {
823
+ const itemRef = extractRefName(schema.items.$ref);
824
+ return `z.array(${itemRef})`;
825
+ }
796
826
  return fallbackName;
797
827
  }
798
828
  function getRequestHeaders(parameters) {
@@ -1042,6 +1072,51 @@ function applyNumericBounds(schema, builder) {
1042
1072
  }
1043
1073
  return builder;
1044
1074
  }
1075
+ function generateHelperFile() {
1076
+ const output = [];
1077
+ output.push("// Generated helper types for Zenko");
1078
+ output.push(
1079
+ "// This file provides type definitions for operation objects and path functions"
1080
+ );
1081
+ output.push("");
1082
+ output.push(
1083
+ "export type PathFn<TArgs extends unknown[] = []> = (...args: TArgs) => string"
1084
+ );
1085
+ output.push("");
1086
+ output.push(
1087
+ 'export type RequestMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"'
1088
+ );
1089
+ output.push("");
1090
+ output.push(
1091
+ "export type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult"
1092
+ );
1093
+ output.push("");
1094
+ output.push(
1095
+ "export type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown)"
1096
+ );
1097
+ output.push("");
1098
+ output.push(
1099
+ "export type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
1100
+ );
1101
+ output.push(" clientErrors?: TClient");
1102
+ output.push(" serverErrors?: TServer");
1103
+ output.push(" defaultErrors?: TDefault");
1104
+ output.push(" otherErrors?: TOther");
1105
+ output.push("}");
1106
+ output.push("");
1107
+ output.push(
1108
+ "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> = {"
1109
+ );
1110
+ output.push(" method: TMethod");
1111
+ output.push(" path: TPath");
1112
+ output.push(" request?: TRequest");
1113
+ output.push(" response?: TResponse");
1114
+ output.push(" headers?: THeaders");
1115
+ output.push(" errors?: TErrors");
1116
+ output.push("}");
1117
+ output.push("");
1118
+ return output.join("\n");
1119
+ }
1045
1120
  function toCamelCase(str) {
1046
1121
  return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
1047
1122
  }
@@ -1206,15 +1281,31 @@ async function generateSingle(options) {
1206
1281
  const resolvedInput = path.resolve(inputFile);
1207
1282
  const resolvedOutput = path.resolve(outputFile);
1208
1283
  const spec = readSpec(resolvedInput);
1209
- const output = generate(spec, {
1284
+ const result = generateWithMetadata(spec, {
1210
1285
  strictDates,
1211
1286
  strictNumeric,
1212
1287
  types: typesConfig
1213
1288
  });
1214
1289
  fs.mkdirSync(path.dirname(resolvedOutput), { recursive: true });
1215
- fs.writeFileSync(resolvedOutput, output);
1290
+ fs.writeFileSync(resolvedOutput, result.output);
1216
1291
  console.log(`\u2705 Generated TypeScript types in ${resolvedOutput}`);
1217
1292
  console.log(`\u{1F4C4} Processed ${Object.keys(spec.paths).length} paths`);
1293
+ if (result.helperFile) {
1294
+ const helperPath = path.isAbsolute(result.helperFile.path) ? result.helperFile.path : path.resolve(path.dirname(resolvedOutput), result.helperFile.path);
1295
+ const absoluteResolvedOutput = path.resolve(resolvedOutput);
1296
+ const absoluteHelperPath = path.resolve(helperPath);
1297
+ if (absoluteResolvedOutput === absoluteHelperPath) {
1298
+ console.warn(
1299
+ `\u26A0\uFE0F Skipping helper file generation: would overwrite main output at ${absoluteResolvedOutput}`
1300
+ );
1301
+ return;
1302
+ }
1303
+ fs.mkdirSync(path.dirname(helperPath), { recursive: true });
1304
+ fs.writeFileSync(helperPath, result.helperFile.content, {
1305
+ encoding: "utf8"
1306
+ });
1307
+ console.log(`\u{1F4E6} Generated helper types in ${helperPath}`);
1308
+ }
1218
1309
  }
1219
1310
  function readSpec(filePath) {
1220
1311
  if (!fs.existsSync(filePath)) {