@kubb/plugin-faker 5.0.0-beta.98 → 5.0.0

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/index.cjs CHANGED
@@ -580,8 +580,9 @@ function Faker({ node, description, name, typeName, printer, seed, canOverride }
580
580
  })();
581
581
  const { dataType, returnType: resolvedReturnType } = resolveFakerTypeUsage(node, typeName, canOverride);
582
582
  if (!useGenericOverride) {
583
+ const dataParamName = /\bdata\b/.test(fakerTextWithOverride) ? "data" : "_data";
583
584
  const params = (0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
584
- name: /\bdata\b/.test(fakerTextWithOverride) ? "data" : "_data",
585
+ name: dataParamName,
585
586
  type: dataType,
586
587
  optional: true
587
588
  })] });
@@ -605,7 +606,7 @@ function Faker({ node, description, name, typeName, printer, seed, canOverride }
605
606
  const functionSignature = `${description ? `/**\n * @description ${jsStringEscape(description)}\n */\n ` : ""}export function ${name}<TData extends Partial<${typeName}> = object>(data?: TData)`;
606
607
  const seedCode = seed ? `faker.seed(${JSON.stringify(seed)})\n ` : "";
607
608
  const { cyclicSchemas, schemaName } = printer.options;
608
- const functionBody = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => kubb_kit.ast.containsCircularRef(p.schema, {
609
+ const functionBody = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => (0, kubb_kit.containsCircularRef)(p.schema, {
609
610
  circularSchemas: cyclicSchemas,
610
611
  excludeName: schemaName
611
612
  })) ? `{
@@ -632,45 +633,64 @@ function Faker({ node, description, name, typeName, printer, seed, canOverride }
632
633
  }
633
634
  //#endregion
634
635
  //#region ../../internals/shared/src/params.ts
635
- const caseParamsCache = /* @__PURE__ */ new WeakMap();
636
636
  /**
637
- * Applies camelCase to parameter names and returns a new array without mutating the input.
637
+ * Drops parameters that share the same name, keeping the first.
638
638
  *
639
- * Run it before handing parameters to schema builders so output property keys get the right casing
640
- * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
641
- * original array is returned unchanged. Results are cached per input array.
639
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
640
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
641
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
642
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
642
643
  */
643
- function caseParams(params, casing) {
644
- if (!casing) return params;
645
- const cached = caseParamsCache.get(params);
646
- if (cached) return cached;
647
- const result = params.map((param) => ({
648
- ...param,
649
- name: camelCase(param.name)
650
- }));
651
- caseParamsCache.set(params, result);
652
- return result;
653
- }
654
- /**
655
- * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
656
- *
657
- * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
658
- * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
659
- * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
660
- * identity so the resulting group is collision-free regardless of the names each caller carries.
661
- */
662
- function dedupeByCasedName(params) {
644
+ function dedupeParams(params) {
663
645
  const seen = /* @__PURE__ */ new Set();
664
646
  return params.filter((param) => {
665
- const key = camelCase(param.name);
666
- if (seen.has(key)) return false;
667
- seen.add(key);
647
+ if (seen.has(param.name)) return false;
648
+ seen.add(param.name);
668
649
  return true;
669
650
  });
670
651
  }
671
652
  //#endregion
672
653
  //#region ../../internals/shared/src/operation.ts
673
654
  /**
655
+ * Builds the `ResolverFileParams` every operation generator passes to
656
+ * `resolver.file`: a file named `name`, tagged by the operation's first
657
+ * tag (or `'default'`), at the operation's path. Centralizes the entry object
658
+ * that was repeated at dozens of call sites across the client and query plugins.
659
+ *
660
+ * @example
661
+ * ```ts
662
+ * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
663
+ * ```
664
+ */
665
+ function operationFileEntry(node, name, extname = ".ts") {
666
+ return {
667
+ name,
668
+ extname,
669
+ tag: node.tags[0] ?? "default",
670
+ path: node.path
671
+ };
672
+ }
673
+ /**
674
+ * Resolves a dependency plugin's generated file for `node.operationId`, cached in `cache` (the
675
+ * current node's `ctx.cache`) under the resolver's own plugin name. Several dependents reading the
676
+ * same dependency for the same operation in one pass (a query plugin's several hook generators, the
677
+ * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
678
+ *
679
+ * @example Cache `plugin-ts`'s file for the current operation
680
+ * ```ts
681
+ * const fileTs = resolveDependencyOperationFile({ cache: ctx.cache, node, resolver: tsResolver, root, output })
682
+ * ```
683
+ */
684
+ function resolveDependencyOperationFile(options) {
685
+ const { cache, node, resolver, root, output, group } = options;
686
+ return cache.ensureItem(`${resolver.pluginName}:operationFile`, () => resolver.file({
687
+ ...operationFileEntry(node, node.operationId),
688
+ root,
689
+ output,
690
+ group: group ?? void 0
691
+ }));
692
+ }
693
+ /**
674
694
  * Maps a content type to the PascalCase suffix used to name per-content-type variants
675
695
  * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
676
696
  */
@@ -718,14 +738,24 @@ function resolveContentTypeVariants(entries, baseName) {
718
738
  };
719
739
  });
720
740
  }
721
- function getOperationParameters(node, options = {}) {
722
- const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
723
- return {
724
- path: dedupeByCasedName(params.filter((param) => param.in === "path")),
725
- query: dedupeByCasedName(params.filter((param) => param.in === "query")),
726
- header: dedupeByCasedName(params.filter((param) => param.in === "header")),
727
- cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
741
+ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
742
+ /**
743
+ * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
744
+ * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
745
+ * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
746
+ * same parameters once per plugin.
747
+ */
748
+ function getOperationParameters(node) {
749
+ const cached = operationParameterGroupsByNode.get(node);
750
+ if (cached) return cached;
751
+ const groups = {
752
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
753
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
754
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
755
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
728
756
  };
757
+ operationParameterGroupsByNode.set(node, groups);
758
+ return groups;
729
759
  }
730
760
  //#endregion
731
761
  //#region ../../internals/shared/src/resolver.ts
@@ -834,7 +864,41 @@ function createGroupConfig(group) {
834
864
  };
835
865
  }
836
866
  //#endregion
867
+ //#region ../../internals/shared/src/schemaTraversal.ts
868
+ /**
869
+ * Maps each member of a union or intersection schema to its transformed output, pairing every
870
+ * result with the original member.
871
+ */
872
+ function mapSchemaMembers(node, transform) {
873
+ return (node.members ?? []).map((schema) => ({
874
+ schema,
875
+ output: transform(schema)
876
+ }));
877
+ }
878
+ /**
879
+ * Maps each item of an array or tuple schema to its transformed output, pairing every result with
880
+ * the original item.
881
+ */
882
+ function mapSchemaItems(node, transform) {
883
+ return (node.items ?? []).map((schema) => ({
884
+ schema,
885
+ output: transform(schema)
886
+ }));
887
+ }
888
+ //#endregion
837
889
  //#region src/printers/printerFaker.ts
890
+ /**
891
+ * Formats that put a whole number inside a `type: 'string'` schema, the way ProtoJSON encodes
892
+ * 64-bit integers, so the mock has to be digits rather than letters.
893
+ *
894
+ * @see https://protobuf.dev/programming-guides/json/#int64-strings
895
+ */
896
+ const integerFormats = /* @__PURE__ */ new Set([
897
+ "int32",
898
+ "int64",
899
+ "uint64"
900
+ ]);
901
+ const maxInt32 = 2147483647;
838
902
  const fakerKeywordMapper = {
839
903
  any: () => "undefined",
840
904
  unknown: () => "undefined",
@@ -852,6 +916,10 @@ const fakerKeywordMapper = {
852
916
  return "faker.number.int()";
853
917
  },
854
918
  bigint: () => "faker.number.bigInt()",
919
+ integerString: (format) => {
920
+ if (format === "int32") return `faker.number.int({ max: ${maxInt32} }).toString()`;
921
+ return "faker.number.bigInt().toString()";
922
+ },
855
923
  string: (min, max) => {
856
924
  if (max !== void 0 && min !== void 0) return `faker.string.alpha({ length: { min: ${min}, max: ${max} } })`;
857
925
  if (max !== void 0) return `faker.string.alpha({ length: ${max} })`;
@@ -951,6 +1019,7 @@ const printerFaker = kubb_kit.ast.createPrinter((options) => {
951
1019
  null: () => fakerKeywordMapper.null(),
952
1020
  string(node) {
953
1021
  if (node.pattern) return fakerKeywordMapper.matches(node.pattern, this.options.regexGenerator);
1022
+ if (node.format && integerFormats.has(node.format)) return fakerKeywordMapper.integerString(node.format);
954
1023
  return fakerKeywordMapper.string(node.min, node.max);
955
1024
  },
956
1025
  email: () => fakerKeywordMapper.email(),
@@ -985,7 +1054,7 @@ const printerFaker = kubb_kit.ast.createPrinter((options) => {
985
1054
  union(node) {
986
1055
  const { discriminatorPropertyName } = node;
987
1056
  const baseTypeName = this.options.typeName;
988
- const items = kubb_kit.ast.mapSchemaMembers(node, (member) => {
1057
+ const items = mapSchemaMembers(node, (member) => {
989
1058
  const value = discriminatorPropertyName ? getDiscriminatorValue(member, discriminatorPropertyName) : void 0;
990
1059
  if (baseTypeName && value !== void 0) {
991
1060
  const typeName = `Extract<NonNullable<${baseTypeName}>, { ${JSON.stringify(discriminatorPropertyName)}: ${parseEnumValue(value)} }>`;
@@ -1003,11 +1072,11 @@ const printerFaker = kubb_kit.ast.createPrinter((options) => {
1003
1072
  return fakerKeywordMapper.union(items);
1004
1073
  },
1005
1074
  intersection(node) {
1006
- const items = kubb_kit.ast.mapSchemaMembers(node, (member) => printNested(member, { nestedInObject: true })).map(({ output }) => output).filter((item) => Boolean(item) && item !== "undefined");
1075
+ const items = mapSchemaMembers(node, (member) => printNested(member, { nestedInObject: true })).map(({ output }) => output).filter((item) => Boolean(item) && item !== "undefined");
1007
1076
  return fakerKeywordMapper.and(items);
1008
1077
  },
1009
1078
  array(node) {
1010
- const items = kubb_kit.ast.mapSchemaItems(node, (member) => printNested(member, {
1079
+ const items = mapSchemaItems(node, (member) => printNested(member, {
1011
1080
  typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[number]` : void 0,
1012
1081
  nestedInObject: true
1013
1082
  })).map(({ output }) => output).filter((item) => Boolean(item));
@@ -1027,7 +1096,7 @@ const printerFaker = kubb_kit.ast.createPrinter((options) => {
1027
1096
  typeName: this.options.typeName ? indexedTypeName(this.options.typeName, property.name, this.options.nestedInUnion) : void 0,
1028
1097
  nestedInObject: true
1029
1098
  }) ?? "undefined";
1030
- if (cyclicSchemas && kubb_kit.ast.containsCircularRef(property.schema, {
1099
+ if (cyclicSchemas && (0, kubb_kit.containsCircularRef)(property.schema, {
1031
1100
  circularSchemas: cyclicSchemas,
1032
1101
  excludeName: this.options.schemaName
1033
1102
  })) return `get ${objectKey(property.name)}() { const _value = ${value}; Object.defineProperty(this, ${JSON.stringify(property.name)}, { value: _value, configurable: true, writable: true, enumerable: true }); return _value }`;
@@ -1176,11 +1245,7 @@ const fakerGenerator = (0, kubb_kit.defineGenerator)({
1176
1245
  const pluginTs = ctx.driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1177
1246
  if (!pluginTs) return;
1178
1247
  const tsResolver = ctx.driver.getResolver(_kubb_plugin_ts.pluginTsName);
1179
- const params = caseParams(node.parameters, "camelcase");
1180
- const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters({
1181
- ...node,
1182
- parameters: params
1183
- }, { paramsCasing: "original" });
1248
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
1184
1249
  const paramGroups = [
1185
1250
  {
1186
1251
  params: pathParams,
@@ -1260,14 +1325,13 @@ const fakerGenerator = (0, kubb_kit.defineGenerator)({
1260
1325
  output,
1261
1326
  group: group ?? void 0
1262
1327
  }),
1263
- typeFile: tsResolver.file({
1264
- name: node.operationId,
1265
- extname: ".ts",
1266
- tag: node.tags[0] ?? "default",
1267
- path: node.path,
1328
+ typeFile: resolveDependencyOperationFile({
1329
+ cache: ctx.cache,
1330
+ node,
1331
+ resolver: tsResolver,
1268
1332
  root,
1269
1333
  output: pluginTs.options?.output ?? output,
1270
- group: pluginTs.options?.group ?? void 0
1334
+ group: pluginTs.options?.group
1271
1335
  })
1272
1336
  };
1273
1337
  function resolveMockImports(schema) {