@orval/mock 8.29.0 → 8.31.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![tests](https://github.com/orval-labs/orval/actions/workflows/tests.yaml/badge.svg)](https://github.com/orval-labs/orval/actions/workflows/tests.yaml)
4
4
 
5
5
  <p align="center">
6
- <img src="./logo/orval-logo-horizontal.svg?raw=true" width="500" height="160" alt="orval - Restful Client Generator" />
6
+ <img src="../../logo/orval-logo-horizontal.svg?raw=true" width="500" height="160" alt="orval - Restful Client Generator" />
7
7
  </p>
8
8
  <h1 align="center">
9
9
  Visit <a href="https://orval.dev" target="_blank">orval.dev</a> for docs, guides, API and beer!
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, isBoolean, isFunction, isMswMock, isNumber, isObject, isReference, isString, jsStringLiteralEscape, mergeDeep, pascal, resolveRef, 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, 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) {
@@ -911,18 +911,20 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
911
911
  case "number":
912
912
  case "integer": {
913
913
  const intFunction = context.output.override.useBigInt && (item.format === "int64" || item.format === "uint64") ? "bigInt" : "int";
914
- const numMin = typeof item.exclusiveMinimum === "number" ? item.exclusiveMinimum : item.minimum ?? safeMockOptions.numberMin;
915
- const numMax = typeof item.exclusiveMaximum === "number" ? item.exclusiveMaximum : item.maximum ?? safeMockOptions.numberMax;
914
+ const specMin = safeNumericConstraint(item.minimum, "minimum");
915
+ const specMax = safeNumericConstraint(item.maximum, "maximum");
916
+ const numMin = typeof item.exclusiveMinimum === "number" ? safeNumericConstraint(item.exclusiveMinimum, "exclusiveMinimum") : specMin ?? safeMockOptions.numberMin;
917
+ const numMax = typeof item.exclusiveMaximum === "number" ? safeNumericConstraint(item.exclusiveMaximum, "exclusiveMaximum") : specMax ?? safeMockOptions.numberMax;
916
918
  const intParts = [];
917
919
  if (numMin !== void 0) intParts.push(`min: ${numMin}`);
918
920
  if (numMax !== void 0) intParts.push(`max: ${numMax}`);
919
- if (isFakerV9 && item.multipleOf !== void 0) intParts.push(`multipleOf: ${item.multipleOf}`);
921
+ if (isFakerV9 && item.multipleOf !== void 0) intParts.push(`multipleOf: ${safeNumericConstraint(item.multipleOf, "multipleOf")}`);
920
922
  let value = getNullable(`faker.number.${intFunction}(${intParts.length > 0 ? `{${intParts.join(", ")}}` : ""})`, isNullable, nonNullableOption);
921
923
  if (type === "number") {
922
924
  const floatParts = [];
923
925
  if (numMin !== void 0) floatParts.push(`min: ${numMin}`);
924
926
  if (numMax !== void 0) floatParts.push(`max: ${numMax}`);
925
- if (isFakerV9 && item.multipleOf !== void 0) floatParts.push(`multipleOf: ${item.multipleOf}`);
927
+ if (isFakerV9 && item.multipleOf !== void 0) floatParts.push(`multipleOf: ${safeNumericConstraint(item.multipleOf, "multipleOf")}`);
926
928
  else if (safeMockOptions.fractionDigits !== void 0) floatParts.push(`fractionDigits: ${safeMockOptions.fractionDigits}`);
927
929
  value = getNullable(`faker.number.float(${floatParts.length > 0 ? `{${floatParts.join(", ")}}` : ""})`, isNullable, nonNullableOption);
928
930
  }
@@ -1027,8 +1029,8 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
1027
1029
  });
1028
1030
  if (extractedItemCall) mapValue = extractedItemCall;
1029
1031
  if (combine && !value.startsWith("faker") && !value.startsWith("{") && !value.startsWith("Array.from")) mapValue = `{${value}}`;
1030
- const arrSchemaMin = item.minItems;
1031
- const arrSchemaMax = item.maxItems;
1032
+ const arrSchemaMin = safeNumericConstraint(item.minItems, "minItems");
1033
+ const arrSchemaMax = safeNumericConstraint(item.maxItems, "maxItems");
1032
1034
  const arrGlobalMin = safeMockOptions.arrayMin;
1033
1035
  const arrGlobalMax = safeMockOptions.arrayMax;
1034
1036
  let arrMin;
@@ -1051,8 +1053,8 @@ function getMockScalar({ item, imports, mockOptions, operationId, tags, combine,
1051
1053
  };
1052
1054
  }
1053
1055
  case "string": {
1054
- const schemaMin = item.minLength;
1055
- const schemaMax = item.maxLength;
1056
+ const schemaMin = safeNumericConstraint(item.minLength, "minLength");
1057
+ const schemaMax = safeNumericConstraint(item.maxLength, "maxLength");
1056
1058
  const globalMin = safeMockOptions.stringMin;
1057
1059
  const globalMax = safeMockOptions.stringMax;
1058
1060
  let strMin;
@@ -1359,7 +1361,7 @@ function resolveMockValue({ schema, mockOptions, operationId, tags, combine, con
1359
1361
  if (!splitMockImplementations.some((f) => f.includes(`export const ${funcName}`))) {
1360
1362
  const discriminatedProperty = newSchema.discriminator?.propertyName;
1361
1363
  let overrideType = `Partial<${newSchema.name}>`;
1362
- if (discriminatedProperty) overrideType = `Omit<${overrideType}, '${discriminatedProperty}'>`;
1364
+ if (discriminatedProperty) overrideType = `Omit<${overrideType}, ${getStringLiteralType(discriminatedProperty)}>`;
1363
1365
  const { param, returnType, returnCast } = getMockFactorySignatureParts(newSchema.name, mockOptions, {
1364
1366
  isOverridable: true,
1365
1367
  overrideType
@@ -1711,6 +1713,24 @@ function getMockOptionsDataOverride(operationTags, operationId, override) {
1711
1713
  }
1712
1714
  //#endregion
1713
1715
  //#region src/msw/index.ts
1716
+ /**
1717
+ * Resolves an OpenAPI response key to the numeric status the generated handler
1718
+ * emits.
1719
+ *
1720
+ * The key comes straight from the document and lands in an unquoted expression
1721
+ * position (`{ status: 200 }`), so there is no quote to escape and no way to
1722
+ * make a non-numeric value safe there — a key like
1723
+ * `2,x:(<expression>)` would splice live code into the handler. Parse it, and
1724
+ * refuse the document rather than emitting whatever it says.
1725
+ *
1726
+ * `default` maps to 200 and a `NXX` wildcard to `N00`, matching what the
1727
+ * handler previously emitted for those keys.
1728
+ */
1729
+ function assertSafeStatusCode(status) {
1730
+ const normalized = status === "default" ? "200" : /^\dXX$/.test(status) ? `${status[0]}00` : status;
1731
+ if (!/^\d{3}$/.test(normalized)) throw new Error(`orval: refusing to generate a mock handler for an OpenAPI response key that is not a status code (got "${status}"). This value would otherwise be emitted verbatim into generated source.`);
1732
+ return Number(normalized);
1733
+ }
1714
1734
  function getMSWDependencies(options) {
1715
1735
  const locale = options?.locale;
1716
1736
  const fakerDependency = {
@@ -1789,7 +1809,7 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
1789
1809
  const binaryTypeRewriteRegex = new RegExp(String.raw`\b(?:${["Blob", ...binaryRefNames].map((n) => escapeRegExp(n)).join("|")})\b`, "g");
1790
1810
  const mockReturnType = isBinaryResponse ? returnType.replaceAll(binaryTypeRewriteRegex, "ArrayBuffer") : returnType;
1791
1811
  const isVoidUnionType = mockReturnType !== "void" && mockReturnType.split("|").some((part) => part.trim() === "void");
1792
- const noContentStatusCode = isVoidUnionType ? responses.find((r) => r.value === "void")?.key ?? "204" : void 0;
1812
+ const noContentStatusCode = isVoidUnionType ? assertSafeStatusCode(responses.find((r) => r.value === "void")?.key ?? "204") : void 0;
1793
1813
  const nonVoidMockReturnType = isVoidUnionType ? mockReturnType.split("|").filter((part) => part.trim() !== "void").join(" | ").trim() : mockReturnType;
1794
1814
  const hasJsonContentType = contentTypesByPreference.some((ct) => ct.includes("json") || ct.includes("+json"));
1795
1815
  const hasStringReturnType = isTypeExactlyString(mockReturnType) || isUnionContainingString(mockReturnType);
@@ -1824,7 +1844,7 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
1824
1844
  const resolvedResponseExpr = `overrideResponse !== undefined
1825
1845
  ? (typeof overrideResponse === "function" ? await overrideResponse(${infoParam}) : overrideResponse)
1826
1846
  : ${getResponseMockFunctionName}()`;
1827
- const statusCode = status === "default" ? 200 : status.replace(/XX$/, "00");
1847
+ const statusCode = assertSafeStatusCode(status);
1828
1848
  const binaryContentType = (preferredContentTypeMatch && isBinaryLikeContentType(preferredContentTypeMatch) ? preferredContentTypeMatch : contentTypes.find((ct) => isBinaryLikeContentType(ct))) ?? "application/octet-stream";
1829
1849
  const firstTextCt = isExactlyStringReturnType && !!preferredContentTypeMatch && !isTextLikeContentType(preferredContentTypeMatch) && hasTextLikeContentType ? contentTypes.find((ct) => isTextLikeContentType(ct)) : contentTypesByPreference.find((ct) => isTextLikeContentType(ct));
1830
1850
  const textHelper = firstTextCt === "application/xml" || firstTextCt?.endsWith("+xml") ? "xml" : firstTextCt === "text/html" ? "html" : "text";