@orval/mock 8.33.0 → 8.35.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.d.mts CHANGED
@@ -5,14 +5,14 @@ import { ClientMockGeneratorBuilder, ContextSpec, FakerMockOptions, FinalizeMock
5
5
  * imports from `msw`, so this only emits `import { faker } from '@faker-js/faker'`
6
6
  * (or the locale-scoped variant) plus any operation-specific imports.
7
7
  */
8
- declare const generateFakerImports: GenerateMockImports;
8
+ export declare const generateFakerImports: GenerateMockImports;
9
9
  /**
10
10
  * Generates the faker-only mock output for a single operation. This reuses
11
11
  * the response-factory portion of {@link generateMSW} and strips out the
12
12
  * handler and aggregator entries so callers can write a standalone
13
13
  * `<file>.faker.ts` with no `msw` dependency.
14
14
  */
15
- declare function generateFaker(generatorVerbOptions: GeneratorVerbOptions, generatorOptions: GeneratorOptions): ClientMockGeneratorBuilder;
15
+ export declare function generateFaker(generatorVerbOptions: GeneratorVerbOptions, generatorOptions: GeneratorOptions): ClientMockGeneratorBuilder;
16
16
  interface GenerateFakerForSchemasResult {
17
17
  implementation: string;
18
18
  imports: GeneratorImport[];
@@ -28,10 +28,10 @@ interface GenerateFakerForSchemasResult {
28
28
  * Returns the function bodies plus any `GeneratorImport` references the
29
29
  * factories need so the writer can hoist them into the file header.
30
30
  */
31
- declare function generateFakerForSchemas(schemas: GeneratorSchema[], context: ContextSpec, options: GlobalMockOptions): GenerateFakerForSchemasResult;
31
+ export declare function generateFakerForSchemas(schemas: GeneratorSchema[], context: ContextSpec, options: GlobalMockOptions): GenerateFakerForSchemasResult;
32
32
  //#endregion
33
33
  //#region src/mock-types.d.ts
34
- declare function buildStrictMockTypeFileHeader(schemaTypeNames: Iterable<string>, kinds?: Readonly<Record<string, StrictMockSchemaKind$1>>): string;
34
+ export declare function buildStrictMockTypeFileHeader(schemaTypeNames: Iterable<string>, kinds?: Readonly<Record<string, StrictMockSchemaKind$1>>): string;
35
35
  /**
36
36
  * Prepends shared strict-mock helper types and each `{Schema}Mock` alias once at
37
37
  * the top of a mock file. Generators pass `strictSchemaTypeNames`; no scraping.
@@ -39,34 +39,34 @@ declare function buildStrictMockTypeFileHeader(schemaTypeNames: Iterable<string>
39
39
  * Not idempotent — callers must invoke this exactly once per aggregated mock
40
40
  * file (writers and `writeFakerSchemaMocks`), not from import hooks.
41
41
  */
42
- declare function dedupeStrictMockTypeDeclarations(implementation: string, options?: FinalizeMockImplementationOptions): string;
42
+ export declare function dedupeStrictMockTypeDeclarations(implementation: string, options?: FinalizeMockImplementationOptions): string;
43
43
  //#endregion
44
44
  //#region src/msw/index.d.ts
45
- declare const generateMSWImports: GenerateMockImports;
46
- declare function generateMSW(generatorVerbOptions: GeneratorVerbOptions, generatorOptions: GeneratorOptions): ClientMockGeneratorBuilder;
45
+ export declare const generateMSWImports: GenerateMockImports;
46
+ export declare function generateMSW(generatorVerbOptions: GeneratorVerbOptions, generatorOptions: GeneratorOptions): ClientMockGeneratorBuilder;
47
47
  //#endregion
48
48
  //#region src/index.d.ts
49
- declare const DEFAULT_MSW_OPTIONS: MswMockOptions;
50
- declare const DEFAULT_FAKER_OPTIONS: FakerMockOptions;
49
+ export declare const DEFAULT_MSW_OPTIONS: MswMockOptions;
50
+ export declare const DEFAULT_FAKER_OPTIONS: FakerMockOptions;
51
51
  /**
52
52
  * Returns the default GlobalMockOptions for a given mock type. Used when
53
53
  * normalizing user-provided entries in `output.mock.generators` so callers
54
54
  * can omit the per-type defaults.
55
55
  */
56
- declare const getDefaultMockOptionsForType: (type: GlobalMockOptions["type"]) => GlobalMockOptions;
56
+ export declare const getDefaultMockOptionsForType: (type: GlobalMockOptions["type"]) => GlobalMockOptions;
57
57
  /**
58
58
  * Dispatches mock-file imports generation to the appropriate generator based
59
59
  * on the `OutputMockType` discriminator on the mock options.
60
60
  */
61
- declare const generateMockImports: GenerateMockImports;
61
+ export declare const generateMockImports: GenerateMockImports;
62
62
  /**
63
63
  * Dispatches per-operation mock generation to the appropriate generator
64
64
  * based on the `OutputMockType` discriminator. Each entry in
65
65
  * `output.mock.generators` is dispatched here individually.
66
66
  */
67
- declare function generateMock(generatorVerbOptions: GeneratorVerbOptions, generatorOptions: Omit<GeneratorOptions, 'mock'> & {
67
+ export declare function generateMock(generatorVerbOptions: GeneratorVerbOptions, generatorOptions: Omit<GeneratorOptions, 'mock'> & {
68
68
  mock: GlobalMockOptions;
69
69
  }): import("@orval/core").ClientMockGeneratorBuilder;
70
70
  //#endregion
71
- export { DEFAULT_FAKER_OPTIONS, DEFAULT_MSW_OPTIONS, type GenerateFakerForSchemasResult, buildStrictMockTypeFileHeader, dedupeStrictMockTypeDeclarations, generateFaker, generateFakerForSchemas, generateFakerImports, generateMSW, generateMSWImports, generateMock, generateMockImports, getDefaultMockOptionsForType };
71
+ export type { GenerateFakerForSchemasResult };
72
72
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { EnumGeneration, OutputMockType, OutputMode, PropertySortOrder, camelPathParamName, compareNatural, compareVersions, escapeRegExp, generalJSTypesWithArray, generateDependencyImports, getKey, getOperationTagKey, getRefInfo, getRequiredKeys, getStringLiteralType, isBoolean, isFunction, isMswMock, isNumber, isObject, isReference, isString, jsStringLiteralEscape, mergeDeep, pascal, resolveRef, safeNumericConstraint, stringify, toColonRoutePath } from "@orval/core";
1
+ import { EnumGeneration, OutputMockType, OutputMode, PropertySortOrder, camelPathParamName, compareNatural, compareVersions, escapeRegExp, generalJSTypesWithArray, generateDependencyImports, getKey, getOperationTagKey, getRefInfo, getRequiredKeys, getStringLiteralType, isBoolean, isFunction, isMswMock, isNumber, isObject, isReference, isSchemaNullable, isString, jsStringLiteralEscape, mergeDeep, pascal, resolveRef, safeNumericConstraint, stringify, toColonRoutePath } from "@orval/core";
2
2
  import { prop } from "remeda";
3
3
  //#region src/mock-types.ts
4
4
  function isStrictMock(mockOptions) {
@@ -45,7 +45,7 @@ function isComposedObjectSchema(schema) {
45
45
  function getStrictMockTypeDeclaration(typeName, kind = "object", options) {
46
46
  const mockTypeName = getStrictMockTypeName(typeName);
47
47
  if (kind === "alias") return `export type ${mockTypeName} = ${typeName};`;
48
- if (kind === "binary") return `export type ${mockTypeName} = ArrayBuffer;`;
48
+ if (kind === "binary") return `export type ${mockTypeName} = Blob;`;
49
49
  const mappedType = `{\n [K in keyof Required<NonNullable<${typeName}>>]: NonNullable<Required<NonNullable<${typeName}>>[K]>;\n}`;
50
50
  return `export type ${mockTypeName} = ${options?.schemaNullableAtRoot ? `${mappedType} | null` : mappedType};`;
51
51
  }
@@ -316,7 +316,7 @@ const isFakerVersionV9 = (packageJson) => {
316
316
  //#region src/faker/constants.ts
317
317
  const DEFAULT_FORMAT_MOCK = {
318
318
  bic: "faker.finance.bic()",
319
- binary: "new ArrayBuffer(faker.number.int({ min: 1, max: 64 }))",
319
+ binary: "new Blob([new Uint8Array(faker.number.int({ min: 1, max: 64 }))])",
320
320
  city: "faker.location.city()",
321
321
  country: "faker.location.country()",
322
322
  date: "faker.date.past().toISOString().slice(0, 10)",
@@ -436,11 +436,12 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
436
436
  return {
437
437
  value: "faker.helpers.arrayElement([{}, null])",
438
438
  imports: [],
439
- name: schemaItem.name
439
+ name: schemaItem.name,
440
+ nullWrapped: true
440
441
  };
441
442
  }
442
443
  const baseItem = schemaItem;
443
- return combineSchemasMock({
444
+ const combined = combineSchemasMock({
444
445
  item: {
445
446
  anyOf: nonNullTypes.map((type) => ({
446
447
  ...baseItem,
@@ -459,6 +460,10 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
459
460
  existingReferencedAllOfRefs,
460
461
  splitMockImplementations
461
462
  });
463
+ return nonNullTypes.includes("null") ? {
464
+ ...combined,
465
+ nullWrapped: true
466
+ } : combined;
462
467
  }
463
468
  if (itemProperties) {
464
469
  let value = !combine || combine.separator === "oneOf" || combine.separator === "anyOf" ? "{" : "";
@@ -471,7 +476,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
471
476
  const propertyScalars = entries.map(([key, prop]) => {
472
477
  if (combine?.includedProperties.includes(key)) return;
473
478
  const isRequired = mockOptions?.required ?? (Array.isArray(itemRequired) ? itemRequired : []).includes(key);
474
- const hasNullable = "nullable" in prop && prop.nullable === true;
479
+ const hasNullable = !isReference(prop) && isSchemaNullable(prop);
475
480
  const refName = isReference(prop) ? getReferenceName$1(prop.$ref, context) : "";
476
481
  const isRecursiveRef = !!refName && existingReferencedProperties.includes(refName);
477
482
  if (isRecursiveRef) {
@@ -513,6 +518,7 @@ function getMockObject({ item, mockOptions, operationId, tags, combine, context,
513
518
  }
514
519
  const hasDefault = "default" in prop && prop.default !== void 0;
515
520
  if (!isRequired && !resolvedValue.overrided && !hasDefault) {
521
+ if (resolvedValue.nullWrapped) return `${keyDefinition}: ${resolvedValue.value}`;
516
522
  const omitValue = mockOptions?.nonNullable || !hasNullable ? "undefined" : "null";
517
523
  return `${keyDefinition}: faker.helpers.arrayElement([${resolvedValue.value}, ${omitValue}])`;
518
524
  }
@@ -639,7 +645,6 @@ function isAmbiguousInlineItemContext(operationId, parentName) {
639
645
  return !parentName.toLowerCase().includes(operationId.toLowerCase());
640
646
  }
641
647
  function isNullableArrayItem(schema) {
642
- if (schema.nullable === true) return true;
643
648
  return Array.isArray(schema.type) && schema.type.includes("null");
644
649
  }
645
650
  function isResolvedSchemaObjectLike(schema) {
@@ -894,7 +899,7 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
894
899
  overrided: false,
895
900
  nullWrapped
896
901
  };
897
- if (item.format && ALL_FORMAT[item.format]) {
902
+ if (item.format && Object.hasOwn(ALL_FORMAT, item.format)) {
898
903
  let value = ALL_FORMAT[item.format];
899
904
  if (["date", "date-time"].includes(item.format) && context.output.override.useDates) value = `new Date(${value})`;
900
905
  return {
@@ -1085,7 +1090,8 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
1085
1090
  case "null": return {
1086
1091
  value: "null",
1087
1092
  imports: [],
1088
- name: item.name
1093
+ name: item.name,
1094
+ nullWrapped: true
1089
1095
  };
1090
1096
  default:
1091
1097
  if (item.enum) {
@@ -1243,11 +1249,21 @@ function resolveRefTarget(ref, context) {
1243
1249
  if (!Array.isArray(refPaths)) return void 0;
1244
1250
  return prop(context.spec, ...refPaths);
1245
1251
  }
1246
- /** OpenAPI 3.0 `nullable: true` or 3.1 `type` unions that include `null`. */
1252
+ /**
1253
+ * Whether a schema accepts `null`, in any of the spellings orval sees.
1254
+ *
1255
+ * Delegates to core's {@link isSchemaNullable} rather than keeping a second,
1256
+ * narrower copy: this one knew only the 3.0 `nullable` keyword and a 3.1 `type`
1257
+ * union, so a schema nullable through a bare `type: 'null'`, a `null` member of
1258
+ * an `enum`, or a `{ type: 'null' }` branch of a `oneOf`/`anyOf` read as
1259
+ * non-nullable here while the type generator emitted `| null` (#4141).
1260
+ *
1261
+ * Kept as a thin wrapper because callers pass `unknown` — resolved `$ref`
1262
+ * targets and mock schemas that are not typed as Schema Objects.
1263
+ */
1247
1264
  function isNullableSchema(schema) {
1248
1265
  if (!schema || typeof schema !== "object") return false;
1249
- const { type, nullable } = schema;
1250
- return nullable === true || Array.isArray(type) && type.includes("null");
1266
+ return isSchemaNullable(schema);
1251
1267
  }
1252
1268
  /** When `nonNullableOption` is true (`override.mock.nonNullable`), omit the null branch. */
1253
1269
  function getNullable(value, nullable, nonNullableOption) {
@@ -1318,7 +1334,7 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
1318
1334
  path: schemaReference.path,
1319
1335
  isRef: true,
1320
1336
  required: [...schemaRef?.required ?? [], ...getRequiredKeys(schemaReference, name)],
1321
- ...schemaReference.nullable === void 0 ? {} : { nullable: schemaReference.nullable }
1337
+ ...Array.isArray(schemaReference.type) ? { type: schemaReference.type } : {}
1322
1338
  };
1323
1339
  if (combine?.separator === "allOf" && newSchema.discriminator && newSchema.oneOf) {
1324
1340
  const parentDiscriminator = newSchema.discriminator;
@@ -1350,7 +1366,9 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
1350
1366
  values: true,
1351
1367
  schemaFactory: true
1352
1368
  };
1353
- const isObjectLike = newSchema.type === "object" || !!newSchema.allOf || resolvesToObjectLike(newSchema, context);
1369
+ const targetNullable = !!schemaRef && isNullableSchema(schemaRef);
1370
+ const siteNullable = isNullableSchema(schemaReference);
1371
+ const isObjectLike = !targetNullable && (newSchema.type === "object" || !!newSchema.allOf || resolvesToObjectLike(newSchema, context));
1354
1372
  const mockTypeName = getStrictMockTypeName(pascal(name));
1355
1373
  const strictMockTypeImport = isStrictMock(mockOptions) && isObjectLike ? {
1356
1374
  name: mockTypeName,
@@ -1358,12 +1376,14 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
1358
1376
  schemaFactory: true
1359
1377
  } : void 0;
1360
1378
  const strictObjectCast = isStrictMock(mockOptions) && isObjectLike ? ` as ${mockTypeName}` : "";
1379
+ const callValue = isObjectLike ? `{ ...${factoryName}()${strictObjectCast} }` : `${factoryName}()`;
1380
+ const delegatesNullable = targetNullable || siteNullable;
1361
1381
  return {
1362
- value: getNullable(isObjectLike ? `{ ...${factoryName}()${strictObjectCast} }` : `${factoryName}()`, Boolean(newSchema.nullable), mockOptions?.nonNullable),
1382
+ value: getNullable(callValue, siteNullable && !targetNullable, mockOptions?.nonNullable),
1363
1383
  imports: strictMockTypeImport ? [factoryImport, strictMockTypeImport] : [factoryImport],
1364
1384
  name: newSchema.name,
1365
1385
  type: getType(newSchema),
1366
- nullWrapped: Boolean(newSchema.nullable) && !mockOptions?.nonNullable
1386
+ nullWrapped: delegatesNullable && !mockOptions?.nonNullable
1367
1387
  };
1368
1388
  }
1369
1389
  const importsBefore = imports.length;
@@ -1396,7 +1416,7 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
1396
1416
  const func = formatMockFactoryDeclaration(funcName, param, returnType, `{${scalar.value.startsWith("...") ? "" : "..."}${scalar.value}, ...${overrideVarName}}`, returnCast, { terminateStatement: true });
1397
1417
  splitMockImplementations.push(func);
1398
1418
  }
1399
- scalar.value = newSchema.nullable ? `${funcName}()` : `{...${funcName}()}`;
1419
+ scalar.value = isNullableSchema(newSchema) ? `${funcName}()` : `{...${funcName}()}`;
1400
1420
  const typeImport = {
1401
1421
  name: newSchema.name,
1402
1422
  values: false
@@ -1569,7 +1589,8 @@ function combineSchemasMock({ item, separator, mockOptions, operationId, tags, c
1569
1589
  value: finalValue,
1570
1590
  imports: combineImports,
1571
1591
  name: item.name,
1572
- includedProperties
1592
+ includedProperties,
1593
+ nullWrapped: isSchemaNullable(item) && !mockOptions?.nonNullable
1573
1594
  };
1574
1595
  }
1575
1596
  //#endregion
@@ -1826,23 +1847,20 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
1826
1847
  const isTextResponse = isExactlyStringReturnType && hasTextLikeContentType || contentTypesByPreference.some((ct) => isTextLikeContentType(ct));
1827
1848
  const isSchemaBinary = (r) => r.originalSchema?.format === "binary" || r.originalSchema?.contentMediaType === "application/octet-stream" && !r.originalSchema.contentEncoding;
1828
1849
  const isBinaryResponse = preferredContentTypeMatch ? responsesByPreference.some((r) => isSchemaBinary(r)) : contentTypesByPreference.some((ct) => isBinaryLikeContentType(ct)) || responsesByPreference.some((r) => isSchemaBinary(r));
1829
- const binaryRefNames = responsesByPreference.filter((r) => isSchemaBinary(r)).flatMap((r) => r.imports.flatMap((imp) => imp.alias ? [imp.name, imp.alias] : [imp.name]));
1830
1850
  const isReturnHttpResponse = value && value !== "undefined";
1831
1851
  const getResponseMockFunctionName = `${getResponseMockFunctionNameBase}${pascal(name)}`;
1832
1852
  const handlerName = `${handlerNameBase}${pascal(name)}`;
1833
1853
  const addedSplitMockImplementations = splitMockImplementations.slice(oldSplitMockImplementations.length);
1834
1854
  splitMockImplementations.push(...addedSplitMockImplementations);
1835
1855
  const mockImplementations = addedSplitMockImplementations.length > 0 ? `${addedSplitMockImplementations.join("\n\n")}\n\n` : "";
1836
- const binaryTypeRewriteRegex = new RegExp(String.raw`\b(?:${["Blob", ...binaryRefNames].map((n) => escapeRegExp(n)).join("|")})\b`, "g");
1837
- const mockReturnType = isBinaryResponse ? returnType.replaceAll(binaryTypeRewriteRegex, "ArrayBuffer") : returnType;
1838
- const isVoidUnionType = mockReturnType !== "void" && mockReturnType.split("|").some((part) => part.trim() === "void");
1856
+ const isVoidUnionType = returnType !== "void" && returnType.split("|").some((part) => part.trim() === "void");
1839
1857
  const noContentStatusCode = isVoidUnionType ? assertSafeStatusCode(responses.find((r) => r.value === "void")?.key ?? "204") : void 0;
1840
- const nonVoidMockReturnType = isVoidUnionType ? mockReturnType.split("|").filter((part) => part.trim() !== "void").join(" | ").trim() : mockReturnType;
1858
+ const nonVoidMockReturnType = isVoidUnionType ? returnType.split("|").filter((part) => part.trim() !== "void").join(" | ").trim() : returnType;
1841
1859
  const hasJsonContentType = contentTypesByPreference.some((ct) => ct.includes("json") || ct.includes("+json"));
1842
- const hasStringReturnType = isTypeExactlyString(mockReturnType) || isUnionContainingString(mockReturnType);
1860
+ const hasStringReturnType = isTypeExactlyString(returnType) || isUnionContainingString(returnType);
1843
1861
  const overrideResponseType = `Partial<Extract<${nonVoidMockReturnType}, object>>`;
1844
1862
  const shouldPreferJsonResponse = hasJsonContentType && !hasStringReturnType;
1845
- const needsRuntimeContentTypeSwitch = isTextResponse && hasJsonContentType && hasStringReturnType && mockReturnType !== "string";
1863
+ const needsRuntimeContentTypeSwitch = isTextResponse && hasJsonContentType && hasStringReturnType && returnType !== "string";
1846
1864
  const mockOptionsFromOverride = override.mock;
1847
1865
  const strictMock = isStrictMock(mockOptionsFromOverride);
1848
1866
  const schemaTypeNames = strictMock ? getSchemaTypeNamesFromResponses(responses) : [];
@@ -1890,10 +1908,7 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
1890
1908
  if (!isReturnHttpResponse) responseBody = `new HttpResponse(null,
1891
1909
  { status: ${statusCode}
1892
1910
  })`;
1893
- else if (isBinaryResponse) responseBody = `HttpResponse.arrayBuffer(
1894
- binaryBody instanceof ArrayBuffer
1895
- ? binaryBody
1896
- : new ArrayBuffer(0),
1911
+ else if (isBinaryResponse) responseBody = `new HttpResponse(binaryBody,
1897
1912
  { status: ${statusCode},
1898
1913
  headers: { 'Content-Type': '${jsStringLiteralEscape(binaryContentType)}' }
1899
1914
  })`;
@@ -1920,7 +1935,7 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
1920
1935
  })`;
1921
1936
  const infoType = `Parameters<Parameters<typeof http.${verb}>[1]>[0]`;
1922
1937
  const handlerImplementation = `
1923
- export const ${handlerName} = (overrideResponse?: ${mockReturnType} | ((${infoParam}: ${infoType}) => Promise<${mockReturnType}> | ${mockReturnType}), options?: RequestHandlerOptions) => {
1938
+ export const ${handlerName} = (overrideResponse?: ${returnType} | ((${infoParam}: ${infoType}) => Promise<${returnType}> | ${returnType}), options?: RequestHandlerOptions) => {
1924
1939
  return http.${verb}('${route}', async (${infoParam}: ${infoType}) => {${delay === false ? "" : `await delay(${isFunction(delay) ? `(${String(delay)})()` : String(delay)});`}
1925
1940
  ${isReturnHttpResponse ? "" : `if (typeof overrideResponse === 'function') {await overrideResponse(info); }`}
1926
1941
  ${responsePrelude}