@orval/core 8.6.1 → 8.7.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
@@ -17,6 +17,11 @@ import { Parser } from "acorn";
17
17
  import { build } from "esbuild";
18
18
 
19
19
  //#region src/types.ts
20
+ const SupportedFormatter = {
21
+ PRETTIER: "prettier",
22
+ BIOME: "biome",
23
+ OXFMT: "oxfmt"
24
+ };
20
25
  const PropertySortOrder = {
21
26
  ALPHABETICAL: "Alphabetical",
22
27
  SPECIFICATION: "Specification"
@@ -281,37 +286,19 @@ function compareVersions(firstVersion, secondVersions, operator = ">=") {
281
286
 
282
287
  //#endregion
283
288
  //#region src/utils/content-type.ts
289
+ const binaryApplicationTypes = new Set(["application/octet-stream", "application/pdf"]);
284
290
  /**
285
- * Determine if a content type is binary (vs text-based).
291
+ * Determine if a content type is binary.
292
+ * Only known binary types return true. Unknown types default to false (non-binary)
293
+ * so that schema type information is preserved rather than being overridden with Blob.
286
294
  */
287
295
  function isBinaryContentType(contentType) {
288
- if (contentType === "application/octet-stream") return true;
289
- if (contentType.startsWith("image/")) return true;
290
- if (contentType.startsWith("audio/")) return true;
291
- if (contentType.startsWith("video/")) return true;
292
- if (contentType.startsWith("font/")) return true;
293
- if (contentType.startsWith("text/")) return false;
294
- if ([
295
- "+json",
296
- "-json",
297
- "+xml",
298
- "-xml",
299
- "+yaml",
300
- "-yaml",
301
- "+rss",
302
- "-rss",
303
- "+csv",
304
- "-csv"
305
- ].some((suffix) => contentType.includes(suffix))) return false;
306
- return !new Set([
307
- "application/json",
308
- "application/xml",
309
- "application/yaml",
310
- "application/x-www-form-urlencoded",
311
- "application/javascript",
312
- "application/ecmascript",
313
- "application/graphql"
314
- ]).has(contentType);
296
+ const baseType = contentType.split(";")[0].trim();
297
+ if (baseType.startsWith("image/")) return true;
298
+ if (baseType.startsWith("audio/")) return true;
299
+ if (baseType.startsWith("video/")) return true;
300
+ if (baseType.startsWith("font/")) return true;
301
+ return binaryApplicationTypes.has(baseType);
315
302
  }
316
303
  /**
317
304
  * Determine if a form-data field should be treated as a file (binary or text).
@@ -566,6 +553,17 @@ function getIsBodyVerb(verb) {
566
553
  //#endregion
567
554
  //#region src/utils/logger.ts
568
555
  const log = console.log;
556
+ let _warningCount = 0;
557
+ function logWarning(message) {
558
+ _warningCount++;
559
+ log(styleText("yellow", message));
560
+ }
561
+ function getWarningCount() {
562
+ return _warningCount;
563
+ }
564
+ function resetWarnings() {
565
+ _warningCount = 0;
566
+ }
569
567
  let _verbose = false;
570
568
  function setVerbose(v) {
571
569
  _verbose = v;
@@ -595,7 +593,7 @@ function logError(err, tag) {
595
593
  ].filter(Boolean).join(" ")));
596
594
  }
597
595
  function mismatchArgsMessage(mismatchArgs) {
598
- log(styleText("yellow", `${mismatchArgs.join(", ")} ${mismatchArgs.length === 1 ? "is" : "are"} not defined in your configuration!`));
596
+ logWarning(`${mismatchArgs.join(", ")} ${mismatchArgs.length === 1 ? "is" : "are"} not defined in your configuration!`);
599
597
  }
600
598
  function createSuccessMessage(backend) {
601
599
  log(`🎉 ${backend ? `${styleText("green", backend)} - ` : ""}Your OpenAPI spec has been converted into ready to use orval!`);
@@ -1389,7 +1387,8 @@ function resolveValue({ schema, name, context, formDataContext }) {
1389
1387
  }
1390
1388
  }).hasReadonlyProps;
1391
1389
  const isAnyOfNullable = schemaObject.anyOf?.some((anyOfItem) => !isReference(anyOfItem) && (anyOfItem.type === "null" || Array.isArray(anyOfItem.type) && anyOfItem.type.includes("null")));
1392
- const nullable = Array.isArray(schemaObject.type) && schemaObject.type.includes("null") || schemaObject.nullable === true || isAnyOfNullable ? " | null" : "";
1390
+ const schemaType = schemaObject.type;
1391
+ const nullable = Array.isArray(schemaType) && schemaType.includes("null") || schemaObject.nullable === true || isAnyOfNullable ? " | null" : "";
1393
1392
  return {
1394
1393
  value: resolvedImport.name + nullable,
1395
1394
  imports: [{
@@ -1595,6 +1594,8 @@ const getSchemaAnyOf = (s) => s.anyOf;
1595
1594
  const getSchemaItems = (s) => s.items;
1596
1595
  const getSchemaRequired = (s) => s.required;
1597
1596
  const getSchemaProperties = (s) => s.properties;
1597
+ const resolveSchemaRef = (schema, context) => resolveRef(schema, context);
1598
+ const resolveResponseOrRequestRef = (schema, context) => resolveRef(schema, context);
1598
1599
  const formDataContentTypes = new Set(["multipart/form-data"]);
1599
1600
  const formUrlEncodedContentTypes = new Set(["application/x-www-form-urlencoded"]);
1600
1601
  function getResReqContentTypes({ mediaType, propName, context, isFormData, contentType }) {
@@ -1618,7 +1619,7 @@ function getResReqContentTypes({ mediaType, propName, context, isFormData, conte
1618
1619
  function getResReqTypes(responsesOrRequests, name, context, defaultType = "unknown", uniqueKey = (item) => item.value) {
1619
1620
  return uniqueBy(responsesOrRequests.filter(([, res]) => Boolean(res)).map(([key, res]) => {
1620
1621
  if (isReference(res)) {
1621
- const { schema: bodySchema, imports: [{ name, schemaName }] } = resolveRef(res, context);
1622
+ const { schema: bodySchema, imports: [{ name, schemaName }] } = resolveResponseOrRequestRef(res, context);
1622
1623
  const firstEntry = Object.entries(bodySchema.content ?? {}).at(0);
1623
1624
  if (!firstEntry) return [{
1624
1625
  value: name,
@@ -1706,7 +1707,7 @@ function getResReqTypes(responsesOrRequests, name, context, defaultType = "unkno
1706
1707
  if (propName && arr.length > 1) propName = propName + pascal(getNumberWord(index + 1));
1707
1708
  let effectivePropName = propName;
1708
1709
  if (mediaType.schema && isReference(mediaType.schema)) {
1709
- const { imports } = resolveRef(mediaType.schema, context);
1710
+ const { imports } = resolveSchemaRef(mediaType.schema, context);
1710
1711
  if (imports[0]?.name) effectivePropName = imports[0].name;
1711
1712
  }
1712
1713
  const isFormData = formDataContentTypes.has(contentType);
@@ -1836,14 +1837,14 @@ function getDefaultContentType(contentTypes) {
1836
1837
  return contentTypes[0];
1837
1838
  }
1838
1839
  function getFormDataAdditionalImports({ schemaObject, context }) {
1839
- const { schema } = resolveRef(schemaObject, context);
1840
+ const { schema } = resolveSchemaRef(schemaObject, context);
1840
1841
  if (schema.type !== "object") return [];
1841
1842
  const combinedSchemas = getSchemaOneOf(schema) ?? getSchemaAnyOf(schema);
1842
1843
  if (!combinedSchemas) return [];
1843
- return combinedSchemas.map((subSchema) => resolveRef(subSchema, context).imports[0]).filter(Boolean);
1844
+ return combinedSchemas.map((subSchema) => resolveSchemaRef(subSchema, context).imports[0]).filter(Boolean);
1844
1845
  }
1845
1846
  function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequestBodyOptional, isUrlEncoded, isRef, encoding }) {
1846
- const { schema, imports } = resolveRef(schemaObject, context);
1847
+ const { schema, imports } = resolveSchemaRef(schemaObject, context);
1847
1848
  const propName = camel(!isRef && isReference(schemaObject) ? imports[0].name : name);
1848
1849
  const additionalImports = [];
1849
1850
  const variableName = isUrlEncoded ? "formUrlEncoded" : "formData";
@@ -1853,7 +1854,7 @@ function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequest
1853
1854
  if (combinedSchemas) {
1854
1855
  const shouldCast = !!getSchemaOneOf(schema) || !!getSchemaAnyOf(schema);
1855
1856
  const combinedSchemasFormData = combinedSchemas.map((subSchema) => {
1856
- const { schema: combinedSchema, imports } = resolveRef(subSchema, context);
1857
+ const { schema: combinedSchema, imports } = resolveSchemaRef(subSchema, context);
1857
1858
  let newPropName = propName;
1858
1859
  let newPropDefinition = "";
1859
1860
  if (shouldCast && imports[0]) {
@@ -1889,7 +1890,7 @@ function getSchemaFormDataAndUrlEncoded({ name, schemaObject, context, isRequest
1889
1890
  let valueStr = "value";
1890
1891
  const schemaItems = getSchemaItems(schema);
1891
1892
  if (schemaItems) {
1892
- const { schema: itemSchema } = resolveRef(schemaItems, context);
1893
+ const { schema: itemSchema } = resolveSchemaRef(schemaItems, context);
1893
1894
  if (itemSchema.type === "object" || itemSchema.type === "array") valueStr = "JSON.stringify(value)";
1894
1895
  else if (itemSchema.type === "number" || itemSchema.type === "integer" || itemSchema.type === "boolean") valueStr = "value.toString()";
1895
1896
  }
@@ -1902,7 +1903,7 @@ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, con
1902
1903
  let formDataValues = "";
1903
1904
  const schemaProps = getSchemaProperties(schema) ?? {};
1904
1905
  for (const [key, value] of Object.entries(schemaProps)) {
1905
- const { schema: property } = resolveRef(value, context);
1906
+ const { schema: property } = resolveSchemaRef(value, context);
1906
1907
  if (property.readOnly) continue;
1907
1908
  let formDataValue = "";
1908
1909
  const partContentType = (depth === 0 ? encoding?.[key] : void 0)?.contentType;
@@ -1929,7 +1930,7 @@ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, con
1929
1930
  let hasNonPrimitiveChild = false;
1930
1931
  const propertyItems = getSchemaItems(property);
1931
1932
  if (propertyItems) {
1932
- const { schema: itemSchema } = resolveRef(propertyItems, context);
1933
+ const { schema: itemSchema } = resolveSchemaRef(propertyItems, context);
1933
1934
  if (itemSchema.type === "object" || itemSchema.type === "array") if (context.output.override.formData.arrayHandling === FormDataArrayHandling.EXPLODE) {
1934
1935
  hasNonPrimitiveChild = true;
1935
1936
  const resolvedValue = resolveSchemaPropertiesToFormData({
@@ -1997,8 +1998,7 @@ function resolveSchemaPropertiesToFormData({ schema, variableName, propName, con
1997
1998
 
1998
1999
  //#endregion
1999
2000
  //#region src/getters/body.ts
2000
- function getBody({ requestBody, operationName, context, contentType }) {
2001
- const filteredBodyTypes = filterByContentType(getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context), contentType);
2001
+ function buildBody(filteredBodyTypes, requestBody, operationName, context) {
2002
2002
  const imports = filteredBodyTypes.flatMap(({ imports }) => imports);
2003
2003
  const schemas = filteredBodyTypes.flatMap(({ schemas }) => schemas);
2004
2004
  const definition = filteredBodyTypes.map(({ value }) => value).join(" | ");
@@ -2036,6 +2036,40 @@ function getBody({ requestBody, operationName, context, contentType }) {
2036
2036
  }
2037
2037
  };
2038
2038
  }
2039
+ function getBody({ requestBody, operationName, context, contentType }) {
2040
+ return buildBody(filterByContentType(getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context), contentType), requestBody, operationName, context);
2041
+ }
2042
+ /**
2043
+ * Returns per-content-type bodies when `splitByContentType` is enabled.
2044
+ * Each entry includes a `contentTypeSuffix` for generating distinct function names.
2045
+ */
2046
+ function getBodiesByContentType({ requestBody, operationName, context, contentType }) {
2047
+ const filteredBodyTypes = filterByContentType(getResReqTypes([[context.output.override.components.requestBodies.suffix, requestBody]], operationName, context, void 0, (item) => `${item.value}::${item.contentType}`), contentType);
2048
+ if (filteredBodyTypes.length <= 1) return [{
2049
+ ...buildBody(filteredBodyTypes, requestBody, operationName, context),
2050
+ contentTypeSuffix: ""
2051
+ }];
2052
+ return filteredBodyTypes.map((bodyType) => {
2053
+ const suffix = getContentTypeSuffix(bodyType.contentType);
2054
+ return {
2055
+ ...buildBody([bodyType], requestBody, operationName, context),
2056
+ contentTypeSuffix: suffix
2057
+ };
2058
+ });
2059
+ }
2060
+ const CONTENT_TYPE_SUFFIX_MAP = {
2061
+ "application/json": "Json",
2062
+ "multipart/form-data": "FormData",
2063
+ "application/x-www-form-urlencoded": "UrlEncoded",
2064
+ "text/plain": "Text",
2065
+ "application/xml": "Xml",
2066
+ "text/xml": "Xml",
2067
+ "application/octet-stream": "Blob"
2068
+ };
2069
+ function getContentTypeSuffix(contentType) {
2070
+ if (CONTENT_TYPE_SUFFIX_MAP[contentType]) return CONTENT_TYPE_SUFFIX_MAP[contentType];
2071
+ return (contentType.split("/")[1] ?? contentType).split(/[-+.]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
2072
+ }
2039
2073
 
2040
2074
  //#endregion
2041
2075
  //#region src/getters/imports.ts
@@ -2405,7 +2439,9 @@ function getScalar({ item, name, context, formDataContext }) {
2405
2439
  };
2406
2440
  }
2407
2441
  case "boolean": {
2408
- let value = "boolean" + nullable;
2442
+ let value = "boolean";
2443
+ if (enumItems && !(enumItems.includes(true) && enumItems.includes(false))) value = enumItems.map((enumItem) => `${enumItem}`).join(" | ");
2444
+ value += nullable;
2409
2445
  if (schemaConst !== void 0) value = schemaConst;
2410
2446
  return {
2411
2447
  value,
@@ -2739,12 +2775,21 @@ function resolveDiscriminators(schemas, context) {
2739
2775
  hasProperty: false,
2740
2776
  value: void 0
2741
2777
  };
2742
- const mergedEnumValues = [...((enumProperty.hasProperty && Array.isArray(enumProperty.value) ? enumProperty.value : void 0) ?? []).filter((value) => value !== mappingKey), mappingKey];
2778
+ const enumValues = enumProperty.hasProperty && Array.isArray(enumProperty.value) ? enumProperty.value : void 0;
2779
+ const propertyType = schemaProperty?.type ?? "string";
2780
+ let typedMappingKey = mappingKey;
2781
+ if (propertyType === "boolean") typedMappingKey = mappingKey === "true";
2782
+ else if (propertyType === "number" || propertyType === "integer") {
2783
+ const parsed = Number(mappingKey);
2784
+ if (!Number.isNaN(parsed)) typedMappingKey = parsed;
2785
+ }
2786
+ const mergedEnumValues = [...(enumValues ?? []).filter((value) => value !== typedMappingKey), typedMappingKey];
2743
2787
  const mergedProperty = {
2744
2788
  ...schemaProperty,
2745
- type: "string",
2789
+ type: propertyType,
2746
2790
  enum: mergedEnumValues
2747
2791
  };
2792
+ delete mergedProperty.const;
2748
2793
  subTypeSchema.properties = {
2749
2794
  ...subTypeSchema.properties,
2750
2795
  [propertyName]: mergedProperty
@@ -2777,8 +2822,10 @@ function getParameters({ parameters, context }) {
2777
2822
  header: []
2778
2823
  };
2779
2824
  for (const p of parameters) if (isReference(p)) {
2780
- const { schema: parameter, imports } = resolveRef(p, context);
2781
- if (parameter.in === "path" || parameter.in === "query" || parameter.in === "header") result[parameter.in].push({
2825
+ const { schema, imports } = resolveRef(p, context);
2826
+ const parameter = schema;
2827
+ const location = parameter.in;
2828
+ if (location === "path" || location === "query" || location === "header") result[location].push({
2782
2829
  parameter,
2783
2830
  imports
2784
2831
  });
@@ -3028,7 +3075,7 @@ function getResponse({ responses, operationName, context, contentType }) {
3028
3075
  success: success || (defaultType ?? "unknown"),
3029
3076
  errors: errors || (defaultType ?? "unknown")
3030
3077
  },
3031
- isBlob: success === "Blob",
3078
+ isBlob: groupedByStatus.success.some((t) => !!t.contentType && isBinaryContentType(t.contentType)),
3032
3079
  types: groupedByStatus,
3033
3080
  contentTypes,
3034
3081
  schemas,
@@ -3038,7 +3085,18 @@ function getResponse({ responses, operationName, context, contentType }) {
3038
3085
 
3039
3086
  //#endregion
3040
3087
  //#region src/getters/route.ts
3041
- const TEMPLATE_TAG_IN_PATH_REGEX = /\/([\w]+)(?:\$\{)/g;
3088
+ function isBaseUrlRuntime(baseUrl) {
3089
+ return isObject(baseUrl) && "runtime" in baseUrl && typeof baseUrl.runtime === "string";
3090
+ }
3091
+ /**
3092
+ * Wraps a runtime expression for generated URL template literals.
3093
+ * Pass the expression only (e.g. `process.env.API_BASE_URL`), not a `${...}` fragment.
3094
+ */
3095
+ function runtimeExpressionToUrlPrefix(expression) {
3096
+ const t = expression.trim();
3097
+ if (!t) return "";
3098
+ return "${" + t + "}";
3099
+ }
3042
3100
  const hasParam = (path) => /[^{]*{[\w*_-]*}.*/.test(path);
3043
3101
  const getRoutePath = (path) => {
3044
3102
  const matches = /([^{]*){?([\w*_-]*)}?(.*)/.exec(path);
@@ -3068,6 +3126,7 @@ function getFullRoute(route, servers, baseUrl) {
3068
3126
  const getBaseUrl = () => {
3069
3127
  if (!baseUrl) return "";
3070
3128
  if (isString(baseUrl)) return baseUrl;
3129
+ if (isBaseUrlRuntime(baseUrl)) return runtimeExpressionToUrlPrefix(baseUrl.runtime);
3071
3130
  if (baseUrl.getBaseUrlFromSpecification) {
3072
3131
  if (!servers) throw new Error("Orval is configured to use baseUrl from the specifications 'servers' field, but there exist no servers in the specification.");
3073
3132
  const server = servers.at(Math.min(baseUrl.index ?? 0, servers.length - 1));
@@ -3095,8 +3154,22 @@ function getFullRoute(route, servers, baseUrl) {
3095
3154
  }
3096
3155
  return fullRoute;
3097
3156
  }
3157
+ /**
3158
+ * Returns `GeneratorImport` entries for {@link BaseUrlRuntime.imports} when `baseUrl` is a runtime config.
3159
+ */
3160
+ function getBaseUrlRuntimeImports(baseUrl) {
3161
+ if (!baseUrl) return [];
3162
+ if (!isBaseUrlRuntime(baseUrl)) return [];
3163
+ return baseUrl.imports ?? [];
3164
+ }
3098
3165
  function getRouteAsArray(route) {
3099
- return route.replaceAll(TEMPLATE_TAG_IN_PATH_REGEX, "/$1/${").split("/").filter((i) => i !== "").map((i) => i.includes("${") ? i.replace(TEMPLATE_TAG_REGEX, "$1") : `'${i}'`).join(",").replace(",,", "");
3166
+ return route.split("/").filter((i) => i !== "").flatMap((segment) => {
3167
+ if (!segment.includes("${")) return [`'${segment}'`];
3168
+ return segment.split(/(\$\{.+?\})/g).filter(Boolean).map((part) => {
3169
+ const match = /^\$\{(.+?)\}$/.exec(part);
3170
+ return match ? match[1] : `'${part}'`;
3171
+ });
3172
+ }).join(",");
3100
3173
  }
3101
3174
 
3102
3175
  //#endregion
@@ -3435,7 +3508,7 @@ function removeComments(file) {
3435
3508
  const getAngularFilteredParamsExpression = (paramsExpression, requiredNullableParamKeys = [], preserveRequiredNullables = false) => {
3436
3509
  const filteredParamValueType = `string | number | boolean${preserveRequiredNullables ? " | null" : ""} | Array<string | number | boolean>`;
3437
3510
  const preserveNullableBranch = preserveRequiredNullables ? ` } else if (value === null && requiredNullableParamKeys.has(key)) {
3438
- filteredParams[key] = value;
3511
+ filteredParams[key] = null;
3439
3512
  ` : "";
3440
3513
  return `(() => {
3441
3514
  const requiredNullableParamKeys = new Set<string>(${JSON.stringify(requiredNullableParamKeys)});
@@ -3505,7 +3578,7 @@ function filterParams(
3505
3578
  value === null &&
3506
3579
  requiredNullableKeys.has(key)
3507
3580
  ) {
3508
- filteredParams[key] = value;
3581
+ filteredParams[key] = null;
3509
3582
  } else if (
3510
3583
  value != null &&
3511
3584
  (typeof value === 'string' ||
@@ -3855,43 +3928,15 @@ function generateSchemaDefinitions(schemaName, schema, context, suffix) {
3855
3928
 
3856
3929
  //#endregion
3857
3930
  //#region src/generators/verbs-options.ts
3858
- async function generateVerbOptions({ verb, output, operation, route, pathRoute, verbParameters = [], context }) {
3859
- const { responses, requestBody, parameters: operationParameters, tags: rawTags, deprecated: rawDeprecated, description: rawDescription, summary: rawSummary } = operation;
3860
- const tags = rawTags ?? [];
3861
- const deprecated = rawDeprecated;
3862
- const description = rawDescription;
3863
- const summary = rawSummary;
3864
- const operationId = getOperationId(operation, route, verb);
3865
- const overrideOperation = output.override.operations[operationId];
3866
- let overrideTag = {};
3867
- for (const [tag, options] of Object.entries(output.override.tags)) if (tags.includes(tag) && options) overrideTag = mergeDeep(overrideTag, options);
3868
- const override = mergeDeep(mergeDeep(output.override, overrideTag), overrideOperation ?? {});
3869
- const overrideOperationName = overrideOperation?.operationName ?? output.override.operationName;
3870
- const operationName = overrideOperationName ? overrideOperationName(operation, route, verb) : sanitize(camel(operationId), { es5keyword: true });
3931
+ async function buildVerbOption({ verb, output, operation, route, pathRoute, verbParameters = [], context, body, operationName, operationId, override, tags, deprecated, description, summary }) {
3871
3932
  const response = getResponse({
3872
- responses: responses ?? {},
3933
+ responses: operation.responses ?? {},
3873
3934
  operationName,
3874
3935
  context,
3875
3936
  contentType: override.contentType
3876
3937
  });
3877
- const body = requestBody ? getBody({
3878
- requestBody,
3879
- operationName,
3880
- context,
3881
- contentType: override.contentType
3882
- }) : {
3883
- originalSchema: {},
3884
- definition: "",
3885
- implementation: "",
3886
- imports: [],
3887
- schemas: [],
3888
- formData: "",
3889
- formUrlEncoded: "",
3890
- contentType: "",
3891
- isOptional: false
3892
- };
3893
3938
  const parameters = getParameters({
3894
- parameters: [...verbParameters, ...operationParameters ?? []],
3939
+ parameters: [...verbParameters, ...operation.parameters ?? []],
3895
3940
  context
3896
3941
  });
3897
3942
  const queryParams = getQueryParams({
@@ -3980,6 +4025,83 @@ async function generateVerbOptions({ verb, output, operation, route, pathRoute,
3980
4025
  const transformer = await dynamicImport(override.transformer, context.workspace);
3981
4026
  return transformer ? transformer(verbOption) : verbOption;
3982
4027
  }
4028
+ async function generateVerbOptions({ verb, output, operation, route, pathRoute, verbParameters = [], context }) {
4029
+ const { requestBody, tags: rawTags, deprecated: rawDeprecated, description: rawDescription, summary: rawSummary } = operation;
4030
+ const tags = rawTags ?? [];
4031
+ const deprecated = rawDeprecated;
4032
+ const description = rawDescription;
4033
+ const summary = rawSummary;
4034
+ const operationId = getOperationId(operation, route, verb);
4035
+ const overrideOperation = output.override.operations[operationId];
4036
+ let overrideTag = {};
4037
+ for (const [tag, options] of Object.entries(output.override.tags)) if (tags.includes(tag) && options) overrideTag = mergeDeep(overrideTag, options);
4038
+ const override = mergeDeep(mergeDeep(output.override, overrideTag), overrideOperation ?? {});
4039
+ const overrideOperationName = overrideOperation?.operationName ?? output.override.operationName;
4040
+ const operationName = overrideOperationName ? overrideOperationName(operation, route, verb) : sanitize(camel(operationId), { es5keyword: true });
4041
+ if (override.splitByContentType && requestBody) {
4042
+ const bodies = getBodiesByContentType({
4043
+ requestBody,
4044
+ operationName,
4045
+ context,
4046
+ contentType: override.contentType
4047
+ });
4048
+ const results = [];
4049
+ for (const bodyEntry of bodies) {
4050
+ const { contentTypeSuffix, ...body } = bodyEntry;
4051
+ const verbOption = await buildVerbOption({
4052
+ verb,
4053
+ output,
4054
+ operation,
4055
+ route,
4056
+ pathRoute,
4057
+ verbParameters,
4058
+ context,
4059
+ body,
4060
+ operationName: contentTypeSuffix ? `${operationName}With${contentTypeSuffix}` : operationName,
4061
+ operationId,
4062
+ override,
4063
+ tags,
4064
+ deprecated,
4065
+ description,
4066
+ summary
4067
+ });
4068
+ results.push(verbOption);
4069
+ }
4070
+ return results;
4071
+ }
4072
+ return [await buildVerbOption({
4073
+ verb,
4074
+ output,
4075
+ operation,
4076
+ route,
4077
+ pathRoute,
4078
+ verbParameters,
4079
+ context,
4080
+ body: requestBody ? getBody({
4081
+ requestBody,
4082
+ operationName,
4083
+ context,
4084
+ contentType: override.contentType
4085
+ }) : {
4086
+ originalSchema: {},
4087
+ definition: "",
4088
+ implementation: "",
4089
+ imports: [],
4090
+ schemas: [],
4091
+ formData: "",
4092
+ formUrlEncoded: "",
4093
+ contentType: "",
4094
+ isOptional: false
4095
+ },
4096
+ operationName,
4097
+ operationId,
4098
+ override,
4099
+ tags,
4100
+ deprecated,
4101
+ description,
4102
+ summary
4103
+ })];
4104
+ }
3983
4105
  function generateVerbsOptions({ verbs, input, output, route, pathRoute, context }) {
3984
4106
  return asyncReduce(_filteredVerbs(verbs, input.filters), async (acc, [verb, operation]) => {
3985
4107
  if (isVerb(verb)) {
@@ -3992,7 +4114,7 @@ function generateVerbsOptions({ verbs, input, output, route, pathRoute, context
3992
4114
  operation,
3993
4115
  context
3994
4116
  });
3995
- acc.push(verbOptions);
4117
+ acc.push(...verbOptions);
3996
4118
  }
3997
4119
  return acc;
3998
4120
  }, []);
@@ -4210,7 +4332,7 @@ async function writeSchemas({ schemaPath, schemas, target, namingConvention, fil
4210
4332
  });
4211
4333
  }
4212
4334
  if (indexFiles) {
4213
- const schemaFilePath = nodePath.join(schemaPath, `index${fileExtension}`);
4335
+ const schemaFilePath = nodePath.join(schemaPath, `index.ts`);
4214
4336
  await fs$1.ensureFile(schemaFilePath);
4215
4337
  const ext = fileExtension.endsWith(".ts") ? fileExtension.slice(0, -3) : fileExtension;
4216
4338
  const conventionNamesSet = new Set(Object.values(schemaGroups).map((group) => conventionName(group[0].name, namingConvention)));
@@ -4229,7 +4351,7 @@ function generateImportsForBuilder(output, imports, relativeSchemasPath) {
4229
4351
  let schemaImports;
4230
4352
  if (output.indexFiles) schemaImports = isZodSchemaOutput ? [{
4231
4353
  exports: imports.filter((i) => !i.importPath),
4232
- dependency: joinSafe(relativeSchemasPath, "index.zod")
4354
+ dependency: relativeSchemasPath
4233
4355
  }] : [{
4234
4356
  exports: imports.filter((i) => !i.importPath),
4235
4357
  dependency: relativeSchemasPath
@@ -4376,7 +4498,7 @@ interface TypedResponse<T> extends Response {
4376
4498
 
4377
4499
  //#endregion
4378
4500
  //#region src/writers/single-mode.ts
4379
- async function writeSingleMode({ builder, output, projectName, header, needSchema }) {
4501
+ async function writeSingleMode({ builder, output, projectName, header, needSchema, generateSchemasInline }) {
4380
4502
  try {
4381
4503
  const { path } = getFileInfo(output.target, {
4382
4504
  backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
@@ -4438,7 +4560,7 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4438
4560
  data += getTypedResponse();
4439
4561
  data += "\n";
4440
4562
  }
4441
- if (!output.schemas && needSchema) data += generateModelsInline(builder.schemas);
4563
+ if (!output.schemas && needSchema) data += generateSchemasInline ? generateSchemasInline() : generateModelsInline(builder.schemas);
4442
4564
  data += `${implementation.trim()}\n`;
4443
4565
  if (output.mock) {
4444
4566
  data += "\n\n";
@@ -4454,7 +4576,7 @@ async function writeSingleMode({ builder, output, projectName, header, needSchem
4454
4576
 
4455
4577
  //#endregion
4456
4578
  //#region src/writers/split-mode.ts
4457
- async function writeSplitMode({ builder, output, projectName, header, needSchema }) {
4579
+ async function writeSplitMode({ builder, output, projectName, header, needSchema, generateSchemasInline }) {
4458
4580
  try {
4459
4581
  const { path: targetPath, filename, dirname, extension } = getFileInfo(output.target, {
4460
4582
  backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
@@ -4463,7 +4585,7 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4463
4585
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, paramsSerializer, fetchReviver } = generateTarget(builder, output);
4464
4586
  let implementationData = header;
4465
4587
  let mockData = header;
4466
- const relativeSchemasPath = output.schemas ? getRelativeImportPath(targetPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas";
4588
+ const relativeSchemasPath = output.schemas ? getRelativeImportPath(targetPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas" + extension.replace(/\.ts$/, "");
4467
4589
  const isAllowSyntheticDefaultImports = isSyntheticDefaultImportsAllow(output.tsconfig);
4468
4590
  const importsForBuilder = generateImportsForBuilder(output, imports, relativeSchemasPath);
4469
4591
  implementationData += builder.imports({
@@ -4489,7 +4611,7 @@ async function writeSplitMode({ builder, output, projectName, header, needSchema
4489
4611
  options: isFunction(output.mock) ? void 0 : output.mock
4490
4612
  });
4491
4613
  const schemasPath = output.schemas ? void 0 : nodePath.join(dirname, filename + ".schemas" + extension);
4492
- if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, header + generateModelsInline(builder.schemas));
4614
+ if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, generateSchemasInline ? header + generateSchemasInline() : header + generateModelsInline(builder.schemas));
4493
4615
  if (mutators) implementationData += generateMutatorImports({
4494
4616
  mutators,
4495
4617
  implementation
@@ -4641,7 +4763,7 @@ function generateTargetForTags(builder, options) {
4641
4763
 
4642
4764
  //#endregion
4643
4765
  //#region src/writers/split-tags-mode.ts
4644
- async function writeSplitTagsMode({ builder, output, projectName, header, needSchema }) {
4766
+ async function writeSplitTagsMode({ builder, output, projectName, header, needSchema, generateSchemasInline }) {
4645
4767
  const { filename, dirname, extension } = getFileInfo(output.target, {
4646
4768
  backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4647
4769
  extension: output.fileExtension
@@ -4658,8 +4780,25 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4658
4780
  let implementationData = header;
4659
4781
  let mockData = header;
4660
4782
  const importerPath = nodePath.join(dirname, tag, tag + extension);
4661
- const relativeSchemasPath = output.schemas ? getRelativeImportPath(importerPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "../" + filename + ".schemas";
4662
- const importsForBuilder = generateImportsForBuilder(output, imports, relativeSchemasPath);
4783
+ const relativeSchemasPath = output.schemas ? getRelativeImportPath(importerPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "../" + filename + ".schemas" + extension.replace(/\.ts$/, "");
4784
+ const tagNames = new Set(tagEntries.map(([t]) => t));
4785
+ const serviceSuffix = OutputClient.ANGULAR === output.client ? ".service" : "";
4786
+ const importsForBuilder = generateImportsForBuilder(output, imports.map((imp) => {
4787
+ if (!imp.importPath) return imp;
4788
+ if (!imp.importPath.startsWith(".")) return imp;
4789
+ const resolvedPath = nodePath.resolve(dirname, imp.importPath);
4790
+ const targetBasename = nodePath.basename(resolvedPath);
4791
+ let targetFile;
4792
+ if (tagNames.has(targetBasename)) {
4793
+ const tagFilename = targetBasename + serviceSuffix + extension;
4794
+ targetFile = nodePath.join(resolvedPath, tagFilename);
4795
+ } else targetFile = resolvedPath + extension;
4796
+ const adjustedPath = getRelativeImportPath(importerPath, targetFile);
4797
+ return {
4798
+ ...imp,
4799
+ importPath: adjustedPath
4800
+ };
4801
+ }), relativeSchemasPath);
4663
4802
  implementationData += builder.imports({
4664
4803
  client: output.client,
4665
4804
  implementation,
@@ -4683,7 +4822,7 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4683
4822
  options: isFunction(output.mock) ? void 0 : output.mock
4684
4823
  });
4685
4824
  const schemasPath = output.schemas ? void 0 : nodePath.join(dirname, filename + ".schemas" + extension);
4686
- if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, header + generateModelsInline(builder.schemas));
4825
+ if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, generateSchemasInline ? header + generateSchemasInline() : header + generateModelsInline(builder.schemas));
4687
4826
  if (mutators) implementationData += generateMutatorImports({
4688
4827
  mutators,
4689
4828
  implementation,
@@ -4745,7 +4884,7 @@ async function writeSplitTagsMode({ builder, output, projectName, header, needSc
4745
4884
 
4746
4885
  //#endregion
4747
4886
  //#region src/writers/tags-mode.ts
4748
- async function writeTagsMode({ builder, output, projectName, header, needSchema }) {
4887
+ async function writeTagsMode({ builder, output, projectName, header, needSchema, generateSchemasInline }) {
4749
4888
  const { path: targetPath, filename, dirname, extension } = getFileInfo(output.target, {
4750
4889
  backupFilename: conventionName(builder.info.title ?? "filename", output.namingConvention),
4751
4890
  extension: output.fileExtension
@@ -4756,7 +4895,7 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4756
4895
  try {
4757
4896
  const { imports, implementation, implementationMock, importsMock, mutators, clientMutators, formData, formUrlEncoded, fetchReviver, paramsSerializer } = target;
4758
4897
  let data = header;
4759
- const schemasPathRelative = output.schemas ? getRelativeImportPath(targetPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas";
4898
+ const schemasPathRelative = output.schemas ? getRelativeImportPath(targetPath, getFileInfo(isString(output.schemas) ? output.schemas : output.schemas.path, { extension: output.fileExtension }).dirname) : "./" + filename + ".schemas" + extension.replace(/\.ts$/, "");
4760
4899
  const normalizedImports = imports.filter((imp) => {
4761
4900
  const searchWords = [imp.alias, imp.name].filter((part) => Boolean(part?.length)).map((part) => escapeRegExp(part)).join("|");
4762
4901
  if (!searchWords) return false;
@@ -4793,7 +4932,7 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4793
4932
  });
4794
4933
  }
4795
4934
  const schemasPath = output.schemas ? void 0 : nodePath.join(dirname, filename + ".schemas" + extension);
4796
- if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, header + generateModelsInline(builder.schemas));
4935
+ if (schemasPath && needSchema) await writeGeneratedFile(schemasPath, generateSchemasInline ? header + generateSchemasInline() : header + generateModelsInline(builder.schemas));
4797
4936
  if (mutators) data += generateMutatorImports({
4798
4937
  mutators,
4799
4938
  implementation
@@ -4827,5 +4966,5 @@ async function writeTagsMode({ builder, output, projectName, header, needSchema
4827
4966
  }
4828
4967
 
4829
4968
  //#endregion
4830
- export { BODY_TYPE_NAME, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, _filteredVerbs, addDependency, asyncReduce, camel, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, escapeRegExp, filterByContentType, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBody, getCombinedEnumValue, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, isBinaryContentType, isBoolean, isDirectory, isFunction, isModule, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resolveDiscriminators, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
4969
+ export { BODY_TYPE_NAME, EnumGeneration, ErrorWithTag, FormDataArrayHandling, GetterPropType, LogLevels, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SchemaType, SupportedFormatter, TEMPLATE_TAG_REGEX, URL_REGEX, VERBS_WITH_BODY, Verbs, _filteredVerbs, addDependency, asyncReduce, camel, combineSchemas, compareVersions, conventionName, count, createDebugger, createLogger, createSuccessMessage, createTypeAliasIfNeeded, dedupeUnionType, dynamicImport, escape, escapeRegExp, filterByContentType, fixCrossDirectoryImports, fixRegularSchemaImports, generalJSTypes, generalJSTypesWithArray, generateAxiosOptions, generateBodyMutatorConfig, generateBodyOptions, generateComponentDefinition, generateDependencyImports, generateFormDataAndUrlEncodedFunction, generateImports, generateModelInline, generateModelsInline, generateMutator, generateMutatorConfig, generateMutatorImports, generateMutatorRequestOptions, generateOptions, generateParameterDefinition, generateQueryParamsAxiosConfig, generateSchemasDefinition, generateTarget, generateTargetForTags, generateVerbImports, generateVerbOptions, generateVerbsOptions, getAngularFilteredParamsCallExpression, getAngularFilteredParamsExpression, getAngularFilteredParamsHelperBody, getArray, getBaseUrlRuntimeImports, getBodiesByContentType, getBody, getCombinedEnumValue, getDefaultContentType, getEnum, getEnumDescriptions, getEnumImplementation, getEnumNames, getEnumUnionFromSchema, getExtension, getFileInfo, getFormDataFieldFileType, getFullRoute, getIsBodyVerb, getKey, getMockFileExtensionByTypeName, getNumberWord, getObject, getOperationId, getOrvalGeneratedTypes, getParameters, getParams, getParamsInPath, getPropertySafe, getProps, getQueryParams, getRefInfo, getResReqTypes, getResponse, getResponseTypeCategory, getRoute, getRouteAsArray, getScalar, getSuccessResponseType, getTypedResponse, getWarningCount, isBinaryContentType, isBoolean, isDirectory, isFunction, isModule, isNullish, isNumber, isNumeric, isObject, isReference, isSchema, isString, isStringLike, isSyntheticDefaultImportsAllow, isUrl, isVerb, isVerbose, jsDoc, jsStringEscape, kebab, keyValuePairsToJsDoc, log, logError, logVerbose, logWarning, mergeDeep, mismatchArgsMessage, pascal, removeFilesAndEmptyFolders, resetWarnings, resolveDiscriminators, resolveExampleRefs, resolveInstalledVersion, resolveInstalledVersions, resolveObject, resolveRef, resolveValue, sanitize, setVerbose, snake, sortByPriority, splitSchemasByType, startMessage, stringify, toObjectString, path_exports as upath, upper, writeModelInline, writeModelsInline, writeSchema, writeSchemas, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode };
4831
4970
  //# sourceMappingURL=index.mjs.map