@orval/mock 8.14.0 → 8.16.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.mjs CHANGED
@@ -1,5 +1,103 @@
1
- import { EnumGeneration, OutputMockType, PropertySortOrder, camel, compareVersions, escape, escapeRegExp, generalJSTypesWithArray, generateDependencyImports, getKey, getRefInfo, isBoolean, isFunction, isMswMock, isNumber, isObject, isReference, isSchema, isString, mergeDeep, pascal, resolveRef, sanitize, stringify } from "@orval/core";
1
+ import { DefaultTag, EnumGeneration, OutputMockType, OutputMode, PropertySortOrder, camel, compareVersions, escape, escapeRegExp, generalJSTypesWithArray, generateDependencyImports, getKey, getRefInfo, isBoolean, isFunction, isMswMock, isNumber, isObject, isReference, isSchema, isString, kebab, mergeDeep, pascal, resolveRef, sanitize, stringify } from "@orval/core";
2
2
  import { prop } from "remeda";
3
+ //#region src/mock-types.ts
4
+ function isStrictMock(mockOptions) {
5
+ return Boolean(mockOptions && mockOptions.required && mockOptions.nonNullable);
6
+ }
7
+ function getStrictMockTypeName(typeName) {
8
+ return `${typeName}Mock`;
9
+ }
10
+ function getStrictMockHelperTypeDeclarations() {
11
+ return `export type KeysWithNull<O> = {
12
+ [K in keyof O]-?: null extends O[K] ? K : never;
13
+ }[keyof O];
14
+
15
+ export type MockWithNullableOverrides<
16
+ T,
17
+ O extends Partial<T>,
18
+ M extends Record<keyof T, unknown>,
19
+ > = Omit<M, Extract<KeysWithNull<O>, keyof T>> & {
20
+ [K in Extract<KeysWithNull<O>, keyof T>]: M[K] | null;
21
+ };`;
22
+ }
23
+ function getStrictMockTypeDeclaration(typeName) {
24
+ return `export type ${getStrictMockTypeName(typeName)} = {\n [K in keyof Required<${typeName}>]: NonNullable<Required<${typeName}>[K]>;\n};`;
25
+ }
26
+ function getStrictMockTypeDeclarations(typeNames) {
27
+ const unique = [...new Set(typeNames)];
28
+ if (unique.length === 0) return "";
29
+ return unique.map((typeName) => getStrictMockTypeDeclaration(typeName)).join("\n\n");
30
+ }
31
+ function getMockFactoryReturnType(typeName, mockOptions) {
32
+ return isStrictMock(mockOptions) ? getStrictMockTypeName(typeName) : typeName;
33
+ }
34
+ function getMockFactorySignatureParts(typeName, mockOptions, options = {}) {
35
+ const isOverridable = options.isOverridable ?? false;
36
+ const overrideType = options.overrideType ?? `Partial<${typeName}>`;
37
+ const mockTypeName = getStrictMockTypeName(typeName);
38
+ if (!isOverridable) return {
39
+ param: "",
40
+ returnType: getMockFactoryReturnType(typeName, mockOptions),
41
+ returnCast: ""
42
+ };
43
+ if (isStrictMock(mockOptions)) return {
44
+ param: `<O extends ${overrideType} = {}>(overrideResponse?: O)`,
45
+ returnType: `MockWithNullableOverrides<${typeName}, O, ${mockTypeName}>`,
46
+ returnCast: ` as MockWithNullableOverrides<${typeName}, O, ${mockTypeName}>`
47
+ };
48
+ return {
49
+ param: `overrideResponse: ${overrideType} = {}`,
50
+ returnType: typeName,
51
+ returnCast: ""
52
+ };
53
+ }
54
+ function getSimpleSchemaReturnType(returnType, schemaTypeNames) {
55
+ const trimmed = returnType.trim();
56
+ return schemaTypeNames.includes(trimmed) ? trimmed : void 0;
57
+ }
58
+ function formatMockFactoryDeclaration(factoryName, param, returnType, body, returnCast, options) {
59
+ return `${param ? param.startsWith("<") ? `export const ${factoryName} = ${param}` : `export const ${factoryName} = (${param})` : `export const ${factoryName} = ()`}${options?.omitReturnType || !returnType ? "" : `: ${returnType}`} => (${body})${returnCast}${returnCast || options?.terminateStatement ? ";" : ""}`;
60
+ }
61
+ function getSchemaTypeNamesFromResponses(responses) {
62
+ const names = /* @__PURE__ */ new Set();
63
+ for (const response of responses) {
64
+ for (const imp of response.imports) {
65
+ if (imp.values || imp.schemaFactory) continue;
66
+ const importName = imp.alias ?? imp.name;
67
+ if (/^[A-Z]\w*$/.test(importName)) names.add(importName);
68
+ }
69
+ const { value } = response;
70
+ if (!value) continue;
71
+ const baseType = value.endsWith("[]") ? value.slice(0, -2) : value;
72
+ if (/^[A-Z]\w*$/.test(baseType)) names.add(baseType);
73
+ }
74
+ return [...names];
75
+ }
76
+ function buildStrictMockTypeFileHeader(schemaTypeNames) {
77
+ const schemaBlock = getStrictMockTypeDeclarations([...new Set(schemaTypeNames)]);
78
+ return [getStrictMockHelperTypeDeclarations(), schemaBlock].filter(Boolean).join("\n\n");
79
+ }
80
+ /**
81
+ * Prepends shared strict-mock helper types and each `{Schema}Mock` alias once at
82
+ * the top of a mock file. Generators pass `strictSchemaTypeNames`; no scraping.
83
+ *
84
+ * Not idempotent — callers must invoke this exactly once per aggregated mock
85
+ * file (writers and `writeFakerSchemaMocks`), not from import hooks.
86
+ */
87
+ function dedupeStrictMockTypeDeclarations(implementation, options = {}) {
88
+ if (!isStrictMock(options.mockOptions)) return implementation;
89
+ const schemaTypeNames = options.strictSchemaTypeNames ? [...new Set(options.strictSchemaTypeNames)] : [];
90
+ if (schemaTypeNames.length === 0) return implementation;
91
+ return `${buildStrictMockTypeFileHeader(schemaTypeNames)}\n\n${implementation.trimStart()}`;
92
+ }
93
+ function applyStrictMockReturnType(returnType, schemaTypeNames) {
94
+ if (schemaTypeNames.length === 0) return returnType;
95
+ let result = returnType;
96
+ const sorted = [...schemaTypeNames].toSorted((a, b) => b.length - a.length);
97
+ for (const name of sorted) result = result.replaceAll(new RegExp(String.raw`\b${escapeRegExp(name)}\b`, "g"), getStrictMockTypeName(name));
98
+ return result;
99
+ }
100
+ //#endregion
3
101
  //#region src/delay.ts
4
102
  const getDelay = (override, options) => {
5
103
  const mswOptions = options && isMswMock(options) ? options : void 0;
@@ -55,7 +153,7 @@ function getReferenceName$1(ref, context) {
55
153
  if (!ref) return "";
56
154
  return getRefInfo(ref, context).name;
57
155
  }
58
- function getMockObject({ item, mockOptions, operationId, tags, combine, context, imports, existingReferencedProperties, splitMockImplementations, allowOverride = false }) {
156
+ function getMockObject({ item, mockOptions, operationId, tags, combine, context, imports, existingReferencedProperties, existingReferencedAllOfRefs = [], splitMockImplementations, allowOverride = false }) {
59
157
  if (isReference(item)) return resolveMockValue({
60
158
  schema: {
61
159
  ...item,
@@ -68,6 +166,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
68
166
  context,
69
167
  imports,
70
168
  existingReferencedProperties,
169
+ existingReferencedAllOfRefs,
71
170
  splitMockImplementations
72
171
  });
73
172
  const schemaItem = item;
@@ -88,6 +187,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
88
187
  context,
89
188
  imports,
90
189
  existingReferencedProperties,
190
+ existingReferencedAllOfRefs,
91
191
  splitMockImplementations
92
192
  });
93
193
  if (Array.isArray(itemType)) {
@@ -108,6 +208,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
108
208
  context,
109
209
  imports,
110
210
  existingReferencedProperties,
211
+ existingReferencedAllOfRefs,
111
212
  splitMockImplementations
112
213
  });
113
214
  }
@@ -131,6 +232,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
131
232
  schema: {
132
233
  ...prop,
133
234
  name: key,
235
+ parentName: schemaItem.name,
134
236
  path: schemaItem.path ? `${schemaItem.path}.${key}` : `#.${key}`
135
237
  },
136
238
  mockOptions,
@@ -139,6 +241,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
139
241
  context,
140
242
  imports,
141
243
  existingReferencedProperties,
244
+ existingReferencedAllOfRefs: [],
142
245
  splitMockImplementations
143
246
  });
144
247
  imports.push(...resolvedValue.imports);
@@ -149,7 +252,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
149
252
  const omitValue = mockOptions?.nonNullable || !hasNullable ? "undefined" : "null";
150
253
  return `${keyDefinition}: faker.helpers.arrayElement([${resolvedValue.value}, ${omitValue}])`;
151
254
  }
152
- if (Array.isArray(prop.type) && prop.type.includes("null") && !resolvedValue.overrided && !mockOptions?.nonNullable) return `${keyDefinition}: faker.helpers.arrayElement([${resolvedValue.value}, null])`;
255
+ if (Array.isArray(prop.type) && prop.type.includes("null") && !resolvedValue.nullWrapped && !resolvedValue.overrided && !mockOptions?.nonNullable) return `${keyDefinition}: faker.helpers.arrayElement([${resolvedValue.value}, null])`;
153
256
  return `${keyDefinition}: ${resolvedValue.value}`;
154
257
  }).filter(Boolean);
155
258
  if (allowOverride) propertyScalars.push(`...${overrideVarName}`);
@@ -186,6 +289,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
186
289
  context,
187
290
  imports,
188
291
  existingReferencedProperties,
292
+ existingReferencedAllOfRefs: [],
189
293
  splitMockImplementations
190
294
  });
191
295
  return {
@@ -202,8 +306,145 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
202
306
  };
203
307
  }
204
308
  //#endregion
309
+ //#region src/faker/getters/array-item-factory.ts
310
+ /**
311
+ * Scope key for file-level array-item factory dedup. Must match how writers
312
+ * group mock output: one bucket per tag file in tags modes, otherwise one
313
+ * bucket for the whole target.
314
+ */
315
+ function getArrayItemMockFileScope(context, tags) {
316
+ const mode = context.output.mode;
317
+ if (mode === OutputMode.TAGS || mode === OutputMode.TAGS_SPLIT) return `tag:${kebab(tags.length > 0 ? tags[0] : DefaultTag)}`;
318
+ if (mode === OutputMode.SPLIT) return "split";
319
+ return "single";
320
+ }
321
+ function getFileLevelExtractedFactories(context, scope) {
322
+ context.arrayItemMockFactories ??= /* @__PURE__ */ new Map();
323
+ const existing = context.arrayItemMockFactories.get(scope);
324
+ if (existing) return existing;
325
+ const factories = /* @__PURE__ */ new Set();
326
+ context.arrayItemMockFactories.set(scope, factories);
327
+ return factories;
328
+ }
329
+ /**
330
+ * True when the active faker generator entry opts into reusable array-item
331
+ * mock factories for object-like array item schemas in operation responses.
332
+ */
333
+ function shouldExtractArrayItemFactories(context) {
334
+ return !!context.output.mock.generators.find((g) => !isFunction(g) && g.type === OutputMockType.FAKER && g.arrayItems === true);
335
+ }
336
+ /**
337
+ * True when `schemas: true` already emits a consolidated factory for this
338
+ * `$ref` item under `components/schemas`, so we must not re-export it from
339
+ * the operation mock file.
340
+ */
341
+ function hasConsolidatedSchemaFactory(items, context) {
342
+ if (!context.output.schemas) return false;
343
+ const itemsRef = extractItemsRef(items);
344
+ if (!itemsRef) return false;
345
+ const { refPaths } = getRefInfo(itemsRef, context);
346
+ if (!(Array.isArray(refPaths) && refPaths[0] === "components" && refPaths[1] === "schemas")) return false;
347
+ return context.output.mock.generators.some((g) => !isFunction(g) && g.type === OutputMockType.FAKER && g.schemas === true);
348
+ }
349
+ /**
350
+ * True when `parentName` looks like a nested property key rather than the
351
+ * generated response wrapper type (e.g. `outer` vs `GetTenants200`). Inlining
352
+ * avoids factory/type-name collisions and mismatched `<Parent><Prop>Item` aliases.
353
+ */
354
+ function isAmbiguousInlineItemContext(operationId, parentName) {
355
+ if (!parentName) return false;
356
+ return !parentName.toLowerCase().includes(operationId.toLowerCase());
357
+ }
358
+ function isNullableArrayItem(schema) {
359
+ if (schema.nullable === true) return true;
360
+ return Array.isArray(schema.type) && schema.type.includes("null");
361
+ }
362
+ function isResolvedSchemaObjectLike(schema) {
363
+ if (schema.type === "object" || schema.properties) return true;
364
+ if (schema.allOf) return true;
365
+ return false;
366
+ }
367
+ /**
368
+ * True when array `items` resolve to an object-like schema worth extracting.
369
+ * Conservative: skips scalar refs, oneOf/anyOf, nullable items, and nested
370
+ * contexts where generated item type names cannot be inferred reliably.
371
+ */
372
+ function shouldExtractArrayItem(items, context, operationId, parentName) {
373
+ const itemsRef = extractItemsRef(items);
374
+ if (itemsRef) try {
375
+ const { schema } = resolveRef({ $ref: itemsRef }, context);
376
+ return isResolvedSchemaObjectLike(schema);
377
+ } catch {
378
+ return false;
379
+ }
380
+ if (isReference(items)) return false;
381
+ const schema = items;
382
+ if (isNullableArrayItem(schema)) return false;
383
+ if (schema.oneOf || schema.anyOf) return false;
384
+ if (schema.allOf) return true;
385
+ if (schema.type === "object" || schema.properties) return !isAmbiguousInlineItemContext(operationId, parentName);
386
+ return false;
387
+ }
388
+ /**
389
+ * True when `mapValue` is already a bare factory call or a single spread of one.
390
+ */
391
+ function isAlreadyFactoryCall(mapValue) {
392
+ return /^(?:\{\s*\.\.\.\s*get\w+Mock\(\)\s*\}|get\w+Mock\(\))$/.test(mapValue.trim());
393
+ }
394
+ /**
395
+ * Derive the exported factory and TypeScript type names for an array item.
396
+ */
397
+ function getArrayItemFactoryNames({ items, propertyName, parentName, operationId, context }) {
398
+ if (!shouldExtractArrayItem(items, context, operationId, parentName)) return;
399
+ const itemsRef = extractItemsRef(items);
400
+ if (itemsRef) {
401
+ const { name } = getRefInfo(itemsRef, context);
402
+ const typeName = pascal(name);
403
+ return {
404
+ factoryName: `get${typeName}Mock`,
405
+ typeName
406
+ };
407
+ }
408
+ const itemSuffix = context.output.override.components.schemas.itemSuffix;
409
+ const typeName = parentName ? `${pascal(parentName)}${pascal(propertyName)}${itemSuffix}` : `${pascal(operationId)}${pascal(propertyName)}${itemSuffix}`;
410
+ return {
411
+ factoryName: `get${pascal(operationId)}Response${pascal(propertyName)}ItemMock`,
412
+ typeName
413
+ };
414
+ }
415
+ /**
416
+ * When `arrayItems: true`, lift an object-like array item mock body into a
417
+ * reusable exported factory and return the call site expression for `.map()`.
418
+ */
419
+ function extractArrayItemMock({ items, propertyName, parentName, operationId, tags, mapValue, context, splitMockImplementations, imports }) {
420
+ if (!shouldExtractArrayItemFactories(context)) return;
421
+ if (!mapValue || mapValue === "[]" || isAlreadyFactoryCall(mapValue) || hasConsolidatedSchemaFactory(items, context)) return;
422
+ const names = getArrayItemFactoryNames({
423
+ items,
424
+ propertyName,
425
+ parentName,
426
+ operationId,
427
+ context
428
+ });
429
+ if (!names) return;
430
+ const { factoryName, typeName } = names;
431
+ const fileLevelFactories = getFileLevelExtractedFactories(context, getArrayItemMockFileScope(context, tags));
432
+ if (!(fileLevelFactories.has(factoryName) || splitMockImplementations.some((f) => f.includes(`export const ${factoryName}`)))) {
433
+ const mockOptions = context.output.override.mock;
434
+ const { param, returnType, returnCast } = getMockFactorySignatureParts(typeName, mockOptions, {
435
+ isOverridable: true,
436
+ overrideType: `Partial<${typeName}>`
437
+ });
438
+ const func = formatMockFactoryDeclaration(factoryName, param, returnType, `{${mapValue.startsWith("...") ? "" : "..."}${mapValue}, ...${overrideVarName}}`, returnCast, { terminateStatement: true });
439
+ splitMockImplementations.push(func);
440
+ fileLevelFactories.add(factoryName);
441
+ }
442
+ imports.push({ name: typeName });
443
+ return `{...${factoryName}()}`;
444
+ }
445
+ //#endregion
205
446
  //#region src/faker/getters/scalar.ts
206
- function getMockScalar({ item, imports, mockOptions, operationId, tags, combine, context, existingReferencedProperties, splitMockImplementations, allowOverride = false }) {
447
+ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine, context, existingReferencedProperties, existingReferencedAllOfRefs = [], splitMockImplementations, allowOverride = false }) {
207
448
  const safeMockOptions = mockOptions ?? {};
208
449
  const nonNullableOption = safeMockOptions.nonNullable;
209
450
  if (item.isRef) existingReferencedProperties = [...existingReferencedProperties, item.name];
@@ -234,12 +475,14 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
234
475
  ...Object.fromEntries(Object.entries(formatOverrides).filter((entry) => typeof entry[1] === "string"))
235
476
  };
236
477
  const isNullable = Array.isArray(item.type) && item.type.includes("null");
478
+ const nullWrapped = isNullable && !nonNullableOption;
237
479
  const schemaContentMediaType = item.contentMediaType;
238
480
  if (!item.format && schemaContentMediaType === "application/octet-stream" && ALL_FORMAT.binary) return {
239
481
  value: getNullable(ALL_FORMAT.binary, isNullable, nonNullableOption),
240
482
  imports: [],
241
483
  name: item.name,
242
- overrided: false
484
+ overrided: false,
485
+ nullWrapped
243
486
  };
244
487
  if (item.format && ALL_FORMAT[item.format]) {
245
488
  let value = ALL_FORMAT[item.format];
@@ -248,7 +491,8 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
248
491
  value: getNullable(value, isNullable, nonNullableOption),
249
492
  imports: [],
250
493
  name: item.name,
251
- overrided: false
494
+ overrided: false,
495
+ nullWrapped
252
496
  };
253
497
  }
254
498
  const type = getItemType(item);
@@ -279,7 +523,8 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
279
523
  value,
280
524
  enums: item.enum,
281
525
  imports: numberImports,
282
- name: item.name
526
+ name: item.name,
527
+ nullWrapped: nullWrapped && !item.enum && !("const" in item)
283
528
  };
284
529
  }
285
530
  case "boolean": {
@@ -306,10 +551,12 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
306
551
  imports: [],
307
552
  name: item.name
308
553
  };
554
+ const resolvedItems = itemsRef && !("$ref" in item.items) ? { $ref: itemsRef } : item.items;
309
555
  const { value, enums, imports: resolvedImports } = resolveMockValue({
310
556
  schema: {
311
- ...itemsRef && !("$ref" in item.items) ? { $ref: itemsRef } : item.items,
557
+ ...resolvedItems,
312
558
  name: item.name,
559
+ parentName: item.parentName,
313
560
  path: item.path ? `${item.path}.[]` : "#.[]"
314
561
  },
315
562
  combine,
@@ -319,6 +566,7 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
319
566
  context,
320
567
  imports,
321
568
  existingReferencedProperties,
569
+ existingReferencedAllOfRefs,
322
570
  splitMockImplementations
323
571
  });
324
572
  if (enums) return {
@@ -327,6 +575,18 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
327
575
  name: item.name
328
576
  };
329
577
  let mapValue = value;
578
+ const extractedItemCall = extractArrayItemMock({
579
+ items: resolvedItems,
580
+ propertyName: item.name,
581
+ parentName: item.parentName,
582
+ operationId,
583
+ tags,
584
+ mapValue,
585
+ context,
586
+ splitMockImplementations,
587
+ imports: resolvedImports
588
+ });
589
+ if (extractedItemCall) mapValue = extractedItemCall;
330
590
  if (combine && !value.startsWith("faker") && !value.startsWith("{") && !value.startsWith("Array.from")) mapValue = `{${value}}`;
331
591
  const arrSchemaMin = item.minItems;
332
592
  const arrSchemaMax = item.maxItems;
@@ -377,7 +637,8 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
377
637
  value: getNullable(value, isNullable, nonNullableOption),
378
638
  enums: item.enum,
379
639
  name: item.name,
380
- imports: stringImports
640
+ imports: stringImports,
641
+ nullWrapped
381
642
  };
382
643
  }
383
644
  case "null": return {
@@ -407,6 +668,7 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
407
668
  context,
408
669
  imports,
409
670
  existingReferencedProperties,
671
+ existingReferencedAllOfRefs,
410
672
  splitMockImplementations,
411
673
  allowOverride
412
674
  });
@@ -470,7 +732,8 @@ function stripArrayMarkerSegments(s) {
470
732
  function resolveMockOverride(properties = {}, item, nonNullableOption) {
471
733
  const path = item.path ?? `#.${item.name}`;
472
734
  const normalizedPath = stripArrayMarkerSegments(path);
473
- const property = Object.entries(properties).find(([key]) => {
735
+ const entries = Object.entries(properties);
736
+ let property = entries.find(([key]) => {
474
737
  if (isRegex(key)) {
475
738
  const regex = new RegExp(key.slice(1, -1));
476
739
  if (regex.test(item.name) || regex.test(path)) return true;
@@ -478,6 +741,7 @@ function resolveMockOverride(properties = {}, item, nonNullableOption) {
478
741
  if (`#.${stripArrayMarkerSegments(key)}` === normalizedPath) return true;
479
742
  return false;
480
743
  });
744
+ if (!property) property = entries.find(([key]) => !isRegex(key) && !key.includes(".") && key === item.name);
481
745
  if (!property) return;
482
746
  return {
483
747
  value: getNullable(property[1], isNullableSchema(item), nonNullableOption),
@@ -544,7 +808,7 @@ function hasOverrideTouchingSchema(schemaProperties, mockOptions, operationId, t
544
808
  });
545
809
  });
546
810
  }
547
- function resolveMockValue({ schema, mockOptions, operationId, tags, combine, context, imports, existingReferencedProperties, splitMockImplementations, allowOverride }) {
811
+ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, context, imports, existingReferencedProperties, existingReferencedAllOfRefs = [], splitMockImplementations, allowOverride }) {
548
812
  if (isReference(schema)) {
549
813
  const schemaReference = schema;
550
814
  const { name, refPaths } = getRefInfo(typeof schema.$ref === "string" ? schema.$ref : "", context);
@@ -586,10 +850,11 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
586
850
  schemaFactory: true
587
851
  });
588
852
  return {
589
- value: getNullable(newSchema.type === "object" || !!newSchema.allOf || !!newSchema.oneOf || !!newSchema.anyOf ? `{ ...${factoryName}() }` : `${factoryName}()`, Boolean(newSchema.nullable), mockOptions?.nonNullable),
853
+ value: getNullable(newSchema.type === "object" || !!newSchema.allOf || resolvesToObjectLike(newSchema, context) ? `{ ...${factoryName}() }` : `${factoryName}()`, Boolean(newSchema.nullable), mockOptions?.nonNullable),
590
854
  imports,
591
855
  name: newSchema.name,
592
- type: getType(newSchema)
856
+ type: getType(newSchema),
857
+ nullWrapped: Boolean(newSchema.nullable) && !mockOptions?.nonNullable
593
858
  };
594
859
  }
595
860
  const scalar = getMockScalar({
@@ -604,6 +869,7 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
604
869
  context,
605
870
  imports,
606
871
  existingReferencedProperties,
872
+ existingReferencedAllOfRefs,
607
873
  splitMockImplementations,
608
874
  allowOverride
609
875
  });
@@ -611,9 +877,13 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
611
877
  const funcName = `get${pascal(operationId)}Response${pascal(newSchema.name)}Mock`;
612
878
  if (!splitMockImplementations.some((f) => f.includes(`export const ${funcName}`))) {
613
879
  const discriminatedProperty = newSchema.discriminator?.propertyName;
614
- let type = `Partial<${newSchema.name}>`;
615
- if (discriminatedProperty) type = `Omit<${type}, '${discriminatedProperty}'>`;
616
- const func = `export const ${funcName} = (${`${overrideVarName}: ${type} = {}`}): ${newSchema.name} => ({${scalar.value.startsWith("...") ? "" : "..."}${scalar.value}, ...${overrideVarName}});`;
880
+ let overrideType = `Partial<${newSchema.name}>`;
881
+ if (discriminatedProperty) overrideType = `Omit<${overrideType}, '${discriminatedProperty}'>`;
882
+ const { param, returnType, returnCast } = getMockFactorySignatureParts(newSchema.name, mockOptions, {
883
+ isOverridable: true,
884
+ overrideType
885
+ });
886
+ const func = formatMockFactoryDeclaration(funcName, param, returnType, `{${scalar.value.startsWith("...") ? "" : "..."}${scalar.value}, ...${overrideVarName}}`, returnCast, { terminateStatement: true });
617
887
  splitMockImplementations.push(func);
618
888
  }
619
889
  scalar.value = newSchema.nullable ? `${funcName}()` : `{...${funcName}()}`;
@@ -634,6 +904,7 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
634
904
  context,
635
905
  imports,
636
906
  existingReferencedProperties,
907
+ existingReferencedAllOfRefs,
637
908
  splitMockImplementations,
638
909
  allowOverride
639
910
  }),
@@ -644,13 +915,27 @@ function getType(schema) {
644
915
  if (isReference(schema)) return;
645
916
  return schema.type ?? (schema.properties ? "object" : schema.items ? "array" : void 0);
646
917
  }
918
+ function resolvesToObjectLike(schema, context, seen = /* @__PURE__ */ new Set()) {
919
+ let resolved;
920
+ if (isReference(schema)) {
921
+ if (typeof schema.$ref !== "string" || seen.has(schema.$ref)) return false;
922
+ seen = new Set(seen).add(schema.$ref);
923
+ const { refPaths } = getRefInfo(schema.$ref, context);
924
+ resolved = Array.isArray(refPaths) ? prop(context.spec, ...refPaths) : void 0;
925
+ } else resolved = schema;
926
+ if (!resolved) return false;
927
+ if (resolved.type === "object" || resolved.properties || resolved.additionalProperties || resolved.allOf) return true;
928
+ const branches = resolved.oneOf ?? resolved.anyOf;
929
+ if (branches && branches.length > 0) return branches.every((branch) => resolvesToObjectLike(branch, context, seen));
930
+ return false;
931
+ }
647
932
  //#endregion
648
933
  //#region src/faker/getters/combine.ts
649
934
  function getReferenceName(ref, context) {
650
935
  if (!ref) return "";
651
936
  return getRefInfo(ref, context).name;
652
937
  }
653
- function combineSchemasMock({ item, separator, mockOptions, operationId, tags, combine, context, imports, existingReferencedProperties, splitMockImplementations }) {
938
+ function combineSchemasMock({ item, separator, mockOptions, operationId, tags, combine, context, imports, existingReferencedProperties, existingReferencedAllOfRefs = [], splitMockImplementations }) {
654
939
  const combineImports = [];
655
940
  const includedProperties = [...combine?.includedProperties ?? []];
656
941
  const separatorItems = item[separator] ?? [];
@@ -687,6 +972,7 @@ function combineSchemasMock({ item, separator, mockOptions, operationId, tags, c
687
972
  context,
688
973
  imports,
689
974
  existingReferencedProperties,
975
+ existingReferencedAllOfRefs,
690
976
  splitMockImplementations
691
977
  }) : void 0;
692
978
  includedProperties.push(...itemResolvedValue?.includedProperties ?? []);
@@ -700,7 +986,7 @@ function combineSchemasMock({ item, separator, mockOptions, operationId, tags, c
700
986
  let value = separator === "allOf" ? "" : "faker.helpers.arrayElement([";
701
987
  for (const val of separatorItems) {
702
988
  const refName = isReference(val) ? getReferenceName(val.$ref, context) : "";
703
- if (separator === "allOf" ? refName && (refName === item.name || existingReferencedProperties.includes(refName) && !item.isRef) : refName && existingReferencedProperties.includes(refName)) {
989
+ if (separator === "allOf" ? refName && (refName === item.name || existingReferencedProperties.includes(refName) && !item.isRef || existingReferencedAllOfRefs.includes(refName)) : refName && existingReferencedProperties.includes(refName)) {
704
990
  if (separatorItems.length === 1) value = "undefined";
705
991
  continue;
706
992
  }
@@ -730,6 +1016,7 @@ function combineSchemasMock({ item, separator, mockOptions, operationId, tags, c
730
1016
  context,
731
1017
  imports,
732
1018
  existingReferencedProperties,
1019
+ existingReferencedAllOfRefs: separator === "allOf" && refName ? [...existingReferencedAllOfRefs, refName] : [],
733
1020
  splitMockImplementations
734
1021
  });
735
1022
  combineImports.push(...resolvedValue.imports);
@@ -825,6 +1112,7 @@ function getMockWithoutFunc(spec, override) {
825
1112
  numberMin: override?.mock?.numberMin,
826
1113
  numberMax: override?.mock?.numberMax,
827
1114
  required: override?.mock?.required,
1115
+ nonNullable: override?.mock?.nonNullable,
828
1116
  fractionDigits: override?.mock?.fractionDigits,
829
1117
  ...override?.mock?.properties ? { properties: getMockPropertiesWithoutFunc(override.mock.properties, spec) } : {},
830
1118
  ...override?.mock?.format ? { format: getMockPropertiesWithoutFunc(override.mock.format, spec) } : {},
@@ -1023,7 +1311,28 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
1023
1311
  const overrideResponseType = `Partial<Extract<${nonVoidMockReturnType}, object>>`;
1024
1312
  const shouldPreferJsonResponse = hasJsonContentType && !hasStringReturnType;
1025
1313
  const needsRuntimeContentTypeSwitch = isTextResponse && hasJsonContentType && hasStringReturnType && mockReturnType !== "string";
1026
- const mockImplementation = isReturnHttpResponse ? `${mockImplementations}export const ${getResponseMockFunctionName} = (${isResponseOverridable ? `overrideResponse: ${overrideResponseType} = {}` : ""})${mockData ? "" : `: ${nonVoidMockReturnType}`} => (${value})\n\n` : mockImplementations;
1314
+ const mockOptionsFromOverride = override.mock;
1315
+ const strictMock = isStrictMock(mockOptionsFromOverride);
1316
+ const schemaTypeNames = strictMock ? getSchemaTypeNamesFromResponses(responses) : [];
1317
+ const strictMockReturnType = strictMock ? applyStrictMockReturnType(nonVoidMockReturnType, schemaTypeNames) : nonVoidMockReturnType;
1318
+ const simpleSchemaReturnType = strictMock ? getSimpleSchemaReturnType(nonVoidMockReturnType, schemaTypeNames) : void 0;
1319
+ let mockFactoryParam = "";
1320
+ let mockFactoryReturnType = nonVoidMockReturnType;
1321
+ let mockFactoryReturnCast = "";
1322
+ if (isResponseOverridable) if (strictMock && simpleSchemaReturnType) {
1323
+ const signature = getMockFactorySignatureParts(simpleSchemaReturnType, mockOptionsFromOverride, {
1324
+ isOverridable: true,
1325
+ overrideType: overrideResponseType
1326
+ });
1327
+ mockFactoryParam = signature.param;
1328
+ mockFactoryReturnType = signature.returnType;
1329
+ mockFactoryReturnCast = signature.returnCast;
1330
+ } else {
1331
+ mockFactoryParam = `overrideResponse: ${overrideResponseType} = {}`;
1332
+ mockFactoryReturnType = strictMock ? strictMockReturnType : nonVoidMockReturnType;
1333
+ }
1334
+ else if (strictMock) mockFactoryReturnType = strictMockReturnType;
1335
+ const mockImplementation = isReturnHttpResponse ? `${mockImplementations}${formatMockFactoryDeclaration(getResponseMockFunctionName, mockFactoryParam, mockFactoryReturnType, value, mockFactoryReturnCast, { omitReturnType: Boolean(mockData) })}\n\n` : mockImplementations;
1027
1336
  const delay = getDelay(override, isFunction(mock) ? void 0 : mock);
1028
1337
  const infoParam = "info";
1029
1338
  const resolvedResponseExpr = `overrideResponse !== undefined
@@ -1093,35 +1402,40 @@ export const ${handlerName} = (overrideResponse?: ${mockReturnType} | ((${infoPa
1093
1402
  handlerName,
1094
1403
  handler: handlerImplementation
1095
1404
  },
1096
- imports: includeResponseImports
1405
+ imports: includeResponseImports,
1406
+ strictMockSchemaTypeNames: strictMock && schemaTypeNames.length > 0 ? schemaTypeNames : void 0
1097
1407
  };
1098
1408
  }
1099
1409
  function generateMSW(generatorVerbOptions, generatorOptions) {
1100
1410
  const { pathRoute, override, mock } = generatorOptions;
1101
- const { operationId, response } = generatorVerbOptions;
1411
+ const { operationName, response } = generatorVerbOptions;
1102
1412
  const overrideBaseUrl = override.mock && "baseUrl" in override.mock ? override.mock.baseUrl : void 0;
1103
1413
  const mockBaseUrl = mock && isMswMock(mock) ? mock.baseUrl : void 0;
1104
1414
  const route = getRouteMSW(pathRoute, overrideBaseUrl ?? mockBaseUrl);
1105
- const handlerName = `get${pascal(operationId)}MockHandler`;
1106
- const getResponseMockFunctionName = `get${pascal(operationId)}ResponseMock`;
1415
+ const handlerName = `get${pascal(operationName)}MockHandler`;
1416
+ const getResponseMockFunctionName = `get${pascal(operationName)}ResponseMock`;
1107
1417
  const splitMockImplementations = [];
1108
1418
  const baseDefinition = generateDefinition("", route, getResponseMockFunctionName, handlerName, generatorVerbOptions, generatorOptions, response.definition.success, response.types.success[0]?.key ?? "200", response.imports, response.types.success, response.contentTypes, splitMockImplementations);
1109
1419
  const mockImplementations = [baseDefinition.implementation.function];
1110
1420
  const handlerImplementations = [baseDefinition.implementation.handler];
1111
1421
  const imports = [...baseDefinition.imports];
1422
+ const strictMockSchemaTypeNames = new Set(baseDefinition.strictMockSchemaTypeNames);
1112
1423
  if (generatorOptions.mock && isObject(generatorOptions.mock) && generatorOptions.mock.generateEachHttpStatus) for (const statusResponse of [...response.types.success, ...response.types.errors]) {
1113
1424
  const definition = generateDefinition(statusResponse.key, route, getResponseMockFunctionName, handlerName, generatorVerbOptions, generatorOptions, statusResponse.value, statusResponse.key, response.imports, [statusResponse], [statusResponse.contentType], splitMockImplementations);
1114
1425
  mockImplementations.push(definition.implementation.function);
1115
1426
  handlerImplementations.push(definition.implementation.handler);
1116
1427
  imports.push(...definition.imports);
1428
+ for (const name of definition.strictMockSchemaTypeNames ?? []) strictMockSchemaTypeNames.add(name);
1117
1429
  }
1430
+ const aggregatedStrictNames = [...strictMockSchemaTypeNames];
1118
1431
  return {
1119
1432
  implementation: {
1120
1433
  function: mockImplementations.join("\n"),
1121
1434
  handlerName,
1122
1435
  handler: handlerImplementations.join("\n")
1123
1436
  },
1124
- imports
1437
+ imports,
1438
+ strictMockSchemaTypeNames: aggregatedStrictNames.length > 0 ? aggregatedStrictNames : void 0
1125
1439
  };
1126
1440
  }
1127
1441
  //#endregion
@@ -1158,7 +1472,8 @@ function generateFaker(generatorVerbOptions, generatorOptions) {
1158
1472
  handler: "",
1159
1473
  handlerName: ""
1160
1474
  },
1161
- imports: result.imports
1475
+ imports: result.imports,
1476
+ strictMockSchemaTypeNames: result.strictMockSchemaTypeNames
1162
1477
  };
1163
1478
  }
1164
1479
  /**
@@ -1172,6 +1487,7 @@ function generateFaker(generatorVerbOptions, generatorOptions) {
1172
1487
  */
1173
1488
  function generateFakerForSchemas(schemas, context, options) {
1174
1489
  const factories = [];
1490
+ const strictMockTypeNames = /* @__PURE__ */ new Set();
1175
1491
  const allImports = [];
1176
1492
  const splitMockImplementations = [];
1177
1493
  const localFactoryNames = new Set(schemas.filter((s) => !!s.schema).map((s) => `get${pascal(s.name)}Mock`));
@@ -1198,7 +1514,12 @@ function generateFakerForSchemas(schemas, context, options) {
1198
1514
  });
1199
1515
  allImports.push(...result.imports, ...factoryImports);
1200
1516
  const typeName = pascal(name);
1201
- const factory = `export const ${factoryName} = (${result.value.includes("overrideResponse") ? `overrideResponse: Partial<${typeName}> = {}` : ""}): ${typeName} => (${result.value});\n`;
1517
+ const { param, returnType, returnCast } = getMockFactorySignatureParts(typeName, mockOptions, {
1518
+ isOverridable: result.value.includes("overrideResponse"),
1519
+ overrideType: `Partial<${typeName}>`
1520
+ });
1521
+ const factory = formatMockFactoryDeclaration(factoryName, param, returnType, result.value, returnCast);
1522
+ if (isStrictMock(mockOptions)) strictMockTypeNames.add(typeName);
1202
1523
  factories.push(factory);
1203
1524
  allImports.push({
1204
1525
  name: pascal(name),
@@ -1217,9 +1538,12 @@ function generateFakerForSchemas(schemas, context, options) {
1217
1538
  if (!existing.values && imp.values) mergedImports.set(key, imp);
1218
1539
  }
1219
1540
  const uniqueImports = [...mergedImports.values()];
1541
+ const implementation = [...splitMockImplementations, ...factories].filter(Boolean).join("\n\n");
1542
+ const aggregatedStrictNames = [...strictMockTypeNames];
1220
1543
  return {
1221
- implementation: [...splitMockImplementations, ...factories].join("\n"),
1222
- imports: uniqueImports
1544
+ implementation,
1545
+ imports: uniqueImports,
1546
+ strictMockSchemaTypeNames: aggregatedStrictNames.length > 0 ? aggregatedStrictNames : void 0
1223
1547
  };
1224
1548
  }
1225
1549
  //#endregion
@@ -1267,6 +1591,6 @@ function generateMock(generatorVerbOptions, generatorOptions) {
1267
1591
  }
1268
1592
  }
1269
1593
  //#endregion
1270
- export { DEFAULT_FAKER_OPTIONS, DEFAULT_MSW_OPTIONS, generateFaker, generateFakerForSchemas, generateFakerImports, generateMSW, generateMSWImports, generateMock, generateMockImports, getDefaultMockOptionsForType };
1594
+ export { DEFAULT_FAKER_OPTIONS, DEFAULT_MSW_OPTIONS, buildStrictMockTypeFileHeader, dedupeStrictMockTypeDeclarations, generateFaker, generateFakerForSchemas, generateFakerImports, generateMSW, generateMSWImports, generateMock, generateMockImports, getDefaultMockOptionsForType };
1271
1595
 
1272
1596
  //# sourceMappingURL=index.mjs.map