orval
Version:
A swagger client generator for typescript
1,042 lines • 129 kB
JavaScript
import { createRequire } from "node:module";
import path from "node:path";
import { DefaultTag, FormDataArrayHandling, GetterPropType, NamingConvention, OutputClient, OutputHttpClient, OutputMockType, OutputMode, PropertySortOrder, RefComponentSuffix, SupportedFormatter, asyncReduce, buildDynamicScope, buildSchemaTagMap, collectReferencedComponents, conventionName, createSuccessMessage, dynamicImport, fixCrossDirectoryImports, fixRegularSchemaImports, generateComponentDefinition, generateDependencyImports, generateMutator, generateParameterDefinition, generateSchemasDefinition, generateVerbsOptions, getBaseUrlRuntimeImports, getFileInfo, getFullRoute, getImportExtension, getMockFileExtensionByTypeName, getRefInfo, getRoute, isBoolean, isComponentRef, isFunction, isNullish, isObject, isReference, isString, isUrl, jsDoc, kebab, log, logError, logVerbose, logWarning, pascal, removeFilesAndEmptyFolders, resolveInstalledVersions, resolveRef, resolveValue, splitSchemasByType, upath, writeGeneratedFile, writeSchemas, writeSchemasTagsSplit, writeSingleMode, writeSplitMode, writeSplitTagsMode, writeTagsMode, writeTagsOperationsMode, writeTagsOperationsSplitMode } from "@orval/core";
import { bundle } from "@scalar/json-magic/bundle";
import { fetchUrls, parseJson, parseYaml, readFiles } from "@scalar/json-magic/bundle/plugins/node";
import { upgrade, validate } from "@scalar/openapi-parser";
import fs, { access, readFile } from "node:fs/promises";
import { isNullish as isNullish$1, pick } from "remeda";
import jsYaml from "js-yaml";
import * as mock from "@orval/mock";
import { dedupeStrictMockTypeDeclarations, generateFakerForSchemas, generateMockImports, getDefaultMockOptionsForType } from "@orval/mock";
import angular from "@orval/angular";
import axios from "@orval/axios";
import effect from "@orval/effect";
import fetchClient from "@orval/fetch";
import hono from "@orval/hono";
import mcp from "@orval/mcp";
import query from "@orval/query";
import solidStart from "@orval/solid-start";
import swr from "@orval/swr";
import zod, { assertZodTarget, dereference, generateFormDataZodSchema, generateZodValidationSchemaDefinition, getZodImportSource, getZodTypeName, parseZodValidationSchemaDefinition, resolveIsZodV4 } from "@orval/zod";
import fs$1, { existsSync } from "node:fs";
import { styleText } from "node:util";
import { findUp, findUpMultiple } from "find-up";
import fs$2 from "fs-extra";
import { parseTsconfig } from "get-tsconfig";
import { ExecaError, execa } from "execa";
import { parseArgsStringToArgv } from "string-argv";
import { createJiti } from "jiti";
//#region package.json
var name = "orval";
var version = "8.23.0";
var description = "A swagger client generator for typescript";
//#endregion
//#region src/client.ts
const DEFAULT_CLIENT = OutputClient.AXIOS;
const getGeneratorClient = (outputClient, output) => {
const angularBuilder = angular();
const GENERATOR_CLIENT = {
axios: axios({ type: "axios" })(),
"axios-functions": axios({ type: "axios-functions" })(),
angular: angularBuilder(output.override.angular),
"angular-query": query({
output,
type: "angular-query"
})(),
"react-query": query({
output,
type: "react-query"
})(),
"solid-start": solidStart()(),
"solid-query": query({
output,
type: "solid-query"
})(),
"svelte-query": query({
output,
type: "svelte-query"
})(),
"vue-query": query({
output,
type: "vue-query"
})(),
swr: swr()(),
zod: zod()(),
effect: effect()(),
hono: hono()(),
fetch: fetchClient()(),
mcp: mcp()()
};
const generator = isFunction(outputClient) ? outputClient(GENERATOR_CLIENT) : GENERATOR_CLIENT[outputClient];
if (!generator) throw new Error(`Unknown output client provided to getGeneratorClient: ${String(outputClient)}`);
return generator;
};
const generateClientImports = ({ client, implementation, imports, projectName, hasSchemaDir, isAllowSyntheticDefaultImports, hasGlobalMutator, hasTagsMutator, hasParamsSerializerOptions, packageJson, output }) => {
const { dependencies } = getGeneratorClient(client, output);
return generateDependencyImports(implementation, dependencies ? [...dependencies(hasGlobalMutator, hasParamsSerializerOptions, packageJson, output.httpClient, hasTagsMutator, output.override), ...imports] : imports, projectName, hasSchemaDir, isAllowSyntheticDefaultImports);
};
const generateClientHeader = ({ outputClient = DEFAULT_CLIENT, isRequestOptions, isGlobalMutator, isMutator, provideIn, hasAwaitedType, titles, output, verbOptions, tag, isDefaultTagBucket, clientImplementation }) => {
const { header } = getGeneratorClient(outputClient, output);
const rawHeader = header ? header({
title: titles.implementation,
isRequestOptions,
isGlobalMutator,
isMutator,
provideIn,
hasAwaitedType,
output,
verbOptions,
tag,
isDefaultTagBucket,
clientImplementation
}) : "";
const normalizedHeader = typeof rawHeader === "string" ? { implementation: rawHeader } : rawHeader;
return {
implementation: normalizedHeader.implementation,
implementationMock: `export const ${titles.implementationMock} = () => [\n`,
sharedTypes: normalizedHeader.sharedTypes
};
};
const generateClientFooter = ({ outputClient, operationNames, operations, hasMutator, hasAwaitedType, titles, output }) => {
const { footer } = getGeneratorClient(outputClient, output);
if (!footer) return {
implementation: "",
implementationMock: `\n]\n`
};
let implementation;
try {
if (isFunction(outputClient)) {
implementation = footer(operationNames);
logWarning("⚠️ Passing an array of strings for operations names to the footer function is deprecated and will be removed in a future major release. Please pass them in an object instead: { operationNames: string[] }.");
} else implementation = footer({
operationNames,
operations,
title: titles.implementation,
hasMutator,
hasAwaitedType
});
} catch {
implementation = footer({
operationNames,
operations,
title: titles.implementation,
hasMutator,
hasAwaitedType
});
}
return {
implementation,
implementationMock: `]\n`
};
};
const generateClientTitle = ({ outputClient = DEFAULT_CLIENT, title, customTitleFunc, output }) => {
const { title: generatorTitle } = getGeneratorClient(outputClient, output);
if (!generatorTitle) return {
implementation: "",
implementationMock: `get${pascal(title)}Mock`
};
if (customTitleFunc) {
const customTitle = customTitleFunc(title);
return {
implementation: generatorTitle(customTitle),
implementationMock: `get${pascal(customTitle)}Mock`
};
}
return {
implementation: generatorTitle(title),
implementationMock: `get${pascal(title)}Mock`
};
};
/**
* Invokes the underlying mock generator (msw, faker, or a user-provided
* ClientMockBuilder) for a single generator entry. Returns the standard
* `ClientMockGeneratorBuilder` shape (function/handler/handlerName +
* imports) regardless of which generator handled it.
*/
const invokeMockGenerator = (verbOption, options, entry) => {
if (isFunction(entry)) return entry(verbOption, {
...options,
mock: entry
});
return mock.generateMock(verbOption, {
...options,
mock: entry
});
};
const generateOperations = (outputClient = DEFAULT_CLIENT, verbsOptions, options, output) => {
const baseUrlImports = getBaseUrlRuntimeImports(output.baseUrl);
return asyncReduce(verbsOptions, async (acc, verbOption) => {
const { client: generatorClient } = getGeneratorClient(outputClient, output);
const client = await generatorClient(verbOption, options, outputClient, output);
if (!client.implementation) return acc;
const mockOutputs = output.mock.generators.filter((entry) => {
if (!isFunction(entry) && entry.type === OutputMockType.FAKER && entry.operationResponses === false) return false;
return true;
}).map((entry) => {
const generated = invokeMockGenerator(verbOption, options, entry);
return {
type: isFunction(entry) ? OutputMockType.MSW : entry.type,
implementation: generated.implementation,
imports: generated.imports,
strictMockSchemaTypeNames: generated.strictMockSchemaTypeNames,
strictMockSchemaKinds: generated.strictMockSchemaKinds
};
});
const hasImplementation = client.implementation.trim().length > 0;
const preferredOperationKey = verbOption.operationName;
const baseOperationKey = verbOption.operationId ? `${verbOption.operationId}::${verbOption.operationName}` : verbOption.operationName;
let operationKey = Object.hasOwn(acc, preferredOperationKey) ? baseOperationKey : preferredOperationKey;
let collisionIndex = 1;
while (Object.hasOwn(acc, operationKey)) {
collisionIndex += 1;
operationKey = `${baseOperationKey}::${collisionIndex}`;
}
acc[operationKey] = {
implementation: hasImplementation ? (client.docComment ?? verbOption.doc) + client.implementation : client.implementation,
imports: [...baseUrlImports, ...client.imports],
mockOutputs,
tags: verbOption.tags,
mutator: verbOption.mutator,
clientMutators: client.mutators,
formData: verbOption.formData,
formUrlEncoded: verbOption.formUrlEncoded,
paramsSerializer: verbOption.paramsSerializer,
paramsFilter: verbOption.paramsFilter,
operationName: verbOption.operationName,
fetchReviver: verbOption.fetchReviver,
...client.returnType ? { types: { result: client.returnType } } : void 0
};
return acc;
}, {});
};
const generateExtraFiles = (outputClient = DEFAULT_CLIENT, verbsOptions, output, context) => {
const { extraFiles: generateExtraFiles } = getGeneratorClient(outputClient, output);
if (!generateExtraFiles) return Promise.resolve([]);
return generateExtraFiles(verbsOptions, output, context);
};
//#endregion
//#region src/api.ts
async function getApiBuilder({ input, output, context }) {
const api = await asyncReduce(Object.entries(context.spec.paths ?? {}), async (acc, [pathRoute, verbs]) => {
if (!verbs) return acc;
const route = getRoute(pathRoute);
let resolvedVerbs = verbs;
if (isReference(verbs)) {
const { schema } = resolveRef(verbs, context);
resolvedVerbs = schema;
}
let verbsOptions = await generateVerbsOptions({
verbs: resolvedVerbs,
input,
output,
route,
pathRoute,
context
});
if (output.override.useDeprecatedOperations === false) verbsOptions = verbsOptions.filter((verb) => {
return !verb.deprecated;
});
const schemas = [];
for (const { queryParams, headers, body, response, props } of verbsOptions) {
schemas.push(...props.flatMap((param) => param.type === GetterPropType.NAMED_PATH_PARAMS ? param.schema : []));
if (queryParams) schemas.push(queryParams.schema, ...queryParams.deps);
if (headers) schemas.push(headers.schema, ...headers.deps);
schemas.push(...body.schemas, ...response.schemas);
}
const fullRoute = getFullRoute(route, resolvedVerbs.servers ?? context.spec.servers, output.baseUrl);
if (!output.target) throw new Error("Output does not have a target");
const pathOperations = await generateOperations(output.client, verbsOptions, {
route: fullRoute,
pathRoute,
override: output.override,
context,
output: output.target
}, output);
for (const verbOption of verbsOptions) acc.verbOptions[verbOption.operationId] = verbOption;
acc.schemas.push(...schemas);
for (const [key, value] of Object.entries(pathOperations)) {
let operationKey = key;
let counter = 1;
while (Object.hasOwn(acc.operations, operationKey)) operationKey = `${key}::${++counter}`;
acc.operations[operationKey] = value;
}
return acc;
}, {
operations: {},
verbOptions: {},
schemas: []
});
const extraFiles = await generateExtraFiles(output.client, api.verbOptions, output, context);
return {
operations: api.operations,
schemas: api.schemas,
verbOptions: api.verbOptions,
title: generateClientTitle,
header: generateClientHeader,
footer: generateClientFooter,
imports: generateClientImports,
importsMock: generateMockImports,
finalizeMockImplementation: dedupeStrictMockTypeDeclarations,
extraFiles
};
}
//#endregion
//#region src/import-open-api.ts
function filterSpecComponents(spec, input) {
const filters = input.filters;
if (!filters?.tags || filters.schemas) return spec;
const referenced = collectReferencedComponents(spec, filters.tags, filters.mode);
const allSchemas = spec.components?.schemas ?? {};
return {
...spec,
components: {
...spec.components,
schemas: filters.includeUnreferencedSchemas ? allSchemas : pick(allSchemas, referenced.schemas),
responses: pick(spec.components?.responses ?? {}, referenced.responses),
parameters: pick(spec.components?.parameters ?? {}, referenced.parameters),
requestBodies: pick(spec.components?.requestBodies ?? {}, referenced.requestBodies)
}
};
}
async function importOpenApi({ spec, input, output, target, workspace, projectName }) {
const filteredSpec = filterSpecComponents(spec, input);
const schemas = getApiSchemas({
input,
output,
target,
workspace,
spec: filteredSpec
});
const api = await getApiBuilder({
input,
output,
context: {
projectName,
target,
workspace,
spec: filteredSpec,
output
}
});
return {
...api,
schemas: [...schemas, ...api.schemas],
target,
info: filteredSpec.info,
spec: filteredSpec
};
}
function getApiSchemas({ input, output, target, workspace, spec }) {
const context = {
target,
workspace,
spec,
output
};
const schemaDefinition = generateSchemasDefinition(spec.components?.schemas, context, output.override.components.schemas.suffix, input.filters);
const responseDefinition = generateComponentDefinition(spec.components?.responses, context, output.override.components.responses.suffix);
const swaggerResponseDefinition = generateComponentDefinition("responses" in spec ? spec.responses : void 0, context, "");
const bodyDefinition = generateComponentDefinition(spec.components?.requestBodies, context, output.override.components.requestBodies.suffix);
const parameters = generateParameterDefinition(spec.components?.parameters, context, output.override.components.parameters.suffix);
return [
...schemaDefinition,
...responseDefinition,
...swaggerResponseDefinition,
...bodyDefinition,
...parameters
];
}
//#endregion
//#region src/utils/package-json.ts
const loadPackageJson = async (packageJson, workspace = process.cwd()) => {
if (!packageJson) {
const pkgPath = await findUp(["package.json"], { cwd: workspace });
if (pkgPath) {
const pkg = await dynamicImport(pkgPath, workspace);
if (isPackageJson(pkg)) return resolveAndAttachVersions(await maybeReplaceCatalog(pkg, workspace), workspace, pkgPath);
else throw new Error("Invalid package.json file");
}
return;
}
const normalizedPath = normalizePath(packageJson, workspace);
if (fs$2.existsSync(normalizedPath)) {
const pkg = await dynamicImport(normalizedPath);
if (isPackageJson(pkg)) return resolveAndAttachVersions(await maybeReplaceCatalog(pkg, workspace), workspace, normalizedPath);
else throw new Error(`Invalid package.json file: ${normalizedPath}`);
}
};
const isPackageJson = (obj) => isObject(obj);
const resolvedCache = /* @__PURE__ */ new Map();
const resolveAndAttachVersions = (pkg, workspace, cacheKey) => {
const cached = resolvedCache.get(cacheKey);
if (cached) {
pkg.resolvedVersions = cached;
return pkg;
}
const resolved = resolveInstalledVersions(pkg, workspace);
if (Object.keys(resolved).length > 0) {
pkg.resolvedVersions = resolved;
resolvedCache.set(cacheKey, resolved);
for (const [name, version] of Object.entries(resolved)) logVerbose(styleText("dim", `Detected ${styleText("white", name)} v${styleText("white", version)}`));
}
return pkg;
};
const hasCatalogReferences = (pkg) => {
return [
...Object.entries(pkg.dependencies ?? {}),
...Object.entries(pkg.devDependencies ?? {}),
...Object.entries(pkg.peerDependencies ?? {})
].some(([, value]) => isString(value) && value.startsWith("catalog:"));
};
const loadPnpmWorkspaceCatalog = async (workspace) => {
const filePath = await findUp("pnpm-workspace.yaml", { cwd: workspace });
if (!filePath) return void 0;
try {
const file = await fs$2.readFile(filePath, "utf8");
const data = jsYaml.load(file);
if (!data?.catalog && !data?.catalogs) return void 0;
return {
catalog: data.catalog,
catalogs: data.catalogs
};
} catch {
return;
}
};
const loadPackageJsonCatalog = async (workspace) => {
const filePaths = await findUpMultiple("package.json", { cwd: workspace });
for (const filePath of filePaths) try {
const pkg = await fs$2.readJson(filePath);
if (pkg.catalog || pkg.catalogs) return {
catalog: pkg.catalog,
catalogs: pkg.catalogs
};
} catch {}
};
const loadYarnrcCatalog = async (workspace) => {
const filePath = await findUp(".yarnrc.yml", { cwd: workspace });
if (!filePath) return void 0;
try {
const file = await fs$2.readFile(filePath, "utf8");
const data = jsYaml.load(file);
if (!data?.catalog && !data?.catalogs) return void 0;
return {
catalog: data.catalog,
catalogs: data.catalogs
};
} catch {
return;
}
};
const maybeReplaceCatalog = async (pkg, workspace) => {
if (!hasCatalogReferences(pkg)) return pkg;
const catalogData = await loadPnpmWorkspaceCatalog(workspace) ?? await loadPackageJsonCatalog(workspace) ?? await loadYarnrcCatalog(workspace);
if (!catalogData) {
logWarning("⚠️ package.json contains catalog: references, but no catalog source was found (checked: pnpm-workspace.yaml, package.json, .yarnrc.yml).");
return pkg;
}
performSubstitution(pkg.dependencies, catalogData);
performSubstitution(pkg.devDependencies, catalogData);
performSubstitution(pkg.peerDependencies, catalogData);
return pkg;
};
const performSubstitution = (dependencies, catalogData) => {
if (!dependencies) return;
for (const [packageName, version] of Object.entries(dependencies)) if (version === "catalog:" || version === "catalog:default") {
if (!catalogData.catalog) {
logWarning(`⚠️ catalog: substitution for the package '${packageName}' failed as there is no default catalog.`);
continue;
}
const sub = catalogData.catalog[packageName];
if (!sub) {
logWarning(`⚠️ catalog: substitution for the package '${packageName}' failed as there is no matching package in the default catalog.`);
continue;
}
dependencies[packageName] = sub;
} else if (version.startsWith("catalog:")) {
const catalogName = version.slice(8);
const catalog = catalogData.catalogs?.[catalogName];
if (!catalog) {
logWarning(`⚠️ '${version}' substitution for the package '${packageName}' failed as there is no matching catalog named '${catalogName}'. (available named catalogs are: ${Object.keys(catalogData.catalogs ?? {}).join(", ")})`);
continue;
}
const sub = catalog[packageName];
if (!sub) {
logWarning(`⚠️ '${version}' substitution for the package '${packageName}' failed as there is no package in the catalog named '${catalogName}'. (packages in the catalog are: ${Object.keys(catalog).join(", ")})`);
continue;
}
dependencies[packageName] = sub;
}
};
//#endregion
//#region src/utils/tsconfig.ts
const convertTarget = (config) => {
if (!config.compilerOptions?.target) return {
baseUrl: config.compilerOptions?.baseUrl,
...config
};
const lowercaseTarget = config.compilerOptions.target.toLowerCase();
return {
baseUrl: config.compilerOptions.baseUrl,
...config,
compilerOptions: {
...config.compilerOptions,
target: lowercaseTarget
}
};
};
const loadTsconfig = async (tsconfig, workspace = process.cwd()) => {
if (isNullish(tsconfig)) {
const configPath = await findUp(["tsconfig.json", "jsconfig.json"], { cwd: workspace });
if (configPath) return convertTarget(parseTsconfig(configPath));
return;
}
if (isString(tsconfig)) {
const normalizedPath = normalizePath(tsconfig, workspace);
if (fs$2.existsSync(normalizedPath)) return convertTarget(parseTsconfig(normalizedPath));
return;
}
if (isObject(tsconfig)) return tsconfig;
};
//#endregion
//#region src/utils/options.ts
const INPUT_TARGET_FETCH_TIMEOUT_MS = 1e4;
/**
* Type helper to make it easier to use orval.config.ts
* accepts a direct {@link ConfigExternal} object.
*/
function defineConfig(options) {
return options;
}
/**
* Type helper to make it easier to write input transformers.
* accepts a direct {@link InputTransformerFn} function.
*/
function defineTransformer(transformer) {
return transformer;
}
function createFormData(workspace, formData) {
const defaultArrayHandling = FormDataArrayHandling.SERIALIZE;
if (formData === void 0) return {
disabled: false,
arrayHandling: defaultArrayHandling
};
if (isBoolean(formData)) return {
disabled: !formData,
arrayHandling: defaultArrayHandling
};
if (isString(formData)) return {
disabled: false,
mutator: normalizeMutator(workspace, formData),
arrayHandling: defaultArrayHandling
};
if ("mutator" in formData || "arrayHandling" in formData) return {
disabled: false,
mutator: normalizeMutator(workspace, formData.mutator),
arrayHandling: formData.arrayHandling ?? defaultArrayHandling
};
return {
disabled: false,
mutator: normalizeMutator(workspace, formData),
arrayHandling: defaultArrayHandling
};
}
function normalizeSchemasOption(schemas, workspace) {
if (!schemas) return;
if (isString(schemas)) return normalizePath(schemas, workspace);
if (!isString(schemas.path) || schemas.path.trim() === "") throw new Error(styleText("red", `\`schemas.path\` is required when \`schemas\` is an object (e.g. \`schemas: { path: './model', type: 'zod' }\`). To generate schemas alongside the target instead, omit \`schemas\` or set \`schemas: false\`.`));
validatePackageSpecifier(schemas.importPath, "schemas.importPath");
return {
path: normalizePath(schemas.path, workspace),
type: schemas.type ?? "typescript",
importPath: schemas.importPath,
splitByTags: schemas.splitByTags ?? false
};
}
/**
* Validates that a config value is a valid package specifier (bare specifier
* or sub-path import like `@acme/models` / `@acme/models/fakers`). Rejects
* empty, whitespace-only, relative (`./`, `../`), and absolute paths with a
* clear, actionable error message. No-op when the value is `undefined`.
*/
function validatePackageSpecifier(value, fieldName) {
if (value === void 0) return;
if (!value) throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received an empty string.`);
if (value.trim() === "") throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received a whitespace-only string.`);
if (value.trim() !== value) throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received a value with leading or trailing whitespace: "${value}"`);
if (value.startsWith(".")) throw new Error(`\`${fieldName}\` must be a package specifier (e.g. '@acme/models'), not a relative path. Received: "${value}"`);
if (path.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\")) throw new Error(`\`${fieldName}\` must be a package specifier (e.g. '@acme/models'), not an absolute path. Received: "${value}"`);
}
function looksLikePackageSpecifier(value) {
return !!value && value.trim() === value && !value.startsWith(".") && !path.isAbsolute(value) && !/^[A-Za-z]:[\\/]/.test(value) && !value.startsWith("\\\\");
}
function resolvePackageSpecifier(workspace, value) {
try {
return createRequire(path.join(workspace, "package.json")).resolve(value);
} catch {
return;
}
}
function isPackageSpecifierCandidate(workspace, value) {
if (!looksLikePackageSpecifier(value)) return false;
if (existsSync(path.resolve(workspace, value))) return false;
if (value.startsWith("@")) return true;
const [packageName] = value.split("/");
if (!value.includes("/")) return true;
for (let dir = workspace;;) {
if (existsSync(path.join(dir, "node_modules", packageName))) return true;
const parent = path.dirname(dir);
if (parent === dir) return false;
dir = parent;
}
}
function normalizeEffectOptions(effect) {
return {
strict: {
param: effect?.strict?.param ?? false,
query: effect?.strict?.query ?? false,
header: effect?.strict?.header ?? false,
body: effect?.strict?.body ?? false,
response: effect?.strict?.response ?? false
},
generate: {
param: effect?.generate?.param ?? true,
query: effect?.generate?.query ?? true,
header: effect?.generate?.header ?? true,
body: effect?.generate?.body ?? true,
response: effect?.generate?.response ?? true
},
generateEachHttpStatus: effect?.generateEachHttpStatus ?? false,
useBrandedTypes: effect?.useBrandedTypes ?? false,
exactOptional: effect?.exactOptional ?? false
};
}
async function normalizeOptions(optionsExport, workspace = process.cwd(), globalOptions = {}) {
const options = await (isFunction(optionsExport) ? optionsExport() : optionsExport);
if (!options.input) throw new Error(styleText("red", `Config requires an input.`));
if (!options.output) throw new Error(styleText("red", `Config requires an output.`));
const inputOptions = isString(options.input) || Array.isArray(options.input) ? { target: options.input } : options.input;
const outputOptions = isString(options.output) ? { target: options.output } : options.output;
const outputWorkspace = normalizePath(outputOptions.workspace ?? "", workspace);
const { clean, client, httpClient, mode } = globalOptions;
const tsconfig = await loadTsconfig(outputOptions.tsconfig ?? globalOptions.tsconfig, workspace);
const packageJson = await loadPackageJson(outputOptions.packageJson ?? globalOptions.packageJson, workspace);
const mocksOption = outputOptions.mock ?? globalOptions.mock;
let mocks = {
indexMockFiles: false,
generators: []
};
if (isBoolean(mocksOption) && mocksOption) mocks = {
indexMockFiles: false,
generators: [getDefaultMockOptionsForType(OutputMockType.MSW), getDefaultMockOptionsForType(OutputMockType.FAKER)]
};
else if (isFunction(mocksOption)) mocks = {
indexMockFiles: false,
generators: [mocksOption]
};
else if (mocksOption && typeof mocksOption === "object") {
if (!Array.isArray(mocksOption.generators)) throw new TypeError("mock.generators must be an array of generator entries (e.g. [{ type: \"msw\" }]).");
const sharedMockPath = mocksOption.path && isString(mocksOption.path) ? normalizePath(mocksOption.path, outputWorkspace) : void 0;
mocks = {
indexMockFiles: mocksOption.indexMockFiles ?? false,
path: sharedMockPath,
generators: mocksOption.generators.map((m) => isFunction(m) ? m : {
...getDefaultMockOptionsForType(m.type),
...m,
path: m.path && isString(m.path) ? normalizePath(m.path, outputWorkspace) : sharedMockPath
})
};
}
const seenMockTypes = /* @__PURE__ */ new Set();
for (const entry of mocks.generators) {
if (isFunction(entry)) continue;
if (seenMockTypes.has(entry.type)) throw new Error(`Duplicate mock generator type "${entry.type}". Each type can only appear once in mock.generators.`);
seenMockTypes.add(entry.type);
if (entry.type === OutputMockType.FAKER) validatePackageSpecifier(entry.schemasImportPath, "mock.generators[faker].schemasImportPath");
}
const defaultFileExtension = ".ts";
const defaultSchemaFileExtension = !!outputOptions.schemas && (!isString(outputOptions.schemas) && outputOptions.schemas.type === "zod" || isString(outputOptions.schemas) && (outputOptions.client ?? client) === "zod" && outputOptions.override?.zod?.generateReusableSchemas === true) ? ".zod.ts" : defaultFileExtension;
const factoryMethodsConfig = outputOptions.factoryMethods;
let factoryMethods = void 0;
if (factoryMethodsConfig) factoryMethods = {
functionNamePrefix: factoryMethodsConfig.functionNamePrefix ?? "create",
mode: factoryMethodsConfig.mode ?? "split",
outputDirectory: factoryMethodsConfig.outputDirectory ? normalizePath(factoryMethodsConfig.outputDirectory, outputWorkspace) : outputOptions.schemas ? normalizePath(isString(outputOptions.schemas) ? outputOptions.schemas : outputOptions.schemas.path, outputWorkspace) : normalizePath(outputWorkspace, outputWorkspace),
includeOptionalProperty: factoryMethodsConfig.includeOptionalProperty ?? true
};
const globalQueryOptions = {
signal: true,
shouldExportMutatorHooks: true,
shouldExportHttpClient: true,
shouldExportQueryKey: true,
shouldFilterQueryKey: false,
shouldSplitQueryKey: false,
...normalizeQueryOptions(outputOptions.override?.query, outputWorkspace)
};
const normalizedOptions = {
input: {
target: globalOptions.input ? Array.isArray(globalOptions.input) ? await resolveFirstValidTarget(globalOptions.input, process.cwd(), inputOptions.parserOptions) : normalizePathOrUrl(globalOptions.input, process.cwd()) : Array.isArray(inputOptions.target) ? await resolveFirstValidTarget(inputOptions.target, workspace, inputOptions.parserOptions) : normalizePathOrUrl(inputOptions.target, workspace),
override: { transformer: normalizePath(inputOptions.override?.transformer, workspace) },
unsafeDisableValidation: inputOptions.unsafeDisableValidation ?? false,
filters: inputOptions.filters,
parserOptions: inputOptions.parserOptions
},
output: {
target: globalOptions.output ? normalizePath(globalOptions.output, process.cwd()) : normalizePath(outputOptions.target, outputWorkspace),
schemas: normalizeSchemasOption(outputOptions.schemas, outputWorkspace),
operationSchemas: outputOptions.operationSchemas ? normalizePath(outputOptions.operationSchemas, outputWorkspace) : void 0,
namingConvention: outputOptions.namingConvention ?? NamingConvention.CAMEL_CASE,
fileExtension: outputOptions.fileExtension ?? defaultFileExtension,
schemaFileExtension: outputOptions.schemaFileExtension ?? outputOptions.fileExtension ?? defaultSchemaFileExtension,
workspace: outputOptions.workspace ? outputWorkspace : void 0,
client: outputOptions.client ?? client ?? OutputClient.AXIOS_FUNCTIONS,
httpClient: outputOptions.httpClient ?? httpClient ?? ((outputOptions.client ?? client) === OutputClient.ANGULAR_QUERY ? OutputHttpClient.ANGULAR : OutputHttpClient.FETCH),
mode: normalizeOutputMode(outputOptions.mode ?? mode),
mock: mocks,
clean: outputOptions.clean ?? clean ?? false,
docs: outputOptions.docs ?? false,
formatter: outputOptions.formatter ?? globalOptions.formatter,
tsconfig,
packageJson,
headers: outputOptions.headers ?? false,
indexFiles: outputOptions.indexFiles ?? true,
baseUrl: outputOptions.baseUrl,
unionAddMissingProperties: outputOptions.unionAddMissingProperties ?? false,
factoryMethods,
tagsSplitDeduplication: outputOptions.tagsSplitDeduplication ?? false,
commonTypesFileName: outputOptions.commonTypesFileName ?? "common-types",
override: {
...outputOptions.override,
mock: {
arrayMin: outputOptions.override?.mock?.arrayMin ?? 1,
arrayMax: outputOptions.override?.mock?.arrayMax ?? 10,
stringMin: outputOptions.override?.mock?.stringMin ?? 10,
stringMax: outputOptions.override?.mock?.stringMax ?? 20,
fractionDigits: outputOptions.override?.mock?.fractionDigits ?? 2,
...outputOptions.override?.mock
},
operations: normalizeOperationsAndTags(outputOptions.override?.operations ?? {}, outputWorkspace, { query: globalQueryOptions }, "operations"),
tags: normalizeOperationsAndTags(outputOptions.override?.tags ?? {}, outputWorkspace, { query: globalQueryOptions }, "tags"),
mutator: normalizeMutator(outputWorkspace, outputOptions.override?.mutator),
formData: createFormData(outputWorkspace, outputOptions.override?.formData),
formUrlEncoded: (isBoolean(outputOptions.override?.formUrlEncoded) ? outputOptions.override.formUrlEncoded : normalizeMutator(outputWorkspace, outputOptions.override?.formUrlEncoded)) ?? true,
paramsSerializer: normalizeMutator(outputWorkspace, outputOptions.override?.paramsSerializer),
paramsFilter: normalizeMutator(outputWorkspace, outputOptions.override?.paramsFilter),
header: outputOptions.override?.header === false ? false : isFunction(outputOptions.override?.header) ? outputOptions.override.header : getDefaultFilesHeader,
requestOptions: outputOptions.override?.requestOptions ?? true,
namingConvention: outputOptions.override?.namingConvention ?? {},
components: {
schemas: {
suffix: RefComponentSuffix.schemas,
itemSuffix: outputOptions.override?.components?.schemas?.itemSuffix ?? "Item",
...outputOptions.override?.components?.schemas
},
responses: {
suffix: RefComponentSuffix.responses,
...outputOptions.override?.components?.responses
},
parameters: {
suffix: RefComponentSuffix.parameters,
...outputOptions.override?.components?.parameters
},
requestBodies: {
suffix: RefComponentSuffix.requestBodies,
...outputOptions.override?.components?.requestBodies
}
},
hono: normalizeHonoOptions(outputOptions.override?.hono, workspace),
mcp: normalizeMcpOptions(outputOptions.override?.mcp, workspace),
jsDoc: normalizeJSDocOptions(outputOptions.override?.jsDoc),
query: globalQueryOptions,
zod: {
strict: {
param: outputOptions.override?.zod?.strict?.param ?? false,
query: outputOptions.override?.zod?.strict?.query ?? false,
header: outputOptions.override?.zod?.strict?.header ?? false,
body: outputOptions.override?.zod?.strict?.body ?? false,
response: outputOptions.override?.zod?.strict?.response ?? false
},
generate: {
param: outputOptions.override?.zod?.generate?.param ?? true,
query: outputOptions.override?.zod?.generate?.query ?? true,
header: outputOptions.override?.zod?.generate?.header ?? true,
body: outputOptions.override?.zod?.generate?.body ?? true,
response: outputOptions.override?.zod?.generate?.response ?? true
},
coerce: {
param: outputOptions.override?.zod?.coerce?.param ?? false,
query: outputOptions.override?.zod?.coerce?.query ?? false,
header: outputOptions.override?.zod?.coerce?.header ?? false,
body: outputOptions.override?.zod?.coerce?.body ?? false,
response: outputOptions.override?.zod?.coerce?.response ?? false
},
preprocess: {
...outputOptions.override?.zod?.preprocess?.param ? { param: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.param) } : {},
...outputOptions.override?.zod?.preprocess?.query ? { query: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.query) } : {},
...outputOptions.override?.zod?.preprocess?.header ? { header: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.header) } : {},
...outputOptions.override?.zod?.preprocess?.body ? { body: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.body) } : {},
...outputOptions.override?.zod?.preprocess?.response ? { response: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.response) } : {}
},
...outputOptions.override?.zod?.params ? { params: normalizeMutator(outputWorkspace, outputOptions.override.zod.params) } : {},
variant: outputOptions.override?.zod?.variant ?? "classic",
version: outputOptions.override?.zod?.version ?? "auto",
generateEachHttpStatus: outputOptions.override?.zod?.generateEachHttpStatus ?? false,
useBrandedTypes: outputOptions.override?.zod?.useBrandedTypes ?? false,
generateReusableSchemas: outputOptions.override?.zod?.generateReusableSchemas ?? false,
generateMeta: outputOptions.override?.zod?.generateMeta ?? false,
generateDiscriminatedUnion: outputOptions.override?.zod?.generateDiscriminatedUnion ?? false,
exactOptional: outputOptions.override?.zod?.exactOptional ?? false,
dateTimeOptions: outputOptions.override?.zod?.dateTimeOptions ?? { offset: true },
timeOptions: outputOptions.override?.zod?.timeOptions ?? {}
},
effect: normalizeEffectOptions(outputOptions.override?.effect),
swr: {
generateErrorTypes: false,
...outputOptions.override?.swr
},
angular: {
provideIn: outputOptions.override?.angular?.provideIn ?? "root",
client: outputOptions.override?.angular?.retrievalClient ?? outputOptions.override?.angular?.client ?? "httpClient",
runtimeValidation: outputOptions.override?.angular?.runtimeValidation ?? false,
queryObjectSerialization: outputOptions.override?.angular?.queryObjectSerialization ?? "spec",
...outputOptions.override?.angular?.httpResource ? { httpResource: outputOptions.override.angular.httpResource } : {}
},
fetch: {
includeHttpResponseReturnType: outputOptions.override?.fetch?.includeHttpResponseReturnType ?? true,
forceSuccessResponse: outputOptions.override?.fetch?.forceSuccessResponse ?? false,
runtimeValidation: outputOptions.override?.fetch?.runtimeValidation ?? false,
useRuntimeFetcher: outputOptions.override?.fetch?.useRuntimeFetcher ?? false,
...outputOptions.override?.fetch?.arrayFormat ? { arrayFormat: outputOptions.override.fetch.arrayFormat } : {},
...outputOptions.override?.fetch,
...outputOptions.override?.fetch?.jsonReviver ? { jsonReviver: normalizeMutator(outputWorkspace, outputOptions.override.fetch.jsonReviver) } : {}
},
useDates: outputOptions.override?.useDates ?? false,
useDeprecatedOperations: outputOptions.override?.useDeprecatedOperations ?? true,
enumGenerationType: outputOptions.override?.enumGenerationType ?? "const",
suppressReadonlyModifier: outputOptions.override?.suppressReadonlyModifier ?? false,
preserveReadonlyRequestBodies: outputOptions.override?.preserveReadonlyRequestBodies ?? "strip",
splitByContentType: outputOptions.override?.splitByContentType ?? false,
aliasCombinedTypes: outputOptions.override?.aliasCombinedTypes ?? false
},
allParamsOptional: outputOptions.allParamsOptional ?? false,
urlEncodeParameters: outputOptions.urlEncodeParameters ?? false,
optionsParamRequired: outputOptions.optionsParamRequired ?? false,
propertySortOrder: outputOptions.propertySortOrder ?? PropertySortOrder.SPECIFICATION
},
hooks: options.hooks ? normalizeHooks(options.hooks) : {}
};
if (!normalizedOptions.input.target) throw new Error(styleText("red", `Config requires an input target.`));
if (!normalizedOptions.output.target && !normalizedOptions.output.schemas) throw new Error(styleText("red", `Config requires an output target or schemas.`));
const fakerWithSchemasImportPath = normalizedOptions.output.mock.generators.find((g) => !isFunction(g) && g.type === OutputMockType.FAKER && !!g.schemasImportPath);
if (fakerWithSchemasImportPath) {
if (fakerWithSchemasImportPath.schemas !== true) throw new Error(styleText("red", `\`mock.generators[faker].schemasImportPath\` requires \`schemas: true\` on the same generator. Schema-level faker factories are only emitted when \`schemas: true\`.`));
if (!(isObject(normalizedOptions.output.schemas) && normalizedOptions.output.schemas.importPath)) throw new Error(styleText("red", `\`mock.generators[faker].schemasImportPath\` requires \`schemas.importPath\` to also be set. It overrides the package specifier used for importing schema-level faker factories.`));
}
const usesAngularGenerator = normalizedOptions.output.client === OutputClient.ANGULAR || normalizedOptions.output.client === OutputClient.ANGULAR_QUERY && normalizedOptions.output.httpClient === OutputHttpClient.ANGULAR;
if (normalizedOptions.output.override.paramsFilter && !usesAngularGenerator) throw new Error(styleText("red", `\`override.paramsFilter\` is only supported by the Angular generator (the \`angular\` client, or \`angular-query\` with \`httpClient: 'angular'\`). It has no effect for other clients — use \`override.paramsSerializer\` instead.`));
if (!usesAngularGenerator) {
const offendingOperation = Object.entries(normalizedOptions.output.override.operations).find(([, opOverride]) => opOverride?.paramsFilter)?.[0];
if (offendingOperation) throw new Error(styleText("red", `\`override.operations["${offendingOperation}"].paramsFilter\` is only supported by the Angular generator (the \`angular\` client, or \`angular-query\` with \`httpClient: 'angular'\`). It has no effect for other clients — use \`override.paramsSerializer\` instead.`));
const offendingTag = Object.entries(normalizedOptions.output.override.tags).find(([, tagOverride]) => tagOverride?.paramsFilter)?.[0];
if (offendingTag) throw new Error(styleText("red", `\`override.tags["${offendingTag}"].paramsFilter\` is only supported by the Angular generator (the \`angular\` client, or \`angular-query\` with \`httpClient: 'angular'\`). It has no effect for other clients — use \`override.paramsSerializer\` instead.`));
}
if (normalizedOptions.output.httpClient === OutputHttpClient.FETCH && normalizedOptions.output.optionsParamRequired && normalizedOptions.output.override.requestOptions !== false) logWarning(`⚠️ With \`httpClient: 'fetch'\`, \`optionsParamRequired: true\` cannot make the generated \`options\` parameter required. The fetch \`options\` parameter remains optional with type \`RequestInit\` (\`optionsParamRequired\` may still affect other generated parameters). Set \`httpClient: 'axios'\` to make the \`options\` parameter required.`);
return normalizedOptions;
}
function normalizeMutator(workspace, mutator) {
if (isObject(mutator)) {
const m = mutator;
if (!m.path) throw new Error(styleText("red", `Mutator requires a path.`));
const resolvedPath = looksLikePackageSpecifier(m.path) ? resolvePackageSpecifier(workspace, m.path) : void 0;
return {
path: !!resolvedPath || isPackageSpecifierCandidate(workspace, m.path) ? m.path : path.resolve(workspace, m.path),
...resolvedPath ? { resolvedPath } : {},
name: m.name,
default: m.default ?? !m.name,
alias: m.alias,
external: m.external,
extension: m.extension
};
}
if (isString(mutator)) {
const resolvedPath = looksLikePackageSpecifier(mutator) ? resolvePackageSpecifier(workspace, mutator) : void 0;
return {
path: !!resolvedPath || isPackageSpecifierCandidate(workspace, mutator) ? mutator : path.resolve(workspace, mutator),
...resolvedPath ? { resolvedPath } : {},
default: true
};
}
}
async function resolveFirstValidTarget(targets, workspace, parserOptions) {
for (const target of targets) {
if (isUrl(target)) {
try {
const headers = getHeadersForUrl(target, parserOptions?.headers);
const headResponse = await fetchWithTimeout(target, {
method: "HEAD",
headers
});
if (headResponse.ok) return target;
if (headResponse.status === 405 || headResponse.status === 501) {
if ((await fetchWithTimeout(target, {
method: "GET",
headers
})).ok) return target;
}
} catch {
continue;
}
continue;
}
const resolvedTarget = normalizePath(target, workspace);
try {
await access(resolvedTarget);
return resolvedTarget;
} catch {
continue;
}
}
throw new Error(styleText("red", `None of the input targets could be resolved:\n${targets.map((target) => ` - ${target}`).join("\n")}`));
}
function getHeadersForUrl(url, headersConfig) {
if (!headersConfig) return {};
const { hostname } = new URL(url);
const matchedHeaders = {};
for (const headerEntry of headersConfig) if (headerEntry.domains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) Object.assign(matchedHeaders, headerEntry.headers);
return matchedHeaders;
}
async function fetchWithTimeout(target, init) {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, INPUT_TARGET_FETCH_TIMEOUT_MS);
try {
return await fetch(target, {
...init,
signal: controller.signal
});
} finally {
clearTimeout(timeoutId);
}
}
function normalizePathOrUrl(path, workspace) {
if (isString(path) && !isUrl(path)) return normalizePath(path, workspace);
return path;
}
function normalizePath(path$1, workspace) {
if (!isString(path$1)) return path$1;
return path.resolve(workspace, path$1);
}
function normalizeOperationsAndTags(operationsOrTags, workspace, global, source) {
const unsupportedZodKeys = [
"version",
"variant",
"dateTimeOptions",
"timeOptions",
"generateEachHttpStatus",
"generateReusableSchemas",
"generateMeta",
"generateDiscriminatedUnion"
];
return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, effect, ...rest }]) => {
const unsupportedOperationZodKeys = zod && unsupportedZodKeys.filter((unsupportedKey) => zod[unsupportedKey] !== void 0);
if (unsupportedOperationZodKeys && unsupportedOperationZodKeys.length) logWarning(`⚠️ override.${source}.${key}.zod only supports strict, generate, coerce, preprocess, params, and useBrandedTypes. Ignoring unsupported ${unsupportedOperationZodKeys.length === 1 ? "field" : "fields"}: ${unsupportedOperationZodKeys.map((unsupportedKey) => `zod.${unsupportedKey}`).join(", ")}.`);
const hasSupportedOperationZodConfig = !!zod && (zod.strict !== void 0 || zod.generate !== void 0 || zod.coerce !== void 0 || zod.preprocess !== void 0 || zod.params !== void 0 || zod.useBrandedTypes !== void 0);
return [key, {
...rest,
...angular ? { angular: {
provideIn: angular.provideIn ?? "root",
client: angular.retrievalClient ?? angular.client ?? "httpClient",
runtimeValidation: angular.runtimeValidation ?? false,
queryObjectSerialization: angular.queryObjectSerialization ?? "spec",
...angular.httpResource ? { httpResource: angular.httpResource } : {}
} } : {},
...query ? { query: normalizeQueryOptions(query, workspace, global.query) } : {},
...hasSupportedOperationZodConfig && zod ? { zod: {
strict: {
param: zod.strict?.param ?? false,
query: zod.strict?.query ?? false,
header: zod.strict?.header ?? false,
body: zod.strict?.body ?? false,
response: zod.strict?.response ?? false
},
generate: {
param: zod.generate?.param ?? true,
query: zod.generate?.query ?? true,
header: zod.generate?.header ?? true,
body: zod.generate?.body ?? true,
response: zod.generate?.response ?? true
},
coerce: {
param: zod.coerce?.param ?? false,
query: zod.coerce?.query ?? false,
header: zod.coerce?.header ?? false,
body: zod.coerce?.body ?? false,
response: zod.coerce?.response ?? false
},
preprocess: {
...zod.preprocess?.param ? { param: normalizeMutator(workspace, zod.preprocess.param) } : {},
...zod.preprocess?.query ? { query: normalizeMutator(workspace, zod.preprocess.query) } : {},
...zod.preprocess?.header ? { header: normalizeMutator(workspace, zod.preprocess.header) } : {},
...zod.preprocess?.body ? { body: normalizeMutator(workspace, zod.preprocess.body) } : {},
...zod.preprocess?.response ? { response: normalizeMutator(workspace, zod.preprocess.response) } : {}
},
...zod.params ? { params: normalizeMutator(workspace, zod.params) } : {},
useBrandedTypes: zod.useBrandedTypes ?? false
} } : {},
...effect ? { effect: normalizeEffectOptions(effect) } : {},
...transformer ? { transformer: normalizePath(transformer, workspace) } : {},
...mutator ? { mutator: normalizeMutator(workspace, mutator) } : {},
...formData === void 0 ? {} : { formData: createFormData(workspace, formData) },
...formUrlEncoded ? { formUrlEncoded: isBoolean(formUrlEncoded) ? formUrlEncoded : normalizeMutator(workspace, formUrlEncoded) } : {},
...paramsSerializer ? { paramsSerializer: normalizeMutator(workspace, paramsSerializer) } : {},
...paramsFilter ? { paramsFilter: normalizeMutator(workspace, paramsFilter) } : {}
}];
}));
}
function normalizeOutputMode(mode) {
if (!mode) return OutputMode.SINGLE;
if (!Object.values(OutputMode).includes(mode)) {
logWarning(`⚠️ Unknown provided mode => ${mode}`);
return OutputMode.SINGLE;
}
return mode;
}
function normalizeHooks(hooks) {
const keys = Object.keys(hooks);
const result = {};
for (const key of keys) if (isString(hooks[key])) result[key] = [hooks[key]];
else if (Array.isArray(hooks[key])) result[key] = hooks[key];
else if (isFunction(hooks[key])) result[key] = [hooks[key]];
else if (isObject(hooks[key])) result[key] = [hooks[key]];
return result;
}
function normalizeHonoOptions(hono = {}, workspace) {
return {
...hono.handlers ? { handlers: path.resolve(workspace, hono.handlers) } : {},
handlerGenerationStrategy: hono.handlerGenerationStrategy ?? "smart",
compositeRoute: hono.compositeRoute ? path.resolve(workspace, hono.compositeRoute) : "",
validator: hono.validator ?? true,
validatorOutputPath: hono.validatorOutputPath ? path.resolve(workspace, hono.validatorOutputPath) : ""
};
}
function normalizeMcpServerOptions(server, workspace) {
return {
path: path.resolve(workspace, server.path),
name: server.name,
default: server.default ?? !server.name
};
}
function normalizeMcpOptions(mcp = {}, workspace) {
return mcp.server ? { server: normalizeMcpServerOptions(mcp.server, workspace) } : {};
}
function normalizeJSDocOptions(jsdoc = {}) {
return { ...jsdoc };
}
function normalizeQueryOptions(queryOptions = {}, outputWorkspace, globalOptions = {}) {
if (queryOptions.options) logWarning("⚠️ Using query options is deprecated and will be removed in a future major release. Please use queryOptions or mutationOptions instead.");
return {
...isNullish(queryOptions.usePrefetch) ? {} : { usePrefetch: queryOptions.usePrefetch },
...isNullish(queryOptions.useInvalidate) ? {} : { useInvalidate: queryOptions.useInvalidate },
...isNullish(queryOptions.useSetQueryData) ? {} : { useSetQueryData: queryOptions.useSetQueryData