@kubb/plugin-faker 5.4.0-canary.20261111T122006 → 5.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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,60 +580,13 @@ 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
586
+ /** The identifier a `dateParser` module is imported and called under: `my-date-lib` becomes `myDateLib`. */
587
+ function dateParserName(dateParser) {
588
+ return ensureValidVarName(casing.camel(dateParser));
589
+ }
739
590
  /**
740
591
  * Returns the `@faker-js/faker` named export for a locale code.
741
592
  *
@@ -790,26 +641,29 @@ function canOverrideSchema(node) {
790
641
  * Whether a schema can supply object properties when merging faker values.
791
642
  */
792
643
  function isObjectSchema(node, seen = /* @__PURE__ */ new Set()) {
793
- const resolved = ast.resolveRef(node);
644
+ const resolved = ast.ref.getSchema(node);
794
645
  if (seen.has(resolved)) return false;
795
646
  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);
647
+ if (ast.schema.narrow(resolved, ast.schema.types.object) || ast.schema.narrow(resolved, ast.schema.types.ref)) return true;
648
+ const composition = ast.schema.narrow(resolved, ast.schema.types.intersection) ?? ast.schema.narrow(resolved, ast.schema.types.union);
798
649
  if (!composition) return false;
799
- return composition.type === ast.schemaTypes.intersection && !composition.members?.length || (composition.members ?? []).some((member) => isObjectSchema(member, seen));
650
+ return composition.type === ast.schema.types.intersection && !composition.members?.length || (composition.members ?? []).some((member) => isObjectSchema(member, seen));
800
651
  }
801
652
  function shouldInlineSingleResponseSchema(schema) {
802
653
  return !NON_INLINABLE_RESPONSE_TYPES.has(schema.type);
803
654
  }
804
655
  /**
805
- * Builds a response schema as a union of all response statuses.
656
+ * Builds a response schema as a union of the success response statuses, like the `<Operation>Response` type of
657
+ * `@kubb/plugin-ts`, so the mock never is an error body. Falls back to every response when no success response has a schema.
806
658
  * Returns null if no responses are provided, or embeds single simple responses inline.
807
659
  */
808
660
  function buildResponseUnionSchema(node, resolver) {
809
- const responses = node.responses.map((response) => ({
661
+ const withSchema = node.responses.map((response) => ({
810
662
  ...response,
811
663
  content: response.content?.map((entry, _, content) => toResponseContent(entry, content))
812
664
  })).filter((response) => response.content?.[0]?.schema);
665
+ const success = withSchema.filter((response) => ast.http.status.isSuccess(response));
666
+ const responses = success.length > 0 ? success : withSchema;
813
667
  if (!responses.length) return null;
814
668
  if (responses.length === 1) {
815
669
  const schemas = responses[0].content?.flatMap((content) => content.schema ? [content.schema] : []) ?? [];
@@ -890,7 +744,7 @@ function getScalarType(node, typeName) {
890
744
  * Determines the data type, return type, and whether it uses the type name.
891
745
  */
892
746
  function resolveFakerTypeUsage(node, typeName, canOverride) {
893
- const resolvedNode = ast.resolveRef(node);
747
+ const resolvedNode = ast.ref.getSchema(node);
894
748
  const scalarType = SCALAR_TYPES.has(resolvedNode.type) ? `${getScalarType(resolvedNode, typeName)}${node.nullable || resolvedNode.nullable ? " | null" : ""}` : void 0;
895
749
  const dataType = scalarType ?? `Partial<${typeName}>`;
896
750
  const returnType = scalarType ?? (canOverride ? typeName : null);
@@ -909,8 +763,8 @@ function Faker({ node, description, name, typeName, printer, canOverride, typeMo
909
763
  imp.name
910
764
  ].join("-")));
911
765
  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;
766
+ const isObject = !!ast.schema.narrow(node, ast.schema.types.object) || node.type === ast.schema.types.intersection && isObjectSchema(node);
767
+ const isValueIntersection = node.type === ast.schema.types.intersection && !isObject;
914
768
  const isTuple = node.type === "tuple";
915
769
  const isScalar = SCALAR_TYPES.has(node.type);
916
770
  const isUnion = node.type === "union";
@@ -919,13 +773,14 @@ function Faker({ node, description, name, typeName, printer, canOverride, typeMo
919
773
  if (canOverride && node.type === "tuple") return `data && data.length === ${node.items?.length ?? 0} && !data.includes(undefined) ? data : ${fakerText}`;
920
774
  if (canOverride && isArray) return `[\n ...${fakerText},\n ...(data || []).filter((item) => item !== undefined),\n]`;
921
775
  if (canOverride && (isScalar || isUnion || isValueIntersection)) return node.nullable ? `data !== undefined ? data : ${fakerText}` : `data ?? ${fakerText}`;
776
+ if (canOverride && node.type === "ref" && node.nullable && SCALAR_TYPES.has(ast.ref.getSchema(node).type)) return `data === null ? data : ${fakerText}`;
922
777
  return fakerText;
923
778
  })();
924
779
  const { dataType, returnType: resolvedReturnType } = resolveFakerTypeUsage(node, typeName, canOverride);
925
780
  if (!useObjectOverride) {
926
781
  const paramsSignature = `${/\bdata\b/.test(fakerTextWithOverride) ? "data" : "_data"}?: ${dataType}`;
927
782
  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;
783
+ 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
784
  return /* @__PURE__ */ jsxs(Fragment, { children: [imports, /* @__PURE__ */ jsx(File.Source, {
930
785
  name,
931
786
  isExportable: true,
@@ -943,10 +798,11 @@ function Faker({ node, description, name, typeName, printer, canOverride, typeMo
943
798
  const { cyclicSchemas, schemaName } = printer.options;
944
799
  const isCyclic = !!schemaName && !!cyclicSchemas?.has(schemaName);
945
800
  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, {
801
+ const hasGetters = node.type === "object" && !!cyclicSchemas && (node.properties ?? []).some((p) => ast.graph.containsCircular(p.schema, {
947
802
  circularSchemas: cyclicSchemas,
948
803
  excludeName: schemaName
949
804
  }));
805
+ const castThroughUnknown = (node.type !== "object" || node.min !== void 0) && typeMode !== "schema";
950
806
  const declaration = `const ${keysToOmit?.length ? `{ ${keysToOmit.map((key, index) => `${objectKey(key)}: _omit${index}`).join(", ")}, ...defaultFakeData }` : "defaultFakeData"} = ${fakerText}`;
951
807
  const functionBody = hasGetters ? `${declaration}
952
808
  if (data) {
@@ -958,7 +814,7 @@ return defaultFakeData as ${returnType}` : `${declaration}
958
814
  return {
959
815
  ...defaultFakeData,
960
816
  ...(data || {}),
961
- } as ${returnType}`;
817
+ } as ${castThroughUnknown ? "unknown as " : ""}${returnType}`;
962
818
  return /* @__PURE__ */ jsxs(Fragment, { children: [imports, /* @__PURE__ */ jsx(File.Source, {
963
819
  name,
964
820
  isExportable: true,
@@ -1035,18 +891,45 @@ const integerFormats = /* @__PURE__ */ new Set([
1035
891
  "uint64"
1036
892
  ]);
1037
893
  const maxInt32 = 2147483647;
894
+ const maxValueByFormat = {
895
+ int32: maxInt32,
896
+ uint32: 4294967295
897
+ };
898
+ function isCyclicRef(node, { schemaName, cyclicSchemas }) {
899
+ if (node.type !== "ref" || !schemaName) return false;
900
+ const refName = ast.ref.getName(node);
901
+ return refName === schemaName || Boolean(refName && cyclicSchemas?.has(schemaName) && cyclicSchemas.has(refName));
902
+ }
1038
903
  /**
1039
- * `faker.number.float()` or `faker.number.int()`, with `min` and `max` passed only when set.
904
+ * `faker.number.float()`, `faker.number.int()` or `faker.number.bigInt()`, with `min` and `max` passed only when set,
905
+ * moved inside an exclusive bound, or needed so a lone bound does not contradict faker's default for the other.
1040
906
  */
1041
- function numberCall(kind, min, max) {
1042
- const args = [min !== void 0 && `min: ${min}`, max !== void 0 && `max: ${max}`].filter(Boolean).join(", ");
907
+ function numberCall(kind, { min, max, exclusive, multipleOf }) {
908
+ const isWhole = kind !== "float";
909
+ const grid = isWhole && !Number.isInteger(multipleOf) ? void 0 : multipleOf;
910
+ const { min: lower, max: upper } = ast.schema.getNumberBounds({
911
+ min,
912
+ max,
913
+ exclusive,
914
+ integer: isWhole,
915
+ step: grid
916
+ });
917
+ const span = 100 * (grid ?? 1);
918
+ const maxValue = kind === "float" && lower !== void 0 && upper === void 0 && (lower >= 1 || grid !== void 0) ? lower + span : upper;
919
+ const minValue = maxValue !== void 0 && lower === void 0 && (isWhole ? maxValue < 0 : maxValue <= 0) ? maxValue - span : lower;
920
+ const suffix = kind === "bigInt" ? "n" : "";
921
+ const args = [
922
+ minValue !== void 0 && `min: ${minValue}${suffix}`,
923
+ maxValue !== void 0 && `max: ${maxValue}${suffix}`,
924
+ grid !== void 0 && `multipleOf: ${grid}${suffix}`
925
+ ].filter(Boolean).join(", ");
1043
926
  return `faker.number.${kind}(${args ? `{ ${args} }` : ""})`;
1044
927
  }
1045
928
  /**
1046
929
  * A `date` or `time` value: an ISO substring, a `dateParser` call, or a `Date` when `representation` is `'date'`.
1047
930
  */
1048
931
  function dateTimeCall(representation, { format, substring }, parser = "faker", schemaName) {
1049
- if (representation === "string") return parser !== "faker" ? `${parser}(faker.date.anytime()).format("${format}")` : `faker.date.anytime().toISOString().substring(${substring})`;
932
+ if (representation === "string") return parser !== "faker" ? `${dateParserName(parser)}(faker.date.anytime()).format("${format}")` : `faker.date.anytime().toISOString().substring(${substring})`;
1050
933
  if (parser !== "faker") throw new Diagnostics.Error({
1051
934
  code: Diagnostics.code.invalidPluginOptions,
1052
935
  severity: "error",
@@ -1057,12 +940,58 @@ function dateTimeCall(representation, { format, substring }, parser = "faker", s
1057
940
  });
1058
941
  return "faker.date.anytime()";
1059
942
  }
943
+ const encodingCalls = /* @__PURE__ */ new Map([["base64", "btoa(faker.string.alpha())"], ["base64url", "faker.string.alphanumeric(8)"]]);
944
+ const stringFormatCalls = /* @__PURE__ */ new Map([
945
+ ["jwt", "faker.internet.jwt()"],
946
+ ["ulid", "faker.string.ulid()"],
947
+ ["iban", "faker.finance.iban()"],
948
+ ["duration", "'P1D'"],
949
+ ["hostname", "faker.internet.domainName()"]
950
+ ]);
951
+ const classEscapes = new Map([
952
+ ["d", "0-9"],
953
+ ["w", "A-Za-z0-9_"],
954
+ ...Array.from("-!@#$&()`.+,\"", (char) => [char, char])
955
+ ]);
956
+ function toFakerPattern(pattern) {
957
+ let result = "";
958
+ let inClass = false;
959
+ let classStart = -1;
960
+ for (let index = 0; index < pattern.length; index++) {
961
+ const char = pattern[index];
962
+ const next = pattern[index + 1];
963
+ if (char === "\\" && next !== void 0) {
964
+ index++;
965
+ if (inClass && next === "-") {
966
+ if (result[classStart] !== "-") result = `${result.slice(0, classStart)}-${result.slice(classStart)}`;
967
+ continue;
968
+ }
969
+ const classMembers = classEscapes.get(next);
970
+ if (classMembers === void 0) {
971
+ result += `\\${next}`;
972
+ continue;
973
+ }
974
+ result += inClass ? classMembers : `[${classMembers}]`;
975
+ continue;
976
+ }
977
+ if (char === "[" && !inClass) {
978
+ inClass = true;
979
+ result += next === "^" ? "[^" : "[";
980
+ if (next === "^") index++;
981
+ classStart = result.length;
982
+ continue;
983
+ }
984
+ if (char === "]") inClass = false;
985
+ result += char;
986
+ }
987
+ return result.replace(/\{(\d+),\}/g, (_, min) => `{${min},${Number(min) + 10}}`);
988
+ }
1060
989
  function matches(value, regexGenerator = "faker") {
1061
990
  if (regexGenerator === "randexp") return `${toRegExpString(value, "RandExp")}.gen()`;
1062
- return `faker.helpers.fromRegExp(${toRegExpString(value, null)})`;
991
+ return `faker.helpers.fromRegExp(${toRegExpString(toFakerPattern(value), null)})`;
1063
992
  }
1064
993
  function parseEnumValue(value) {
1065
- if (typeof value === "string") return stringify(value);
994
+ if (typeof value === "string") return text.quote(value);
1066
995
  return value;
1067
996
  }
1068
997
  /**
@@ -1073,11 +1002,11 @@ function parseEnumValue(value) {
1073
1002
  * (a `ref` to the enum shared by every branch, say) identifies no branch and narrows nothing.
1074
1003
  */
1075
1004
  function getDiscriminator(member, discriminatorPropertyName) {
1076
- for (const property of ast.resolveSchemaProperties({
1005
+ for (const property of ast.schema.findProperties({
1077
1006
  node: member,
1078
1007
  propertyName: discriminatorPropertyName
1079
1008
  })) {
1080
- const values = ast.getSchemaLiteralValues(property.schema);
1009
+ const values = ast.schema.getLiterals(property.schema);
1081
1010
  if (values.length === 1) return {
1082
1011
  value: values[0],
1083
1012
  required: property.required
@@ -1094,11 +1023,14 @@ function indexedTypeName(typeName, propertyName, nestedInUnion) {
1094
1023
  const key = JSON.stringify(propertyName);
1095
1024
  return nestedInUnion ? `(NonNullable<${typeName}> & Record<${key}, unknown>)[${key}]` : `NonNullable<${typeName}>[${key}]`;
1096
1025
  }
1026
+ function itemTypeName(typeName, nestedInUnion) {
1027
+ return nestedInUnion ? `Extract<NonNullable<${typeName}>, ReadonlyArray<unknown>>[number]` : `NonNullable<${typeName}>[number]`;
1028
+ }
1097
1029
  /**
1098
1030
  * Creates a Faker printer that generates mock data generation code from schema nodes.
1099
1031
  * Handles circular references gracefully by emitting memoizing getters for cyclic properties.
1100
1032
  */
1101
- const printerFaker = ast.createPrinter((options) => {
1033
+ const printerFaker = createPrinter((options) => {
1102
1034
  const printNested = (node, overrideOptions = {}) => {
1103
1035
  return printerFaker({
1104
1036
  ...options,
@@ -1118,22 +1050,38 @@ const printerFaker = ast.createPrinter((options) => {
1118
1050
  string(node) {
1119
1051
  if (node.pattern) return matches(node.pattern, this.options.regexGenerator);
1120
1052
  if (node.format && integerFormats.has(node.format)) return node.format === "int32" ? `faker.number.int({ max: ${maxInt32} }).toString()` : "faker.number.bigInt().toString()";
1053
+ const formatCall = stringFormatCalls.get(node.format);
1054
+ if (formatCall) return formatCall;
1055
+ const contentSchema = ast.schema.getEmbeddedJson(node);
1056
+ if (contentSchema) return `JSON.stringify(${printNested(contentSchema, {
1057
+ typeName: void 0,
1058
+ nestedInObject: true,
1059
+ nestedInUnion: false
1060
+ })})`;
1061
+ const encodingCall = encodingCalls.get(node.content?.encoding?.toLowerCase());
1062
+ if (encodingCall) return encodingCall;
1121
1063
  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} })`;
1064
+ if (max !== void 0) return `faker.string.alpha({ length: { min: ${min ?? Math.min(1, max)}, max: ${Math.min(max, (min ?? 0) + 100)} } })`;
1065
+ if (min !== void 0) return `faker.string.alpha({ length: ${min} })`;
1124
1066
  return "faker.string.alpha()";
1125
1067
  },
1126
1068
  email: () => "faker.internet.email()",
1127
1069
  url: () => "faker.internet.url()",
1128
1070
  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",
1071
+ ipv4: () => "faker.internet.ipv4()",
1072
+ ipv6: () => "faker.internet.ipv6()",
1073
+ number: (node) => numberCall("float", node),
1074
+ integer: (node) => numberCall("int", node.format && node.format in maxValueByFormat ? {
1075
+ ...node,
1076
+ max: Math.min(node.max ?? Infinity, maxValueByFormat[node.format])
1077
+ } : node),
1078
+ bigint: (node) => numberCall("bigInt", node),
1079
+ blob: () => "new Blob([faker.string.alpha()])",
1133
1080
  datetime(node) {
1134
1081
  const parser = this.options.dateParser ?? "faker";
1135
1082
  if (parser === "faker") return "faker.date.anytime().toISOString()";
1136
- return `${parser}(faker.date.anytime()).format("${node.local ? "YYYY-MM-DDTHH:mm:ss" : node.offset ? "YYYY-MM-DDTHH:mm:ssZ" : "YYYY-MM-DDTHH:mm:ss[Z]"}")`;
1083
+ const format = node.local ? "YYYY-MM-DDTHH:mm:ss" : node.offset ? "YYYY-MM-DDTHH:mm:ssZ" : "YYYY-MM-DDTHH:mm:ss[Z]";
1084
+ return `${dateParserName(parser)}(faker.date.anytime()).format("${format}")`;
1137
1085
  },
1138
1086
  date(node) {
1139
1087
  return dateTimeCall(node.representation ?? "string", {
@@ -1148,28 +1096,30 @@ const printerFaker = ast.createPrinter((options) => {
1148
1096
  }, this.options.dateParser, this.options.schemaName);
1149
1097
  },
1150
1098
  ref(node) {
1151
- const refName = ast.resolveRefName(node);
1099
+ const refName = ast.ref.getName(node);
1152
1100
  if (!refName) throw new Diagnostics.Error({
1153
1101
  code: Diagnostics.code.pluginFailed,
1154
1102
  severity: "error",
1155
1103
  message: `Ref node "${node.ref ?? node.name ?? "unknown"}"${this.options.schemaName ? ` in schema "${this.options.schemaName}"` : ""} has no name.`,
1156
1104
  plugin: "plugin-faker"
1157
1105
  });
1158
- if (this.options.schemaName && refName === this.options.schemaName) return this.options.typeName ? `undefined as unknown as ${this.options.typeName}` : "undefined as unknown";
1106
+ if (isCyclicRef(node, this.options)) return this.options.typeName ? `undefined as unknown as ${this.options.typeName}` : "undefined as unknown";
1159
1107
  const resolvedName = node.ref ? this.options.resolver.name(refName) : refName;
1160
- if (!this.options.nestedInObject && canOverrideSchema(ast.resolveRef(node))) return `${resolvedName}(data)`;
1108
+ if (!this.options.nestedInObject && canOverrideSchema(ast.ref.getSchema(node))) return `${resolvedName}(data)`;
1161
1109
  return `${resolvedName}()`;
1162
1110
  },
1163
1111
  enum(node) {
1164
1112
  const { typeName } = this.options;
1165
- const values = ast.getSchemaLiteralValues(node);
1113
+ const values = ast.schema.getLiterals(node);
1166
1114
  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(", ")}])`;
1115
+ return `faker.helpers.arrayElement([${values.map(parseEnumValue).join(", ")}]${typeName ? ` as Array<${typeName}>` : ""})`;
1168
1116
  },
1169
1117
  union(node) {
1170
1118
  const { discriminatorPropertyName } = node;
1171
1119
  const baseTypeName = this.options.typeName;
1172
- return `faker.helpers.arrayElement([${mapSchemaMembers(node, (member) => {
1120
+ const isCyclicMember = (member) => isCyclicRef(member, this.options) || member.type === "intersection" && (member.members ?? []).some((part) => isCyclicRef(part, this.options));
1121
+ const acyclicMembers = (node.members ?? []).filter((member) => !isCyclicMember(member));
1122
+ return `faker.helpers.arrayElement([${((acyclicMembers.length ? acyclicMembers : node.members) ?? []).map((member) => {
1173
1123
  const discriminator = discriminatorPropertyName ? getDiscriminator(member, discriminatorPropertyName) : void 0;
1174
1124
  if (baseTypeName && discriminator) {
1175
1125
  const key = `${JSON.stringify(discriminatorPropertyName)}${discriminator.required ? "" : "?"}`;
@@ -1185,29 +1135,31 @@ const printerFaker = ast.createPrinter((options) => {
1185
1135
  nestedInObject: true,
1186
1136
  nestedInUnion: true
1187
1137
  });
1188
- }).map(({ output }) => output).filter((item) => Boolean(item)).join(", ")}])`;
1138
+ }).filter((item) => Boolean(item)).join(", ")}])`;
1189
1139
  },
1190
1140
  intersection(node) {
1191
1141
  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");
1142
+ const memberRank = (member) => member.type === "ref" ? 0 : member.type === "union" ? 2 : 1;
1143
+ const items = ((objectMembers.length ? objectMembers.toSorted((a, b) => memberRank(a) - memberRank(b)) : node.members) ?? []).map((member) => printNested(member, {
1144
+ nestedInObject: true,
1145
+ ...member.type === "ref" && ast.ref.getName(member) !== this.options.schemaName ? { cyclicSchemas: void 0 } : {}
1146
+ })).filter((item) => Boolean(item) && item !== "undefined");
1197
1147
  if (items.length === 0) return "{}";
1198
1148
  return !objectMembers.length || items.length === 1 ? items.at(-1) : `{...${items.join(", ...")}}`;
1199
1149
  },
1200
1150
  array(node) {
1201
- const items = mapSchemaItems(node, (member) => printNested(member, {
1202
- typeName: this.options.typeName ? `NonNullable<${this.options.typeName}>[number]` : void 0,
1151
+ if (node.items?.length === 1 && isCyclicRef(node.items[0], this.options)) return this.options.typeName ? `[] as ${this.options.typeName}` : "[]";
1152
+ const items = (node.items ?? []).map((member) => printNested(member, {
1153
+ typeName: this.options.typeName ? itemTypeName(this.options.typeName, this.options.nestedInUnion) : void 0,
1203
1154
  nestedInObject: true
1204
- })).map(({ output }) => output).filter((item) => Boolean(item));
1155
+ })).filter((item) => Boolean(item));
1205
1156
  if (items.length > 1) return `faker.helpers.arrayElements([${items.join(", ")}])`;
1206
1157
  const { min, max } = node;
1207
1158
  const item = items[0];
1208
- if (min !== void 0 && max !== void 0) return `faker.helpers.multiple(() => (${item}), { count: { min: ${min}, max: ${max} }})`;
1159
+ const countRange = max === void 0 ? void 0 : `{ min: ${min ?? 0}, max: ${Math.min(max, (min ?? 0) + 100)} }`;
1160
+ if (node.unique) return `faker.helpers.uniqueArray(() => (${item}), ${countRange ? `faker.number.int(${countRange})` : min ?? 3})`;
1161
+ if (countRange) return `faker.helpers.multiple(() => (${item}), { count: ${countRange}})`;
1209
1162
  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
1163
  return `faker.helpers.multiple(() => (${item}))`;
1212
1164
  },
1213
1165
  tuple(node) {
@@ -1218,18 +1170,30 @@ const printerFaker = ast.createPrinter((options) => {
1218
1170
  },
1219
1171
  object(node) {
1220
1172
  const cyclicSchemas = this.options.cyclicSchemas;
1221
- const keySchema = node.propertyNames && syncSchemaRef(node.propertyNames);
1173
+ const keySchema = node.propertyNames && ast.ref.mergeSchema(node.propertyNames);
1222
1174
  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({
1175
+ 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
1176
  name,
1225
1177
  schema: valueSchema
1226
1178
  })) : [];
1227
- return buildObject([...node.properties, ...recordProperties].map((property) => {
1179
+ const properties = [...node.properties, ...recordProperties];
1180
+ const keptProperties = node.max === void 0 ? properties : properties.toSorted((a, b) => Number(b.required) - Number(a.required)).slice(0, node.max);
1181
+ const declaredNames = new Set(properties.map((property) => property.name));
1182
+ const extras = [];
1183
+ for (let index = 1; typeof valueSchema === "object" && keptProperties.length + extras.length < (node.min ?? 0); index++) {
1184
+ const name = `additionalProperty${index}`;
1185
+ if (!declaredNames.has(name)) extras.push(ast.factory.createProperty({
1186
+ name,
1187
+ schema: valueSchema
1188
+ }));
1189
+ }
1190
+ return buildObject([...keptProperties, ...extras].map((property) => {
1228
1191
  const value = printNested(property.schema, {
1229
1192
  typeName: this.options.typeName ? indexedTypeName(this.options.typeName, property.name, this.options.nestedInUnion) : void 0,
1230
- nestedInObject: true
1193
+ nestedInObject: true,
1194
+ ...property.required && property.schema.type === "ref" ? { cyclicSchemas: void 0 } : {}
1231
1195
  }) ?? "undefined";
1232
- if (cyclicSchemas && containsCircularRef(property.schema, {
1196
+ if (cyclicSchemas && ast.graph.containsCircular(property.schema, {
1233
1197
  circularSchemas: cyclicSchemas,
1234
1198
  excludeName: this.options.schemaName
1235
1199
  })) 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 +1221,7 @@ const fakerGenerator = defineGenerator({
1257
1221
  if (!node.name) return;
1258
1222
  const tsResolver = ctx.driver.getResolver(pluginTsName);
1259
1223
  const schemaName = node.name;
1260
- const isEnumSchema = !!ast.narrowSchema(node, ast.schemaTypes.enum);
1224
+ const isEnumSchema = !!ast.schema.narrow(node, ast.schema.types.enum);
1261
1225
  const schemaTypeName = enumTypeName({
1262
1226
  resolver: tsResolver,
1263
1227
  name: schemaName,
@@ -1306,7 +1270,8 @@ const fakerGenerator = defineGenerator({
1306
1270
  node,
1307
1271
  root,
1308
1272
  output,
1309
- group: group ?? void 0
1273
+ group: group ?? void 0,
1274
+ checks: true
1310
1275
  }), fakerText);
1311
1276
  return /* @__PURE__ */ jsxs(File, {
1312
1277
  baseName: meta.file.baseName,
@@ -1342,7 +1307,7 @@ const fakerGenerator = defineGenerator({
1342
1307
  }),
1343
1308
  dateParser !== "faker" && /* @__PURE__ */ jsx(File.Import, {
1344
1309
  path: dateParser,
1345
- name: dateParser
1310
+ name: dateParserName(dateParser)
1346
1311
  }),
1347
1312
  typeReference.importPath && /* @__PURE__ */ jsx(File.Import, {
1348
1313
  isTypeOnly: true,
@@ -1359,7 +1324,7 @@ const fakerGenerator = defineGenerator({
1359
1324
  imp.path,
1360
1325
  imp.name
1361
1326
  ].join("-"))),
1362
- seed ? /* @__PURE__ */ jsx(File.Source, {
1327
+ seed !== void 0 ? /* @__PURE__ */ jsx(File.Source, {
1363
1328
  name: "faker-seed",
1364
1329
  children: `faker.seed(${JSON.stringify(seed)})`
1365
1330
  }) : void 0,
@@ -1380,7 +1345,7 @@ const fakerGenerator = defineGenerator({
1380
1345
  const { output, group, dateParser, regexGenerator, seed, locale, printer, typeMode } = ctx.options;
1381
1346
  const pluginTs = ctx.requirePlugin(pluginTsName);
1382
1347
  const tsResolver = ctx.driver.getResolver(pluginTsName);
1383
- const { path: pathParams, query: queryParams, header: headerParams, cookie: cookieParams } = getOperationParameters(node);
1348
+ const { path: pathParams, query: queryParams, header: headerParams, cookie: cookieParams } = ast.http.groupParameters(node);
1384
1349
  const paramGroups = [
1385
1350
  {
1386
1351
  params: pathParams,
@@ -1449,7 +1414,7 @@ const fakerGenerator = defineGenerator({
1449
1414
  description: node.requestBody?.description ?? schema.description
1450
1415
  }));
1451
1416
  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);
1417
+ const hasResponseNameCollision = new Set(ast.http.responses.getImportedNames(node.responses).map((name) => resolver.name(name))).has(responseName);
1453
1418
  const localHelperNames = /* @__PURE__ */ new Set([
1454
1419
  ...paramGroups.map((group) => group.name),
1455
1420
  ...responseUnits.map((unit) => unit.name),
@@ -1480,12 +1445,13 @@ const fakerGenerator = defineGenerator({
1480
1445
  node: schema,
1481
1446
  root,
1482
1447
  output,
1483
- group: group ?? void 0
1448
+ group: group ?? void 0,
1449
+ checks: true
1484
1450
  }).filter((entry) => entry.path !== meta.file.path);
1485
1451
  }
1486
1452
  function renderEntry({ schema, name, typeName, description, keysToOmit, skipImportNames = [] }) {
1487
1453
  if (!schema) return null;
1488
- if (schema.type === ast.schemaTypes.object && keysToOmit?.length) {
1454
+ if (schema.type === ast.schema.types.object && keysToOmit?.length) {
1489
1455
  schema = {
1490
1456
  ...schema,
1491
1457
  properties: schema.properties.filter((property) => !keysToOmit?.includes(property.name))
@@ -1579,9 +1545,9 @@ const fakerGenerator = defineGenerator({
1579
1545
  }),
1580
1546
  dateParser !== "faker" && /* @__PURE__ */ jsx(File.Import, {
1581
1547
  path: dateParser,
1582
- name: dateParser
1548
+ name: dateParserName(dateParser)
1583
1549
  }),
1584
- seed ? /* @__PURE__ */ jsx(File.Source, {
1550
+ seed !== void 0 ? /* @__PURE__ */ jsx(File.Source, {
1585
1551
  name: "faker-seed",
1586
1552
  children: `faker.seed(${JSON.stringify(seed)})`
1587
1553
  }) : void 0,
@@ -1600,11 +1566,11 @@ const fakerGenerator = defineGenerator({
1600
1566
  });
1601
1567
  // kubb-extension.json
1602
1568
  var compatibility = {
1603
- "kubb": ">=5.16.0",
1569
+ "kubb": ">=5.18.4",
1604
1570
  "node": ">=22.22"
1605
1571
  };
1606
1572
  // package.json
1607
- var version = "5.4.0-canary.20261111T122006";
1573
+ var version = "5.4.1";
1608
1574
  // src/resolvers/resolverFaker.ts
1609
1575
  /**
1610
1576
  * Default resolver used by `@kubb/plugin-faker`. Decides the names and file
@@ -1621,9 +1587,9 @@ var version = "5.4.0-canary.20261111T122006";
1621
1587
  const resolverFaker = createResolver({
1622
1588
  pluginName: "plugin-faker",
1623
1589
  name(name) {
1624
- return this.memo(`name:${name}`, () => ensureValidVarName(camelCase(name, { prefix: "create" })));
1590
+ return this.memo(`name:${name}`, () => ensureValidVarName(casing.camel(name, { prefix: "create" })));
1625
1591
  },
1626
- file: createCasedFile((part) => camelCase(part, { prefix: "create" })),
1592
+ file: createCasedFile((part) => casing.camel(part, { prefix: "create" })),
1627
1593
  param: createOperationParamResolver(),
1628
1594
  response: createOperationResponseResolver()
1629
1595
  });
@@ -1681,7 +1647,7 @@ const pluginFaker = defineExtension({
1681
1647
  include,
1682
1648
  override,
1683
1649
  typeMode,
1684
- group: createGroupConfig(group),
1650
+ group: group ?? null,
1685
1651
  dateParser,
1686
1652
  regexGenerator,
1687
1653
  printer