zenko 0.1.3 → 0.1.5

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
@@ -233,7 +233,7 @@ function isErrorStatus(status) {
233
233
  }
234
234
 
235
235
  // src/zenko.ts
236
- function generate(spec, options = {}) {
236
+ function generateWithMetadata(spec, options = {}) {
237
237
  const output = [];
238
238
  const generatedTypes = /* @__PURE__ */ new Set();
239
239
  const { strictDates = false, strictNumeric = false } = options;
@@ -266,8 +266,11 @@ function generate(spec, options = {}) {
266
266
  const pathParamNames = op.pathParams.map((p) => p.name);
267
267
  const hasPathParams = pathParamNames.length > 0;
268
268
  const hasQueryParams = op.queryParams.length > 0;
269
+ const camelCaseOperationId = toCamelCase(op.operationId);
269
270
  if (!hasPathParams && !hasQueryParams) {
270
- output.push(` ${op.operationId}: () => "${op.path}",`);
271
+ output.push(
272
+ ` ${formatPropertyName(camelCaseOperationId)}: () => "${op.path}",`
273
+ );
271
274
  continue;
272
275
  }
273
276
  const allParamNames = [
@@ -290,11 +293,13 @@ function generate(spec, options = {}) {
290
293
  const pathWithParams = op.path.replace(/{([^}]+)}/g, "${$1}");
291
294
  if (!hasQueryParams) {
292
295
  output.push(
293
- ` ${op.operationId}: (${signature}) => \`${pathWithParams}\`,`
296
+ ` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => \`${pathWithParams}\`,`
294
297
  );
295
298
  continue;
296
299
  }
297
- output.push(` ${op.operationId}: (${signature}) => {`);
300
+ output.push(
301
+ ` ${formatPropertyName(camelCaseOperationId)}: (${signature}) => {`
302
+ );
298
303
  output.push(" const params = new URLSearchParams()");
299
304
  for (const param of op.queryParams) {
300
305
  const propertyKey = formatPropertyName(param.name);
@@ -339,83 +344,57 @@ function generate(spec, options = {}) {
339
344
  }
340
345
  output.push("} as const;");
341
346
  output.push("");
342
- output.push("// Header Functions");
343
- output.push("export const headers = {");
347
+ output.push("// Header Schemas");
348
+ output.push("export const headerSchemas = {");
344
349
  for (const op of operations) {
350
+ const camelCaseOperationId = toCamelCase(op.operationId);
345
351
  if (!op.requestHeaders || op.requestHeaders.length === 0) {
346
- output.push(` ${op.operationId}: () => ({}),`);
347
- continue;
348
- }
349
- const typeEntries = op.requestHeaders.map(
350
- (header) => `${formatPropertyName(header.name)}${header.required ? "" : "?"}: ${mapHeaderType(
351
- header
352
- )}`
353
- ).join(", ");
354
- const requiredHeaders = op.requestHeaders.filter(
355
- (header) => header.required
356
- );
357
- const optionalHeaders = op.requestHeaders.filter(
358
- (header) => !header.required
359
- );
360
- const hasRequired = requiredHeaders.length > 0;
361
- const signature = hasRequired ? `(params: { ${typeEntries} })` : `(params: { ${typeEntries} } = {})`;
362
- if (optionalHeaders.length === 0) {
363
- output.push(` ${op.operationId}: ${signature} => ({`);
364
- for (const header of requiredHeaders) {
365
- const propertyKey = formatPropertyName(header.name);
366
- const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
367
- output.push(` ${propertyKey}: ${accessor},`);
368
- }
369
- output.push(" }),");
352
+ output.push(
353
+ ` ${formatPropertyName(camelCaseOperationId)}: z.object({}),`
354
+ );
370
355
  continue;
371
356
  }
372
- if (!hasRequired && optionalHeaders.length === 1 && optionalHeaders[0]) {
373
- const header = optionalHeaders[0];
374
- const propertyKey = formatPropertyName(header.name);
375
- const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
376
- output.push(` ${op.operationId}: ${signature} =>`);
357
+ const schemaFields = op.requestHeaders.map((header) => {
358
+ const zodType = mapHeaderToZodType(header);
359
+ const optional = header.required ? "" : ".optional()";
360
+ return ` ${formatPropertyName(header.name)}: ${zodType}${optional},`;
361
+ }).join("\n");
362
+ output.push(` ${formatPropertyName(camelCaseOperationId)}: z.object({`);
363
+ output.push(schemaFields);
364
+ output.push(" }),");
365
+ }
366
+ output.push("} as const;");
367
+ output.push("");
368
+ output.push("// Header Functions");
369
+ output.push("export const headers = {");
370
+ for (const op of operations) {
371
+ const camelCaseOperationId = toCamelCase(op.operationId);
372
+ if (!op.requestHeaders || op.requestHeaders.length === 0) {
377
373
  output.push(
378
- ` ${accessor} !== undefined ? { ${propertyKey}: ${accessor} } : {},`
374
+ ` ${formatPropertyName(camelCaseOperationId)}: () => ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse({}),`
379
375
  );
380
376
  continue;
381
377
  }
382
- const valueTypes = Array.from(
383
- new Set(optionalHeaders.map((header) => mapHeaderType(header)))
384
- ).join(" | ");
385
- output.push(` ${op.operationId}: ${signature} => {`);
386
- if (hasRequired) {
387
- output.push(" const headers = {");
388
- for (const header of requiredHeaders) {
389
- const propertyKey = formatPropertyName(header.name);
390
- const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
391
- output.push(` ${propertyKey}: ${accessor},`);
392
- }
393
- output.push(" }");
394
- } else {
395
- output.push(` const headers: Record<string, ${valueTypes}> = {}`);
396
- }
397
- for (const header of optionalHeaders) {
398
- const propertyKey = formatPropertyName(header.name);
399
- const accessor = isValidJSIdentifier(header.name) ? `params.${header.name}` : `params[${propertyKey}]`;
400
- const assignment = isValidJSIdentifier(header.name) ? `headers.${header.name}` : `headers[${propertyKey}]`;
401
- output.push(` if (${accessor} !== undefined) {`);
402
- output.push(` ${assignment} = ${accessor}`);
403
- output.push(" }");
404
- }
405
- output.push(" return headers");
378
+ output.push(
379
+ ` ${formatPropertyName(camelCaseOperationId)}: (params: z.input<${isValidJSIdentifier(camelCaseOperationId) ? `typeof headerSchemas.${camelCaseOperationId}` : `(typeof headerSchemas)[${formatPropertyName(camelCaseOperationId)}]`}>) => {`
380
+ );
381
+ output.push(
382
+ ` return ${isValidJSIdentifier(camelCaseOperationId) ? `headerSchemas.${camelCaseOperationId}` : `headerSchemas[${formatPropertyName(camelCaseOperationId)}]`}.parse(params)`
383
+ );
406
384
  output.push(" },");
407
385
  }
408
386
  output.push("} as const;");
409
387
  output.push("");
410
388
  output.push("// Operation Objects");
411
389
  for (const op of operations) {
412
- output.push(`export const ${op.operationId} = {`);
390
+ const camelCaseOperationId = toCamelCase(op.operationId);
391
+ output.push(`export const ${camelCaseOperationId} = {`);
413
392
  output.push(` method: "${op.method}",`);
414
- output.push(` path: paths.${op.operationId},`);
393
+ output.push(` path: paths.${camelCaseOperationId},`);
415
394
  appendOperationField(output, "request", op.requestType);
416
395
  appendOperationField(output, "response", op.responseType);
417
396
  if (op.requestHeaders && op.requestHeaders.length > 0) {
418
- output.push(` headers: headers.${op.operationId},`);
397
+ output.push(` headers: headers.${camelCaseOperationId},`);
419
398
  }
420
399
  if (op.errors && hasAnyErrors(op.errors)) {
421
400
  output.push(" errors: {");
@@ -429,7 +408,16 @@ function generate(spec, options = {}) {
429
408
  output.push("");
430
409
  }
431
410
  generateOperationTypes(output, operations, typesConfig);
432
- 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;
433
421
  }
434
422
  function appendOperationField(buffer, key, value) {
435
423
  if (!value) return;
@@ -466,6 +454,36 @@ function isRequestMethod(method) {
466
454
  return false;
467
455
  }
468
456
  }
457
+ var CONTENT_TYPE_MAP = {
458
+ "application/json": "unknown",
459
+ // Will use schema when available
460
+ "text/csv": "string",
461
+ "text/plain": "string",
462
+ // Binary/ambiguous types default to unknown for cross-platform compatibility
463
+ "application/octet-stream": "unknown",
464
+ "application/pdf": "unknown"
465
+ };
466
+ function findContentType(content) {
467
+ const contentTypes = Object.keys(content);
468
+ if (contentTypes.includes("application/json")) {
469
+ return "application/json";
470
+ }
471
+ for (const contentType of contentTypes) {
472
+ if (contentType in CONTENT_TYPE_MAP) {
473
+ return contentType;
474
+ }
475
+ }
476
+ return contentTypes[0] || "";
477
+ }
478
+ function inferResponseType(contentType, statusCode) {
479
+ if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
480
+ return "undefined";
481
+ }
482
+ if (contentType in CONTENT_TYPE_MAP) {
483
+ return CONTENT_TYPE_MAP[contentType];
484
+ }
485
+ return "unknown";
486
+ }
469
487
  function parseOperations(spec) {
470
488
  const operations = [];
471
489
  for (const [path2, pathItem] of Object.entries(spec.paths)) {
@@ -527,6 +545,9 @@ function appendHelperTypesImport(buffer, config) {
527
545
  buffer.push(
528
546
  "type HeaderFn<TArgs extends unknown[] = [], TResult = Record<string, unknown> | Record<string, never>> = (...args: TArgs) => TResult;"
529
547
  );
548
+ buffer.push(
549
+ "type AnyHeaderFn = HeaderFn<any, unknown> | (() => unknown);"
550
+ );
530
551
  buffer.push(
531
552
  "type OperationErrors<TClient = unknown, TServer = unknown, TDefault = unknown, TOther = unknown> = {"
532
553
  );
@@ -536,7 +557,7 @@ function appendHelperTypesImport(buffer, config) {
536
557
  buffer.push(" otherErrors?: TOther;");
537
558
  buffer.push("};");
538
559
  buffer.push(
539
- "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> = {"
540
561
  );
541
562
  buffer.push(" method: TMethod;");
542
563
  buffer.push(" path: TPath;");
@@ -552,15 +573,18 @@ function generateOperationTypes(buffer, operations, config) {
552
573
  if (!config.emit) return;
553
574
  buffer.push("// Operation Types");
554
575
  for (const op of operations) {
555
- const headerType = op.requestHeaders?.length ? `typeof headers.${op.operationId}` : "undefined";
576
+ const camelCaseOperationId = toCamelCase(op.operationId);
577
+ const headerType = op.requestHeaders?.length ? isValidJSIdentifier(camelCaseOperationId) ? `typeof headers.${camelCaseOperationId}` : `(typeof headers)[${formatPropertyName(camelCaseOperationId)}]` : "undefined";
556
578
  const requestType = wrapTypeReference(op.requestType);
557
579
  const responseType = wrapTypeReference(op.responseType);
558
580
  const errorsType = buildOperationErrorsType(op.errors);
559
581
  buffer.push(
560
- `export type ${capitalize(op.operationId)}Operation = OperationDefinition<`
582
+ `export type ${capitalize(camelCaseOperationId)}Operation = OperationDefinition<`
561
583
  );
562
584
  buffer.push(` "${op.method}",`);
563
- buffer.push(` typeof paths.${op.operationId},`);
585
+ buffer.push(
586
+ ` ${isValidJSIdentifier(camelCaseOperationId) ? `typeof paths.${camelCaseOperationId}` : `(typeof paths)[${formatPropertyName(camelCaseOperationId)}]`},`
587
+ );
564
588
  buffer.push(` ${requestType},`);
565
589
  buffer.push(` ${responseType},`);
566
590
  buffer.push(` ${headerType},`);
@@ -604,26 +628,35 @@ var TYPE_KEYWORDS = /* @__PURE__ */ new Set([
604
628
  "bigint",
605
629
  "symbol"
606
630
  ]);
631
+ var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
607
632
  function wrapTypeReference(typeName) {
608
633
  if (!typeName) return "undefined";
609
- if (typeName === "undefined") return "undefined";
610
- if (TYPE_KEYWORDS.has(typeName)) return typeName;
611
- if (typeName.startsWith("typeof ")) return typeName;
612
- const identifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
613
- if (identifierPattern.test(typeName)) {
614
- return `typeof ${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])}>`;
615
641
  }
616
- return typeName;
642
+ if (IDENTIFIER_PATTERN.test(normalized)) {
643
+ return `typeof ${normalized}`;
644
+ }
645
+ return normalized;
617
646
  }
618
647
  function wrapErrorValueType(typeName) {
619
648
  if (!typeName) return "unknown";
620
- if (TYPE_KEYWORDS.has(typeName)) return typeName;
621
- if (typeName.startsWith("typeof ")) return typeName;
622
- const identifierPattern = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
623
- if (identifierPattern.test(typeName)) {
624
- 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])}>`;
625
655
  }
626
- return typeName;
656
+ if (IDENTIFIER_PATTERN.test(normalized)) {
657
+ return `typeof ${normalized}`;
658
+ }
659
+ return normalized;
627
660
  }
628
661
  function collectParameters(pathItem, operation, spec) {
629
662
  const parametersMap = /* @__PURE__ */ new Map();
@@ -683,13 +716,39 @@ function getResponseTypes(operation, operationId) {
683
716
  const successCodes = /* @__PURE__ */ new Map();
684
717
  const errorEntries = [];
685
718
  for (const [statusCode, response] of Object.entries(responses)) {
686
- const resolvedSchema = response?.content?.["application/json"]?.schema;
687
- if (!resolvedSchema) continue;
719
+ const content = response?.content;
720
+ if (!content || Object.keys(content).length === 0) {
721
+ if (statusCode === "204" || /^3\d\d$/.test(statusCode)) {
722
+ successCodes.set(statusCode, "undefined");
723
+ } else if (isErrorStatus(statusCode)) {
724
+ errorEntries.push({
725
+ code: statusCode,
726
+ schema: "undefined"
727
+ });
728
+ }
729
+ continue;
730
+ }
731
+ const contentType = findContentType(content);
732
+ const resolvedSchema = content[contentType]?.schema;
733
+ if (!resolvedSchema) {
734
+ const inferredType = inferResponseType(contentType, statusCode);
735
+ if (inferredType) {
736
+ if (isErrorStatus(statusCode)) {
737
+ errorEntries.push({
738
+ code: statusCode,
739
+ schema: inferredType
740
+ });
741
+ } else if (/^2\d\d$/.test(statusCode)) {
742
+ successCodes.set(statusCode, inferredType);
743
+ }
744
+ }
745
+ continue;
746
+ }
688
747
  if (isErrorStatus(statusCode)) {
689
748
  errorEntries.push({ code: statusCode, schema: resolvedSchema });
690
749
  continue;
691
750
  }
692
- if (/^2\d\d$/.test(statusCode) || statusCode === "default") {
751
+ if (/^2\d\d$/.test(statusCode)) {
693
752
  successCodes.set(statusCode, resolvedSchema);
694
753
  }
695
754
  }
@@ -703,6 +762,9 @@ function selectSuccessResponse(responses, operationId) {
703
762
  for (const code of preferredOrder) {
704
763
  const schema = responses.get(code);
705
764
  if (schema) {
765
+ if (typeof schema === "string") {
766
+ return schema;
767
+ }
706
768
  return resolveResponseType(
707
769
  schema,
708
770
  `${capitalize(operationId)}Response${code}`
@@ -711,6 +773,9 @@ function selectSuccessResponse(responses, operationId) {
711
773
  }
712
774
  const [firstCode, firstSchema] = responses.entries().next().value ?? [];
713
775
  if (!firstSchema) return void 0;
776
+ if (typeof firstSchema === "string") {
777
+ return firstSchema;
778
+ }
714
779
  return resolveResponseType(
715
780
  firstSchema,
716
781
  `${capitalize(operationId)}Response${firstCode ?? "Default"}`
@@ -748,9 +813,16 @@ function buildErrorGroups(errors = [], operationId) {
748
813
  return group;
749
814
  }
750
815
  function resolveResponseType(schema, fallbackName) {
816
+ if (typeof schema === "string") {
817
+ return schema;
818
+ }
751
819
  if (schema.$ref) {
752
820
  return extractRefName(schema.$ref);
753
821
  }
822
+ if (schema.type === "array" && schema.items?.$ref) {
823
+ const itemRef = extractRefName(schema.items.$ref);
824
+ return `z.array(${itemRef})`;
825
+ }
754
826
  return fallbackName;
755
827
  }
756
828
  function getRequestHeaders(parameters) {
@@ -781,16 +853,22 @@ function getQueryParams(parameters) {
781
853
  }
782
854
  return queryParams;
783
855
  }
784
- function mapHeaderType(header) {
785
- const schemaType = header.schema?.type;
856
+ function mapHeaderToZodType(header) {
857
+ const schema = header.schema ?? {};
858
+ const schemaType = schema.type;
786
859
  switch (schemaType) {
787
860
  case "integer":
788
861
  case "number":
789
- return "number";
862
+ return "z.coerce.number()";
790
863
  case "boolean":
791
- return "boolean";
864
+ return "z.coerce.boolean()";
865
+ case "array": {
866
+ const items = schema.items ?? { type: "string" };
867
+ const itemType = items.type === "integer" || items.type === "number" ? "z.coerce.number()" : items.type === "boolean" ? "z.coerce.boolean()" : "z.string()";
868
+ return `z.array(${itemType})`;
869
+ }
792
870
  default:
793
- return "string";
871
+ return "z.string()";
794
872
  }
795
873
  }
796
874
  function mapQueryType(param) {
@@ -994,6 +1072,54 @@ function applyNumericBounds(schema, builder) {
994
1072
  }
995
1073
  return builder;
996
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
+ }
1120
+ function toCamelCase(str) {
1121
+ return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
1122
+ }
997
1123
  function capitalize(str) {
998
1124
  return str.charAt(0).toUpperCase() + str.slice(1);
999
1125
  }
@@ -1155,15 +1281,31 @@ async function generateSingle(options) {
1155
1281
  const resolvedInput = path.resolve(inputFile);
1156
1282
  const resolvedOutput = path.resolve(outputFile);
1157
1283
  const spec = readSpec(resolvedInput);
1158
- const output = generate(spec, {
1284
+ const result = generateWithMetadata(spec, {
1159
1285
  strictDates,
1160
1286
  strictNumeric,
1161
1287
  types: typesConfig
1162
1288
  });
1163
1289
  fs.mkdirSync(path.dirname(resolvedOutput), { recursive: true });
1164
- fs.writeFileSync(resolvedOutput, output);
1290
+ fs.writeFileSync(resolvedOutput, result.output);
1165
1291
  console.log(`\u2705 Generated TypeScript types in ${resolvedOutput}`);
1166
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
+ }
1167
1309
  }
1168
1310
  function readSpec(filePath) {
1169
1311
  if (!fs.existsSync(filePath)) {