@orval/mock 8.28.1 → 8.30.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 +52 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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, 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
|
|
915
|
-
const
|
|
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;
|
|
@@ -1137,9 +1139,23 @@ function getItemType(item) {
|
|
|
1137
1139
|
if (!type) return;
|
|
1138
1140
|
return ["string", "number"].includes(type) ? type : void 0;
|
|
1139
1141
|
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Renders one enum member as a literal for the generated faker array.
|
|
1144
|
+
*
|
|
1145
|
+
* The branch is on the member's own type, never on the schema's declared
|
|
1146
|
+
* `type`: both come from the document and nothing makes them agree. Keying off
|
|
1147
|
+
* the declared type meant a string member under `type: 'integer'` was spliced
|
|
1148
|
+
* in as a live expression, a numeric member under `type: 'string'` crashed the
|
|
1149
|
+
* generator, and an object member emitted `[object Object]`.
|
|
1150
|
+
*/
|
|
1151
|
+
function formatEnumMember(value) {
|
|
1152
|
+
if (isString(value)) return `'${jsStringLiteralEscape(value)}'`;
|
|
1153
|
+
if (isNumber(value) || isBoolean(value)) return String(value);
|
|
1154
|
+
return JSON.stringify(value);
|
|
1155
|
+
}
|
|
1140
1156
|
function getEnum(item, imports, context, existingReferencedProperties, type) {
|
|
1141
1157
|
if (!item.enum) return "";
|
|
1142
|
-
let enumValue = `[${item.enum.filter((e) => e !== null).map((e) =>
|
|
1158
|
+
let enumValue = `[${item.enum.filter((e) => e !== null).map((e) => formatEnumMember(e)).join(",")}]`;
|
|
1143
1159
|
if (context.output.override.enumGenerationType === EnumGeneration.ENUM) {
|
|
1144
1160
|
const isRootSchema = !item.parentName && existingReferencedProperties.at(-1) === item.name;
|
|
1145
1161
|
if (item.isRef || existingReferencedProperties.length === 0 || isRootSchema) {
|
|
@@ -1153,7 +1169,7 @@ function getEnum(item, imports, context, existingReferencedProperties, type) {
|
|
|
1153
1169
|
imports.push({ name: parentReference });
|
|
1154
1170
|
}
|
|
1155
1171
|
} else enumValue += " as const";
|
|
1156
|
-
if (item.isRef && type === "string" && context.output.override.enumGenerationType !== EnumGeneration.UNION) {
|
|
1172
|
+
if (item.isRef && type === "string" && item.enum.every((e) => isString(e)) && context.output.override.enumGenerationType !== EnumGeneration.UNION) {
|
|
1157
1173
|
enumValue = `Object.values(${item.name})`;
|
|
1158
1174
|
imports.push({
|
|
1159
1175
|
name: item.name,
|
|
@@ -1531,7 +1547,7 @@ function combineSchemasMock({ item, separator, mockOptions, operationId, tags, c
|
|
|
1531
1547
|
}
|
|
1532
1548
|
//#endregion
|
|
1533
1549
|
//#region src/faker/getters/route.ts
|
|
1534
|
-
const getRouteMSW = (route, baseUrl = "*") => `${baseUrl}${toColonRoutePath(route.replaceAll(":", String.raw`\\:`), camelPathParamName)}`;
|
|
1550
|
+
const getRouteMSW = (route, baseUrl = "*") => `${jsStringLiteralEscape(baseUrl)}${toColonRoutePath(jsStringLiteralEscape(route).replaceAll(":", String.raw`\\:`), camelPathParamName)}`;
|
|
1535
1551
|
//#endregion
|
|
1536
1552
|
//#region src/msw/mocks.ts
|
|
1537
1553
|
function getMockPropertiesWithoutFunc(properties, spec) {
|
|
@@ -1649,7 +1665,7 @@ function getResponsesMockDefinition({ operationId, tags, returnType, responses,
|
|
|
1649
1665
|
};
|
|
1650
1666
|
else if (!originalSchema) continue;
|
|
1651
1667
|
const resolvedSchema = resolveRef(originalSchema, context).schema;
|
|
1652
|
-
const responseImports = imports
|
|
1668
|
+
const responseImports = imports ? [...imports] : [];
|
|
1653
1669
|
const importsBefore = responseImports.length;
|
|
1654
1670
|
const scalar = getMockScalar({
|
|
1655
1671
|
item: {
|
|
@@ -1697,6 +1713,24 @@ function getMockOptionsDataOverride(operationTags, operationId, override) {
|
|
|
1697
1713
|
}
|
|
1698
1714
|
//#endregion
|
|
1699
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
|
+
}
|
|
1700
1734
|
function getMSWDependencies(options) {
|
|
1701
1735
|
const locale = options?.locale;
|
|
1702
1736
|
const fakerDependency = {
|
|
@@ -1775,7 +1809,7 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
|
|
|
1775
1809
|
const binaryTypeRewriteRegex = new RegExp(String.raw`\b(?:${["Blob", ...binaryRefNames].map((n) => escapeRegExp(n)).join("|")})\b`, "g");
|
|
1776
1810
|
const mockReturnType = isBinaryResponse ? returnType.replaceAll(binaryTypeRewriteRegex, "ArrayBuffer") : returnType;
|
|
1777
1811
|
const isVoidUnionType = mockReturnType !== "void" && mockReturnType.split("|").some((part) => part.trim() === "void");
|
|
1778
|
-
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;
|
|
1779
1813
|
const nonVoidMockReturnType = isVoidUnionType ? mockReturnType.split("|").filter((part) => part.trim() !== "void").join(" | ").trim() : mockReturnType;
|
|
1780
1814
|
const hasJsonContentType = contentTypesByPreference.some((ct) => ct.includes("json") || ct.includes("+json"));
|
|
1781
1815
|
const hasStringReturnType = isTypeExactlyString(mockReturnType) || isUnionContainingString(mockReturnType);
|
|
@@ -1810,14 +1844,14 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
|
|
|
1810
1844
|
const resolvedResponseExpr = `overrideResponse !== undefined
|
|
1811
1845
|
? (typeof overrideResponse === "function" ? await overrideResponse(${infoParam}) : overrideResponse)
|
|
1812
1846
|
: ${getResponseMockFunctionName}()`;
|
|
1813
|
-
const statusCode = status
|
|
1847
|
+
const statusCode = assertSafeStatusCode(status);
|
|
1814
1848
|
const binaryContentType = (preferredContentTypeMatch && isBinaryLikeContentType(preferredContentTypeMatch) ? preferredContentTypeMatch : contentTypes.find((ct) => isBinaryLikeContentType(ct))) ?? "application/octet-stream";
|
|
1815
1849
|
const firstTextCt = isExactlyStringReturnType && !!preferredContentTypeMatch && !isTextLikeContentType(preferredContentTypeMatch) && hasTextLikeContentType ? contentTypes.find((ct) => isTextLikeContentType(ct)) : contentTypesByPreference.find((ct) => isTextLikeContentType(ct));
|
|
1816
1850
|
const textHelper = firstTextCt === "application/xml" || firstTextCt?.endsWith("+xml") ? "xml" : firstTextCt === "text/html" ? "html" : "text";
|
|
1817
1851
|
const firstJsonCt = contentTypesByPreference.find((ct) => ct.includes("json"));
|
|
1818
1852
|
const textHelperDefaultContentType = textHelper === "xml" ? "text/xml" : textHelper === "html" ? "text/html" : "text/plain";
|
|
1819
|
-
const jsonCtHeaderSuffix = firstJsonCt && firstJsonCt !== "application/json" ? `, headers: { 'Content-Type': '${firstJsonCt}' }` : preferredContentTypeMatch && !preferredContentTypeMatch.includes("json") ? `, headers: { 'Content-Type': '${preferredContentTypeMatch}' }` : "";
|
|
1820
|
-
const textCtHeaderSuffix = firstTextCt && firstTextCt !== textHelperDefaultContentType ? `, headers: { 'Content-Type': '${firstTextCt}' }` : "";
|
|
1853
|
+
const jsonCtHeaderSuffix = firstJsonCt && firstJsonCt !== "application/json" ? `, headers: { 'Content-Type': '${jsStringLiteralEscape(firstJsonCt)}' }` : preferredContentTypeMatch && !preferredContentTypeMatch.includes("json") ? `, headers: { 'Content-Type': '${jsStringLiteralEscape(preferredContentTypeMatch)}' }` : "";
|
|
1854
|
+
const textCtHeaderSuffix = firstTextCt && firstTextCt !== textHelperDefaultContentType ? `, headers: { 'Content-Type': '${jsStringLiteralEscape(firstTextCt)}' }` : "";
|
|
1821
1855
|
let responseBody;
|
|
1822
1856
|
let responsePrelude = "";
|
|
1823
1857
|
if (isReturnHttpResponse) {
|
|
@@ -1834,7 +1868,7 @@ function generateDefinition(name, route, getResponseMockFunctionNameBase, handle
|
|
|
1834
1868
|
? binaryBody
|
|
1835
1869
|
: new ArrayBuffer(0),
|
|
1836
1870
|
{ status: ${statusCode},
|
|
1837
|
-
headers: { 'Content-Type': '${binaryContentType}' }
|
|
1871
|
+
headers: { 'Content-Type': '${jsStringLiteralEscape(binaryContentType)}' }
|
|
1838
1872
|
})`;
|
|
1839
1873
|
else if (isVoidUnionType) {
|
|
1840
1874
|
let nonVoidBody;
|