@kubb/plugin-faker 5.4.0-canary.20261111T122006 → 5.4.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.js CHANGED
@@ -1,5 +1,5 @@
1
+ import { Diagnostics, Resolver, addGenerator, ast, casing, createPrinter, createResolver, defineExtension, defineGenerator, setMacros, setResolver, text } from "@kubb/kit";
1
2
  import { posix } from "node:path";
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/renderer-jsx";
4
4
  import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
5
5
  import { buildParams, enumTypeName, pluginTsName } from "@kubb/plugin-ts";
@@ -142,67 +142,76 @@ function ensureValidVarName(name) {
142
142
  function isIdentifier(name) {
143
143
  return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
144
144
  }
145
- // ../../internals/utils/src/strings.ts
145
+ // ../../internals/utils/src/codegen.ts
146
146
  /**
147
- * Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
148
- * any backslash or single quote in the content.
147
+ * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
148
+ * level and closing the brace at column zero. Entries that are themselves multi-line objects indent
149
+ * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
149
150
  *
150
151
  * @example
151
152
  * ```ts
152
- * singleQuote('foo') // "'foo'"
153
- * singleQuote("o'clock") // "'o\\'clock'"
153
+ * buildObject(['id: z.number()', 'name: z.string()'])
154
+ * // '{\n id: z.number(),\n name: z.string(),\n}'
154
155
  * ```
155
156
  */
156
- function singleQuote(value) {
157
- if (value === void 0 || value === null) return "''";
158
- return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
157
+ function buildObject(entries) {
158
+ if (entries.length === 0) return "{}";
159
+ return `{\n${entries.map((entry) => `${text.indent({ text: entry })},`).join("\n")}\n}`;
159
160
  }
160
161
  /**
161
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
162
- * Returns the string unchanged when no balanced quote pair is found.
162
+ * An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.
163
163
  *
164
164
  * @example
165
165
  * ```ts
166
- * trimQuotes('"hello"') // 'hello'
167
- * trimQuotes('hello') // 'hello'
166
+ * objectKey('x-total') // "'x-total'"
168
167
  * ```
169
168
  */
170
- function trimQuotes(text) {
171
- if (text.length >= 2) {
172
- const first = text[0];
173
- const last = text[text.length - 1];
174
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
175
- }
176
- return text;
169
+ function objectKey(name) {
170
+ return isIdentifier(name) ? name : text.quote(name);
171
+ }
172
+ // ../../internals/utils/src/fs.ts
173
+ function toSlash(p) {
174
+ if (p.startsWith("\\\\?\\")) return p;
175
+ return p.replaceAll("\\", "/");
177
176
  }
178
177
  /**
179
- * Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
180
- *
181
- * Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
182
- * code matches the repo style without a formatter.
178
+ * Returns the relative path from `rootDir` to `filePath`, always using forward slashes
179
+ * and prefixed with `./` when not already traversing upward.
183
180
  *
184
181
  * @example
185
182
  * ```ts
186
- * stringify('hello') // "'hello'"
187
- * stringify('"hello"') // "'hello'"
183
+ * getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
184
+ * getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
188
185
  * ```
189
186
  */
190
- function stringify(value) {
191
- if (value === void 0 || value === null) return "''";
192
- return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
187
+ function getRelativePath(rootDir, filePath) {
188
+ const relativePath = posix.relative(toSlash(rootDir), toSlash(filePath));
189
+ return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
193
190
  }
194
191
  /**
195
- * Escapes an `@` at the start of a line, so a multi-line description can't start a new JSDoc tag.
196
- * Editors render `\@` as `@` in hovers.
192
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
193
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
194
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
197
195
  *
198
- * @example
199
- * ```ts
200
- * jsDocEscape('intro\n@example value') // 'intro\n\\@example value'
201
- * ```
196
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
197
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
198
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
199
+ * absolute path, letting generated files escape the configured output directory.
200
+ *
201
+ * @example Nested path from a dotted name
202
+ * `toFilePath('pet.petId') // 'pet/petId'`
203
+ *
204
+ * @example PascalCase the final segment
205
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
206
+ *
207
+ * @example Suffix applied to the final segment only
208
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
202
209
  */
203
- function jsDocEscape(input) {
204
- return `${input}`.replace(/^(\s*)@/gm, "$1\\@");
210
+ function toFilePath(name, caseLast = casing.camel) {
211
+ const parts = name.split(/\.(?=[a-zA-Z])/);
212
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : casing.camel(part)).filter(Boolean).join("/");
205
213
  }
214
+ // ../../internals/utils/src/strings.ts
206
215
  const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
207
216
  const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
208
217
  /**
@@ -242,8 +251,8 @@ function toRegExp(pattern, flags) {
242
251
  * toRegExpString('^(?im)foo', null) // '/^foo/im'
243
252
  * ```
244
253
  */
245
- function toRegExpString(text, func = "RegExp") {
246
- const raw = trimQuotes(text);
254
+ function toRegExpString(pattern, func = "RegExp") {
255
+ const raw = text.unquote(pattern);
247
256
  const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
248
257
  const replacementTarget = match?.[1] ?? "";
249
258
  const matchedFlags = match?.[2];
@@ -251,84 +260,17 @@ function toRegExpString(text, func = "RegExp") {
251
260
  if (func === null) return `/${source}/${flags}`;
252
261
  return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
253
262
  }
254
- // ../../internals/utils/src/codegen.ts
255
- const INDENT = " ";
256
- /**
257
- * Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
258
- */
259
- function indentLines(text) {
260
- if (!text) return "";
261
- return text.split("\n").map((line) => line.trim() ? `${INDENT}${line}` : "").join("\n");
262
- }
263
- /**
264
- * Renders an object key, quoting it with single quotes only when it is not a valid identifier.
265
- * Reserved words and globals (`name`, `class`, …) are valid bare keys and stay unquoted.
266
- *
267
- * @example
268
- * ```ts
269
- * objectKey('name') // 'name'
270
- * objectKey('x-total') // "'x-total'"
271
- * ```
272
- */
273
- function objectKey(name) {
274
- return isIdentifier(name) ? name : singleQuote(name);
275
- }
276
263
  /**
277
- * Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
278
- * level and closing the brace at column zero. Entries that are themselves multi-line objects indent
279
- * cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
280
- *
281
- * @example
282
- * ```ts
283
- * buildObject(['id: z.number()', 'name: z.string()'])
284
- * // '{\n id: z.number(),\n name: z.string(),\n}'
285
- * ```
286
- */
287
- function buildObject(entries) {
288
- if (entries.length === 0) return "{}";
289
- return `{\n${entries.map((entry) => `${indentLines(entry)},`).join("\n")}\n}`;
290
- }
291
- // ../../internals/utils/src/fs.ts
292
- function toSlash(p) {
293
- if (p.startsWith("\\\\?\\")) return p;
294
- return p.replaceAll("\\", "/");
295
- }
296
- /**
297
- * Returns the relative path from `rootDir` to `filePath`, always using forward slashes
298
- * and prefixed with `./` when not already traversing upward.
264
+ * Escapes an `@` at the start of a line, so a multi-line description can't start a new JSDoc tag.
265
+ * Editors render `\@` as `@` in hovers.
299
266
  *
300
267
  * @example
301
268
  * ```ts
302
- * getRelativePath('/src/components', '/src/components/Button.tsx') // './Button.tsx'
303
- * getRelativePath('/src/components', '/src/utils/helpers.ts') // '../utils/helpers.ts'
269
+ * jsDocEscape('intro\n@example value') // 'intro\n\\@example value'
304
270
  * ```
305
271
  */
306
- function getRelativePath(rootDir, filePath) {
307
- const relativePath = posix.relative(toSlash(rootDir), toSlash(filePath));
308
- return relativePath.startsWith("../") ? relativePath : `./${relativePath}`;
309
- }
310
- /**
311
- * Builds a nested file path from a dotted name. Splits on dots that precede a letter
312
- * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
313
- * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
314
- *
315
- * Empty segments are dropped before joining. They arise when the name starts with a dot
316
- * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
317
- * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
318
- * absolute path, letting generated files escape the configured output directory.
319
- *
320
- * @example Nested path from a dotted name
321
- * `toFilePath('pet.petId') // 'pet/petId'`
322
- *
323
- * @example PascalCase the final segment
324
- * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
325
- *
326
- * @example Suffix applied to the final segment only
327
- * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
328
- */
329
- function toFilePath(name, caseLast = camelCase) {
330
- const parts = name.split(/\.(?=[a-zA-Z])/);
331
- return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
272
+ function jsDocEscape(input) {
273
+ return `${input}`.replace(/^(\s*)@/gm, "$1\\@");
332
274
  }
333
275
  // ../../internals/shared/src/operation.ts
334
276
  /**
@@ -395,19 +337,12 @@ function resolveOperationFile(options) {
395
337
  return file;
396
338
  });
397
339
  }
398
- function baseContentType(contentType) {
399
- return contentType.split(";")[0].trim().toLowerCase();
400
- }
401
340
  /**
402
- * Whether a content entry is a sequence of JSON values the adapter marks as a stream, such as `application/jsonl`,
403
- * `application/x-ndjson`, `application/json-seq`, or any media type with an OpenAPI 3.2 `itemSchema`.
341
+ * How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.
404
342
  */
405
- function isJsonLines(entry) {
406
- const baseType = baseContentType(entry.contentType);
407
- return Boolean(entry.stream) && baseType !== "text/event-stream" && !baseType.startsWith("multipart/");
408
- }
409
- function isJsonContentType(baseType) {
410
- return baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json";
343
+ function getStreamFraming(entry) {
344
+ if (ast.mediaType.getBase(entry) === "text/event-stream") return "events";
345
+ return entry.stream && ast.mediaType.isStream(entry) ? "lines" : void 0;
411
346
  }
412
347
  /**
413
348
  * Parser for one response content entry.
@@ -418,10 +353,10 @@ function isJsonContentType(baseType) {
418
353
  * `image/*`, `audio/*`, and `video/*` are blobs.
419
354
  */
420
355
  function responseTypeForContent(entry) {
421
- const baseType = baseContentType(entry.contentType);
356
+ const baseType = ast.mediaType.getBase(entry);
422
357
  if (!baseType) return void 0;
423
- if (baseType === "text/event-stream" || isJsonLines(entry)) return "stream";
424
- if (isJsonContentType(baseType)) return void 0;
358
+ if (getStreamFraming(entry)) return "stream";
359
+ if (ast.mediaType.isJson(baseType)) return void 0;
425
360
  if (entry.schema?.type === "blob") return "blob";
426
361
  if (baseType.startsWith("text/")) return "text";
427
362
  if (isBinaryContentType(baseType)) return "blob";
@@ -459,14 +394,14 @@ function getPerContentTypeName(baseName, suffix) {
459
394
  function toResponseContent(entry, entries = [entry]) {
460
395
  const { contentType, schema } = entry;
461
396
  if (!schema || entry.stream) return entry;
462
- const baseType = baseContentType(contentType);
463
- const resolved = ast.resolveRef(schema);
397
+ const baseType = ast.mediaType.getBase(contentType);
398
+ const resolved = ast.ref.getSchema(schema);
464
399
  if (isBinaryContentType(baseType)) return resolved.type === "blob" ? entry : {
465
400
  ...entry,
466
401
  schema: ast.factory.createSchema({ type: "blob" }),
467
402
  keysToOmit: null
468
403
  };
469
- const isPlainMediaType = Boolean(baseType) && !isJsonContentType(baseType) && !baseType.includes("*");
404
+ const isPlainMediaType = Boolean(baseType) && !ast.mediaType.isJson(baseType) && !baseType.includes("*");
470
405
  if (resolved.type === "blob") return isPlainMediaType && !entries.every((item) => item.schema?.type === "blob") ? {
471
406
  ...entry,
472
407
  schema: ast.factory.createSchema({ type: "string" }),
@@ -509,50 +444,6 @@ function resolveContentTypeVariants(entries, baseName) {
509
444
  };
510
445
  });
511
446
  }
512
- const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
513
- /**
514
- * An OpenAPI 3.2 `querystring` parameter is the whole query string, so a form-encoded object becomes one query parameter
515
- * per property, typed and serialized like any other.
516
- * Another content type, such as JSON, stays one parameter named after it, and the runtime sends its value as the whole query string.
517
- */
518
- function spreadQuerystring(param) {
519
- const schema = param.schema.type === "ref" ? param.schema.schema : param.schema;
520
- const formEncoded = !param.contentType || param.contentType.split(";")[0].trim().toLowerCase() === "application/x-www-form-urlencoded";
521
- if (schema?.type !== "object" || !formEncoded) return [param];
522
- return schema.properties.map((property) => ast.factory.createParameter({
523
- name: property.name,
524
- in: "query",
525
- schema: property.schema,
526
- required: param.required && property.required
527
- }));
528
- }
529
- /**
530
- * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
531
- * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
532
- * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
533
- * same parameters once per plugin.
534
- */
535
- function getOperationParameters(node) {
536
- const cached = operationParameterGroupsByNode.get(node);
537
- if (cached) return cached;
538
- const dedupe = (params) => {
539
- const seen = /* @__PURE__ */ new Set();
540
- return params.filter((param) => {
541
- if (seen.has(param.name)) return false;
542
- seen.add(param.name);
543
- return true;
544
- });
545
- };
546
- const group = (location) => dedupe(node.parameters.filter((param) => param.in === location));
547
- const groups = {
548
- path: group("path"),
549
- query: dedupe([...group("query"), ...group("querystring").flatMap(spreadQuerystring)]),
550
- header: group("header"),
551
- cookie: group("cookie")
552
- };
553
- operationParameterGroupsByNode.set(node, groups);
554
- return groups;
555
- }
556
447
  // ../../internals/shared/src/resolver.ts
557
448
  /**
558
449
  * Builds the allocation key for a declaration an operation owns. `scope` names which declaration
@@ -641,6 +532,13 @@ function createOperationResponseResolver() {
641
532
  name
642
533
  });
643
534
  },
535
+ headers(node, statusCode) {
536
+ const name = this.name(`${node.operationId} Status ${statusCode} Headers`);
537
+ return this.resolveName({
538
+ key: operationKey(node, "response", "headers", statusCode),
539
+ name
540
+ });
541
+ },
644
542
  body(node) {
645
543
  const name = this.name(`${node.operationId} Body`);
646
544
  return this.resolveName({
@@ -682,58 +580,7 @@ function createCasedFile(caseLast) {
682
580
  * Names of the schemas in a circular reference chain, computed once per build.
683
581
  */
684
582
  function getCircularNames(ctx) {
685
- return ctx.memo("circularNames:set", () => ast.findCircularSchemas(ctx.driver.inputNode?.schemas ?? []));
686
- }
687
- // ../../internals/shared/src/group.ts
688
- /**
689
- * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
690
- * shared default naming so every plugin groups output consistently:
691
- *
692
- * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
693
- * - other groups use the camelCased group (`pet store` → `petStore`).
694
- *
695
- * A user-provided `group.name` always wins over the default namer, so callers stay in
696
- * control of their output folders. Returns `null` when grouping is disabled, matching the
697
- * per-plugin convention.
698
- *
699
- * @param group - The user-supplied group option, or `undefined` to disable grouping.
700
- *
701
- * @example
702
- * ```ts
703
- * createGroupConfig(group) // shared across every plugin
704
- * ```
705
- */
706
- function createGroupConfig(group) {
707
- if (!group) return null;
708
- const defaultName = (ctx) => {
709
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
710
- return camelCase(ctx.group);
711
- };
712
- return {
713
- ...group,
714
- name: group.name ? group.name : defaultName
715
- };
716
- }
717
- // ../../internals/shared/src/schemaTraversal.ts
718
- /**
719
- * Maps each member of a union or intersection schema to its transformed output, pairing every
720
- * result with the original member.
721
- */
722
- function mapSchemaMembers(node, transform) {
723
- return (node.members ?? []).map((schema) => ({
724
- schema,
725
- output: transform(schema)
726
- }));
727
- }
728
- /**
729
- * Maps each item of an array or tuple schema to its transformed output, pairing every result with
730
- * the original item.
731
- */
732
- function mapSchemaItems(node, transform) {
733
- return (node.items ?? []).map((schema) => ({
734
- schema,
735
- output: transform(schema)
736
- }));
583
+ return ctx.memo("circularNames:set", () => ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []));
737
584
  }
738
585
  // src/utils.ts
739
586
  /**
@@ -790,26 +637,29 @@ function canOverrideSchema(node) {
790
637
  * Whether a schema can supply object properties when merging faker values.
791
638
  */
792
639
  function isObjectSchema(node, seen = /* @__PURE__ */ new Set()) {
793
- const resolved = ast.resolveRef(node);
640
+ const resolved = ast.ref.getSchema(node);
794
641
  if (seen.has(resolved)) return false;
795
642
  seen.add(resolved);
796
- if (ast.narrowSchema(resolved, ast.schemaTypes.object) || ast.narrowSchema(resolved, ast.schemaTypes.ref)) return true;
797
- const composition = ast.narrowSchema(resolved, ast.schemaTypes.intersection) ?? ast.narrowSchema(resolved, ast.schemaTypes.union);
643
+ if (ast.schema.narrow(resolved, ast.schema.types.object) || ast.schema.narrow(resolved, ast.schema.types.ref)) return true;
644
+ const composition = ast.schema.narrow(resolved, ast.schema.types.intersection) ?? ast.schema.narrow(resolved, ast.schema.types.union);
798
645
  if (!composition) return false;
799
- return composition.type === ast.schemaTypes.intersection && !composition.members?.length || (composition.members ?? []).some((member) => isObjectSchema(member, seen));
646
+ return composition.type === ast.schema.types.intersection && !composition.members?.length || (composition.members ?? []).some((member) => isObjectSchema(member, seen));
800
647
  }
801
648
  function shouldInlineSingleResponseSchema(schema) {
802
649
  return !NON_INLINABLE_RESPONSE_TYPES.has(schema.type);
803
650
  }
804
651
  /**
805
- * Builds a response schema as a union of all response statuses.
652
+ * Builds a response schema as a union of the success response statuses, like the `<Operation>Response` type of
653
+ * `@kubb/plugin-ts`, so the mock never is an error body. Falls back to every response when no success response has a schema.
806
654
  * Returns null if no responses are provided, or embeds single simple responses inline.
807
655
  */
808
656
  function buildResponseUnionSchema(node, resolver) {
809
- const responses = node.responses.map((response) => ({
657
+ const withSchema = node.responses.map((response) => ({
810
658
  ...response,
811
659
  content: response.content?.map((entry, _, content) => toResponseContent(entry, content))
812
660
  })).filter((response) => response.content?.[0]?.schema);
661
+ const success = withSchema.filter((response) => ast.http.status.isSuccess(response));
662
+ const responses = success.length > 0 ? success : withSchema;
813
663
  if (!responses.length) return null;
814
664
  if (responses.length === 1) {
815
665
  const schemas = responses[0].content?.flatMap((content) => content.schema ? [content.schema] : []) ?? [];
@@ -890,7 +740,7 @@ function getScalarType(node, typeName) {
890
740
  * Determines the data type, return type, and whether it uses the type name.
891
741
  */
892
742
  function resolveFakerTypeUsage(node, typeName, canOverride) {
893
- const resolvedNode = ast.resolveRef(node);
743
+ const resolvedNode = ast.ref.getSchema(node);
894
744
  const scalarType = SCALAR_TYPES.has(resolvedNode.type) ? `${getScalarType(resolvedNode, typeName)}${node.nullable || resolvedNode.nullable ? " | null" : ""}` : void 0;
895
745
  const dataType = scalarType ?? `Partial<${typeName}>`;
896
746
  const returnType = scalarType ?? (canOverride ? typeName : null);
@@ -909,8 +759,8 @@ function Faker({ node, description, name, typeName, printer, canOverride, typeMo
909
759
  imp.name
910
760
  ].join("-")));
911
761
  const isArray = node.type === "array";
912
- const isObject = !!ast.narrowSchema(node, ast.schemaTypes.object) || node.type === ast.schemaTypes.intersection && isObjectSchema(node);
913
- const isValueIntersection = node.type === ast.schemaTypes.intersection && !isObject;
762
+ const isObject = !!ast.schema.narrow(node, ast.schema.types.object) || node.type === ast.schema.types.intersection && isObjectSchema(node);
763
+ const isValueIntersection = node.type === ast.schema.types.intersection && !isObject;
914
764
  const isTuple = node.type === "tuple";
915
765
  const isScalar = SCALAR_TYPES.has(node.type);
916
766
  const isUnion = node.type === "union";
@@ -919,13 +769,14 @@ function Faker({ node, description, name, typeName, printer, canOverride, typeMo
919
769
  if (canOverride && node.type === "tuple") return `data && data.length === ${node.items?.length ?? 0} && !data.includes(undefined) ? data : ${fakerText}`;
920
770
  if (canOverride && isArray) return `[\n ...${fakerText},\n ...(data || []).filter((item) => item !== undefined),\n]`;
921
771
  if (canOverride && (isScalar || isUnion || isValueIntersection)) return node.nullable ? `data !== undefined ? data : ${fakerText}` : `data ?? ${fakerText}`;
772
+ if (canOverride && node.type === "ref" && node.nullable && SCALAR_TYPES.has(ast.ref.getSchema(node).type)) return `data === null ? data : ${fakerText}`;
922
773
  return fakerText;
923
774
  })();
924
775
  const { dataType, returnType: resolvedReturnType } = resolveFakerTypeUsage(node, typeName, canOverride);
925
776
  if (!useObjectOverride) {
926
777
  const paramsSignature = `${/\bdata\b/.test(fakerTextWithOverride) ? "data" : "_data"}?: ${dataType}`;
927
778
  const returnType = resolvedReturnType;
928
- const returnExpression = canOverride && !!returnType && (node.type === "ref" && !SCALAR_TYPES.has(ast.resolveRef(node).type) || isArray || isTuple || isUnion) ? `${isTuple || isUnion ? `(${fakerTextWithOverride})` : fakerTextWithOverride} as ${returnType}` : fakerTextWithOverride;
779
+ const returnExpression = canOverride && !!returnType && (node.type === "ref" && !SCALAR_TYPES.has(ast.ref.getSchema(node).type) || isArray || isTuple || isUnion) ? `${isTuple || isUnion ? `(${fakerTextWithOverride})` : fakerTextWithOverride} as ${returnType}` : fakerTextWithOverride;
929
780
  return /* @__PURE__ */ jsxs(Fragment, { children: [imports, /* @__PURE__ */ jsx(File.Source, {
930
781
  name,
931
782
  isExportable: true,
@@ -943,10 +794,11 @@ function Faker({ node, description, name, typeName, printer, canOverride, typeMo
943
794
  const { cyclicSchemas, schemaName } = printer.options;
944
795
  const isCyclic = !!schemaName && !!cyclicSchemas?.has(schemaName);
945
796
  const returnType = typeMode === "schema" ? typeName : `Omit<${isCyclic ? typeName : "typeof defaultFakeData"}, keyof NoInfer<TData>> & NoInfer<TData>`;
946
- const hasGetters = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => containsCircularRef(p.schema, {
797
+ const hasGetters = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => ast.graph.containsCircular(p.schema, {
947
798
  circularSchemas: cyclicSchemas,
948
799
  excludeName: schemaName
949
800
  }));
801
+ const castThroughUnknown = (node.type !== "object" || node.min !== void 0) && typeMode !== "schema";
950
802
  const declaration = `const ${keysToOmit?.length ? `{ ${keysToOmit.map((key, index) => `${objectKey(key)}: _omit${index}`).join(", ")}, ...defaultFakeData }` : "defaultFakeData"} = ${fakerText}`;
951
803
  const functionBody = hasGetters ? `${declaration}
952
804
  if (data) {
@@ -958,7 +810,7 @@ return defaultFakeData as ${returnType}` : `${declaration}
958
810
  return {
959
811
  ...defaultFakeData,
960
812
  ...(data || {}),
961
- } as ${returnType}`;
813
+ } as ${castThroughUnknown ? "unknown as " : ""}${returnType}`;
962
814
  return /* @__PURE__ */ jsxs(Fragment, { children: [imports, /* @__PURE__ */ jsx(File.Source, {
963
815
  name,
964
816
  isExportable: true,
@@ -1035,11 +887,38 @@ const integerFormats = /* @__PURE__ */ new Set([
1035
887
  "uint64"
1036
888
  ]);
1037
889
  const maxInt32 = 2147483647;
890
+ const maxValueByFormat = {
891
+ int32: maxInt32,
892
+ uint32: 4294967295
893
+ };
894
+ function isCyclicRef(node, { schemaName, cyclicSchemas }) {
895
+ if (node.type !== "ref" || !schemaName) return false;
896
+ const refName = ast.ref.getName(node);
897
+ return refName === schemaName || Boolean(refName && cyclicSchemas?.has(schemaName) && cyclicSchemas.has(refName));
898
+ }
1038
899
  /**
1039
- * `faker.number.float()` or `faker.number.int()`, with `min` and `max` passed only when set.
900
+ * `faker.number.float()`, `faker.number.int()` or `faker.number.bigInt()`, with `min` and `max` passed only when set,
901
+ * moved inside an exclusive bound, or needed so a lone bound does not contradict faker's default for the other.
1040
902
  */
1041
- function numberCall(kind, min, max) {
1042
- const args = [min !== void 0 && `min: ${min}`, max !== void 0 && `max: ${max}`].filter(Boolean).join(", ");
903
+ function numberCall(kind, { min, max, exclusive, multipleOf }) {
904
+ const isWhole = kind !== "float";
905
+ const grid = isWhole && !Number.isInteger(multipleOf) ? void 0 : multipleOf;
906
+ const { min: lower, max: upper } = ast.schema.getNumberBounds({
907
+ min,
908
+ max,
909
+ exclusive,
910
+ integer: isWhole,
911
+ step: grid
912
+ });
913
+ const span = 100 * (grid ?? 1);
914
+ const maxValue = kind === "float" && lower !== void 0 && upper === void 0 && (lower >= 1 || grid !== void 0) ? lower + span : upper;
915
+ const minValue = maxValue !== void 0 && lower === void 0 && (isWhole ? maxValue < 0 : maxValue <= 0) ? maxValue - span : lower;
916
+ const suffix = kind === "bigInt" ? "n" : "";
917
+ const args = [
918
+ minValue !== void 0 && `min: ${minValue}${suffix}`,
919
+ maxValue !== void 0 && `max: ${maxValue}${suffix}`,
920
+ grid !== void 0 && `multipleOf: ${grid}${suffix}`
921
+ ].filter(Boolean).join(", ");
1043
922
  return `faker.number.${kind}(${args ? `{ ${args} }` : ""})`;
1044
923
  }
1045
924
  /**
@@ -1057,12 +936,58 @@ function dateTimeCall(representation, { format, substring }, parser = "faker", s
1057
936
  });
1058
937
  return "faker.date.anytime()";
1059
938
  }
939
+ const encodingCalls = /* @__PURE__ */ new Map([["base64", "btoa(faker.string.alpha())"], ["base64url", "faker.string.alphanumeric(8)"]]);
940
+ const stringFormatCalls = /* @__PURE__ */ new Map([
941
+ ["jwt", "faker.internet.jwt()"],
942
+ ["ulid", "faker.string.ulid()"],
943
+ ["iban", "faker.finance.iban()"],
944
+ ["duration", "'P1D'"],
945
+ ["hostname", "faker.internet.domainName()"]
946
+ ]);
947
+ const classEscapes = new Map([
948
+ ["d", "0-9"],
949
+ ["w", "A-Za-z0-9_"],
950
+ ...Array.from("-!@#$&()`.+,\"", (char) => [char, char])
951
+ ]);
952
+ function toFakerPattern(pattern) {
953
+ let result = "";
954
+ let inClass = false;
955
+ let classStart = -1;
956
+ for (let index = 0; index < pattern.length; index++) {
957
+ const char = pattern[index];
958
+ const next = pattern[index + 1];
959
+ if (char === "\\" && next !== void 0) {
960
+ index++;
961
+ if (inClass && next === "-") {
962
+ if (result[classStart] !== "-") result = `${result.slice(0, classStart)}-${result.slice(classStart)}`;
963
+ continue;
964
+ }
965
+ const classMembers = classEscapes.get(next);
966
+ if (classMembers === void 0) {
967
+ result += `\\${next}`;
968
+ continue;
969
+ }
970
+ result += inClass ? classMembers : `[${classMembers}]`;
971
+ continue;
972
+ }
973
+ if (char === "[" && !inClass) {
974
+ inClass = true;
975
+ result += next === "^" ? "[^" : "[";
976
+ if (next === "^") index++;
977
+ classStart = result.length;
978
+ continue;
979
+ }
980
+ if (char === "]") inClass = false;
981
+ result += char;
982
+ }
983
+ return result.replace(/\{(\d+),\}/g, (_, min) => `{${min},${Number(min) + 10}}`);
984
+ }
1060
985
  function matches(value, regexGenerator = "faker") {
1061
986
  if (regexGenerator === "randexp") return `${toRegExpString(value, "RandExp")}.gen()`;
1062
- return `faker.helpers.fromRegExp(${toRegExpString(value, null)})`;
987
+ return `faker.helpers.fromRegExp(${toRegExpString(toFakerPattern(value), null)})`;
1063
988
  }
1064
989
  function parseEnumValue(value) {
1065
- if (typeof value === "string") return stringify(value);
990
+ if (typeof value === "string") return text.quote(value);
1066
991
  return value;
1067
992
  }
1068
993
  /**
@@ -1073,11 +998,11 @@ function parseEnumValue(value) {
1073
998
  * (a `ref` to the enum shared by every branch, say) identifies no branch and narrows nothing.
1074
999
  */
1075
1000
  function getDiscriminator(member, discriminatorPropertyName) {
1076
- for (const property of ast.resolveSchemaProperties({
1001
+ for (const property of ast.schema.findProperties({
1077
1002
  node: member,
1078
1003
  propertyName: discriminatorPropertyName
1079
1004
  })) {
1080
- const values = ast.getSchemaLiteralValues(property.schema);
1005
+ const values = ast.schema.getLiterals(property.schema);
1081
1006
  if (values.length === 1) return {
1082
1007
  value: values[0],
1083
1008
  required: property.required
@@ -1094,11 +1019,14 @@ function indexedTypeName(typeName, propertyName, nestedInUnion) {
1094
1019
  const key = JSON.stringify(propertyName);
1095
1020
  return nestedInUnion ? `(NonNullable<${typeName}> & Record<${key}, unknown>)[${key}]` : `NonNullable<${typeName}>[${key}]`;
1096
1021
  }
1022
+ function itemTypeName(typeName, nestedInUnion) {
1023
+ return nestedInUnion ? `Extract<NonNullable<${typeName}>, ReadonlyArray<unknown>>[number]` : `NonNullable<${typeName}>[number]`;
1024
+ }
1097
1025
  /**
1098
1026
  * Creates a Faker printer that generates mock data generation code from schema nodes.
1099
1027
  * Handles circular references gracefully by emitting memoizing getters for cyclic properties.
1100
1028
  */
1101
- const printerFaker = ast.createPrinter((options) => {
1029
+ const printerFaker = createPrinter((options) => {
1102
1030
  const printNested = (node, overrideOptions = {}) => {
1103
1031
  return printerFaker({
1104
1032
  ...options,
@@ -1118,18 +1046,33 @@ const printerFaker = ast.createPrinter((options) => {
1118
1046
  string(node) {
1119
1047
  if (node.pattern) return matches(node.pattern, this.options.regexGenerator);
1120
1048
  if (node.format && integerFormats.has(node.format)) return node.format === "int32" ? `faker.number.int({ max: ${maxInt32} }).toString()` : "faker.number.bigInt().toString()";
1049
+ const formatCall = stringFormatCalls.get(node.format);
1050
+ if (formatCall) return formatCall;
1051
+ const contentSchema = ast.schema.getEmbeddedJson(node);
1052
+ if (contentSchema) return `JSON.stringify(${printNested(contentSchema, {
1053
+ typeName: void 0,
1054
+ nestedInObject: true,
1055
+ nestedInUnion: false
1056
+ })})`;
1057
+ const encodingCall = encodingCalls.get(node.content?.encoding?.toLowerCase());
1058
+ if (encodingCall) return encodingCall;
1121
1059
  const { min, max } = node;
1122
- if (min !== void 0 && max !== void 0) return `faker.string.alpha({ length: { min: ${min}, max: ${max} } })`;
1123
- if (max !== void 0 || min !== void 0) return `faker.string.alpha({ length: ${max ?? min} })`;
1060
+ if (max !== void 0) return `faker.string.alpha({ length: { min: ${min ?? Math.min(1, max)}, max: ${Math.min(max, (min ?? 0) + 100)} } })`;
1061
+ if (min !== void 0) return `faker.string.alpha({ length: ${min} })`;
1124
1062
  return "faker.string.alpha()";
1125
1063
  },
1126
1064
  email: () => "faker.internet.email()",
1127
1065
  url: () => "faker.internet.url()",
1128
1066
  uuid: () => "faker.string.uuid()",
1129
- number: (node) => numberCall("float", node.min, node.max),
1130
- integer: (node) => numberCall("int", node.min, node.max),
1131
- bigint: () => "faker.number.bigInt()",
1132
- blob: () => "faker.image.url() as unknown as Blob",
1067
+ ipv4: () => "faker.internet.ipv4()",
1068
+ ipv6: () => "faker.internet.ipv6()",
1069
+ number: (node) => numberCall("float", node),
1070
+ integer: (node) => numberCall("int", node.format && node.format in maxValueByFormat ? {
1071
+ ...node,
1072
+ max: Math.min(node.max ?? Infinity, maxValueByFormat[node.format])
1073
+ } : node),
1074
+ bigint: (node) => numberCall("bigInt", node),
1075
+ blob: () => "new Blob([faker.string.alpha()])",
1133
1076
  datetime(node) {
1134
1077
  const parser = this.options.dateParser ?? "faker";
1135
1078
  if (parser === "faker") return "faker.date.anytime().toISOString()";
@@ -1148,28 +1091,30 @@ const printerFaker = ast.createPrinter((options) => {
1148
1091
  }, this.options.dateParser, this.options.schemaName);
1149
1092
  },
1150
1093
  ref(node) {
1151
- const refName = ast.resolveRefName(node);
1094
+ const refName = ast.ref.getName(node);
1152
1095
  if (!refName) throw new Diagnostics.Error({
1153
1096
  code: Diagnostics.code.pluginFailed,
1154
1097
  severity: "error",
1155
1098
  message: `Ref node "${node.ref ?? node.name ?? "unknown"}"${this.options.schemaName ? ` in schema "${this.options.schemaName}"` : ""} has no name.`,
1156
1099
  plugin: "plugin-faker"
1157
1100
  });
1158
- if (this.options.schemaName && refName === this.options.schemaName) return this.options.typeName ? `undefined as unknown as ${this.options.typeName}` : "undefined as unknown";
1101
+ if (isCyclicRef(node, this.options)) return this.options.typeName ? `undefined as unknown as ${this.options.typeName}` : "undefined as unknown";
1159
1102
  const resolvedName = node.ref ? this.options.resolver.name(refName) : refName;
1160
- if (!this.options.nestedInObject && canOverrideSchema(ast.resolveRef(node))) return `${resolvedName}(data)`;
1103
+ if (!this.options.nestedInObject && canOverrideSchema(ast.ref.getSchema(node))) return `${resolvedName}(data)`;
1161
1104
  return `${resolvedName}()`;
1162
1105
  },
1163
1106
  enum(node) {
1164
1107
  const { typeName } = this.options;
1165
- const values = ast.getSchemaLiteralValues(node);
1108
+ const values = ast.schema.getLiterals(node);
1166
1109
  if (values.length === 1 && values[0] !== null && typeof values[0] === "object") return JSON.stringify(values[0]);
1167
- return `faker.helpers.arrayElement${typeName ? `<${typeName}>` : ""}([${values.map(parseEnumValue).join(", ")}])`;
1110
+ return `faker.helpers.arrayElement([${values.map(parseEnumValue).join(", ")}]${typeName ? ` as Array<${typeName}>` : ""})`;
1168
1111
  },
1169
1112
  union(node) {
1170
1113
  const { discriminatorPropertyName } = node;
1171
1114
  const baseTypeName = this.options.typeName;
1172
- return `faker.helpers.arrayElement([${mapSchemaMembers(node, (member) => {
1115
+ const isCyclicMember = (member) => isCyclicRef(member, this.options) || member.type === "intersection" && (member.members ?? []).some((part) => isCyclicRef(part, this.options));
1116
+ const acyclicMembers = (node.members ?? []).filter((member) => !isCyclicMember(member));
1117
+ return `faker.helpers.arrayElement([${((acyclicMembers.length ? acyclicMembers : node.members) ?? []).map((member) => {
1173
1118
  const discriminator = discriminatorPropertyName ? getDiscriminator(member, discriminatorPropertyName) : void 0;
1174
1119
  if (baseTypeName && discriminator) {
1175
1120
  const key = `${JSON.stringify(discriminatorPropertyName)}${discriminator.required ? "" : "?"}`;
@@ -1185,29 +1130,31 @@ const printerFaker = ast.createPrinter((options) => {
1185
1130
  nestedInObject: true,
1186
1131
  nestedInUnion: true
1187
1132
  });
1188
- }).map(({ output }) => output).filter((item) => Boolean(item)).join(", ")}])`;
1133
+ }).filter((item) => Boolean(item)).join(", ")}])`;
1189
1134
  },
1190
1135
  intersection(node) {
1191
1136
  const objectMembers = (node.members ?? []).filter((member) => isObjectSchema(member));
1192
- const members = objectMembers.length ? objectMembers : node.members;
1193
- const items = mapSchemaMembers({
1194
- ...node,
1195
- members
1196
- }, (member) => printNested(member, { nestedInObject: true })).map(({ output }) => output).filter((item) => Boolean(item) && item !== "undefined");
1137
+ const memberRank = (member) => member.type === "ref" ? 0 : member.type === "union" ? 2 : 1;
1138
+ const items = ((objectMembers.length ? objectMembers.toSorted((a, b) => memberRank(a) - memberRank(b)) : node.members) ?? []).map((member) => printNested(member, {
1139
+ nestedInObject: true,
1140
+ ...member.type === "ref" && ast.ref.getName(member) !== this.options.schemaName ? { cyclicSchemas: void 0 } : {}
1141
+ })).filter((item) => Boolean(item) && item !== "undefined");
1197
1142
  if (items.length === 0) return "{}";
1198
1143
  return !objectMembers.length || items.length === 1 ? items.at(-1) : `{...${items.join(", ...")}}`;
1199
1144
  },
1200
1145
  array(node) {
1201
- const items = mapSchemaItems(node, (member) => printNested(member, {
1202
- typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[number]` : void 0,
1146
+ if (node.items?.length === 1 && isCyclicRef(node.items[0], this.options)) return this.options.typeName ? `[] as ${this.options.typeName}` : "[]";
1147
+ const items = (node.items ?? []).map((member) => printNested(member, {
1148
+ typeName: this.options.typeName ? itemTypeName(this.options.typeName, this.options.nestedInUnion) : void 0,
1203
1149
  nestedInObject: true
1204
- })).map(({ output }) => output).filter((item) => Boolean(item));
1150
+ })).filter((item) => Boolean(item));
1205
1151
  if (items.length > 1) return `faker.helpers.arrayElements([${items.join(", ")}])`;
1206
1152
  const { min, max } = node;
1207
1153
  const item = items[0];
1208
- if (min !== void 0 && max !== void 0) return `faker.helpers.multiple(() => (${item}), { count: { min: ${min}, max: ${max} }})`;
1154
+ const countRange = max === void 0 ? void 0 : `{ min: ${min ?? 0}, max: ${Math.min(max, (min ?? 0) + 100)} }`;
1155
+ if (node.unique) return `faker.helpers.uniqueArray(() => (${item}), ${countRange ? `faker.number.int(${countRange})` : min ?? 3})`;
1156
+ if (countRange) return `faker.helpers.multiple(() => (${item}), { count: ${countRange}})`;
1209
1157
  if (min !== void 0) return `faker.helpers.multiple(() => (${item}), { count: ${min} })`;
1210
- if (max !== void 0) return `faker.helpers.multiple(() => (${item}), { count: { min: 0, max: ${max} }})`;
1211
1158
  return `faker.helpers.multiple(() => (${item}))`;
1212
1159
  },
1213
1160
  tuple(node) {
@@ -1218,18 +1165,30 @@ const printerFaker = ast.createPrinter((options) => {
1218
1165
  },
1219
1166
  object(node) {
1220
1167
  const cyclicSchemas = this.options.cyclicSchemas;
1221
- const keySchema = node.propertyNames && syncSchemaRef(node.propertyNames);
1168
+ const keySchema = node.propertyNames && ast.ref.mergeSchema(node.propertyNames);
1222
1169
  const valueSchema = node.additionalProperties;
1223
- 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({
1170
+ const recordProperties = keySchema?.type === "enum" && typeof valueSchema === "object" ? ast.schema.getLiterals(keySchema).filter((key) => typeof key === "string" && !node.properties.some((property) => property.name === key)).map((name) => ast.factory.createProperty({
1224
1171
  name,
1225
1172
  schema: valueSchema
1226
1173
  })) : [];
1227
- return buildObject([...node.properties, ...recordProperties].map((property) => {
1174
+ const properties = [...node.properties, ...recordProperties];
1175
+ const keptProperties = node.max === void 0 ? properties : properties.toSorted((a, b) => Number(b.required) - Number(a.required)).slice(0, node.max);
1176
+ const declaredNames = new Set(properties.map((property) => property.name));
1177
+ const extras = [];
1178
+ for (let index = 1; typeof valueSchema === "object" && keptProperties.length + extras.length < (node.min ?? 0); index++) {
1179
+ const name = `additionalProperty${index}`;
1180
+ if (!declaredNames.has(name)) extras.push(ast.factory.createProperty({
1181
+ name,
1182
+ schema: valueSchema
1183
+ }));
1184
+ }
1185
+ return buildObject([...keptProperties, ...extras].map((property) => {
1228
1186
  const value = printNested(property.schema, {
1229
1187
  typeName: this.options.typeName ? indexedTypeName(this.options.typeName, property.name, this.options.nestedInUnion) : void 0,
1230
- nestedInObject: true
1188
+ nestedInObject: true,
1189
+ ...property.required && property.schema.type === "ref" ? { cyclicSchemas: void 0 } : {}
1231
1190
  }) ?? "undefined";
1232
- if (cyclicSchemas && containsCircularRef(property.schema, {
1191
+ if (cyclicSchemas && ast.graph.containsCircular(property.schema, {
1233
1192
  circularSchemas: cyclicSchemas,
1234
1193
  excludeName: this.options.schemaName
1235
1194
  })) 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 }`;
@@ -1257,7 +1216,7 @@ const fakerGenerator = defineGenerator({
1257
1216
  if (!node.name) return;
1258
1217
  const tsResolver = ctx.driver.getResolver(pluginTsName);
1259
1218
  const schemaName = node.name;
1260
- const isEnumSchema = !!ast.narrowSchema(node, ast.schemaTypes.enum);
1219
+ const isEnumSchema = !!ast.schema.narrow(node, ast.schema.types.enum);
1261
1220
  const schemaTypeName = enumTypeName({
1262
1221
  resolver: tsResolver,
1263
1222
  name: schemaName,
@@ -1306,7 +1265,8 @@ const fakerGenerator = defineGenerator({
1306
1265
  node,
1307
1266
  root,
1308
1267
  output,
1309
- group: group ?? void 0
1268
+ group: group ?? void 0,
1269
+ checks: true
1310
1270
  }), fakerText);
1311
1271
  return /* @__PURE__ */ jsxs(File, {
1312
1272
  baseName: meta.file.baseName,
@@ -1380,7 +1340,7 @@ const fakerGenerator = defineGenerator({
1380
1340
  const { output, group, dateParser, regexGenerator, seed, locale, printer, typeMode } = ctx.options;
1381
1341
  const pluginTs = ctx.requirePlugin(pluginTsName);
1382
1342
  const tsResolver = ctx.driver.getResolver(pluginTsName);
1383
- const { path: pathParams, query: queryParams, header: headerParams, cookie: cookieParams } = getOperationParameters(node);
1343
+ const { path: pathParams, query: queryParams, header: headerParams, cookie: cookieParams } = ast.http.groupParameters(node);
1384
1344
  const paramGroups = [
1385
1345
  {
1386
1346
  params: pathParams,
@@ -1449,7 +1409,7 @@ const fakerGenerator = defineGenerator({
1449
1409
  description: node.requestBody?.description ?? schema.description
1450
1410
  }));
1451
1411
  const responseName = resolver.response.response(node);
1452
- const hasResponseNameCollision = new Set(node.responses.flatMap((response) => (response.content ?? []).flatMap((entry) => entry.schema ? ast.collectImportedRefNames(entry.schema).map((name) => resolver.name(name)) : []))).has(responseName);
1412
+ const hasResponseNameCollision = new Set(ast.http.responses.getImportedNames(node.responses).map((name) => resolver.name(name))).has(responseName);
1453
1413
  const localHelperNames = /* @__PURE__ */ new Set([
1454
1414
  ...paramGroups.map((group) => group.name),
1455
1415
  ...responseUnits.map((unit) => unit.name),
@@ -1480,12 +1440,13 @@ const fakerGenerator = defineGenerator({
1480
1440
  node: schema,
1481
1441
  root,
1482
1442
  output,
1483
- group: group ?? void 0
1443
+ group: group ?? void 0,
1444
+ checks: true
1484
1445
  }).filter((entry) => entry.path !== meta.file.path);
1485
1446
  }
1486
1447
  function renderEntry({ schema, name, typeName, description, keysToOmit, skipImportNames = [] }) {
1487
1448
  if (!schema) return null;
1488
- if (schema.type === ast.schemaTypes.object && keysToOmit?.length) {
1449
+ if (schema.type === ast.schema.types.object && keysToOmit?.length) {
1489
1450
  schema = {
1490
1451
  ...schema,
1491
1452
  properties: schema.properties.filter((property) => !keysToOmit?.includes(property.name))
@@ -1600,11 +1561,11 @@ const fakerGenerator = defineGenerator({
1600
1561
  });
1601
1562
  // kubb-extension.json
1602
1563
  var compatibility = {
1603
- "kubb": ">=5.16.0",
1564
+ "kubb": ">=5.18.4",
1604
1565
  "node": ">=22.22"
1605
1566
  };
1606
1567
  // package.json
1607
- var version = "5.4.0-canary.20261111T122006";
1568
+ var version = "5.4.0";
1608
1569
  // src/resolvers/resolverFaker.ts
1609
1570
  /**
1610
1571
  * Default resolver used by `@kubb/plugin-faker`. Decides the names and file
@@ -1621,9 +1582,9 @@ var version = "5.4.0-canary.20261111T122006";
1621
1582
  const resolverFaker = createResolver({
1622
1583
  pluginName: "plugin-faker",
1623
1584
  name(name) {
1624
- return this.memo(`name:${name}`, () => ensureValidVarName(camelCase(name, { prefix: "create" })));
1585
+ return this.memo(`name:${name}`, () => ensureValidVarName(casing.camel(name, { prefix: "create" })));
1625
1586
  },
1626
- file: createCasedFile((part) => camelCase(part, { prefix: "create" })),
1587
+ file: createCasedFile((part) => casing.camel(part, { prefix: "create" })),
1627
1588
  param: createOperationParamResolver(),
1628
1589
  response: createOperationResponseResolver()
1629
1590
  });
@@ -1681,7 +1642,7 @@ const pluginFaker = defineExtension({
1681
1642
  include,
1682
1643
  override,
1683
1644
  typeMode,
1684
- group: createGroupConfig(group),
1645
+ group: group ?? null,
1685
1646
  dateParser,
1686
1647
  regexGenerator,
1687
1648
  printer