@kubb/plugin-faker 5.2.1 → 5.2.3

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.js CHANGED
@@ -1,48 +1,9 @@
1
1
  import { posix } from "node:path";
2
- import { Diagnostics, Resolver, addGenerator, ast, containsCircularRef, createResolver, defineExtension, defineGenerator, setMacros, setResolver } from "kubb/kit";
2
+ import { Diagnostics, Resolver, addGenerator, ast, camelCase, containsCircularRef, createResolver, defineExtension, defineGenerator, setMacros, setResolver, syncSchemaRef } from "kubb/kit";
3
3
  import { File, Function, jsxRenderer } from "kubb/jsx";
4
4
  import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
5
5
  import { buildParams, enumTypeName, pluginTsName } from "@kubb/plugin-ts";
6
- //#endregion
7
- //#region ../../internals/utils/src/casing.ts
8
- /**
9
- * Shared implementation for camelCase and PascalCase conversion.
10
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
11
- * and capitalizes each word according to `pascal`.
12
- *
13
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
14
- */
15
- function convert(text, pascal) {
16
- return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
17
- if (word.length > 1 && word === word.toUpperCase()) return word;
18
- return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
19
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
20
- }
21
- const cache = /* @__PURE__ */ new Map();
22
- const cacheLimit = 1e4;
23
- function toCamelOrPascal(text, pascal) {
24
- const key = `${pascal ? "P" : "c"}${text}`;
25
- const cached = cache.get(key);
26
- if (cached !== void 0) return cached;
27
- const result = convert(text, pascal);
28
- if (cache.size >= cacheLimit) cache.clear();
29
- cache.set(key, result);
30
- return result;
31
- }
32
- /**
33
- * Converts `text` to camelCase.
34
- *
35
- * @example Word boundaries
36
- * `camelCase('hello-world') // 'helloWorld'`
37
- *
38
- * @example With a prefix
39
- * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
40
- */
41
- function camelCase(text, { prefix = "", suffix = "" } = {}) {
42
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
43
- }
44
- //#endregion
45
- //#region ../../internals/utils/src/reserved.ts
6
+ // ../../internals/utils/src/reserved.ts
46
7
  /**
47
8
  * JavaScript and Java reserved words.
48
9
  * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
@@ -50,6 +11,7 @@ function camelCase(text, { prefix = "", suffix = "" } = {}) {
50
11
  const reservedWords = /* @__PURE__ */ new Set([
51
12
  "abstract",
52
13
  "arguments",
14
+ "await",
53
15
  "boolean",
54
16
  "break",
55
17
  "byte",
@@ -180,8 +142,7 @@ function ensureValidVarName(name) {
180
142
  function isIdentifier(name) {
181
143
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
182
144
  }
183
- //#endregion
184
- //#region ../../internals/utils/src/strings.ts
145
+ // ../../internals/utils/src/strings.ts
185
146
  /**
186
147
  * Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
187
148
  * any backslash or single quote in the content.
@@ -242,6 +203,34 @@ function stringify(value) {
242
203
  function jsDocEscape(input) {
243
204
  return `${input}`.replace(/^(\s*)@/gm, "$1\\@");
244
205
  }
206
+ const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
207
+ const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
208
+ /**
209
+ * Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
210
+ * not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
211
+ */
212
+ function dropIdentityEscapes(pattern) {
213
+ let inClass = false;
214
+ return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
215
+ if (escaped === void 0) {
216
+ inClass = match === "[";
217
+ return match;
218
+ }
219
+ return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
220
+ });
221
+ }
222
+ /**
223
+ * Builds the `RegExp` for a pattern, adding the `u` flag when it uses `\p{...}`, `\P{...}` or `\u{...}`, or
224
+ * holds a character outside the BMP. A pattern that is not valid under `u` keeps the flags it came with.
225
+ */
226
+ function toRegExp(pattern, flags) {
227
+ if (flags?.includes("u") || !NEEDS_UNICODE.test(pattern)) return new RegExp(pattern, flags);
228
+ try {
229
+ return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
230
+ } catch {
231
+ return new RegExp(pattern, flags);
232
+ }
233
+ }
245
234
  /**
246
235
  * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
247
236
  * Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
@@ -258,13 +247,11 @@ function toRegExpString(text, func = "RegExp") {
258
247
  const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
259
248
  const replacementTarget = match?.[1] ?? "";
260
249
  const matchedFlags = match?.[2];
261
- const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
262
- const { source, flags } = new RegExp(cleaned, matchedFlags);
250
+ const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
263
251
  if (func === null) return `/${source}/${flags}`;
264
252
  return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
265
253
  }
266
- //#endregion
267
- //#region ../../internals/utils/src/codegen.ts
254
+ // ../../internals/utils/src/codegen.ts
268
255
  const INDENT = " ";
269
256
  /**
270
257
  * Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
@@ -301,8 +288,7 @@ function buildObject(entries) {
301
288
  if (entries.length === 0) return "{}";
302
289
  return `{\n${entries.map((entry) => `${indentLines(entry)},`).join("\n")}\n}`;
303
290
  }
304
- //#endregion
305
- //#region ../../internals/utils/src/fs.ts
291
+ // ../../internals/utils/src/fs.ts
306
292
  function toSlash(p) {
307
293
  if (p.startsWith("\\\\?\\")) return p;
308
294
  return p.replaceAll("\\", "/");
@@ -344,8 +330,7 @@ function toFilePath(name, caseLast = camelCase) {
344
330
  const parts = name.split(/\.(?=[a-zA-Z])/);
345
331
  return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
346
332
  }
347
- //#endregion
348
- //#region src/utils.ts
333
+ // src/utils.ts
349
334
  /**
350
335
  * Returns the `@faker-js/faker` named export for a locale code.
351
336
  *
@@ -396,6 +381,18 @@ const NON_INLINABLE_RESPONSE_TYPES = /* @__PURE__ */ new Set([
396
381
  function canOverrideSchema(node) {
397
382
  return !NON_OVERRIDABLE_TYPES.has(node.type);
398
383
  }
384
+ /**
385
+ * Whether a schema can supply object properties when merging faker values.
386
+ */
387
+ function isObjectSchema(node, seen = /* @__PURE__ */ new Set()) {
388
+ const resolved = ast.resolveRef(node);
389
+ if (seen.has(resolved)) return false;
390
+ seen.add(resolved);
391
+ if (ast.narrowSchema(resolved, ast.schemaTypes.object) || ast.narrowSchema(resolved, ast.schemaTypes.ref)) return true;
392
+ const composition = ast.narrowSchema(resolved, ast.schemaTypes.intersection) ?? ast.narrowSchema(resolved, ast.schemaTypes.union);
393
+ if (!composition) return false;
394
+ return composition.type === ast.schemaTypes.intersection && !composition.members?.length || (composition.members ?? []).some((member) => isObjectSchema(member, seen));
395
+ }
399
396
  function shouldInlineSingleResponseSchema(schema) {
400
397
  return !NON_INLINABLE_RESPONSE_TYPES.has(schema.type);
401
398
  }
@@ -407,11 +404,16 @@ function buildResponseUnionSchema(node, resolver) {
407
404
  const responses = node.responses.filter((response) => response.content?.[0]?.schema);
408
405
  if (!responses.length) return null;
409
406
  if (responses.length === 1) {
410
- const schema = responses[0].content?.[0]?.schema;
407
+ const schemas = responses[0].content?.flatMap((content) => content.schema ? [content.schema] : []) ?? [];
408
+ const schema = schemas.length > 1 ? ast.factory.createSchema({
409
+ type: "union",
410
+ members: schemas
411
+ }) : schemas[0];
411
412
  if (schema && shouldInlineSingleResponseSchema(schema)) return schema;
412
413
  return ast.factory.createSchema({
413
414
  type: "ref",
414
- name: resolver.response.status(node, responses[0].statusCode)
415
+ name: resolver.response.status(node, responses[0].statusCode),
416
+ schema: responses[0].content?.length === 1 ? schema : void 0
415
417
  });
416
418
  }
417
419
  return ast.factory.createSchema({
@@ -480,7 +482,8 @@ function getScalarType(node, typeName) {
480
482
  * Determines the data type, return type, and whether it uses the type name.
481
483
  */
482
484
  function resolveFakerTypeUsage(node, typeName, canOverride) {
483
- const scalarType = SCALAR_TYPES.has(node.type) ? `${getScalarType(node, typeName)}${node.nullable ? " | null" : ""}` : void 0;
485
+ const resolvedNode = ast.resolveRef(node);
486
+ const scalarType = SCALAR_TYPES.has(resolvedNode.type) ? `${getScalarType(resolvedNode, typeName)}${node.nullable || resolvedNode.nullable ? " | null" : ""}` : void 0;
484
487
  const dataType = scalarType ?? `Partial<${typeName}>`;
485
488
  const returnType = scalarType ?? (canOverride ? typeName : null);
486
489
  return {
@@ -489,28 +492,27 @@ function resolveFakerTypeUsage(node, typeName, canOverride) {
489
492
  usesTypeName: canOverride && dataType.includes(typeName) || Boolean(returnType?.includes(typeName))
490
493
  };
491
494
  }
492
- //#endregion
493
- //#region src/components/Faker.tsx
494
- const OBJECT_TYPES = /* @__PURE__ */ new Set(["object", "intersection"]);
495
- function Faker({ node, description, name, typeName, printer, canOverride, typeMode = "inferred" }) {
495
+ // src/components/Faker.tsx
496
+ function Faker({ node, description, name, typeName, printer, canOverride, typeMode = "inferred", keysToOmit }) {
496
497
  const fakerText = printer.print(node) ?? "undefined";
497
498
  const isArray = node.type === "array";
498
- const isObject = OBJECT_TYPES.has(node.type);
499
+ const isObject = !!ast.narrowSchema(node, ast.schemaTypes.object) || node.type === ast.schemaTypes.intersection && isObjectSchema(node);
500
+ const isValueIntersection = node.type === ast.schemaTypes.intersection && !isObject;
499
501
  const isTuple = node.type === "tuple";
500
502
  const isScalar = SCALAR_TYPES.has(node.type);
501
503
  const isUnion = node.type === "union";
502
- const useObjectOverride = canOverride && isObject;
504
+ const useObjectOverride = canOverride && (isObject || !!keysToOmit?.length);
503
505
  const fakerTextWithOverride = (() => {
504
506
  if (canOverride && node.type === "tuple") return `data && data.length === ${node.items?.length ?? 0} && !data.includes(undefined) ? data : ${fakerText}`;
505
507
  if (canOverride && isArray) return `[\n ...${fakerText},\n ...(data || []).filter((item) => item !== undefined),\n]`;
506
- if (canOverride && (isScalar || isUnion)) return node.nullable ? `data !== undefined ? data : ${fakerText}` : `data ?? ${fakerText}`;
508
+ if (canOverride && (isScalar || isUnion || isValueIntersection)) return node.nullable ? `data !== undefined ? data : ${fakerText}` : `data ?? ${fakerText}`;
507
509
  return fakerText;
508
510
  })();
509
511
  const { dataType, returnType: resolvedReturnType } = resolveFakerTypeUsage(node, typeName, canOverride);
510
512
  if (!useObjectOverride) {
511
513
  const paramsSignature = `${/\bdata\b/.test(fakerTextWithOverride) ? "data" : "_data"}?: ${dataType}`;
512
514
  const returnType = resolvedReturnType;
513
- const returnExpression = canOverride && !!returnType && (node.type === "ref" || isArray || isTuple || isUnion) ? `${isTuple || isUnion ? `(${fakerTextWithOverride})` : fakerTextWithOverride} as ${returnType}` : fakerTextWithOverride;
515
+ const returnExpression = canOverride && !!returnType && (node.type === "ref" && !SCALAR_TYPES.has(ast.resolveRef(node).type) || isArray || isTuple || isUnion) ? `${isTuple || isUnion ? `(${fakerTextWithOverride})` : fakerTextWithOverride} as ${returnType}` : fakerTextWithOverride;
514
516
  return /* @__PURE__ */ jsx(File.Source, {
515
517
  name,
516
518
  isExportable: true,
@@ -525,18 +527,21 @@ function Faker({ node, description, name, typeName, printer, canOverride, typeMo
525
527
  })
526
528
  });
527
529
  }
528
- const returnType = typeMode === "schema" ? typeName : "Omit<typeof defaultFakeData, keyof NoInfer<TData>> & NoInfer<TData>";
529
530
  const { cyclicSchemas, schemaName } = printer.options;
530
- const functionBody = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => containsCircularRef(p.schema, {
531
+ const isCyclic = !!schemaName && !!cyclicSchemas?.has(schemaName);
532
+ const returnType = typeMode === "schema" ? typeName : `Omit<${isCyclic ? typeName : "typeof defaultFakeData"}, keyof NoInfer<TData>> & NoInfer<TData>`;
533
+ const hasGetters = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => containsCircularRef(p.schema, {
531
534
  circularSchemas: cyclicSchemas,
532
535
  excludeName: schemaName
533
- })) ? `const defaultFakeData = ${fakerText}
536
+ }));
537
+ const declaration = `const ${keysToOmit?.length ? `{ ${keysToOmit.map((key, index) => `${objectKey(key)}: _omit${index}`).join(", ")}, ...defaultFakeData }` : "defaultFakeData"} = ${fakerText}`;
538
+ const functionBody = hasGetters ? `${declaration}
534
539
  if (data) {
535
540
  for (const [key, value] of Object.entries(data)) {
536
541
  Object.defineProperty(defaultFakeData, key, { value, configurable: true, writable: true, enumerable: true })
537
542
  }
538
543
  }
539
- return defaultFakeData as ${returnType}` : `const defaultFakeData = ${fakerText}
544
+ return defaultFakeData as ${returnType}` : `${declaration}
540
545
  return {
541
546
  ...defaultFakeData,
542
547
  ...(data || {}),
@@ -551,13 +556,12 @@ return {
551
556
  JSDoc: { comments: description ? [`@description ${jsDocEscape(description)}`] : [] },
552
557
  generics: typeMode === "schema" ? void 0 : `TData extends Partial<${typeName}> = object`,
553
558
  params: typeMode === "schema" ? `data?: Partial<${typeName}>` : "data?: TData",
554
- returnType: typeMode === "schema" ? typeName : void 0,
559
+ returnType: typeMode === "schema" || isCyclic ? returnType : void 0,
555
560
  children: functionBody
556
561
  })
557
562
  });
558
563
  }
559
- //#endregion
560
- //#region ../../internals/shared/src/operation.ts
564
+ // ../../internals/shared/src/operation.ts
561
565
  /**
562
566
  * Builds the `ResolverFileParams` every operation generator passes to
563
567
  * `resolver.file`: a file named `name`, tagged by the operation's first
@@ -672,17 +676,24 @@ function getOperationParameters(node) {
672
676
  operationParameterGroupsByNode.set(node, groups);
673
677
  return groups;
674
678
  }
675
- //#endregion
676
- //#region ../../internals/shared/src/resolver.ts
679
+ // ../../internals/shared/src/resolver.ts
677
680
  /**
678
- * Resolves a single operation parameter name with the
679
- * `<operationId> <in> <name>` template.
681
+ * Builds the allocation key for a declaration an operation owns. `scope` names which declaration
682
+ * it is, so two of them never share an allocation even when a custom resolver gives both the same
683
+ * name. Leave `scope` empty for the operation's own declaration.
680
684
  *
681
685
  * @example
682
- * `operationParamName.call(resolver, node, param) // → 'DeletePetPathPetId'`
686
+ * ```ts
687
+ * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
688
+ * ```
683
689
  */
684
- function operationParamName(node, param) {
685
- return this.name(`${node.operationId} ${param.in} ${param.name}`);
690
+ function operationKey(node, ...scope) {
691
+ return [
692
+ "operation",
693
+ node.method,
694
+ node.path,
695
+ ...scope.map(String)
696
+ ];
686
697
  }
687
698
  /**
688
699
  * Builds the shared `param` namespace. Spread the result into `createResolver`
@@ -695,15 +706,33 @@ function operationParamName(node, param) {
695
706
  */
696
707
  function createOperationParamResolver() {
697
708
  return {
698
- name: operationParamName,
709
+ name(node, param) {
710
+ const name = this.name(`${node.operationId} ${param.in} ${param.name}`);
711
+ return this.resolveName({
712
+ key: operationKey(node, "param", "name", param.in, param.name),
713
+ name
714
+ });
715
+ },
699
716
  path(node) {
700
- return this.name(`${node.operationId} Path`);
717
+ const name = this.name(`${node.operationId} Path`);
718
+ return this.resolveName({
719
+ key: operationKey(node, "param", "path"),
720
+ name
721
+ });
701
722
  },
702
723
  query(node) {
703
- return this.name(`${node.operationId} Query`);
724
+ const name = this.name(`${node.operationId} Query`);
725
+ return this.resolveName({
726
+ key: operationKey(node, "param", "query"),
727
+ name
728
+ });
704
729
  },
705
730
  headers(node) {
706
- return this.name(`${node.operationId} Headers`);
731
+ const name = this.name(`${node.operationId} Headers`);
732
+ return this.resolveName({
733
+ key: operationKey(node, "param", "headers"),
734
+ name
735
+ });
707
736
  }
708
737
  };
709
738
  }
@@ -720,16 +749,32 @@ function createOperationParamResolver() {
720
749
  function createOperationResponseResolver() {
721
750
  return {
722
751
  status(node, statusCode) {
723
- return this.name(`${node.operationId} Status ${statusCode}`);
752
+ const name = this.name(`${node.operationId} Status ${statusCode}`);
753
+ return this.resolveName({
754
+ key: operationKey(node, "response", "status", statusCode),
755
+ name
756
+ });
724
757
  },
725
758
  body(node) {
726
- return this.name(`${node.operationId} Body`);
759
+ const name = this.name(`${node.operationId} Body`);
760
+ return this.resolveName({
761
+ key: operationKey(node, "response", "body"),
762
+ name
763
+ });
727
764
  },
728
765
  responses(node) {
729
- return this.name(`${node.operationId} Responses`);
766
+ const name = this.name(`${node.operationId} Responses`);
767
+ return this.resolveName({
768
+ key: operationKey(node, "response", "responses"),
769
+ name
770
+ });
730
771
  },
731
772
  response(node) {
732
- return this.name(`${node.operationId} Response`);
773
+ const name = this.name(`${node.operationId} Response`);
774
+ return this.resolveName({
775
+ key: operationKey(node, "response", "response"),
776
+ name
777
+ });
733
778
  }
734
779
  };
735
780
  }
@@ -744,11 +789,10 @@ function createOperationResponseResolver() {
744
789
  */
745
790
  function createCasedFile(caseLast) {
746
791
  return { baseName({ name, extname }) {
747
- return `${toFilePath(name, caseLast)}${extname}`;
792
+ return this.memo(`file:${name}\0${extname}`, () => `${toFilePath(name, caseLast)}${extname}`);
748
793
  } };
749
794
  }
750
- //#endregion
751
- //#region ../../internals/shared/src/group.ts
795
+ // ../../internals/shared/src/group.ts
752
796
  /**
753
797
  * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
754
798
  * shared default naming so every plugin groups output consistently:
@@ -778,8 +822,7 @@ function createGroupConfig(group) {
778
822
  name: group.name ? group.name : defaultName
779
823
  };
780
824
  }
781
- //#endregion
782
- //#region ../../internals/shared/src/schemaTraversal.ts
825
+ // ../../internals/shared/src/schemaTraversal.ts
783
826
  /**
784
827
  * Maps each member of a union or intersection schema to its transformed output, pairing every
785
828
  * result with the original member.
@@ -800,8 +843,7 @@ function mapSchemaItems(node, transform) {
800
843
  output: transform(schema)
801
844
  }));
802
845
  }
803
- //#endregion
804
- //#region src/utils/imports.ts
846
+ // src/utils/imports.ts
805
847
  function escapeRegExp(value) {
806
848
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
807
849
  }
@@ -849,8 +891,7 @@ function aliasConflictingImports(imports, reservedNames) {
849
891
  function rewriteAliasedImports(text, aliases) {
850
892
  return Array.from(aliases).reduce((acc, [name, alias]) => acc.replace(new RegExp(`\\b${escapeRegExp(name)}\\b`, "g"), alias), text);
851
893
  }
852
- //#endregion
853
- //#region src/printers/printerFaker.ts
894
+ // src/printers/printerFaker.ts
854
895
  /**
855
896
  * Formats that put a whole number inside a `type: 'string'` schema, the way ProtoJSON encodes
856
897
  * 64-bit integers, so the mock has to be digits rather than letters.
@@ -986,7 +1027,9 @@ const printerFaker = ast.createPrinter((options) => {
986
1027
  },
987
1028
  enum(node) {
988
1029
  const { typeName } = this.options;
989
- return `faker.helpers.arrayElement${typeName ? `<${typeName}>` : ""}([${ast.getSchemaLiteralValues(node).map(parseEnumValue).join(", ")}])`;
1030
+ const values = ast.getSchemaLiteralValues(node);
1031
+ if (values.length === 1 && values[0] !== null && typeof values[0] === "object") return JSON.stringify(values[0]);
1032
+ return `faker.helpers.arrayElement${typeName ? `<${typeName}>` : ""}([${values.map(parseEnumValue).join(", ")}])`;
990
1033
  },
991
1034
  union(node) {
992
1035
  const { discriminatorPropertyName } = node;
@@ -1010,9 +1053,14 @@ const printerFaker = ast.createPrinter((options) => {
1010
1053
  }).map(({ output }) => output).filter((item) => Boolean(item)).join(", ")}])`;
1011
1054
  },
1012
1055
  intersection(node) {
1013
- const items = mapSchemaMembers(node, (member) => printNested(member, { nestedInObject: true })).map(({ output }) => output).filter((item) => Boolean(item) && item !== "undefined");
1056
+ const objectMembers = (node.members ?? []).filter((member) => isObjectSchema(member));
1057
+ const members = objectMembers.length ? objectMembers : node.members;
1058
+ const items = mapSchemaMembers({
1059
+ ...node,
1060
+ members
1061
+ }, (member) => printNested(member, { nestedInObject: true })).map(({ output }) => output).filter((item) => Boolean(item) && item !== "undefined");
1014
1062
  if (items.length === 0) return "{}";
1015
- return items.length === 1 ? items[0] : `{...${items.join(", ...")}}`;
1063
+ return !objectMembers.length || items.length === 1 ? items.at(-1) : `{...${items.join(", ...")}}`;
1016
1064
  },
1017
1065
  array(node) {
1018
1066
  const items = mapSchemaItems(node, (member) => printNested(member, {
@@ -1035,7 +1083,13 @@ const printerFaker = ast.createPrinter((options) => {
1035
1083
  },
1036
1084
  object(node) {
1037
1085
  const cyclicSchemas = this.options.cyclicSchemas;
1038
- return buildObject((node.properties ?? []).map((property) => {
1086
+ const keySchema = node.propertyNames && syncSchemaRef(node.propertyNames);
1087
+ const valueSchema = node.additionalProperties;
1088
+ const recordProperties = keySchema?.type === "enum" && typeof valueSchema === "object" ? ast.getSchemaLiteralValues(keySchema).filter((key) => typeof key === "string" && !node.properties.some((property) => property.name === key)).map((name) => ast.factory.createProperty({
1089
+ name,
1090
+ schema: valueSchema
1091
+ })) : [];
1092
+ return buildObject([...node.properties, ...recordProperties].map((property) => {
1039
1093
  const value = printNested(property.schema, {
1040
1094
  typeName: this.options.typeName ? indexedTypeName(this.options.typeName, property.name, this.options.nestedInUnion) : void 0,
1041
1095
  nestedInObject: true
@@ -1051,8 +1105,7 @@ const printerFaker = ast.createPrinter((options) => {
1051
1105
  overrides: options.nodes
1052
1106
  };
1053
1107
  });
1054
- //#endregion
1055
- //#region src/generators/fakerGenerator.tsx
1108
+ // src/generators/fakerGenerator.tsx
1056
1109
  /**
1057
1110
  * Built-in generator for `@kubb/plugin-faker`. Emits one `createX` factory
1058
1111
  * per schema in the spec plus per-operation request/response factories. Each
@@ -1224,7 +1277,7 @@ const fakerGenerator = defineGenerator({
1224
1277
  name: baseName,
1225
1278
  typeName: tsBaseName,
1226
1279
  description,
1227
- skipImportNames: []
1280
+ keysToOmit: primary.keysToOmit
1228
1281
  }];
1229
1282
  }
1230
1283
  const variants = resolveContentTypeVariants(entries, baseName);
@@ -1241,7 +1294,7 @@ const fakerGenerator = defineGenerator({
1241
1294
  name: variant.name,
1242
1295
  typeName: getPerContentTypeName(tsBaseName, variant.suffix),
1243
1296
  description,
1244
- skipImportNames: []
1297
+ keysToOmit: variant.keysToOmit
1245
1298
  })), {
1246
1299
  schema: unionSchema,
1247
1300
  name: baseName,
@@ -1291,8 +1344,15 @@ const fakerGenerator = defineGenerator({
1291
1344
  group: group ?? void 0
1292
1345
  }).filter((entry) => entry.path !== meta.file.path);
1293
1346
  }
1294
- function renderEntry({ schema, name, typeName, description, skipImportNames = [] }) {
1347
+ function renderEntry({ schema, name, typeName, description, keysToOmit, skipImportNames = [] }) {
1295
1348
  if (!schema) return null;
1349
+ if (schema.type === ast.schemaTypes.object && keysToOmit?.length) {
1350
+ schema = {
1351
+ ...schema,
1352
+ properties: schema.properties.filter((property) => !keysToOmit?.includes(property.name))
1353
+ };
1354
+ keysToOmit = void 0;
1355
+ }
1296
1356
  const canOverride = canOverrideSchema(schema);
1297
1357
  const printerInstance = printerFaker({
1298
1358
  resolver,
@@ -1301,7 +1361,8 @@ const fakerGenerator = defineGenerator({
1301
1361
  dateParser,
1302
1362
  regexGenerator,
1303
1363
  nodes: printer?.nodes,
1304
- cyclicSchemas
1364
+ cyclicSchemas,
1365
+ nestedInObject: !!keysToOmit?.length
1305
1366
  });
1306
1367
  const fakerText = printerInstance.print(schema) ?? "undefined";
1307
1368
  const { imports, aliases } = aliasConflictingImports(filterUsedImports(resolveMockImports(schema), fakerText, skipImportNames), localHelperNames);
@@ -1340,7 +1401,8 @@ const fakerGenerator = defineGenerator({
1340
1401
  print: () => rewrittenFakerText
1341
1402
  },
1342
1403
  canOverride,
1343
- typeMode
1404
+ typeMode,
1405
+ keysToOmit
1344
1406
  })
1345
1407
  ] });
1346
1408
  }
@@ -1397,17 +1459,14 @@ const fakerGenerator = defineGenerator({
1397
1459
  });
1398
1460
  }
1399
1461
  });
1400
- //#endregion
1401
- //#region kubb-extension.json
1462
+ // kubb-extension.json
1402
1463
  var compatibility = {
1403
- "kubb": ">=5.8.3",
1464
+ "kubb": ">=5.14.6",
1404
1465
  "node": ">=22.22"
1405
1466
  };
1406
- //#endregion
1407
- //#region package.json
1408
- var version = "5.2.1";
1409
- //#endregion
1410
- //#region src/resolvers/resolverFaker.ts
1467
+ // package.json
1468
+ var version = "5.2.3";
1469
+ // src/resolvers/resolverFaker.ts
1411
1470
  /**
1412
1471
  * Default resolver used by `@kubb/plugin-faker`. Decides the names and file
1413
1472
  * paths for every generated mock factory. Functions and files are prefixed
@@ -1423,14 +1482,13 @@ var version = "5.2.1";
1423
1482
  const resolverFaker = createResolver({
1424
1483
  pluginName: "plugin-faker",
1425
1484
  name(name) {
1426
- return ensureValidVarName(camelCase(name, { prefix: "create" }));
1485
+ return this.memo(`name:${name}`, () => ensureValidVarName(camelCase(name, { prefix: "create" })));
1427
1486
  },
1428
1487
  file: createCasedFile((part) => camelCase(part, { prefix: "create" })),
1429
1488
  param: createOperationParamResolver(),
1430
1489
  response: createOperationResponseResolver()
1431
1490
  });
1432
- //#endregion
1433
- //#region src/plugin.ts
1491
+ // src/plugin.ts
1434
1492
  /**
1435
1493
  * Canonical plugin name for `@kubb/plugin-faker`. Used for driver lookups and
1436
1494
  * cross-plugin dependency references.
@@ -1491,7 +1549,6 @@ const pluginFaker = defineExtension({
1491
1549
  };
1492
1550
  }
1493
1551
  });
1494
- //#endregion
1495
1552
  export { Faker, pluginFaker as default, pluginFaker, fakerGenerator, pluginFakerName, printerFaker, resolverFaker };
1496
1553
 
1497
1554
  //# sourceMappingURL=index.js.map