orval 8.20.0 → 8.22.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/bin/orval.mjs +1 -1
- package/dist/{config-C8B9Lol2.mjs → config-CotJKggp.mjs} +1169 -1045
- package/dist/config-CotJKggp.mjs.map +1 -0
- package/dist/index.mjs +1 -1
- package/package.json +13 -13
- package/dist/config-C8B9Lol2.mjs.map +0 -1
|
@@ -4,7 +4,9 @@ import { DefaultTag, FormDataArrayHandling, GetterPropType, NamingConvention, Ou
|
|
|
4
4
|
import { bundle } from "@scalar/json-magic/bundle";
|
|
5
5
|
import { fetchUrls, parseJson, parseYaml, readFiles } from "@scalar/json-magic/bundle/plugins/node";
|
|
6
6
|
import { upgrade, validate } from "@scalar/openapi-parser";
|
|
7
|
+
import fs, { access, readFile } from "node:fs/promises";
|
|
7
8
|
import { isNullish as isNullish$1, pick, unique } from "remeda";
|
|
9
|
+
import jsYaml from "js-yaml";
|
|
8
10
|
import * as mock from "@orval/mock";
|
|
9
11
|
import { dedupeStrictMockTypeDeclarations, generateFakerForSchemas, generateMockImports, getDefaultMockOptionsForType } from "@orval/mock";
|
|
10
12
|
import angular from "@orval/angular";
|
|
@@ -17,19 +19,17 @@ import query from "@orval/query";
|
|
|
17
19
|
import solidStart from "@orval/solid-start";
|
|
18
20
|
import swr from "@orval/swr";
|
|
19
21
|
import zod, { assertZodTarget, dereference, generateFormDataZodSchema, generateZodValidationSchemaDefinition, getZodImportSource, getZodTypeName, parseZodValidationSchemaDefinition, resolveIsZodV4 } from "@orval/zod";
|
|
20
|
-
import {
|
|
21
|
-
import fs from "fs-extra";
|
|
22
|
-
import fs$1, { access } from "node:fs/promises";
|
|
22
|
+
import fs$1, { existsSync } from "node:fs";
|
|
23
23
|
import { styleText } from "node:util";
|
|
24
|
-
import { parseArgsStringToArgv } from "string-argv";
|
|
25
|
-
import fs$2, { existsSync } from "node:fs";
|
|
26
24
|
import { findUp, findUpMultiple } from "find-up";
|
|
27
|
-
import
|
|
25
|
+
import fs$2 from "fs-extra";
|
|
28
26
|
import { parseTsconfig } from "get-tsconfig";
|
|
27
|
+
import { ExecaError, execa } from "execa";
|
|
28
|
+
import { parseArgsStringToArgv } from "string-argv";
|
|
29
29
|
import { createJiti } from "jiti";
|
|
30
30
|
//#region package.json
|
|
31
31
|
var name = "orval";
|
|
32
|
-
var version = "8.
|
|
32
|
+
var version = "8.22.0";
|
|
33
33
|
var description = "A swagger client generator for typescript";
|
|
34
34
|
//#endregion
|
|
35
35
|
//#region src/client.ts
|
|
@@ -98,7 +98,7 @@ const generateClientHeader = ({ outputClient = DEFAULT_CLIENT, isRequestOptions,
|
|
|
98
98
|
sharedTypes: normalizedHeader.sharedTypes
|
|
99
99
|
};
|
|
100
100
|
};
|
|
101
|
-
const generateClientFooter = ({ outputClient, operationNames, hasMutator, hasAwaitedType, titles, output }) => {
|
|
101
|
+
const generateClientFooter = ({ outputClient, operationNames, operations, hasMutator, hasAwaitedType, titles, output }) => {
|
|
102
102
|
const { footer } = getGeneratorClient(outputClient, output);
|
|
103
103
|
if (!footer) return {
|
|
104
104
|
implementation: "",
|
|
@@ -111,6 +111,7 @@ const generateClientFooter = ({ outputClient, operationNames, hasMutator, hasAwa
|
|
|
111
111
|
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[] }.");
|
|
112
112
|
} else implementation = footer({
|
|
113
113
|
operationNames,
|
|
114
|
+
operations,
|
|
114
115
|
title: titles.implementation,
|
|
115
116
|
hasMutator,
|
|
116
117
|
hasAwaitedType
|
|
@@ -118,6 +119,7 @@ const generateClientFooter = ({ outputClient, operationNames, hasMutator, hasAwa
|
|
|
118
119
|
} catch {
|
|
119
120
|
implementation = footer({
|
|
120
121
|
operationNames,
|
|
122
|
+
operations,
|
|
121
123
|
title: titles.implementation,
|
|
122
124
|
hasMutator,
|
|
123
125
|
hasAwaitedType
|
|
@@ -202,7 +204,8 @@ const generateOperations = (outputClient = DEFAULT_CLIENT, verbsOptions, options
|
|
|
202
204
|
paramsSerializer: verbOption.paramsSerializer,
|
|
203
205
|
paramsFilter: verbOption.paramsFilter,
|
|
204
206
|
operationName: verbOption.operationName,
|
|
205
|
-
fetchReviver: verbOption.fetchReviver
|
|
207
|
+
fetchReviver: verbOption.fetchReviver,
|
|
208
|
+
...client.returnType ? { types: { result: client.returnType } } : void 0
|
|
206
209
|
};
|
|
207
210
|
return acc;
|
|
208
211
|
}, {});
|
|
@@ -252,10 +255,12 @@ async function getApiBuilder({ input, output, context }) {
|
|
|
252
255
|
}, output);
|
|
253
256
|
for (const verbOption of verbsOptions) acc.verbOptions[verbOption.operationId] = verbOption;
|
|
254
257
|
acc.schemas.push(...schemas);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
for (const [key, value] of Object.entries(pathOperations)) {
|
|
259
|
+
let operationKey = key;
|
|
260
|
+
let counter = 1;
|
|
261
|
+
while (Object.hasOwn(acc.operations, operationKey)) operationKey = `${key}::${++counter}`;
|
|
262
|
+
acc.operations[operationKey] = value;
|
|
263
|
+
}
|
|
259
264
|
return acc;
|
|
260
265
|
}, {
|
|
261
266
|
operations: {},
|
|
@@ -343,1085 +348,1204 @@ function getApiSchemas({ input, output, target, workspace, spec }) {
|
|
|
343
348
|
];
|
|
344
349
|
}
|
|
345
350
|
//#endregion
|
|
346
|
-
//#region src/
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
351
|
+
//#region src/utils/package-json.ts
|
|
352
|
+
const loadPackageJson = async (packageJson, workspace = process.cwd()) => {
|
|
353
|
+
if (!packageJson) {
|
|
354
|
+
const pkgPath = await findUp(["package.json"], { cwd: workspace });
|
|
355
|
+
if (pkgPath) {
|
|
356
|
+
const pkg = await dynamicImport(pkgPath, workspace);
|
|
357
|
+
if (isPackageJson(pkg)) return resolveAndAttachVersions(await maybeReplaceCatalog(pkg, workspace), workspace, pkgPath);
|
|
358
|
+
else throw new Error("Invalid package.json file");
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
353
361
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
362
|
+
const normalizedPath = normalizePath(packageJson, workspace);
|
|
363
|
+
if (fs$2.existsSync(normalizedPath)) {
|
|
364
|
+
const pkg = await dynamicImport(normalizedPath);
|
|
365
|
+
if (isPackageJson(pkg)) return resolveAndAttachVersions(await maybeReplaceCatalog(pkg, workspace), workspace, normalizedPath);
|
|
366
|
+
else throw new Error(`Invalid package.json file: ${normalizedPath}`);
|
|
359
367
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
throw new Error(`input.override.transformer must return an OpenAPI document object; got ${result === void 0 ? "undefined" : typeof result} from ${source}. Ensure your transformer returns the (possibly modified) spec.`);
|
|
368
|
+
};
|
|
369
|
+
const isPackageJson = (obj) => isObject(obj);
|
|
370
|
+
const resolvedCache = /* @__PURE__ */ new Map();
|
|
371
|
+
const resolveAndAttachVersions = (pkg, workspace, cacheKey) => {
|
|
372
|
+
const cached = resolvedCache.get(cacheKey);
|
|
373
|
+
if (cached) {
|
|
374
|
+
pkg.resolvedVersions = cached;
|
|
375
|
+
return pkg;
|
|
369
376
|
}
|
|
370
|
-
|
|
377
|
+
const resolved = resolveInstalledVersions(pkg, workspace);
|
|
378
|
+
if (Object.keys(resolved).length > 0) {
|
|
379
|
+
pkg.resolvedVersions = resolved;
|
|
380
|
+
resolvedCache.set(cacheKey, resolved);
|
|
381
|
+
for (const [name, version] of Object.entries(resolved)) logVerbose(styleText("dim", `Detected ${styleText("white", name)} v${styleText("white", version)}`));
|
|
382
|
+
}
|
|
383
|
+
return pkg;
|
|
384
|
+
};
|
|
385
|
+
const hasCatalogReferences = (pkg) => {
|
|
386
|
+
return [
|
|
387
|
+
...Object.entries(pkg.dependencies ?? {}),
|
|
388
|
+
...Object.entries(pkg.devDependencies ?? {}),
|
|
389
|
+
...Object.entries(pkg.peerDependencies ?? {})
|
|
390
|
+
].some(([, value]) => isString(value) && value.startsWith("catalog:"));
|
|
391
|
+
};
|
|
392
|
+
const loadPnpmWorkspaceCatalog = async (workspace) => {
|
|
393
|
+
const filePath = await findUp("pnpm-workspace.yaml", { cwd: workspace });
|
|
394
|
+
if (!filePath) return void 0;
|
|
395
|
+
try {
|
|
396
|
+
const file = await fs$2.readFile(filePath, "utf8");
|
|
397
|
+
const data = jsYaml.load(file);
|
|
398
|
+
if (!data?.catalog && !data?.catalogs) return void 0;
|
|
399
|
+
return {
|
|
400
|
+
catalog: data.catalog,
|
|
401
|
+
catalogs: data.catalogs
|
|
402
|
+
};
|
|
403
|
+
} catch {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
const loadPackageJsonCatalog = async (workspace) => {
|
|
408
|
+
const filePaths = await findUpMultiple("package.json", { cwd: workspace });
|
|
409
|
+
for (const filePath of filePaths) try {
|
|
410
|
+
const pkg = await fs$2.readJson(filePath);
|
|
411
|
+
if (pkg.catalog || pkg.catalogs) return {
|
|
412
|
+
catalog: pkg.catalog,
|
|
413
|
+
catalogs: pkg.catalogs
|
|
414
|
+
};
|
|
415
|
+
} catch {}
|
|
416
|
+
};
|
|
417
|
+
const loadYarnrcCatalog = async (workspace) => {
|
|
418
|
+
const filePath = await findUp(".yarnrc.yml", { cwd: workspace });
|
|
419
|
+
if (!filePath) return void 0;
|
|
420
|
+
try {
|
|
421
|
+
const file = await fs$2.readFile(filePath, "utf8");
|
|
422
|
+
const data = jsYaml.load(file);
|
|
423
|
+
if (!data?.catalog && !data?.catalogs) return void 0;
|
|
424
|
+
return {
|
|
425
|
+
catalog: data.catalog,
|
|
426
|
+
catalogs: data.catalogs
|
|
427
|
+
};
|
|
428
|
+
} catch {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
const maybeReplaceCatalog = async (pkg, workspace) => {
|
|
433
|
+
if (!hasCatalogReferences(pkg)) return pkg;
|
|
434
|
+
const catalogData = await loadPnpmWorkspaceCatalog(workspace) ?? await loadPackageJsonCatalog(workspace) ?? await loadYarnrcCatalog(workspace);
|
|
435
|
+
if (!catalogData) {
|
|
436
|
+
logWarning("⚠️ package.json contains catalog: references, but no catalog source was found (checked: pnpm-workspace.yaml, package.json, .yarnrc.yml).");
|
|
437
|
+
return pkg;
|
|
438
|
+
}
|
|
439
|
+
performSubstitution(pkg.dependencies, catalogData);
|
|
440
|
+
performSubstitution(pkg.devDependencies, catalogData);
|
|
441
|
+
performSubstitution(pkg.peerDependencies, catalogData);
|
|
442
|
+
return pkg;
|
|
443
|
+
};
|
|
444
|
+
const performSubstitution = (dependencies, catalogData) => {
|
|
445
|
+
if (!dependencies) return;
|
|
446
|
+
for (const [packageName, version] of Object.entries(dependencies)) if (version === "catalog:" || version === "catalog:default") {
|
|
447
|
+
if (!catalogData.catalog) {
|
|
448
|
+
logWarning(`⚠️ catalog: substitution for the package '${packageName}' failed as there is no default catalog.`);
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
const sub = catalogData.catalog[packageName];
|
|
452
|
+
if (!sub) {
|
|
453
|
+
logWarning(`⚠️ catalog: substitution for the package '${packageName}' failed as there is no matching package in the default catalog.`);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
dependencies[packageName] = sub;
|
|
457
|
+
} else if (version.startsWith("catalog:")) {
|
|
458
|
+
const catalogName = version.slice(8);
|
|
459
|
+
const catalog = catalogData.catalogs?.[catalogName];
|
|
460
|
+
if (!catalog) {
|
|
461
|
+
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(", ")})`);
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const sub = catalog[packageName];
|
|
465
|
+
if (!sub) {
|
|
466
|
+
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(", ")})`);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
dependencies[packageName] = sub;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
//#endregion
|
|
473
|
+
//#region src/utils/tsconfig.ts
|
|
474
|
+
const convertTarget = (config) => {
|
|
475
|
+
if (!config.compilerOptions?.target) return {
|
|
476
|
+
baseUrl: config.compilerOptions?.baseUrl,
|
|
477
|
+
...config
|
|
478
|
+
};
|
|
479
|
+
const lowercaseTarget = config.compilerOptions.target.toLowerCase();
|
|
480
|
+
return {
|
|
481
|
+
baseUrl: config.compilerOptions.baseUrl,
|
|
482
|
+
...config,
|
|
483
|
+
compilerOptions: {
|
|
484
|
+
...config.compilerOptions,
|
|
485
|
+
target: lowercaseTarget
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
};
|
|
489
|
+
const loadTsconfig = async (tsconfig, workspace = process.cwd()) => {
|
|
490
|
+
if (isNullish(tsconfig)) {
|
|
491
|
+
const configPath = await findUp(["tsconfig.json", "jsconfig.json"], { cwd: workspace });
|
|
492
|
+
if (configPath) return convertTarget(parseTsconfig(configPath));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (isString(tsconfig)) {
|
|
496
|
+
const normalizedPath = normalizePath(tsconfig, workspace);
|
|
497
|
+
if (fs$2.existsSync(normalizedPath)) return convertTarget(parseTsconfig(normalizedPath));
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (isObject(tsconfig)) return tsconfig;
|
|
501
|
+
};
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region src/utils/options.ts
|
|
504
|
+
const INPUT_TARGET_FETCH_TIMEOUT_MS = 1e4;
|
|
505
|
+
/**
|
|
506
|
+
* Type helper to make it easier to use orval.config.ts
|
|
507
|
+
* accepts a direct {@link ConfigExternal} object.
|
|
508
|
+
*/
|
|
509
|
+
function defineConfig(options) {
|
|
510
|
+
return options;
|
|
371
511
|
}
|
|
372
512
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* the post-transformer pass (#3327); `origin` lets the second pass resolve refs
|
|
376
|
-
* relative to the original spec file when the input is an in-memory object.
|
|
513
|
+
* Type helper to make it easier to write input transformers.
|
|
514
|
+
* accepts a direct {@link InputTransformerFn} function.
|
|
377
515
|
*/
|
|
378
|
-
|
|
379
|
-
return
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
516
|
+
function defineTransformer(transformer) {
|
|
517
|
+
return transformer;
|
|
518
|
+
}
|
|
519
|
+
function createFormData(workspace, formData) {
|
|
520
|
+
const defaultArrayHandling = FormDataArrayHandling.SERIALIZE;
|
|
521
|
+
if (formData === void 0) return {
|
|
522
|
+
disabled: false,
|
|
523
|
+
arrayHandling: defaultArrayHandling
|
|
524
|
+
};
|
|
525
|
+
if (isBoolean(formData)) return {
|
|
526
|
+
disabled: !formData,
|
|
527
|
+
arrayHandling: defaultArrayHandling
|
|
528
|
+
};
|
|
529
|
+
if (isString(formData)) return {
|
|
530
|
+
disabled: false,
|
|
531
|
+
mutator: normalizeMutator(workspace, formData),
|
|
532
|
+
arrayHandling: defaultArrayHandling
|
|
533
|
+
};
|
|
534
|
+
if ("mutator" in formData || "arrayHandling" in formData) return {
|
|
535
|
+
disabled: false,
|
|
536
|
+
mutator: normalizeMutator(workspace, formData.mutator),
|
|
537
|
+
arrayHandling: formData.arrayHandling ?? defaultArrayHandling
|
|
538
|
+
};
|
|
539
|
+
return {
|
|
540
|
+
disabled: false,
|
|
541
|
+
mutator: normalizeMutator(workspace, formData),
|
|
542
|
+
arrayHandling: defaultArrayHandling
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function normalizeSchemasOption(schemas, workspace) {
|
|
546
|
+
if (!schemas) return;
|
|
547
|
+
if (isString(schemas)) return normalizePath(schemas, workspace);
|
|
548
|
+
validatePackageSpecifier(schemas.importPath, "schemas.importPath");
|
|
549
|
+
return {
|
|
550
|
+
path: normalizePath(schemas.path, workspace),
|
|
551
|
+
type: schemas.type ?? "typescript",
|
|
552
|
+
importPath: schemas.importPath,
|
|
553
|
+
splitByTags: schemas.splitByTags ?? false
|
|
554
|
+
};
|
|
389
555
|
}
|
|
390
556
|
/**
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* the already-bundled spec is returned untouched.
|
|
557
|
+
* Validates that a config value is a valid package specifier (bare specifier
|
|
558
|
+
* or sub-path import like `@acme/models` / `@acme/models/fakers`). Rejects
|
|
559
|
+
* empty, whitespace-only, relative (`./`, `../`), and absolute paths with a
|
|
560
|
+
* clear, actionable error message. No-op when the value is `undefined`.
|
|
396
561
|
*/
|
|
397
|
-
function
|
|
398
|
-
if (
|
|
399
|
-
if (
|
|
400
|
-
|
|
401
|
-
|
|
562
|
+
function validatePackageSpecifier(value, fieldName) {
|
|
563
|
+
if (value === void 0) return;
|
|
564
|
+
if (!value) throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received an empty string.`);
|
|
565
|
+
if (value.trim() === "") throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received a whitespace-only string.`);
|
|
566
|
+
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}"`);
|
|
567
|
+
if (value.startsWith(".")) throw new Error(`\`${fieldName}\` must be a package specifier (e.g. '@acme/models'), not a relative path. Received: "${value}"`);
|
|
568
|
+
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}"`);
|
|
569
|
+
}
|
|
570
|
+
function looksLikePackageSpecifier(value) {
|
|
571
|
+
return !!value && value.trim() === value && !value.startsWith(".") && !path.isAbsolute(value) && !/^[A-Za-z]:[\\/]/.test(value) && !value.startsWith("\\\\");
|
|
572
|
+
}
|
|
573
|
+
function resolvePackageSpecifier(workspace, value) {
|
|
574
|
+
try {
|
|
575
|
+
return createRequire(path.join(workspace, "package.json")).resolve(value);
|
|
576
|
+
} catch {
|
|
577
|
+
return;
|
|
402
578
|
}
|
|
403
|
-
return false;
|
|
404
579
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
return
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
target: isString(input.target) ? input.target : workspace,
|
|
417
|
-
workspace,
|
|
418
|
-
projectName
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
const COMPONENT_KEY_PATTERN = /^[a-zA-Z0-9.\-_]+$/;
|
|
422
|
-
const COMPONENT_SECTIONS = [
|
|
423
|
-
"schemas",
|
|
424
|
-
"responses",
|
|
425
|
-
"parameters",
|
|
426
|
-
"examples",
|
|
427
|
-
"requestBodies",
|
|
428
|
-
"headers",
|
|
429
|
-
"securitySchemes",
|
|
430
|
-
"links",
|
|
431
|
-
"callbacks",
|
|
432
|
-
"pathItems"
|
|
433
|
-
];
|
|
434
|
-
/**
|
|
435
|
-
* Validate that all component keys conform to the OAS regex: ^[a-zA-Z0-9.\-_]+$
|
|
436
|
-
* @see https://spec.openapis.org/oas/v3.0.3.html#fixed-fields-5
|
|
437
|
-
* @see https://spec.openapis.org/oas/v3.1.0#fixed-fields-5
|
|
438
|
-
*/
|
|
439
|
-
function validateComponentKeys(data) {
|
|
440
|
-
const components = data.components;
|
|
441
|
-
if (!isObject(components)) return;
|
|
442
|
-
const invalidKeys = [];
|
|
443
|
-
for (const section of COMPONENT_SECTIONS) {
|
|
444
|
-
const sectionObj = components[section];
|
|
445
|
-
if (!isObject(sectionObj)) continue;
|
|
446
|
-
for (const key of Object.keys(sectionObj)) if (!COMPONENT_KEY_PATTERN.test(key)) invalidKeys.push(`components.${section}.${key}`);
|
|
580
|
+
function isPackageSpecifierCandidate(workspace, value) {
|
|
581
|
+
if (!looksLikePackageSpecifier(value)) return false;
|
|
582
|
+
if (existsSync(path.resolve(workspace, value))) return false;
|
|
583
|
+
if (value.startsWith("@")) return true;
|
|
584
|
+
const [packageName] = value.split("/");
|
|
585
|
+
if (!value.includes("/")) return true;
|
|
586
|
+
for (let dir = workspace;;) {
|
|
587
|
+
if (existsSync(path.join(dir, "node_modules", packageName))) return true;
|
|
588
|
+
const parent = path.dirname(dir);
|
|
589
|
+
if (parent === dir) return false;
|
|
590
|
+
dir = parent;
|
|
447
591
|
}
|
|
448
|
-
if (invalidKeys.length > 0) throw new Error(`Invalid component key${invalidKeys.length > 1 ? "s" : ""} found. OpenAPI component keys must match the pattern ${COMPONENT_KEY_PATTERN} (non-ASCII characters are not allowed per the spec).\n See: https://spec.openapis.org/oas/v3.0.3.html#components-object\n Invalid keys:\n` + invalidKeys.map((k) => ` - ${k}`).join("\n"));
|
|
449
592
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const schemaNameMappings = {};
|
|
470
|
-
if (Object.keys(extensions).length === 0) return schemaNameMappings;
|
|
471
|
-
data.components ??= {};
|
|
472
|
-
const mainComponents = data.components;
|
|
473
|
-
mainComponents.schemas ??= {};
|
|
474
|
-
const mainSchemas = mainComponents.schemas;
|
|
475
|
-
for (const [extKey, extDoc] of Object.entries(extensions)) {
|
|
476
|
-
schemaNameMappings[extKey] = {};
|
|
477
|
-
if (isObject(extDoc) && "components" in extDoc) {
|
|
478
|
-
const extComponents = extDoc.components;
|
|
479
|
-
if (isObject(extComponents) && "schemas" in extComponents) {
|
|
480
|
-
const extSchemas = extComponents.schemas;
|
|
481
|
-
for (const [schemaName, schema] of Object.entries(extSchemas)) {
|
|
482
|
-
const existingSchema = mainSchemas[schemaName];
|
|
483
|
-
const isXExtRef = isObject(existingSchema) && "$ref" in existingSchema && isString(existingSchema.$ref) && existingSchema.$ref.startsWith("#/x-ext/");
|
|
484
|
-
let finalSchemaName = schemaName;
|
|
485
|
-
if (schemaName in mainSchemas && !isXExtRef) {
|
|
486
|
-
finalSchemaName = `${schemaName}_${extKey.replaceAll(/[^a-zA-Z0-9]/g, "_")}`;
|
|
487
|
-
schemaNameMappings[extKey][schemaName] = finalSchemaName;
|
|
488
|
-
} else schemaNameMappings[extKey][schemaName] = schemaName;
|
|
489
|
-
mainSchemas[finalSchemaName] = scrubUnwantedKeys(schema);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
for (const [extKey, mapping] of Object.entries(schemaNameMappings)) for (const [, finalName] of Object.entries(mapping)) {
|
|
495
|
-
const schema = mainSchemas[finalName];
|
|
496
|
-
if (schema) mainSchemas[finalName] = updateInternalRefs(schema, extKey, schemaNameMappings);
|
|
497
|
-
}
|
|
498
|
-
return schemaNameMappings;
|
|
593
|
+
function normalizeEffectOptions(effect) {
|
|
594
|
+
return {
|
|
595
|
+
strict: {
|
|
596
|
+
param: effect?.strict?.param ?? false,
|
|
597
|
+
query: effect?.strict?.query ?? false,
|
|
598
|
+
header: effect?.strict?.header ?? false,
|
|
599
|
+
body: effect?.strict?.body ?? false,
|
|
600
|
+
response: effect?.strict?.response ?? false
|
|
601
|
+
},
|
|
602
|
+
generate: {
|
|
603
|
+
param: effect?.generate?.param ?? true,
|
|
604
|
+
query: effect?.generate?.query ?? true,
|
|
605
|
+
header: effect?.generate?.header ?? true,
|
|
606
|
+
body: effect?.generate?.body ?? true,
|
|
607
|
+
response: effect?.generate?.response ?? true
|
|
608
|
+
},
|
|
609
|
+
generateEachHttpStatus: effect?.generateEachHttpStatus ?? false,
|
|
610
|
+
useBrandedTypes: effect?.useBrandedTypes ?? false
|
|
611
|
+
};
|
|
499
612
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
613
|
+
async function normalizeOptions(optionsExport, workspace = process.cwd(), globalOptions = {}) {
|
|
614
|
+
const options = await (isFunction(optionsExport) ? optionsExport() : optionsExport);
|
|
615
|
+
if (!options.input) throw new Error(styleText("red", `Config requires an input.`));
|
|
616
|
+
if (!options.output) throw new Error(styleText("red", `Config requires an output.`));
|
|
617
|
+
const inputOptions = isString(options.input) || Array.isArray(options.input) ? { target: options.input } : options.input;
|
|
618
|
+
const outputOptions = isString(options.output) ? { target: options.output } : options.output;
|
|
619
|
+
const outputWorkspace = normalizePath(outputOptions.workspace ?? "", workspace);
|
|
620
|
+
const { clean, client, httpClient, mode } = globalOptions;
|
|
621
|
+
const tsconfig = await loadTsconfig(outputOptions.tsconfig ?? globalOptions.tsconfig, workspace);
|
|
622
|
+
const packageJson = await loadPackageJson(outputOptions.packageJson ?? globalOptions.packageJson, workspace);
|
|
623
|
+
const mocksOption = outputOptions.mock ?? globalOptions.mock;
|
|
624
|
+
let mocks = {
|
|
625
|
+
indexMockFiles: false,
|
|
626
|
+
generators: []
|
|
627
|
+
};
|
|
628
|
+
if (isBoolean(mocksOption) && mocksOption) mocks = {
|
|
629
|
+
indexMockFiles: false,
|
|
630
|
+
generators: [getDefaultMockOptionsForType(OutputMockType.MSW), getDefaultMockOptionsForType(OutputMockType.FAKER)]
|
|
631
|
+
};
|
|
632
|
+
else if (isFunction(mocksOption)) mocks = {
|
|
633
|
+
indexMockFiles: false,
|
|
634
|
+
generators: [mocksOption]
|
|
635
|
+
};
|
|
636
|
+
else if (mocksOption && typeof mocksOption === "object") {
|
|
637
|
+
if (!Array.isArray(mocksOption.generators)) throw new TypeError("mock.generators must be an array of generator entries (e.g. [{ type: \"msw\" }]).");
|
|
638
|
+
const sharedMockPath = mocksOption.path && isString(mocksOption.path) ? normalizePath(mocksOption.path, outputWorkspace) : void 0;
|
|
639
|
+
mocks = {
|
|
640
|
+
indexMockFiles: mocksOption.indexMockFiles ?? false,
|
|
641
|
+
path: sharedMockPath,
|
|
642
|
+
generators: mocksOption.generators.map((m) => isFunction(m) ? m : {
|
|
643
|
+
...getDefaultMockOptionsForType(m.type),
|
|
644
|
+
...m,
|
|
645
|
+
path: m.path && isString(m.path) ? normalizePath(m.path, outputWorkspace) : sharedMockPath
|
|
646
|
+
})
|
|
647
|
+
};
|
|
515
648
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
if (obj === null || obj === void 0) return obj;
|
|
523
|
-
if (Array.isArray(obj)) return obj.map((element) => updateInternalRefs(element, extKey, schemaNameMappings));
|
|
524
|
-
if (isObject(obj)) {
|
|
525
|
-
const record = obj;
|
|
526
|
-
if ("$ref" in record && isString(record.$ref)) {
|
|
527
|
-
const refValue = record.$ref;
|
|
528
|
-
if (refValue.startsWith("#/components/schemas/")) {
|
|
529
|
-
const schemaName = refValue.replace("#/components/schemas/", "");
|
|
530
|
-
const mappedName = schemaNameMappings[extKey][schemaName];
|
|
531
|
-
if (mappedName) return { $ref: `#/components/schemas/${mappedName}` };
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
const result = {};
|
|
535
|
-
for (const [key, value] of Object.entries(record)) result[key] = updateInternalRefs(value, extKey, schemaNameMappings);
|
|
536
|
-
return result;
|
|
649
|
+
const seenMockTypes = /* @__PURE__ */ new Set();
|
|
650
|
+
for (const entry of mocks.generators) {
|
|
651
|
+
if (isFunction(entry)) continue;
|
|
652
|
+
if (seenMockTypes.has(entry.type)) throw new Error(`Duplicate mock generator type "${entry.type}". Each type can only appear once in mock.generators.`);
|
|
653
|
+
seenMockTypes.add(entry.type);
|
|
654
|
+
if (entry.type === OutputMockType.FAKER) validatePackageSpecifier(entry.schemasImportPath, "mock.generators[faker].schemasImportPath");
|
|
537
655
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
656
|
+
const defaultFileExtension = ".ts";
|
|
657
|
+
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;
|
|
658
|
+
const factoryMethodsConfig = outputOptions.factoryMethods;
|
|
659
|
+
let factoryMethods = void 0;
|
|
660
|
+
if (factoryMethodsConfig) factoryMethods = {
|
|
661
|
+
functionNamePrefix: factoryMethodsConfig.functionNamePrefix ?? "create",
|
|
662
|
+
mode: factoryMethodsConfig.mode ?? "split",
|
|
663
|
+
outputDirectory: factoryMethodsConfig.outputDirectory ? normalizePath(factoryMethodsConfig.outputDirectory, outputWorkspace) : outputOptions.schemas ? normalizePath(isString(outputOptions.schemas) ? outputOptions.schemas : outputOptions.schemas.path, outputWorkspace) : normalizePath(outputWorkspace, outputWorkspace),
|
|
664
|
+
includeOptionalProperty: factoryMethodsConfig.includeOptionalProperty ?? true
|
|
665
|
+
};
|
|
666
|
+
const globalQueryOptions = {
|
|
667
|
+
signal: true,
|
|
668
|
+
shouldExportMutatorHooks: true,
|
|
669
|
+
shouldExportHttpClient: true,
|
|
670
|
+
shouldExportQueryKey: true,
|
|
671
|
+
shouldFilterQueryKey: false,
|
|
672
|
+
shouldSplitQueryKey: false,
|
|
673
|
+
...normalizeQueryOptions(outputOptions.override?.query, outputWorkspace)
|
|
674
|
+
};
|
|
675
|
+
const normalizedOptions = {
|
|
676
|
+
input: {
|
|
677
|
+
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),
|
|
678
|
+
override: { transformer: normalizePath(inputOptions.override?.transformer, workspace) },
|
|
679
|
+
unsafeDisableValidation: inputOptions.unsafeDisableValidation ?? false,
|
|
680
|
+
filters: inputOptions.filters,
|
|
681
|
+
parserOptions: inputOptions.parserOptions
|
|
682
|
+
},
|
|
683
|
+
output: {
|
|
684
|
+
target: globalOptions.output ? normalizePath(globalOptions.output, process.cwd()) : normalizePath(outputOptions.target, outputWorkspace),
|
|
685
|
+
schemas: normalizeSchemasOption(outputOptions.schemas, outputWorkspace),
|
|
686
|
+
operationSchemas: outputOptions.operationSchemas ? normalizePath(outputOptions.operationSchemas, outputWorkspace) : void 0,
|
|
687
|
+
namingConvention: outputOptions.namingConvention ?? NamingConvention.CAMEL_CASE,
|
|
688
|
+
fileExtension: outputOptions.fileExtension ?? defaultFileExtension,
|
|
689
|
+
schemaFileExtension: outputOptions.schemaFileExtension ?? outputOptions.fileExtension ?? defaultSchemaFileExtension,
|
|
690
|
+
workspace: outputOptions.workspace ? outputWorkspace : void 0,
|
|
691
|
+
client: outputOptions.client ?? client ?? OutputClient.AXIOS_FUNCTIONS,
|
|
692
|
+
httpClient: outputOptions.httpClient ?? httpClient ?? ((outputOptions.client ?? client) === OutputClient.ANGULAR_QUERY ? OutputHttpClient.ANGULAR : OutputHttpClient.FETCH),
|
|
693
|
+
mode: normalizeOutputMode(outputOptions.mode ?? mode),
|
|
694
|
+
mock: mocks,
|
|
695
|
+
clean: outputOptions.clean ?? clean ?? false,
|
|
696
|
+
docs: outputOptions.docs ?? false,
|
|
697
|
+
formatter: outputOptions.formatter ?? globalOptions.formatter,
|
|
698
|
+
tsconfig,
|
|
699
|
+
packageJson,
|
|
700
|
+
headers: outputOptions.headers ?? false,
|
|
701
|
+
indexFiles: outputOptions.indexFiles ?? true,
|
|
702
|
+
baseUrl: outputOptions.baseUrl,
|
|
703
|
+
unionAddMissingProperties: outputOptions.unionAddMissingProperties ?? false,
|
|
704
|
+
factoryMethods,
|
|
705
|
+
tagsSplitDeduplication: outputOptions.tagsSplitDeduplication ?? false,
|
|
706
|
+
commonTypesFileName: outputOptions.commonTypesFileName ?? "common-types",
|
|
707
|
+
override: {
|
|
708
|
+
...outputOptions.override,
|
|
709
|
+
mock: {
|
|
710
|
+
arrayMin: outputOptions.override?.mock?.arrayMin ?? 1,
|
|
711
|
+
arrayMax: outputOptions.override?.mock?.arrayMax ?? 10,
|
|
712
|
+
stringMin: outputOptions.override?.mock?.stringMin ?? 10,
|
|
713
|
+
stringMax: outputOptions.override?.mock?.stringMax ?? 20,
|
|
714
|
+
fractionDigits: outputOptions.override?.mock?.fractionDigits ?? 2,
|
|
715
|
+
...outputOptions.override?.mock
|
|
716
|
+
},
|
|
717
|
+
operations: normalizeOperationsAndTags(outputOptions.override?.operations ?? {}, outputWorkspace, { query: globalQueryOptions }, "operations"),
|
|
718
|
+
tags: normalizeOperationsAndTags(outputOptions.override?.tags ?? {}, outputWorkspace, { query: globalQueryOptions }, "tags"),
|
|
719
|
+
mutator: normalizeMutator(outputWorkspace, outputOptions.override?.mutator),
|
|
720
|
+
formData: createFormData(outputWorkspace, outputOptions.override?.formData),
|
|
721
|
+
formUrlEncoded: (isBoolean(outputOptions.override?.formUrlEncoded) ? outputOptions.override.formUrlEncoded : normalizeMutator(outputWorkspace, outputOptions.override?.formUrlEncoded)) ?? true,
|
|
722
|
+
paramsSerializer: normalizeMutator(outputWorkspace, outputOptions.override?.paramsSerializer),
|
|
723
|
+
paramsFilter: normalizeMutator(outputWorkspace, outputOptions.override?.paramsFilter),
|
|
724
|
+
header: outputOptions.override?.header === false ? false : isFunction(outputOptions.override?.header) ? outputOptions.override.header : getDefaultFilesHeader,
|
|
725
|
+
requestOptions: outputOptions.override?.requestOptions ?? true,
|
|
726
|
+
namingConvention: outputOptions.override?.namingConvention ?? {},
|
|
727
|
+
components: {
|
|
728
|
+
schemas: {
|
|
729
|
+
suffix: RefComponentSuffix.schemas,
|
|
730
|
+
itemSuffix: outputOptions.override?.components?.schemas?.itemSuffix ?? "Item",
|
|
731
|
+
...outputOptions.override?.components?.schemas
|
|
732
|
+
},
|
|
733
|
+
responses: {
|
|
734
|
+
suffix: RefComponentSuffix.responses,
|
|
735
|
+
...outputOptions.override?.components?.responses
|
|
736
|
+
},
|
|
737
|
+
parameters: {
|
|
738
|
+
suffix: RefComponentSuffix.parameters,
|
|
739
|
+
...outputOptions.override?.components?.parameters
|
|
740
|
+
},
|
|
741
|
+
requestBodies: {
|
|
742
|
+
suffix: RefComponentSuffix.requestBodies,
|
|
743
|
+
...outputOptions.override?.components?.requestBodies
|
|
595
744
|
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
745
|
+
},
|
|
746
|
+
hono: normalizeHonoOptions(outputOptions.override?.hono, workspace),
|
|
747
|
+
mcp: normalizeMcpOptions(outputOptions.override?.mcp, workspace),
|
|
748
|
+
jsDoc: normalizeJSDocOptions(outputOptions.override?.jsDoc),
|
|
749
|
+
query: globalQueryOptions,
|
|
750
|
+
zod: {
|
|
751
|
+
strict: {
|
|
752
|
+
param: outputOptions.override?.zod?.strict?.param ?? false,
|
|
753
|
+
query: outputOptions.override?.zod?.strict?.query ?? false,
|
|
754
|
+
header: outputOptions.override?.zod?.strict?.header ?? false,
|
|
755
|
+
body: outputOptions.override?.zod?.strict?.body ?? false,
|
|
756
|
+
response: outputOptions.override?.zod?.strict?.response ?? false
|
|
757
|
+
},
|
|
758
|
+
generate: {
|
|
759
|
+
param: outputOptions.override?.zod?.generate?.param ?? true,
|
|
760
|
+
query: outputOptions.override?.zod?.generate?.query ?? true,
|
|
761
|
+
header: outputOptions.override?.zod?.generate?.header ?? true,
|
|
762
|
+
body: outputOptions.override?.zod?.generate?.body ?? true,
|
|
763
|
+
response: outputOptions.override?.zod?.generate?.response ?? true
|
|
764
|
+
},
|
|
765
|
+
coerce: {
|
|
766
|
+
param: outputOptions.override?.zod?.coerce?.param ?? false,
|
|
767
|
+
query: outputOptions.override?.zod?.coerce?.query ?? false,
|
|
768
|
+
header: outputOptions.override?.zod?.coerce?.header ?? false,
|
|
769
|
+
body: outputOptions.override?.zod?.coerce?.body ?? false,
|
|
770
|
+
response: outputOptions.override?.zod?.coerce?.response ?? false
|
|
771
|
+
},
|
|
772
|
+
preprocess: {
|
|
773
|
+
...outputOptions.override?.zod?.preprocess?.param ? { param: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.param) } : {},
|
|
774
|
+
...outputOptions.override?.zod?.preprocess?.query ? { query: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.query) } : {},
|
|
775
|
+
...outputOptions.override?.zod?.preprocess?.header ? { header: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.header) } : {},
|
|
776
|
+
...outputOptions.override?.zod?.preprocess?.body ? { body: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.body) } : {},
|
|
777
|
+
...outputOptions.override?.zod?.preprocess?.response ? { response: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.response) } : {}
|
|
778
|
+
},
|
|
779
|
+
...outputOptions.override?.zod?.params ? { params: normalizeMutator(outputWorkspace, outputOptions.override.zod.params) } : {},
|
|
780
|
+
variant: outputOptions.override?.zod?.variant ?? "classic",
|
|
781
|
+
version: outputOptions.override?.zod?.version ?? "auto",
|
|
782
|
+
generateEachHttpStatus: outputOptions.override?.zod?.generateEachHttpStatus ?? false,
|
|
783
|
+
useBrandedTypes: outputOptions.override?.zod?.useBrandedTypes ?? false,
|
|
784
|
+
generateReusableSchemas: outputOptions.override?.zod?.generateReusableSchemas ?? false,
|
|
785
|
+
generateMeta: outputOptions.override?.zod?.generateMeta ?? false,
|
|
786
|
+
generateDiscriminatedUnion: outputOptions.override?.zod?.generateDiscriminatedUnion ?? false,
|
|
787
|
+
dateTimeOptions: outputOptions.override?.zod?.dateTimeOptions ?? { offset: true },
|
|
788
|
+
timeOptions: outputOptions.override?.zod?.timeOptions ?? {}
|
|
789
|
+
},
|
|
790
|
+
effect: normalizeEffectOptions(outputOptions.override?.effect),
|
|
791
|
+
swr: {
|
|
792
|
+
generateErrorTypes: false,
|
|
793
|
+
...outputOptions.override?.swr
|
|
794
|
+
},
|
|
795
|
+
angular: {
|
|
796
|
+
provideIn: outputOptions.override?.angular?.provideIn ?? "root",
|
|
797
|
+
client: outputOptions.override?.angular?.retrievalClient ?? outputOptions.override?.angular?.client ?? "httpClient",
|
|
798
|
+
runtimeValidation: outputOptions.override?.angular?.runtimeValidation ?? false,
|
|
799
|
+
queryObjectSerialization: outputOptions.override?.angular?.queryObjectSerialization ?? "spec",
|
|
800
|
+
...outputOptions.override?.angular?.httpResource ? { httpResource: outputOptions.override.angular.httpResource } : {}
|
|
801
|
+
},
|
|
802
|
+
fetch: {
|
|
803
|
+
includeHttpResponseReturnType: outputOptions.override?.fetch?.includeHttpResponseReturnType ?? true,
|
|
804
|
+
forceSuccessResponse: outputOptions.override?.fetch?.forceSuccessResponse ?? false,
|
|
805
|
+
runtimeValidation: outputOptions.override?.fetch?.runtimeValidation ?? false,
|
|
806
|
+
useRuntimeFetcher: outputOptions.override?.fetch?.useRuntimeFetcher ?? false,
|
|
807
|
+
...outputOptions.override?.fetch?.arrayFormat ? { arrayFormat: outputOptions.override.fetch.arrayFormat } : {},
|
|
808
|
+
...outputOptions.override?.fetch,
|
|
809
|
+
...outputOptions.override?.fetch?.jsonReviver ? { jsonReviver: normalizeMutator(outputWorkspace, outputOptions.override.fetch.jsonReviver) } : {}
|
|
810
|
+
},
|
|
811
|
+
useDates: outputOptions.override?.useDates ?? false,
|
|
812
|
+
useDeprecatedOperations: outputOptions.override?.useDeprecatedOperations ?? true,
|
|
813
|
+
enumGenerationType: outputOptions.override?.enumGenerationType ?? "const",
|
|
814
|
+
suppressReadonlyModifier: outputOptions.override?.suppressReadonlyModifier ?? false,
|
|
815
|
+
preserveReadonlyRequestBodies: outputOptions.override?.preserveReadonlyRequestBodies ?? "strip",
|
|
816
|
+
splitByContentType: outputOptions.override?.splitByContentType ?? false,
|
|
817
|
+
aliasCombinedTypes: outputOptions.override?.aliasCombinedTypes ?? false
|
|
818
|
+
},
|
|
819
|
+
allParamsOptional: outputOptions.allParamsOptional ?? false,
|
|
820
|
+
urlEncodeParameters: outputOptions.urlEncodeParameters ?? false,
|
|
821
|
+
optionsParamRequired: outputOptions.optionsParamRequired ?? false,
|
|
822
|
+
propertySortOrder: outputOptions.propertySortOrder ?? PropertySortOrder.SPECIFICATION
|
|
823
|
+
},
|
|
824
|
+
hooks: options.hooks ? normalizeHooks(options.hooks) : {}
|
|
825
|
+
};
|
|
826
|
+
if (!normalizedOptions.input.target) throw new Error(styleText("red", `Config requires an input target.`));
|
|
827
|
+
if (!normalizedOptions.output.target && !normalizedOptions.output.schemas) throw new Error(styleText("red", `Config requires an output target or schemas.`));
|
|
828
|
+
const fakerWithSchemasImportPath = normalizedOptions.output.mock.generators.find((g) => !isFunction(g) && g.type === OutputMockType.FAKER && !!g.schemasImportPath);
|
|
829
|
+
if (fakerWithSchemasImportPath) {
|
|
830
|
+
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\`.`));
|
|
831
|
+
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.`));
|
|
669
832
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (isString(command)) await executeCommand(command, args);
|
|
678
|
-
else if (isFunction(command)) await command(args);
|
|
679
|
-
else if (isObject(command)) await executeObjectCommand(command, args);
|
|
680
|
-
} catch (error) {
|
|
681
|
-
logError(error, `Failed to run ${name} hook`);
|
|
833
|
+
const usesAngularGenerator = normalizedOptions.output.client === OutputClient.ANGULAR || normalizedOptions.output.client === OutputClient.ANGULAR_QUERY && normalizedOptions.output.httpClient === OutputHttpClient.ANGULAR;
|
|
834
|
+
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.`));
|
|
835
|
+
if (!usesAngularGenerator) {
|
|
836
|
+
const offendingOperation = Object.entries(normalizedOptions.output.override.operations).find(([, opOverride]) => opOverride?.paramsFilter)?.[0];
|
|
837
|
+
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.`));
|
|
838
|
+
const offendingTag = Object.entries(normalizedOptions.output.override.tags).find(([, tagOverride]) => tagOverride?.paramsFilter)?.[0];
|
|
839
|
+
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.`));
|
|
682
840
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const [cmd, ..._args] = [...parseArgsStringToArgv(command), ...args];
|
|
686
|
-
await execa(cmd, _args);
|
|
687
|
-
}
|
|
688
|
-
async function executeObjectCommand(command, args) {
|
|
689
|
-
if (command.injectGeneratedDirsAndFiles === false) args = [];
|
|
690
|
-
if (isString(command.command)) await executeCommand(command.command, args);
|
|
691
|
-
else if (isFunction(command.command)) await command.command();
|
|
841
|
+
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.`);
|
|
842
|
+
return normalizedOptions;
|
|
692
843
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
if (pkgPath) {
|
|
699
|
-
const pkg = await dynamicImport(pkgPath, workspace);
|
|
700
|
-
if (isPackageJson(pkg)) return resolveAndAttachVersions(await maybeReplaceCatalog(pkg, workspace), workspace, pkgPath);
|
|
701
|
-
else throw new Error("Invalid package.json file");
|
|
702
|
-
}
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
const normalizedPath = normalizePath(packageJson, workspace);
|
|
706
|
-
if (fs.existsSync(normalizedPath)) {
|
|
707
|
-
const pkg = await dynamicImport(normalizedPath);
|
|
708
|
-
if (isPackageJson(pkg)) return resolveAndAttachVersions(await maybeReplaceCatalog(pkg, workspace), workspace, normalizedPath);
|
|
709
|
-
else throw new Error(`Invalid package.json file: ${normalizedPath}`);
|
|
710
|
-
}
|
|
711
|
-
};
|
|
712
|
-
const isPackageJson = (obj) => isObject(obj);
|
|
713
|
-
const resolvedCache = /* @__PURE__ */ new Map();
|
|
714
|
-
const resolveAndAttachVersions = (pkg, workspace, cacheKey) => {
|
|
715
|
-
const cached = resolvedCache.get(cacheKey);
|
|
716
|
-
if (cached) {
|
|
717
|
-
pkg.resolvedVersions = cached;
|
|
718
|
-
return pkg;
|
|
719
|
-
}
|
|
720
|
-
const resolved = resolveInstalledVersions(pkg, workspace);
|
|
721
|
-
if (Object.keys(resolved).length > 0) {
|
|
722
|
-
pkg.resolvedVersions = resolved;
|
|
723
|
-
resolvedCache.set(cacheKey, resolved);
|
|
724
|
-
for (const [name, version] of Object.entries(resolved)) logVerbose(styleText("dim", `Detected ${styleText("white", name)} v${styleText("white", version)}`));
|
|
725
|
-
}
|
|
726
|
-
return pkg;
|
|
727
|
-
};
|
|
728
|
-
const hasCatalogReferences = (pkg) => {
|
|
729
|
-
return [
|
|
730
|
-
...Object.entries(pkg.dependencies ?? {}),
|
|
731
|
-
...Object.entries(pkg.devDependencies ?? {}),
|
|
732
|
-
...Object.entries(pkg.peerDependencies ?? {})
|
|
733
|
-
].some(([, value]) => isString(value) && value.startsWith("catalog:"));
|
|
734
|
-
};
|
|
735
|
-
const loadPnpmWorkspaceCatalog = async (workspace) => {
|
|
736
|
-
const filePath = await findUp("pnpm-workspace.yaml", { cwd: workspace });
|
|
737
|
-
if (!filePath) return void 0;
|
|
738
|
-
try {
|
|
739
|
-
const file = await fs.readFile(filePath, "utf8");
|
|
740
|
-
const data = yaml.load(file);
|
|
741
|
-
if (!data?.catalog && !data?.catalogs) return void 0;
|
|
844
|
+
function normalizeMutator(workspace, mutator) {
|
|
845
|
+
if (isObject(mutator)) {
|
|
846
|
+
const m = mutator;
|
|
847
|
+
if (!m.path) throw new Error(styleText("red", `Mutator requires a path.`));
|
|
848
|
+
const resolvedPath = looksLikePackageSpecifier(m.path) ? resolvePackageSpecifier(workspace, m.path) : void 0;
|
|
742
849
|
return {
|
|
743
|
-
|
|
744
|
-
|
|
850
|
+
path: !!resolvedPath || isPackageSpecifierCandidate(workspace, m.path) ? m.path : path.resolve(workspace, m.path),
|
|
851
|
+
...resolvedPath ? { resolvedPath } : {},
|
|
852
|
+
name: m.name,
|
|
853
|
+
default: m.default ?? !m.name,
|
|
854
|
+
alias: m.alias,
|
|
855
|
+
external: m.external,
|
|
856
|
+
extension: m.extension
|
|
745
857
|
};
|
|
746
|
-
} catch {
|
|
747
|
-
return;
|
|
748
858
|
}
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
const filePaths = await findUpMultiple("package.json", { cwd: workspace });
|
|
752
|
-
for (const filePath of filePaths) try {
|
|
753
|
-
const pkg = await fs.readJson(filePath);
|
|
754
|
-
if (pkg.catalog || pkg.catalogs) return {
|
|
755
|
-
catalog: pkg.catalog,
|
|
756
|
-
catalogs: pkg.catalogs
|
|
757
|
-
};
|
|
758
|
-
} catch {}
|
|
759
|
-
};
|
|
760
|
-
const loadYarnrcCatalog = async (workspace) => {
|
|
761
|
-
const filePath = await findUp(".yarnrc.yml", { cwd: workspace });
|
|
762
|
-
if (!filePath) return void 0;
|
|
763
|
-
try {
|
|
764
|
-
const file = await fs.readFile(filePath, "utf8");
|
|
765
|
-
const data = yaml.load(file);
|
|
766
|
-
if (!data?.catalog && !data?.catalogs) return void 0;
|
|
859
|
+
if (isString(mutator)) {
|
|
860
|
+
const resolvedPath = looksLikePackageSpecifier(mutator) ? resolvePackageSpecifier(workspace, mutator) : void 0;
|
|
767
861
|
return {
|
|
768
|
-
|
|
769
|
-
|
|
862
|
+
path: !!resolvedPath || isPackageSpecifierCandidate(workspace, mutator) ? mutator : path.resolve(workspace, mutator),
|
|
863
|
+
...resolvedPath ? { resolvedPath } : {},
|
|
864
|
+
default: true
|
|
770
865
|
};
|
|
771
|
-
} catch {
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
const maybeReplaceCatalog = async (pkg, workspace) => {
|
|
776
|
-
if (!hasCatalogReferences(pkg)) return pkg;
|
|
777
|
-
const catalogData = await loadPnpmWorkspaceCatalog(workspace) ?? await loadPackageJsonCatalog(workspace) ?? await loadYarnrcCatalog(workspace);
|
|
778
|
-
if (!catalogData) {
|
|
779
|
-
logWarning("⚠️ package.json contains catalog: references, but no catalog source was found (checked: pnpm-workspace.yaml, package.json, .yarnrc.yml).");
|
|
780
|
-
return pkg;
|
|
781
866
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
const catalog = catalogData.catalogs?.[catalogName];
|
|
803
|
-
if (!catalog) {
|
|
804
|
-
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(", ")})`);
|
|
867
|
+
}
|
|
868
|
+
async function resolveFirstValidTarget(targets, workspace, parserOptions) {
|
|
869
|
+
for (const target of targets) {
|
|
870
|
+
if (isUrl(target)) {
|
|
871
|
+
try {
|
|
872
|
+
const headers = getHeadersForUrl(target, parserOptions?.headers);
|
|
873
|
+
const headResponse = await fetchWithTimeout(target, {
|
|
874
|
+
method: "HEAD",
|
|
875
|
+
headers
|
|
876
|
+
});
|
|
877
|
+
if (headResponse.ok) return target;
|
|
878
|
+
if (headResponse.status === 405 || headResponse.status === 501) {
|
|
879
|
+
if ((await fetchWithTimeout(target, {
|
|
880
|
+
method: "GET",
|
|
881
|
+
headers
|
|
882
|
+
})).ok) return target;
|
|
883
|
+
}
|
|
884
|
+
} catch {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
805
887
|
continue;
|
|
806
888
|
}
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
|
|
889
|
+
const resolvedTarget = normalizePath(target, workspace);
|
|
890
|
+
try {
|
|
891
|
+
await access(resolvedTarget);
|
|
892
|
+
return resolvedTarget;
|
|
893
|
+
} catch {
|
|
810
894
|
continue;
|
|
811
895
|
}
|
|
812
|
-
dependencies[packageName] = sub;
|
|
813
896
|
}
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
if (configPath) return convertTarget(parseTsconfig(configPath));
|
|
836
|
-
return;
|
|
897
|
+
throw new Error(styleText("red", `None of the input targets could be resolved:\n${targets.map((target) => ` - ${target}`).join("\n")}`));
|
|
898
|
+
}
|
|
899
|
+
function getHeadersForUrl(url, headersConfig) {
|
|
900
|
+
if (!headersConfig) return {};
|
|
901
|
+
const { hostname } = new URL(url);
|
|
902
|
+
const matchedHeaders = {};
|
|
903
|
+
for (const headerEntry of headersConfig) if (headerEntry.domains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) Object.assign(matchedHeaders, headerEntry.headers);
|
|
904
|
+
return matchedHeaders;
|
|
905
|
+
}
|
|
906
|
+
async function fetchWithTimeout(target, init) {
|
|
907
|
+
const controller = new AbortController();
|
|
908
|
+
const timeoutId = setTimeout(() => {
|
|
909
|
+
controller.abort();
|
|
910
|
+
}, INPUT_TARGET_FETCH_TIMEOUT_MS);
|
|
911
|
+
try {
|
|
912
|
+
return await fetch(target, {
|
|
913
|
+
...init,
|
|
914
|
+
signal: controller.signal
|
|
915
|
+
});
|
|
916
|
+
} finally {
|
|
917
|
+
clearTimeout(timeoutId);
|
|
837
918
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
919
|
+
}
|
|
920
|
+
function normalizePathOrUrl(path, workspace) {
|
|
921
|
+
if (isString(path) && !isUrl(path)) return normalizePath(path, workspace);
|
|
922
|
+
return path;
|
|
923
|
+
}
|
|
924
|
+
function normalizePath(path$1, workspace) {
|
|
925
|
+
if (!isString(path$1)) return path$1;
|
|
926
|
+
return path.resolve(workspace, path$1);
|
|
927
|
+
}
|
|
928
|
+
function normalizeOperationsAndTags(operationsOrTags, workspace, global, source) {
|
|
929
|
+
const unsupportedZodKeys = [
|
|
930
|
+
"version",
|
|
931
|
+
"variant",
|
|
932
|
+
"dateTimeOptions",
|
|
933
|
+
"timeOptions",
|
|
934
|
+
"generateEachHttpStatus",
|
|
935
|
+
"generateReusableSchemas",
|
|
936
|
+
"generateMeta",
|
|
937
|
+
"generateDiscriminatedUnion"
|
|
938
|
+
];
|
|
939
|
+
return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, effect, ...rest }]) => {
|
|
940
|
+
const unsupportedOperationZodKeys = zod && unsupportedZodKeys.filter((unsupportedKey) => zod[unsupportedKey] !== void 0);
|
|
941
|
+
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(", ")}.`);
|
|
942
|
+
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);
|
|
943
|
+
return [key, {
|
|
944
|
+
...rest,
|
|
945
|
+
...angular ? { angular: {
|
|
946
|
+
provideIn: angular.provideIn ?? "root",
|
|
947
|
+
client: angular.retrievalClient ?? angular.client ?? "httpClient",
|
|
948
|
+
runtimeValidation: angular.runtimeValidation ?? false,
|
|
949
|
+
queryObjectSerialization: angular.queryObjectSerialization ?? "spec",
|
|
950
|
+
...angular.httpResource ? { httpResource: angular.httpResource } : {}
|
|
951
|
+
} } : {},
|
|
952
|
+
...query ? { query: normalizeQueryOptions(query, workspace, global.query) } : {},
|
|
953
|
+
...hasSupportedOperationZodConfig && zod ? { zod: {
|
|
954
|
+
strict: {
|
|
955
|
+
param: zod.strict?.param ?? false,
|
|
956
|
+
query: zod.strict?.query ?? false,
|
|
957
|
+
header: zod.strict?.header ?? false,
|
|
958
|
+
body: zod.strict?.body ?? false,
|
|
959
|
+
response: zod.strict?.response ?? false
|
|
960
|
+
},
|
|
961
|
+
generate: {
|
|
962
|
+
param: zod.generate?.param ?? true,
|
|
963
|
+
query: zod.generate?.query ?? true,
|
|
964
|
+
header: zod.generate?.header ?? true,
|
|
965
|
+
body: zod.generate?.body ?? true,
|
|
966
|
+
response: zod.generate?.response ?? true
|
|
967
|
+
},
|
|
968
|
+
coerce: {
|
|
969
|
+
param: zod.coerce?.param ?? false,
|
|
970
|
+
query: zod.coerce?.query ?? false,
|
|
971
|
+
header: zod.coerce?.header ?? false,
|
|
972
|
+
body: zod.coerce?.body ?? false,
|
|
973
|
+
response: zod.coerce?.response ?? false
|
|
974
|
+
},
|
|
975
|
+
preprocess: {
|
|
976
|
+
...zod.preprocess?.param ? { param: normalizeMutator(workspace, zod.preprocess.param) } : {},
|
|
977
|
+
...zod.preprocess?.query ? { query: normalizeMutator(workspace, zod.preprocess.query) } : {},
|
|
978
|
+
...zod.preprocess?.header ? { header: normalizeMutator(workspace, zod.preprocess.header) } : {},
|
|
979
|
+
...zod.preprocess?.body ? { body: normalizeMutator(workspace, zod.preprocess.body) } : {},
|
|
980
|
+
...zod.preprocess?.response ? { response: normalizeMutator(workspace, zod.preprocess.response) } : {}
|
|
981
|
+
},
|
|
982
|
+
...zod.params ? { params: normalizeMutator(workspace, zod.params) } : {},
|
|
983
|
+
useBrandedTypes: zod.useBrandedTypes ?? false
|
|
984
|
+
} } : {},
|
|
985
|
+
...effect ? { effect: normalizeEffectOptions(effect) } : {},
|
|
986
|
+
...transformer ? { transformer: normalizePath(transformer, workspace) } : {},
|
|
987
|
+
...mutator ? { mutator: normalizeMutator(workspace, mutator) } : {},
|
|
988
|
+
...formData === void 0 ? {} : { formData: createFormData(workspace, formData) },
|
|
989
|
+
...formUrlEncoded ? { formUrlEncoded: isBoolean(formUrlEncoded) ? formUrlEncoded : normalizeMutator(workspace, formUrlEncoded) } : {},
|
|
990
|
+
...paramsSerializer ? { paramsSerializer: normalizeMutator(workspace, paramsSerializer) } : {},
|
|
991
|
+
...paramsFilter ? { paramsFilter: normalizeMutator(workspace, paramsFilter) } : {}
|
|
992
|
+
}];
|
|
993
|
+
}));
|
|
994
|
+
}
|
|
995
|
+
function normalizeOutputMode(mode) {
|
|
996
|
+
if (!mode) return OutputMode.SINGLE;
|
|
997
|
+
if (!Object.values(OutputMode).includes(mode)) {
|
|
998
|
+
logWarning(`⚠️ Unknown provided mode => ${mode}`);
|
|
999
|
+
return OutputMode.SINGLE;
|
|
842
1000
|
}
|
|
843
|
-
|
|
844
|
-
};
|
|
845
|
-
//#endregion
|
|
846
|
-
//#region src/utils/options.ts
|
|
847
|
-
const INPUT_TARGET_FETCH_TIMEOUT_MS = 1e4;
|
|
848
|
-
/**
|
|
849
|
-
* Type helper to make it easier to use orval.config.ts
|
|
850
|
-
* accepts a direct {@link ConfigExternal} object.
|
|
851
|
-
*/
|
|
852
|
-
function defineConfig(options) {
|
|
853
|
-
return options;
|
|
1001
|
+
return mode;
|
|
854
1002
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1003
|
+
function normalizeHooks(hooks) {
|
|
1004
|
+
const keys = Object.keys(hooks);
|
|
1005
|
+
const result = {};
|
|
1006
|
+
for (const key of keys) if (isString(hooks[key])) result[key] = [hooks[key]];
|
|
1007
|
+
else if (Array.isArray(hooks[key])) result[key] = hooks[key];
|
|
1008
|
+
else if (isFunction(hooks[key])) result[key] = [hooks[key]];
|
|
1009
|
+
else if (isObject(hooks[key])) result[key] = [hooks[key]];
|
|
1010
|
+
return result;
|
|
861
1011
|
}
|
|
862
|
-
function
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
disabled: !formData,
|
|
870
|
-
arrayHandling: defaultArrayHandling
|
|
871
|
-
};
|
|
872
|
-
if (isString(formData)) return {
|
|
873
|
-
disabled: false,
|
|
874
|
-
mutator: normalizeMutator(workspace, formData),
|
|
875
|
-
arrayHandling: defaultArrayHandling
|
|
876
|
-
};
|
|
877
|
-
if ("mutator" in formData || "arrayHandling" in formData) return {
|
|
878
|
-
disabled: false,
|
|
879
|
-
mutator: normalizeMutator(workspace, formData.mutator),
|
|
880
|
-
arrayHandling: formData.arrayHandling ?? defaultArrayHandling
|
|
1012
|
+
function normalizeHonoOptions(hono = {}, workspace) {
|
|
1013
|
+
return {
|
|
1014
|
+
...hono.handlers ? { handlers: path.resolve(workspace, hono.handlers) } : {},
|
|
1015
|
+
handlerGenerationStrategy: hono.handlerGenerationStrategy ?? "smart",
|
|
1016
|
+
compositeRoute: hono.compositeRoute ? path.resolve(workspace, hono.compositeRoute) : "",
|
|
1017
|
+
validator: hono.validator ?? true,
|
|
1018
|
+
validatorOutputPath: hono.validatorOutputPath ? path.resolve(workspace, hono.validatorOutputPath) : ""
|
|
881
1019
|
};
|
|
1020
|
+
}
|
|
1021
|
+
function normalizeMcpServerOptions(server, workspace) {
|
|
882
1022
|
return {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1023
|
+
path: path.resolve(workspace, server.path),
|
|
1024
|
+
name: server.name,
|
|
1025
|
+
default: server.default ?? !server.name
|
|
886
1026
|
};
|
|
887
1027
|
}
|
|
888
|
-
function
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1028
|
+
function normalizeMcpOptions(mcp = {}, workspace) {
|
|
1029
|
+
return mcp.server ? { server: normalizeMcpServerOptions(mcp.server, workspace) } : {};
|
|
1030
|
+
}
|
|
1031
|
+
function normalizeJSDocOptions(jsdoc = {}) {
|
|
1032
|
+
return { ...jsdoc };
|
|
1033
|
+
}
|
|
1034
|
+
function normalizeQueryOptions(queryOptions = {}, outputWorkspace, globalOptions = {}) {
|
|
1035
|
+
if (queryOptions.options) logWarning("⚠️ Using query options is deprecated and will be removed in a future major release. Please use queryOptions or mutationOptions instead.");
|
|
892
1036
|
return {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1037
|
+
...isNullish(queryOptions.usePrefetch) ? {} : { usePrefetch: queryOptions.usePrefetch },
|
|
1038
|
+
...isNullish(queryOptions.useInvalidate) ? {} : { useInvalidate: queryOptions.useInvalidate },
|
|
1039
|
+
...isNullish(queryOptions.useSetQueryData) ? {} : { useSetQueryData: queryOptions.useSetQueryData },
|
|
1040
|
+
...isNullish(queryOptions.useGetQueryData) ? {} : { useGetQueryData: queryOptions.useGetQueryData },
|
|
1041
|
+
...isNullish(queryOptions.useQuery) ? {} : { useQuery: queryOptions.useQuery },
|
|
1042
|
+
...isNullish(queryOptions.useSuspenseQuery) ? {} : { useSuspenseQuery: queryOptions.useSuspenseQuery },
|
|
1043
|
+
...isNullish(queryOptions.useMutation) ? {} : { useMutation: queryOptions.useMutation },
|
|
1044
|
+
...isNullish(queryOptions.useInfinite) ? {} : { useInfinite: queryOptions.useInfinite },
|
|
1045
|
+
...isNullish(queryOptions.useSuspenseInfiniteQuery) ? {} : { useSuspenseInfiniteQuery: queryOptions.useSuspenseInfiniteQuery },
|
|
1046
|
+
...queryOptions.useInfiniteQueryParam ? { useInfiniteQueryParam: queryOptions.useInfiniteQueryParam } : {},
|
|
1047
|
+
...queryOptions.options ? { options: queryOptions.options } : {},
|
|
1048
|
+
...globalOptions.queryKey ? { queryKey: globalOptions.queryKey } : {},
|
|
1049
|
+
...queryOptions.queryKey ? { queryKey: normalizeMutator(outputWorkspace, queryOptions.queryKey) } : {},
|
|
1050
|
+
...globalOptions.queryOptions ? { queryOptions: globalOptions.queryOptions } : {},
|
|
1051
|
+
...queryOptions.queryOptions ? { queryOptions: normalizeMutator(outputWorkspace, queryOptions.queryOptions) } : {},
|
|
1052
|
+
...globalOptions.mutationOptions ? { mutationOptions: globalOptions.mutationOptions } : {},
|
|
1053
|
+
...queryOptions.mutationOptions ? { mutationOptions: normalizeMutator(outputWorkspace, queryOptions.mutationOptions) } : {},
|
|
1054
|
+
...isNullish(globalOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: globalOptions.shouldExportQueryKey },
|
|
1055
|
+
...isNullish(queryOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: queryOptions.shouldExportQueryKey },
|
|
1056
|
+
...isNullish(globalOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: globalOptions.shouldFilterQueryKey },
|
|
1057
|
+
...isNullish(queryOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: queryOptions.shouldFilterQueryKey },
|
|
1058
|
+
...isNullish(globalOptions.queryKeyFilter) ? {} : { queryKeyFilter: globalOptions.queryKeyFilter },
|
|
1059
|
+
...isNullish(queryOptions.queryKeyFilter) ? {} : { queryKeyFilter: queryOptions.queryKeyFilter },
|
|
1060
|
+
...isNullish(globalOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: globalOptions.shouldExportHttpClient },
|
|
1061
|
+
...isNullish(queryOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: queryOptions.shouldExportHttpClient },
|
|
1062
|
+
...isNullish(globalOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: globalOptions.shouldExportMutatorHooks },
|
|
1063
|
+
...isNullish(queryOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: queryOptions.shouldExportMutatorHooks },
|
|
1064
|
+
...isNullish(globalOptions.shouldSplitQueryKey) ? {} : { shouldSplitQueryKey: globalOptions.shouldSplitQueryKey },
|
|
1065
|
+
...isNullish(queryOptions.shouldSplitQueryKey) ? {} : { shouldSplitQueryKey: queryOptions.shouldSplitQueryKey },
|
|
1066
|
+
...isNullish(globalOptions.signal) ? {} : { signal: globalOptions.signal },
|
|
1067
|
+
...isNullish(globalOptions.useOperationIdAsQueryKey) ? {} : { useOperationIdAsQueryKey: globalOptions.useOperationIdAsQueryKey },
|
|
1068
|
+
...isNullish(queryOptions.useOperationIdAsQueryKey) ? {} : { useOperationIdAsQueryKey: queryOptions.useOperationIdAsQueryKey },
|
|
1069
|
+
...isNullish(globalOptions.signal) ? {} : { signal: globalOptions.signal },
|
|
1070
|
+
...isNullish(queryOptions.signal) ? {} : { signal: queryOptions.signal },
|
|
1071
|
+
...isNullish(globalOptions.version) ? {} : { version: globalOptions.version },
|
|
1072
|
+
...isNullish(queryOptions.version) ? {} : { version: queryOptions.version },
|
|
1073
|
+
...queryOptions.mutationInvalidates ? { mutationInvalidates: queryOptions.mutationInvalidates } : {},
|
|
1074
|
+
...isNullish(globalOptions.runtimeValidation) ? {} : { runtimeValidation: globalOptions.runtimeValidation },
|
|
1075
|
+
...isNullish(queryOptions.runtimeValidation) ? {} : { runtimeValidation: queryOptions.runtimeValidation }
|
|
897
1076
|
};
|
|
898
1077
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
if (!value) throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received an empty string.`);
|
|
908
|
-
if (value.trim() === "") throw new Error(`\`${fieldName}\` must be a non-empty package specifier (e.g. '@acme/models'). Received a whitespace-only string.`);
|
|
909
|
-
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}"`);
|
|
910
|
-
if (value.startsWith(".")) throw new Error(`\`${fieldName}\` must be a package specifier (e.g. '@acme/models'), not a relative path. Received: "${value}"`);
|
|
911
|
-
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}"`);
|
|
1078
|
+
function getDefaultFilesHeader({ title, description, version: version$1 } = {}) {
|
|
1079
|
+
return [
|
|
1080
|
+
`Generated by ${name} v${version} 🍺`,
|
|
1081
|
+
`Do not edit manually.`,
|
|
1082
|
+
...title ? [title] : [],
|
|
1083
|
+
...description ? [description] : [],
|
|
1084
|
+
...version$1 ? [`OpenAPI spec version: ${version$1}`] : []
|
|
1085
|
+
];
|
|
912
1086
|
}
|
|
913
|
-
|
|
914
|
-
|
|
1087
|
+
//#endregion
|
|
1088
|
+
//#region src/import-specs.ts
|
|
1089
|
+
async function resolveSpec(input, { parserOptions, transformer, workspace, unsafeDisableValidation = false }) {
|
|
1090
|
+
const allowedRefs = parserOptions?.externalRefs?.allow ?? [];
|
|
1091
|
+
const isWildcard = allowedRefs.includes("*");
|
|
1092
|
+
const { data: specData, origin } = await loadSpec(input, parserOptions?.headers);
|
|
1093
|
+
if (!isWildcard) {
|
|
1094
|
+
const disallowed = collectExternalRefs(specData).filter((ref) => !isAllowedRef(ref, allowedRefs, origin));
|
|
1095
|
+
if (disallowed.length > 0) throw new Error(formatDisallowedRefsError(disallowed, allowedRefs));
|
|
1096
|
+
} else {
|
|
1097
|
+
const docs = [...new Set(collectExternalRefs(specData).map(getRefDocument))];
|
|
1098
|
+
if (docs.length > 0) logWarning(`External $ref documents being resolved:\n` + docs.map((d) => ` - ${d}`).join("\n"));
|
|
1099
|
+
}
|
|
1100
|
+
const dereferencedData = await bundleAndDereferenceExternalRefs(specData, parserOptions, origin, isWildcard, allowedRefs);
|
|
1101
|
+
let transformedData = dereferencedData;
|
|
1102
|
+
if (transformer) {
|
|
1103
|
+
const applied = await applyInputTransformer(dereferencedData, transformer, workspace);
|
|
1104
|
+
transformedData = collectExternalRefs(applied).length > 0 ? await bundleAndDereferenceExternalRefs(applied, parserOptions, origin, isWildcard, allowedRefs) : applied;
|
|
1105
|
+
}
|
|
1106
|
+
if (unsafeDisableValidation) logWarning("🚨 OpenAPI spec validation is disabled.\n Code generation with invalid specs is not guaranteed to work and may break in minor updates.\n Bug reports with validation disabled will not be accepted.");
|
|
1107
|
+
else {
|
|
1108
|
+
validateComponentKeys(transformedData);
|
|
1109
|
+
const { valid, errors } = await validate(transformedData);
|
|
1110
|
+
if (!valid) throw new Error(`OpenAPI spec validation failed:\n${JSON.stringify(errors, void 0, 2)}`);
|
|
1111
|
+
}
|
|
1112
|
+
const { specification } = upgrade(transformedData);
|
|
1113
|
+
return specification;
|
|
915
1114
|
}
|
|
916
|
-
function
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1115
|
+
async function applyInputTransformer(data, transformer, workspace) {
|
|
1116
|
+
const transformerFn = await dynamicImport(transformer, workspace);
|
|
1117
|
+
const result = await transformerFn(data);
|
|
1118
|
+
if (!isObject(result)) {
|
|
1119
|
+
const source = isString(transformer) ? transformer : transformerFn.name || "<inline function>";
|
|
1120
|
+
throw new Error(`input.override.transformer must return an OpenAPI document object; got ${result === void 0 ? "undefined" : typeof result} from ${source}. Ensure your transformer returns the (possibly modified) spec.`);
|
|
921
1121
|
}
|
|
1122
|
+
return result;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Bundle external references into the document and then resolve the `x-ext`
|
|
1126
|
+
* entries that `@scalar/json-magic` produces. Shared by the initial pass and
|
|
1127
|
+
* the post-transformer pass (#3327); `origin` lets the second pass resolve refs
|
|
1128
|
+
* relative to the original spec file when the input is an in-memory object.
|
|
1129
|
+
*/
|
|
1130
|
+
async function bundleAndDereferenceExternalRefs(input, parserOptions, origin, isWildcard = false, allowedExternalRefs = []) {
|
|
1131
|
+
return dereferenceExternalRef(await bundle(input, {
|
|
1132
|
+
plugins: [
|
|
1133
|
+
createSafeFileLoader(origin, isWildcard, allowedExternalRefs),
|
|
1134
|
+
createSafeUrlLoader(origin, isWildcard, allowedExternalRefs, parserOptions?.headers),
|
|
1135
|
+
parseJson(),
|
|
1136
|
+
parseYaml()
|
|
1137
|
+
],
|
|
1138
|
+
treeShake: false,
|
|
1139
|
+
...origin ? { origin } : {}
|
|
1140
|
+
}));
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Load the top-level spec into an inline object so we can scan it for external
|
|
1144
|
+
* `$ref`s before `bundle()` resolves them. The top-level target is trusted
|
|
1145
|
+
* (user-configured `input.target`); only `$ref` values inside the spec are
|
|
1146
|
+
* untrusted.
|
|
1147
|
+
*/
|
|
1148
|
+
function parseSpec(text) {
|
|
1149
|
+
const result = jsYaml.load(text);
|
|
1150
|
+
if (!isObject(result)) throw new Error("OpenAPI spec must be a valid JSON/YAML object.");
|
|
1151
|
+
return result;
|
|
922
1152
|
}
|
|
923
|
-
function
|
|
924
|
-
if (!
|
|
925
|
-
if (
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (parent === dir) return false;
|
|
933
|
-
dir = parent;
|
|
1153
|
+
async function loadSpec(input, headers) {
|
|
1154
|
+
if (!isString(input)) return { data: input };
|
|
1155
|
+
if (isUrl(input)) {
|
|
1156
|
+
const response = await fetch(input, { headers: getHeadersForUrl(input, headers) });
|
|
1157
|
+
if (!response.ok) throw new Error(`Failed to fetch OpenAPI spec from ${input}: ${response.status} ${response.statusText}`);
|
|
1158
|
+
return {
|
|
1159
|
+
data: parseSpec(await response.text()),
|
|
1160
|
+
origin: input
|
|
1161
|
+
};
|
|
934
1162
|
}
|
|
935
|
-
}
|
|
936
|
-
function normalizeEffectOptions(effect) {
|
|
937
1163
|
return {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
query: effect?.strict?.query ?? false,
|
|
941
|
-
header: effect?.strict?.header ?? false,
|
|
942
|
-
body: effect?.strict?.body ?? false,
|
|
943
|
-
response: effect?.strict?.response ?? false
|
|
944
|
-
},
|
|
945
|
-
generate: {
|
|
946
|
-
param: effect?.generate?.param ?? true,
|
|
947
|
-
query: effect?.generate?.query ?? true,
|
|
948
|
-
header: effect?.generate?.header ?? true,
|
|
949
|
-
body: effect?.generate?.body ?? true,
|
|
950
|
-
response: effect?.generate?.response ?? true
|
|
951
|
-
},
|
|
952
|
-
generateEachHttpStatus: effect?.generateEachHttpStatus ?? false,
|
|
953
|
-
useBrandedTypes: effect?.useBrandedTypes ?? false
|
|
1164
|
+
data: parseSpec(await readFile(input, "utf-8")),
|
|
1165
|
+
origin: input
|
|
954
1166
|
};
|
|
955
1167
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
else if (mocksOption && typeof mocksOption === "object") {
|
|
980
|
-
if (!Array.isArray(mocksOption.generators)) throw new TypeError("mock.generators must be an array of generator entries (e.g. [{ type: \"msw\" }]).");
|
|
981
|
-
const sharedMockPath = mocksOption.path && isString(mocksOption.path) ? normalizePath(mocksOption.path, outputWorkspace) : void 0;
|
|
982
|
-
mocks = {
|
|
983
|
-
indexMockFiles: mocksOption.indexMockFiles ?? false,
|
|
984
|
-
path: sharedMockPath,
|
|
985
|
-
generators: mocksOption.generators.map((m) => isFunction(m) ? m : {
|
|
986
|
-
...getDefaultMockOptionsForType(m.type),
|
|
987
|
-
...m,
|
|
988
|
-
path: m.path && isString(m.path) ? normalizePath(m.path, outputWorkspace) : sharedMockPath
|
|
989
|
-
})
|
|
990
|
-
};
|
|
991
|
-
}
|
|
992
|
-
const seenMockTypes = /* @__PURE__ */ new Set();
|
|
993
|
-
for (const entry of mocks.generators) {
|
|
994
|
-
if (isFunction(entry)) continue;
|
|
995
|
-
if (seenMockTypes.has(entry.type)) throw new Error(`Duplicate mock generator type "${entry.type}". Each type can only appear once in mock.generators.`);
|
|
996
|
-
seenMockTypes.add(entry.type);
|
|
997
|
-
if (entry.type === OutputMockType.FAKER) validatePackageSpecifier(entry.schemasImportPath, "mock.generators[faker].schemasImportPath");
|
|
1168
|
+
/**
|
|
1169
|
+
* Strip the JSON pointer fragment (`#/...`) from a `$ref` value, leaving only
|
|
1170
|
+
* the document target (file path or URL).
|
|
1171
|
+
*/
|
|
1172
|
+
function getRefDocument(ref) {
|
|
1173
|
+
const hashIndex = ref.indexOf("#");
|
|
1174
|
+
return hashIndex === -1 ? ref : ref.slice(0, hashIndex);
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Collect all external `$ref` document targets from a spec object. Returns
|
|
1178
|
+
* deduplicated ref strings in their raw form (before fragment stripping).
|
|
1179
|
+
*/
|
|
1180
|
+
function collectExternalRefs(obj) {
|
|
1181
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1182
|
+
function walk(val) {
|
|
1183
|
+
if (Array.isArray(val)) {
|
|
1184
|
+
val.forEach(walk);
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
if (isObject(val)) {
|
|
1188
|
+
if ("$ref" in val && isString(val.$ref) && !val.$ref.startsWith("#")) refs.add(val.$ref);
|
|
1189
|
+
Object.values(val).forEach(walk);
|
|
1190
|
+
}
|
|
998
1191
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
tagsSplitDeduplication: outputOptions.tagsSplitDeduplication ?? false,
|
|
1049
|
-
commonTypesFileName: outputOptions.commonTypesFileName ?? "common-types",
|
|
1050
|
-
override: {
|
|
1051
|
-
...outputOptions.override,
|
|
1052
|
-
mock: {
|
|
1053
|
-
arrayMin: outputOptions.override?.mock?.arrayMin ?? 1,
|
|
1054
|
-
arrayMax: outputOptions.override?.mock?.arrayMax ?? 10,
|
|
1055
|
-
stringMin: outputOptions.override?.mock?.stringMin ?? 10,
|
|
1056
|
-
stringMax: outputOptions.override?.mock?.stringMax ?? 20,
|
|
1057
|
-
fractionDigits: outputOptions.override?.mock?.fractionDigits ?? 2,
|
|
1058
|
-
...outputOptions.override?.mock
|
|
1059
|
-
},
|
|
1060
|
-
operations: normalizeOperationsAndTags(outputOptions.override?.operations ?? {}, outputWorkspace, { query: globalQueryOptions }, "operations"),
|
|
1061
|
-
tags: normalizeOperationsAndTags(outputOptions.override?.tags ?? {}, outputWorkspace, { query: globalQueryOptions }, "tags"),
|
|
1062
|
-
mutator: normalizeMutator(outputWorkspace, outputOptions.override?.mutator),
|
|
1063
|
-
formData: createFormData(outputWorkspace, outputOptions.override?.formData),
|
|
1064
|
-
formUrlEncoded: (isBoolean(outputOptions.override?.formUrlEncoded) ? outputOptions.override.formUrlEncoded : normalizeMutator(outputWorkspace, outputOptions.override?.formUrlEncoded)) ?? true,
|
|
1065
|
-
paramsSerializer: normalizeMutator(outputWorkspace, outputOptions.override?.paramsSerializer),
|
|
1066
|
-
paramsFilter: normalizeMutator(outputWorkspace, outputOptions.override?.paramsFilter),
|
|
1067
|
-
header: outputOptions.override?.header === false ? false : isFunction(outputOptions.override?.header) ? outputOptions.override.header : getDefaultFilesHeader,
|
|
1068
|
-
requestOptions: outputOptions.override?.requestOptions ?? true,
|
|
1069
|
-
namingConvention: outputOptions.override?.namingConvention ?? {},
|
|
1070
|
-
components: {
|
|
1071
|
-
schemas: {
|
|
1072
|
-
suffix: RefComponentSuffix.schemas,
|
|
1073
|
-
itemSuffix: outputOptions.override?.components?.schemas?.itemSuffix ?? "Item",
|
|
1074
|
-
...outputOptions.override?.components?.schemas
|
|
1075
|
-
},
|
|
1076
|
-
responses: {
|
|
1077
|
-
suffix: RefComponentSuffix.responses,
|
|
1078
|
-
...outputOptions.override?.components?.responses
|
|
1079
|
-
},
|
|
1080
|
-
parameters: {
|
|
1081
|
-
suffix: RefComponentSuffix.parameters,
|
|
1082
|
-
...outputOptions.override?.components?.parameters
|
|
1083
|
-
},
|
|
1084
|
-
requestBodies: {
|
|
1085
|
-
suffix: RefComponentSuffix.requestBodies,
|
|
1086
|
-
...outputOptions.override?.components?.requestBodies
|
|
1087
|
-
}
|
|
1088
|
-
},
|
|
1089
|
-
hono: normalizeHonoOptions(outputOptions.override?.hono, workspace),
|
|
1090
|
-
mcp: normalizeMcpOptions(outputOptions.override?.mcp, workspace),
|
|
1091
|
-
jsDoc: normalizeJSDocOptions(outputOptions.override?.jsDoc),
|
|
1092
|
-
query: globalQueryOptions,
|
|
1093
|
-
zod: {
|
|
1094
|
-
strict: {
|
|
1095
|
-
param: outputOptions.override?.zod?.strict?.param ?? false,
|
|
1096
|
-
query: outputOptions.override?.zod?.strict?.query ?? false,
|
|
1097
|
-
header: outputOptions.override?.zod?.strict?.header ?? false,
|
|
1098
|
-
body: outputOptions.override?.zod?.strict?.body ?? false,
|
|
1099
|
-
response: outputOptions.override?.zod?.strict?.response ?? false
|
|
1100
|
-
},
|
|
1101
|
-
generate: {
|
|
1102
|
-
param: outputOptions.override?.zod?.generate?.param ?? true,
|
|
1103
|
-
query: outputOptions.override?.zod?.generate?.query ?? true,
|
|
1104
|
-
header: outputOptions.override?.zod?.generate?.header ?? true,
|
|
1105
|
-
body: outputOptions.override?.zod?.generate?.body ?? true,
|
|
1106
|
-
response: outputOptions.override?.zod?.generate?.response ?? true
|
|
1107
|
-
},
|
|
1108
|
-
coerce: {
|
|
1109
|
-
param: outputOptions.override?.zod?.coerce?.param ?? false,
|
|
1110
|
-
query: outputOptions.override?.zod?.coerce?.query ?? false,
|
|
1111
|
-
header: outputOptions.override?.zod?.coerce?.header ?? false,
|
|
1112
|
-
body: outputOptions.override?.zod?.coerce?.body ?? false,
|
|
1113
|
-
response: outputOptions.override?.zod?.coerce?.response ?? false
|
|
1114
|
-
},
|
|
1115
|
-
preprocess: {
|
|
1116
|
-
...outputOptions.override?.zod?.preprocess?.param ? { param: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.param) } : {},
|
|
1117
|
-
...outputOptions.override?.zod?.preprocess?.query ? { query: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.query) } : {},
|
|
1118
|
-
...outputOptions.override?.zod?.preprocess?.header ? { header: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.header) } : {},
|
|
1119
|
-
...outputOptions.override?.zod?.preprocess?.body ? { body: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.body) } : {},
|
|
1120
|
-
...outputOptions.override?.zod?.preprocess?.response ? { response: normalizeMutator(outputWorkspace, outputOptions.override.zod.preprocess.response) } : {}
|
|
1121
|
-
},
|
|
1122
|
-
...outputOptions.override?.zod?.params ? { params: normalizeMutator(outputWorkspace, outputOptions.override.zod.params) } : {},
|
|
1123
|
-
variant: outputOptions.override?.zod?.variant ?? "classic",
|
|
1124
|
-
version: outputOptions.override?.zod?.version ?? "auto",
|
|
1125
|
-
generateEachHttpStatus: outputOptions.override?.zod?.generateEachHttpStatus ?? false,
|
|
1126
|
-
useBrandedTypes: outputOptions.override?.zod?.useBrandedTypes ?? false,
|
|
1127
|
-
generateReusableSchemas: outputOptions.override?.zod?.generateReusableSchemas ?? false,
|
|
1128
|
-
generateMeta: outputOptions.override?.zod?.generateMeta ?? false,
|
|
1129
|
-
dateTimeOptions: outputOptions.override?.zod?.dateTimeOptions ?? { offset: true },
|
|
1130
|
-
timeOptions: outputOptions.override?.zod?.timeOptions ?? {}
|
|
1131
|
-
},
|
|
1132
|
-
effect: normalizeEffectOptions(outputOptions.override?.effect),
|
|
1133
|
-
swr: {
|
|
1134
|
-
generateErrorTypes: false,
|
|
1135
|
-
...outputOptions.override?.swr
|
|
1136
|
-
},
|
|
1137
|
-
angular: {
|
|
1138
|
-
provideIn: outputOptions.override?.angular?.provideIn ?? "root",
|
|
1139
|
-
client: outputOptions.override?.angular?.retrievalClient ?? outputOptions.override?.angular?.client ?? "httpClient",
|
|
1140
|
-
runtimeValidation: outputOptions.override?.angular?.runtimeValidation ?? false,
|
|
1141
|
-
...outputOptions.override?.angular?.httpResource ? { httpResource: outputOptions.override.angular.httpResource } : {}
|
|
1142
|
-
},
|
|
1143
|
-
fetch: {
|
|
1144
|
-
includeHttpResponseReturnType: outputOptions.override?.fetch?.includeHttpResponseReturnType ?? true,
|
|
1145
|
-
forceSuccessResponse: outputOptions.override?.fetch?.forceSuccessResponse ?? false,
|
|
1146
|
-
runtimeValidation: outputOptions.override?.fetch?.runtimeValidation ?? false,
|
|
1147
|
-
useRuntimeFetcher: outputOptions.override?.fetch?.useRuntimeFetcher ?? false,
|
|
1148
|
-
...outputOptions.override?.fetch?.arrayFormat ? { arrayFormat: outputOptions.override.fetch.arrayFormat } : {},
|
|
1149
|
-
...outputOptions.override?.fetch,
|
|
1150
|
-
...outputOptions.override?.fetch?.jsonReviver ? { jsonReviver: normalizeMutator(outputWorkspace, outputOptions.override.fetch.jsonReviver) } : {}
|
|
1151
|
-
},
|
|
1152
|
-
useDates: outputOptions.override?.useDates ?? false,
|
|
1153
|
-
useDeprecatedOperations: outputOptions.override?.useDeprecatedOperations ?? true,
|
|
1154
|
-
enumGenerationType: outputOptions.override?.enumGenerationType ?? "const",
|
|
1155
|
-
suppressReadonlyModifier: outputOptions.override?.suppressReadonlyModifier ?? false,
|
|
1156
|
-
preserveReadonlyRequestBodies: outputOptions.override?.preserveReadonlyRequestBodies ?? "strip",
|
|
1157
|
-
splitByContentType: outputOptions.override?.splitByContentType ?? false,
|
|
1158
|
-
aliasCombinedTypes: outputOptions.override?.aliasCombinedTypes ?? false
|
|
1159
|
-
},
|
|
1160
|
-
allParamsOptional: outputOptions.allParamsOptional ?? false,
|
|
1161
|
-
urlEncodeParameters: outputOptions.urlEncodeParameters ?? false,
|
|
1162
|
-
optionsParamRequired: outputOptions.optionsParamRequired ?? false,
|
|
1163
|
-
propertySortOrder: outputOptions.propertySortOrder ?? PropertySortOrder.SPECIFICATION
|
|
1164
|
-
},
|
|
1165
|
-
hooks: options.hooks ? normalizeHooks(options.hooks) : {}
|
|
1192
|
+
walk(obj);
|
|
1193
|
+
return [...refs];
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Resolve a ref document target (the part before `#`) to a canonical path or
|
|
1197
|
+
* URL, so it can be compared against allow-list entries that were resolved the
|
|
1198
|
+
* same way.
|
|
1199
|
+
*/
|
|
1200
|
+
function resolveRefTarget(ref, origin) {
|
|
1201
|
+
const doc = getRefDocument(ref);
|
|
1202
|
+
if (isUrl(doc)) return new URL(doc).href;
|
|
1203
|
+
if (origin && isUrl(origin)) return new URL(doc, origin).href;
|
|
1204
|
+
if (origin) return path.resolve(path.dirname(origin), doc);
|
|
1205
|
+
return path.resolve(doc);
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Check whether a `$ref` is allowed by the user's allow-list. Both the ref and
|
|
1209
|
+
* the allow-list entries are resolved against the spec origin so that
|
|
1210
|
+
* `./schemas/pet.yaml` in the spec matches `./schemas/pet.yaml` in the config.
|
|
1211
|
+
*/
|
|
1212
|
+
function isAllowedRef(ref, allowedExternalRefs, origin) {
|
|
1213
|
+
const resolved = resolveRefTarget(ref, origin);
|
|
1214
|
+
return allowedExternalRefs.some((entry) => resolveRefTarget(entry, origin) === resolved);
|
|
1215
|
+
}
|
|
1216
|
+
function formatDisallowedRefsError(disallowed, currentAllowed) {
|
|
1217
|
+
const docs = [...new Set(disallowed.map(getRefDocument))];
|
|
1218
|
+
const all = [...new Set([...currentAllowed, ...docs])];
|
|
1219
|
+
const configSnippet = JSON.stringify({ input: { parserOptions: { externalRefs: { allow: all } } } }, null, 2);
|
|
1220
|
+
return `External \$ref targets are not allowed by default.
|
|
1221
|
+
Add them to your config, or use externalRefs.allow: ['*'] to allow all.
|
|
1222
|
+
|
|
1223
|
+
Disallowed refs:\n${disallowed.map((r) => ` - ${r}`).join("\n")}\n\nSuggested config:\n${configSnippet}`;
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Wrap `readFiles()` so every file read is checked against the allow-list.
|
|
1227
|
+
* The top-level spec file (matching `origin`) is always allowed; subsequent
|
|
1228
|
+
* reads must match an explicit entry or the wildcard.
|
|
1229
|
+
*/
|
|
1230
|
+
function createSafeFileLoader(origin, isWildcard, allowedExternalRefs) {
|
|
1231
|
+
const base = readFiles();
|
|
1232
|
+
return {
|
|
1233
|
+
type: "loader",
|
|
1234
|
+
validate: base.validate,
|
|
1235
|
+
async exec(value) {
|
|
1236
|
+
if (isWildcard) return base.exec(value);
|
|
1237
|
+
if (origin && path.resolve(value) === path.resolve(origin)) return base.exec(value);
|
|
1238
|
+
if (!isAllowedRef(value, allowedExternalRefs, origin)) throw new Error(`Refused to read external file: ${value}\nAdd it to externalRefs.allow or use ['*'] to allow all.`);
|
|
1239
|
+
return base.exec(value);
|
|
1240
|
+
}
|
|
1166
1241
|
};
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Wrap `fetchUrls()` so every URL fetch is checked against the allow-list.
|
|
1245
|
+
* The top-level spec URL (matching `origin`) is always allowed; subsequent
|
|
1246
|
+
* fetches must match an explicit entry or the wildcard.
|
|
1247
|
+
*/
|
|
1248
|
+
function createSafeUrlLoader(origin, isWildcard, allowedExternalRefs, headers) {
|
|
1249
|
+
const base = fetchUrls({ headers });
|
|
1250
|
+
return {
|
|
1251
|
+
type: "loader",
|
|
1252
|
+
validate: base.validate,
|
|
1253
|
+
async exec(value) {
|
|
1254
|
+
if (isWildcard) return base.exec(value);
|
|
1255
|
+
const resolved = resolveRefTarget(value, origin);
|
|
1256
|
+
if (origin && resolved === resolveRefTarget(origin)) return base.exec(value);
|
|
1257
|
+
if (!isAllowedRef(value, allowedExternalRefs, origin)) throw new Error(`Refused to fetch external URL: ${value}\nAdd it to externalRefs.allow or use ['*'] to allow all.`);
|
|
1258
|
+
return base.exec(value);
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
async function importSpecs(workspace, options, projectName) {
|
|
1263
|
+
const { input, output } = options;
|
|
1264
|
+
return importOpenApi({
|
|
1265
|
+
spec: await resolveSpec(input.target, {
|
|
1266
|
+
parserOptions: input.parserOptions,
|
|
1267
|
+
transformer: input.override.transformer,
|
|
1268
|
+
workspace,
|
|
1269
|
+
unsafeDisableValidation: input.unsafeDisableValidation
|
|
1270
|
+
}),
|
|
1271
|
+
input,
|
|
1272
|
+
output,
|
|
1273
|
+
target: isString(input.target) ? input.target : workspace,
|
|
1274
|
+
workspace,
|
|
1275
|
+
projectName
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
const COMPONENT_KEY_PATTERN = /^[a-zA-Z0-9.\-_]+$/;
|
|
1279
|
+
const COMPONENT_SECTIONS = [
|
|
1280
|
+
"schemas",
|
|
1281
|
+
"responses",
|
|
1282
|
+
"parameters",
|
|
1283
|
+
"examples",
|
|
1284
|
+
"requestBodies",
|
|
1285
|
+
"headers",
|
|
1286
|
+
"securitySchemes",
|
|
1287
|
+
"links",
|
|
1288
|
+
"callbacks",
|
|
1289
|
+
"pathItems"
|
|
1290
|
+
];
|
|
1291
|
+
/**
|
|
1292
|
+
* Validate that all component keys conform to the OAS regex: ^[a-zA-Z0-9.\-_]+$
|
|
1293
|
+
* @see https://spec.openapis.org/oas/v3.0.3.html#fixed-fields-5
|
|
1294
|
+
* @see https://spec.openapis.org/oas/v3.1.0#fixed-fields-5
|
|
1295
|
+
*/
|
|
1296
|
+
function validateComponentKeys(data) {
|
|
1297
|
+
const components = data.components;
|
|
1298
|
+
if (!isObject(components)) return;
|
|
1299
|
+
const invalidKeys = [];
|
|
1300
|
+
for (const section of COMPONENT_SECTIONS) {
|
|
1301
|
+
const sectionObj = components[section];
|
|
1302
|
+
if (!isObject(sectionObj)) continue;
|
|
1303
|
+
for (const key of Object.keys(sectionObj)) if (!COMPONENT_KEY_PATTERN.test(key)) invalidKeys.push(`components.${section}.${key}`);
|
|
1173
1304
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1305
|
+
if (invalidKeys.length > 0) throw new Error(`Invalid component key${invalidKeys.length > 1 ? "s" : ""} found. OpenAPI component keys must match the pattern ${COMPONENT_KEY_PATTERN} (non-ASCII characters are not allowed per the spec).\n See: https://spec.openapis.org/oas/v3.0.3.html#components-object\n Invalid keys:\n` + invalidKeys.map((k) => ` - ${k}`).join("\n"));
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* The plugins from `@scalar/json-magic` does not dereference $ref.
|
|
1309
|
+
* Instead it fetches them and puts them under x-ext, and changes the $ref to point to #x-ext/<name>.
|
|
1310
|
+
* This function:
|
|
1311
|
+
* 1. Merges external schemas into main spec's components.schemas (with collision handling)
|
|
1312
|
+
* 2. Replaces x-ext refs with standard component refs or inlined content
|
|
1313
|
+
*/
|
|
1314
|
+
function dereferenceExternalRef(data) {
|
|
1315
|
+
const extensions = data["x-ext"] ?? {};
|
|
1316
|
+
const schemaNameMappings = mergeExternalSchemas(data, extensions);
|
|
1317
|
+
const result = {};
|
|
1318
|
+
for (const [key, value] of Object.entries(data)) if (key !== "x-ext") result[key] = replaceXExtRefs(value, extensions, schemaNameMappings);
|
|
1319
|
+
return result;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Merge external document schemas into main spec's components.schemas
|
|
1323
|
+
* Returns mapping of original schema names to final names (with suffixes for collisions)
|
|
1324
|
+
*/
|
|
1325
|
+
function mergeExternalSchemas(data, extensions) {
|
|
1326
|
+
const schemaNameMappings = {};
|
|
1327
|
+
if (Object.keys(extensions).length === 0) return schemaNameMappings;
|
|
1328
|
+
data.components ??= {};
|
|
1329
|
+
const mainComponents = data.components;
|
|
1330
|
+
mainComponents.schemas ??= {};
|
|
1331
|
+
const mainSchemas = mainComponents.schemas;
|
|
1332
|
+
for (const [extKey, extDoc] of Object.entries(extensions)) {
|
|
1333
|
+
schemaNameMappings[extKey] = {};
|
|
1334
|
+
if (isObject(extDoc) && "components" in extDoc) {
|
|
1335
|
+
const extComponents = extDoc.components;
|
|
1336
|
+
if (isObject(extComponents) && "schemas" in extComponents) {
|
|
1337
|
+
const extSchemas = extComponents.schemas;
|
|
1338
|
+
for (const [schemaName, schema] of Object.entries(extSchemas)) {
|
|
1339
|
+
const existingSchema = mainSchemas[schemaName];
|
|
1340
|
+
const isXExtRef = isObject(existingSchema) && "$ref" in existingSchema && isString(existingSchema.$ref) && existingSchema.$ref.startsWith("#/x-ext/");
|
|
1341
|
+
let finalSchemaName = schemaName;
|
|
1342
|
+
if (schemaName in mainSchemas && !isXExtRef) {
|
|
1343
|
+
finalSchemaName = `${schemaName}_${extKey.replaceAll(/[^a-zA-Z0-9]/g, "_")}`;
|
|
1344
|
+
schemaNameMappings[extKey][schemaName] = finalSchemaName;
|
|
1345
|
+
} else schemaNameMappings[extKey][schemaName] = schemaName;
|
|
1346
|
+
mainSchemas[finalSchemaName] = scrubUnwantedKeys(schema);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1181
1350
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1351
|
+
for (const [extKey, mapping] of Object.entries(schemaNameMappings)) for (const [, finalName] of Object.entries(mapping)) {
|
|
1352
|
+
const schema = mainSchemas[finalName];
|
|
1353
|
+
if (schema) mainSchemas[finalName] = updateInternalRefs(schema, extKey, schemaNameMappings);
|
|
1354
|
+
}
|
|
1355
|
+
return schemaNameMappings;
|
|
1184
1356
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Remove unwanted keys like $schema and $id from objects
|
|
1359
|
+
*/
|
|
1360
|
+
function scrubUnwantedKeys(obj) {
|
|
1361
|
+
const UNWANTED_KEYS = new Set(["$schema", "$id"]);
|
|
1362
|
+
if (obj === null || obj === void 0) return obj;
|
|
1363
|
+
if (Array.isArray(obj)) return obj.map((x) => scrubUnwantedKeys(x));
|
|
1364
|
+
if (isObject(obj)) {
|
|
1365
|
+
const rec = obj;
|
|
1366
|
+
const out = {};
|
|
1367
|
+
for (const [k, v] of Object.entries(rec)) {
|
|
1368
|
+
if (UNWANTED_KEYS.has(k)) continue;
|
|
1369
|
+
out[k] = scrubUnwantedKeys(v);
|
|
1370
|
+
}
|
|
1371
|
+
return out;
|
|
1199
1372
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1373
|
+
return obj;
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Update internal refs within an external schema to use suffixed names
|
|
1377
|
+
*/
|
|
1378
|
+
function updateInternalRefs(obj, extKey, schemaNameMappings) {
|
|
1379
|
+
if (obj === null || obj === void 0) return obj;
|
|
1380
|
+
if (Array.isArray(obj)) return obj.map((element) => updateInternalRefs(element, extKey, schemaNameMappings));
|
|
1381
|
+
if (isObject(obj)) {
|
|
1382
|
+
const record = obj;
|
|
1383
|
+
if ("$ref" in record && isString(record.$ref)) {
|
|
1384
|
+
const refValue = record.$ref;
|
|
1385
|
+
if (refValue.startsWith("#/components/schemas/")) {
|
|
1386
|
+
const schemaName = refValue.replace("#/components/schemas/", "");
|
|
1387
|
+
const mappedName = schemaNameMappings[extKey][schemaName];
|
|
1388
|
+
if (mappedName) return { $ref: `#/components/schemas/${mappedName}` };
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
const result = {};
|
|
1392
|
+
for (const [key, value] of Object.entries(record)) result[key] = updateInternalRefs(value, extKey, schemaNameMappings);
|
|
1393
|
+
return result;
|
|
1207
1394
|
}
|
|
1395
|
+
return obj;
|
|
1208
1396
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1397
|
+
/**
|
|
1398
|
+
* Decode a single JSON Pointer reference token taken from an x-ext `$ref`.
|
|
1399
|
+
*
|
|
1400
|
+
* The token carries two layers of encoding: it sits in a URI fragment, so it
|
|
1401
|
+
* may be percent-encoded (e.g. `%7B` for `{` in templated paths), and it is a
|
|
1402
|
+
* JSON Pointer token, so `~1`/`~0` stand for `/`/`~` (RFC 6901). Percent-
|
|
1403
|
+
* encoding is the outer layer and is removed first; a malformed sequence is
|
|
1404
|
+
* left as-is rather than throwing. Without this, tokens such as `~1pets`
|
|
1405
|
+
* never match the real `/pets` key and the external `$ref` fails to resolve.
|
|
1406
|
+
*/
|
|
1407
|
+
function decodeRefToken(token) {
|
|
1408
|
+
let decoded = token;
|
|
1409
|
+
try {
|
|
1410
|
+
decoded = decodeURIComponent(token);
|
|
1411
|
+
} catch {}
|
|
1412
|
+
return decoded.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Replace x-ext refs with standard component refs, or inline the content.
|
|
1416
|
+
* `inliningRefs` tracks the inline chain to break cycles in recursive
|
|
1417
|
+
* external schemas that aren't under `components.schemas` (#1642).
|
|
1418
|
+
*/
|
|
1419
|
+
function replaceXExtRefs(obj, extensions, schemaNameMappings, inliningRefs = /* @__PURE__ */ new Set()) {
|
|
1420
|
+
if (isNullish$1(obj)) return obj;
|
|
1421
|
+
if (Array.isArray(obj)) return obj.map((element) => replaceXExtRefs(element, extensions, schemaNameMappings, inliningRefs));
|
|
1422
|
+
if (isObject(obj)) {
|
|
1423
|
+
const record = obj;
|
|
1424
|
+
if ("$ref" in record && isString(record.$ref)) {
|
|
1425
|
+
const refValue = record.$ref;
|
|
1426
|
+
if (refValue.startsWith("#/x-ext/")) {
|
|
1427
|
+
const parts = refValue.replace("#/x-ext/", "").split("/");
|
|
1428
|
+
const extKey = parts.shift();
|
|
1429
|
+
if (extKey) {
|
|
1430
|
+
if (parts.length >= 3 && parts[0] === "components" && parts[1] === "schemas") {
|
|
1431
|
+
const schemaName = parts.slice(2).join("/");
|
|
1432
|
+
return { $ref: `#/components/schemas/${schemaNameMappings[extKey][schemaName] || schemaName}` };
|
|
1433
|
+
}
|
|
1434
|
+
if (inliningRefs.has(refValue)) {
|
|
1435
|
+
logWarning(`Detected a circular external $ref while inlining "${refValue}". Replacing with an empty schema to avoid infinite recursion. Move the schema under "components.schemas" in its source file or pre-bundle the spec to keep the recursion intact.`);
|
|
1436
|
+
return {};
|
|
1437
|
+
}
|
|
1438
|
+
let refObj = extensions[extKey];
|
|
1439
|
+
for (const rawPart of parts) {
|
|
1440
|
+
const p = decodeRefToken(rawPart);
|
|
1441
|
+
if (refObj && (isObject(refObj) || Array.isArray(refObj)) && p in refObj) refObj = refObj[p];
|
|
1442
|
+
else {
|
|
1443
|
+
refObj = void 0;
|
|
1444
|
+
break;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
if (refObj) {
|
|
1448
|
+
const cleaned = scrubUnwantedKeys(refObj);
|
|
1449
|
+
const nextInlining = new Set(inliningRefs);
|
|
1450
|
+
nextInlining.add(refValue);
|
|
1451
|
+
return replaceXExtRefs(cleaned, extensions, schemaNameMappings, nextInlining);
|
|
1452
|
+
}
|
|
1224
1453
|
}
|
|
1225
|
-
} catch {
|
|
1226
|
-
continue;
|
|
1227
1454
|
}
|
|
1228
|
-
continue;
|
|
1229
|
-
}
|
|
1230
|
-
const resolvedTarget = normalizePath(target, workspace);
|
|
1231
|
-
try {
|
|
1232
|
-
await access(resolvedTarget);
|
|
1233
|
-
return resolvedTarget;
|
|
1234
|
-
} catch {
|
|
1235
|
-
continue;
|
|
1236
1455
|
}
|
|
1456
|
+
const result = {};
|
|
1457
|
+
for (const [key, value] of Object.entries(record)) result[key] = replaceXExtRefs(value, extensions, schemaNameMappings, inliningRefs);
|
|
1458
|
+
return result;
|
|
1237
1459
|
}
|
|
1238
|
-
|
|
1239
|
-
}
|
|
1240
|
-
function getHeadersForUrl(url, headersConfig) {
|
|
1241
|
-
if (!headersConfig) return {};
|
|
1242
|
-
const { hostname } = new URL(url);
|
|
1243
|
-
const matchedHeaders = {};
|
|
1244
|
-
for (const headerEntry of headersConfig) if (headerEntry.domains.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) Object.assign(matchedHeaders, headerEntry.headers);
|
|
1245
|
-
return matchedHeaders;
|
|
1460
|
+
return obj;
|
|
1246
1461
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1462
|
+
//#endregion
|
|
1463
|
+
//#region src/formatters/prettier.ts
|
|
1464
|
+
/**
|
|
1465
|
+
* Format files with prettier.
|
|
1466
|
+
* Tries the programmatic API first (project dependency),
|
|
1467
|
+
* then falls back to the globally installed CLI.
|
|
1468
|
+
*/
|
|
1469
|
+
async function formatWithPrettier(paths, projectTitle) {
|
|
1470
|
+
const prettier = await tryImportPrettier();
|
|
1471
|
+
if (prettier) {
|
|
1472
|
+
const filePaths = [...new Set(await collectFilePaths(paths))];
|
|
1473
|
+
if (filePaths.length === 0) return;
|
|
1474
|
+
const config = await prettier.resolveConfig(filePaths[0]) ?? {};
|
|
1475
|
+
await Promise.all(filePaths.map(async (filePath) => {
|
|
1476
|
+
try {
|
|
1477
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
1478
|
+
const formatted = await prettier.format(content, {
|
|
1479
|
+
...config,
|
|
1480
|
+
filepath: filePath
|
|
1481
|
+
});
|
|
1482
|
+
await fs.writeFile(filePath, formatted);
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
if (isMissingFileError(error)) return;
|
|
1485
|
+
if (error instanceof Error) if (error.name === "UndefinedParserError") {} else logWarning(`⚠️ ${projectTitle ? `${projectTitle} - ` : ""}Failed to format file ${filePath}: ${error.toString()}`);
|
|
1486
|
+
else logWarning(`⚠️ ${projectTitle ? `${projectTitle} - ` : ""}Failed to format file ${filePath}: unknown error`);
|
|
1487
|
+
}
|
|
1488
|
+
}));
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1252
1491
|
try {
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
});
|
|
1257
|
-
} finally {
|
|
1258
|
-
clearTimeout(timeoutId);
|
|
1492
|
+
await execa("prettier", ["--write", ...paths]);
|
|
1493
|
+
} catch {
|
|
1494
|
+
logWarning(`⚠️ ${projectTitle ? `${projectTitle} - ` : ""}prettier not found. Install it as a project dependency or globally.`);
|
|
1259
1495
|
}
|
|
1260
1496
|
}
|
|
1261
|
-
function
|
|
1262
|
-
|
|
1263
|
-
return path;
|
|
1264
|
-
}
|
|
1265
|
-
function normalizePath(path$1, workspace) {
|
|
1266
|
-
if (!isString(path$1)) return path$1;
|
|
1267
|
-
return path.resolve(workspace, path$1);
|
|
1268
|
-
}
|
|
1269
|
-
function normalizeOperationsAndTags(operationsOrTags, workspace, global, source) {
|
|
1270
|
-
const unsupportedZodKeys = [
|
|
1271
|
-
"version",
|
|
1272
|
-
"variant",
|
|
1273
|
-
"dateTimeOptions",
|
|
1274
|
-
"timeOptions",
|
|
1275
|
-
"generateEachHttpStatus",
|
|
1276
|
-
"generateReusableSchemas",
|
|
1277
|
-
"generateMeta"
|
|
1278
|
-
];
|
|
1279
|
-
return Object.fromEntries(Object.entries(operationsOrTags).map(([key, { transformer, mutator, formData, formUrlEncoded, paramsSerializer, paramsFilter, query, angular, zod, effect, ...rest }]) => {
|
|
1280
|
-
const unsupportedOperationZodKeys = zod && unsupportedZodKeys.filter((unsupportedKey) => zod[unsupportedKey] !== void 0);
|
|
1281
|
-
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(", ")}.`);
|
|
1282
|
-
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);
|
|
1283
|
-
return [key, {
|
|
1284
|
-
...rest,
|
|
1285
|
-
...angular ? { angular: {
|
|
1286
|
-
provideIn: angular.provideIn ?? "root",
|
|
1287
|
-
client: angular.retrievalClient ?? angular.client ?? "httpClient",
|
|
1288
|
-
runtimeValidation: angular.runtimeValidation ?? false,
|
|
1289
|
-
...angular.httpResource ? { httpResource: angular.httpResource } : {}
|
|
1290
|
-
} } : {},
|
|
1291
|
-
...query ? { query: normalizeQueryOptions(query, workspace, global.query) } : {},
|
|
1292
|
-
...hasSupportedOperationZodConfig && zod ? { zod: {
|
|
1293
|
-
strict: {
|
|
1294
|
-
param: zod.strict?.param ?? false,
|
|
1295
|
-
query: zod.strict?.query ?? false,
|
|
1296
|
-
header: zod.strict?.header ?? false,
|
|
1297
|
-
body: zod.strict?.body ?? false,
|
|
1298
|
-
response: zod.strict?.response ?? false
|
|
1299
|
-
},
|
|
1300
|
-
generate: {
|
|
1301
|
-
param: zod.generate?.param ?? true,
|
|
1302
|
-
query: zod.generate?.query ?? true,
|
|
1303
|
-
header: zod.generate?.header ?? true,
|
|
1304
|
-
body: zod.generate?.body ?? true,
|
|
1305
|
-
response: zod.generate?.response ?? true
|
|
1306
|
-
},
|
|
1307
|
-
coerce: {
|
|
1308
|
-
param: zod.coerce?.param ?? false,
|
|
1309
|
-
query: zod.coerce?.query ?? false,
|
|
1310
|
-
header: zod.coerce?.header ?? false,
|
|
1311
|
-
body: zod.coerce?.body ?? false,
|
|
1312
|
-
response: zod.coerce?.response ?? false
|
|
1313
|
-
},
|
|
1314
|
-
preprocess: {
|
|
1315
|
-
...zod.preprocess?.param ? { param: normalizeMutator(workspace, zod.preprocess.param) } : {},
|
|
1316
|
-
...zod.preprocess?.query ? { query: normalizeMutator(workspace, zod.preprocess.query) } : {},
|
|
1317
|
-
...zod.preprocess?.header ? { header: normalizeMutator(workspace, zod.preprocess.header) } : {},
|
|
1318
|
-
...zod.preprocess?.body ? { body: normalizeMutator(workspace, zod.preprocess.body) } : {},
|
|
1319
|
-
...zod.preprocess?.response ? { response: normalizeMutator(workspace, zod.preprocess.response) } : {}
|
|
1320
|
-
},
|
|
1321
|
-
...zod.params ? { params: normalizeMutator(workspace, zod.params) } : {},
|
|
1322
|
-
useBrandedTypes: zod.useBrandedTypes ?? false
|
|
1323
|
-
} } : {},
|
|
1324
|
-
...effect ? { effect: normalizeEffectOptions(effect) } : {},
|
|
1325
|
-
...transformer ? { transformer: normalizePath(transformer, workspace) } : {},
|
|
1326
|
-
...mutator ? { mutator: normalizeMutator(workspace, mutator) } : {},
|
|
1327
|
-
...formData === void 0 ? {} : { formData: createFormData(workspace, formData) },
|
|
1328
|
-
...formUrlEncoded ? { formUrlEncoded: isBoolean(formUrlEncoded) ? formUrlEncoded : normalizeMutator(workspace, formUrlEncoded) } : {},
|
|
1329
|
-
...paramsSerializer ? { paramsSerializer: normalizeMutator(workspace, paramsSerializer) } : {},
|
|
1330
|
-
...paramsFilter ? { paramsFilter: normalizeMutator(workspace, paramsFilter) } : {}
|
|
1331
|
-
}];
|
|
1332
|
-
}));
|
|
1497
|
+
function isMissingFileError(error) {
|
|
1498
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1333
1499
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1500
|
+
/**
|
|
1501
|
+
* Try to import prettier from the project's dependencies.
|
|
1502
|
+
* Returns undefined if prettier is not installed.
|
|
1503
|
+
*/
|
|
1504
|
+
async function tryImportPrettier() {
|
|
1505
|
+
try {
|
|
1506
|
+
return await import("prettier");
|
|
1507
|
+
} catch {
|
|
1508
|
+
return;
|
|
1339
1509
|
}
|
|
1340
|
-
return mode;
|
|
1341
|
-
}
|
|
1342
|
-
function normalizeHooks(hooks) {
|
|
1343
|
-
const keys = Object.keys(hooks);
|
|
1344
|
-
const result = {};
|
|
1345
|
-
for (const key of keys) if (isString(hooks[key])) result[key] = [hooks[key]];
|
|
1346
|
-
else if (Array.isArray(hooks[key])) result[key] = hooks[key];
|
|
1347
|
-
else if (isFunction(hooks[key])) result[key] = [hooks[key]];
|
|
1348
|
-
else if (isObject(hooks[key])) result[key] = [hooks[key]];
|
|
1349
|
-
return result;
|
|
1350
|
-
}
|
|
1351
|
-
function normalizeHonoOptions(hono = {}, workspace) {
|
|
1352
|
-
return {
|
|
1353
|
-
...hono.handlers ? { handlers: path.resolve(workspace, hono.handlers) } : {},
|
|
1354
|
-
handlerGenerationStrategy: hono.handlerGenerationStrategy ?? "smart",
|
|
1355
|
-
compositeRoute: hono.compositeRoute ? path.resolve(workspace, hono.compositeRoute) : "",
|
|
1356
|
-
validator: hono.validator ?? true,
|
|
1357
|
-
validatorOutputPath: hono.validatorOutputPath ? path.resolve(workspace, hono.validatorOutputPath) : ""
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
function normalizeMcpServerOptions(server, workspace) {
|
|
1361
|
-
return {
|
|
1362
|
-
path: path.resolve(workspace, server.path),
|
|
1363
|
-
name: server.name,
|
|
1364
|
-
default: server.default ?? !server.name
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
function normalizeMcpOptions(mcp = {}, workspace) {
|
|
1368
|
-
return mcp.server ? { server: normalizeMcpServerOptions(mcp.server, workspace) } : {};
|
|
1369
1510
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1511
|
+
/**
|
|
1512
|
+
* Recursively collect absolute file paths from a mix of files and directories.
|
|
1513
|
+
*/
|
|
1514
|
+
async function collectFilePaths(paths) {
|
|
1515
|
+
const results = [];
|
|
1516
|
+
for (const p of paths) {
|
|
1517
|
+
const absolute = path.resolve(p);
|
|
1518
|
+
try {
|
|
1519
|
+
const stat = await fs.stat(absolute);
|
|
1520
|
+
if (stat.isFile()) results.push(absolute);
|
|
1521
|
+
else if (stat.isDirectory()) {
|
|
1522
|
+
const subFiles = await collectFilePaths((await fs.readdir(absolute)).map((entry) => path.join(absolute, entry)));
|
|
1523
|
+
results.push(...subFiles);
|
|
1524
|
+
}
|
|
1525
|
+
} catch {}
|
|
1526
|
+
}
|
|
1527
|
+
return results;
|
|
1372
1528
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
...queryOptions.queryKey ? { queryKey: normalizeMutator(outputWorkspace, queryOptions.queryKey) } : {},
|
|
1389
|
-
...globalOptions.queryOptions ? { queryOptions: globalOptions.queryOptions } : {},
|
|
1390
|
-
...queryOptions.queryOptions ? { queryOptions: normalizeMutator(outputWorkspace, queryOptions.queryOptions) } : {},
|
|
1391
|
-
...globalOptions.mutationOptions ? { mutationOptions: globalOptions.mutationOptions } : {},
|
|
1392
|
-
...queryOptions.mutationOptions ? { mutationOptions: normalizeMutator(outputWorkspace, queryOptions.mutationOptions) } : {},
|
|
1393
|
-
...isNullish(globalOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: globalOptions.shouldExportQueryKey },
|
|
1394
|
-
...isNullish(queryOptions.shouldExportQueryKey) ? {} : { shouldExportQueryKey: queryOptions.shouldExportQueryKey },
|
|
1395
|
-
...isNullish(globalOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: globalOptions.shouldFilterQueryKey },
|
|
1396
|
-
...isNullish(queryOptions.shouldFilterQueryKey) ? {} : { shouldFilterQueryKey: queryOptions.shouldFilterQueryKey },
|
|
1397
|
-
...isNullish(globalOptions.queryKeyFilter) ? {} : { queryKeyFilter: globalOptions.queryKeyFilter },
|
|
1398
|
-
...isNullish(queryOptions.queryKeyFilter) ? {} : { queryKeyFilter: queryOptions.queryKeyFilter },
|
|
1399
|
-
...isNullish(globalOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: globalOptions.shouldExportHttpClient },
|
|
1400
|
-
...isNullish(queryOptions.shouldExportHttpClient) ? {} : { shouldExportHttpClient: queryOptions.shouldExportHttpClient },
|
|
1401
|
-
...isNullish(globalOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: globalOptions.shouldExportMutatorHooks },
|
|
1402
|
-
...isNullish(queryOptions.shouldExportMutatorHooks) ? {} : { shouldExportMutatorHooks: queryOptions.shouldExportMutatorHooks },
|
|
1403
|
-
...isNullish(globalOptions.shouldSplitQueryKey) ? {} : { shouldSplitQueryKey: globalOptions.shouldSplitQueryKey },
|
|
1404
|
-
...isNullish(queryOptions.shouldSplitQueryKey) ? {} : { shouldSplitQueryKey: queryOptions.shouldSplitQueryKey },
|
|
1405
|
-
...isNullish(globalOptions.signal) ? {} : { signal: globalOptions.signal },
|
|
1406
|
-
...isNullish(globalOptions.useOperationIdAsQueryKey) ? {} : { useOperationIdAsQueryKey: globalOptions.useOperationIdAsQueryKey },
|
|
1407
|
-
...isNullish(queryOptions.useOperationIdAsQueryKey) ? {} : { useOperationIdAsQueryKey: queryOptions.useOperationIdAsQueryKey },
|
|
1408
|
-
...isNullish(globalOptions.signal) ? {} : { signal: globalOptions.signal },
|
|
1409
|
-
...isNullish(queryOptions.signal) ? {} : { signal: queryOptions.signal },
|
|
1410
|
-
...isNullish(globalOptions.version) ? {} : { version: globalOptions.version },
|
|
1411
|
-
...isNullish(queryOptions.version) ? {} : { version: queryOptions.version },
|
|
1412
|
-
...queryOptions.mutationInvalidates ? { mutationInvalidates: queryOptions.mutationInvalidates } : {},
|
|
1413
|
-
...isNullish(globalOptions.runtimeValidation) ? {} : { runtimeValidation: globalOptions.runtimeValidation },
|
|
1414
|
-
...isNullish(queryOptions.runtimeValidation) ? {} : { runtimeValidation: queryOptions.runtimeValidation }
|
|
1415
|
-
};
|
|
1529
|
+
//#endregion
|
|
1530
|
+
//#region src/utils/execute-hook.ts
|
|
1531
|
+
const executeHook = async (name, commands = [], args = []) => {
|
|
1532
|
+
log(styleText("white", `Running ${name} hook...`));
|
|
1533
|
+
for (const command of commands) try {
|
|
1534
|
+
if (isString(command)) await executeCommand(command, args);
|
|
1535
|
+
else if (isFunction(command)) await command(args);
|
|
1536
|
+
else if (isObject(command)) await executeObjectCommand(command, args);
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
logError(error, `Failed to run ${name} hook`);
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
async function executeCommand(command, args) {
|
|
1542
|
+
const [cmd, ..._args] = [...parseArgsStringToArgv(command), ...args];
|
|
1543
|
+
await execa(cmd, _args);
|
|
1416
1544
|
}
|
|
1417
|
-
function
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
...title ? [title] : [],
|
|
1422
|
-
...description ? [description] : [],
|
|
1423
|
-
...version$1 ? [`OpenAPI spec version: ${version$1}`] : []
|
|
1424
|
-
];
|
|
1545
|
+
async function executeObjectCommand(command, args) {
|
|
1546
|
+
if (command.injectGeneratedDirsAndFiles === false) args = [];
|
|
1547
|
+
if (isString(command.command)) await executeCommand(command.command, args);
|
|
1548
|
+
else if (isFunction(command.command)) await command.command();
|
|
1425
1549
|
}
|
|
1426
1550
|
//#endregion
|
|
1427
1551
|
//#region src/utils/watcher.ts
|
|
@@ -1792,8 +1916,8 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
|
|
|
1792
1916
|
const importFileExtension = getImportExtension(fileExtension, tsconfig);
|
|
1793
1917
|
const indexPath = path.join(schemasPath, `index.ts`);
|
|
1794
1918
|
let existingExports = "";
|
|
1795
|
-
if (shouldMergeExisting && await fs.pathExists(indexPath)) {
|
|
1796
|
-
const existingContent = await fs.readFile(indexPath, "utf8");
|
|
1919
|
+
if (shouldMergeExisting && await fs$2.pathExists(indexPath)) {
|
|
1920
|
+
const existingContent = await fs$2.readFile(indexPath, "utf8");
|
|
1797
1921
|
const headerMatch = /^(\/\*\*[\s\S]*?\*\/\n)?/.exec(existingContent);
|
|
1798
1922
|
const headerPart = headerMatch ? headerMatch[0] : "";
|
|
1799
1923
|
existingExports = existingContent.slice(headerPart.length).trim();
|
|
@@ -1803,7 +1927,7 @@ async function writeZodSchemaIndex(schemasPath, fileExtension, header, schemaNam
|
|
|
1803
1927
|
}).toSorted().join("\n");
|
|
1804
1928
|
const allExports = existingExports ? `${existingExports}\n${newExports}` : newExports;
|
|
1805
1929
|
const uniqueExports = [...new Set(allExports.split("\n"))].filter((line) => line.trim()).toSorted().join("\n");
|
|
1806
|
-
await fs.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
|
|
1930
|
+
await fs$2.outputFile(indexPath, `${header}\n${uniqueExports}\n`);
|
|
1807
1931
|
}
|
|
1808
1932
|
async function writeZodSchemaTagsSplitBarrel(schemasPath, fileExtension, header, componentDirs, verbDirs, namingConvention, tsconfig) {
|
|
1809
1933
|
const importExt = getImportExtension(fileExtension, tsconfig);
|
|
@@ -1826,7 +1950,7 @@ async function writeZodSchemaTagsSplitBarrel(schemasPath, fileExtension, header,
|
|
|
1826
1950
|
const allExports = [...rootExports, ...tagExports];
|
|
1827
1951
|
const rootIndexPath = path.join(schemasPath, "index.ts");
|
|
1828
1952
|
const content = `${header}\n${allExports.join("\n")}\n`;
|
|
1829
|
-
await fs.outputFile(rootIndexPath, content);
|
|
1953
|
+
await fs$2.outputFile(rootIndexPath, content);
|
|
1830
1954
|
}
|
|
1831
1955
|
function generateZodSchemasInline(builder, output, includeZodImport = true, paramsMutator, includeParamsImport = false) {
|
|
1832
1956
|
if (output.override.zod.generateReusableSchemas === true) return generateZodSchemasInlineReusable(builder, output, includeZodImport, paramsMutator, includeParamsImport);
|
|
@@ -1929,7 +2053,7 @@ async function writeZodSchemas(builder, schemasPath, fileExtension, header, outp
|
|
|
1929
2053
|
const groupedSchemasToWrite = groupSchemasByFilePath(schemasToWrite);
|
|
1930
2054
|
for (const schemaGroup of groupedSchemasToWrite) {
|
|
1931
2055
|
const fileContent = generateZodSchemaFileContent(header, schemaGroup, output.override.zod.variant);
|
|
1932
|
-
await fs.outputFile(schemaGroup[0].filePath, fileContent);
|
|
2056
|
+
await fs$2.outputFile(schemaGroup[0].filePath, fileContent);
|
|
1933
2057
|
}
|
|
1934
2058
|
const writtenSchemaNames = groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName);
|
|
1935
2059
|
if (output.indexFiles && !isSplit) await writeZodSchemaIndex(schemasPath, fileExtension, header, writtenSchemaNames, output.namingConvention, false, output.tsconfig);
|
|
@@ -1996,7 +2120,7 @@ async function writeZodSchemasReusable(builder, schemasPath, fileExtension, head
|
|
|
1996
2120
|
}) : void 0;
|
|
1997
2121
|
const imports = [...mutatorImportStr ? [mutatorImportStr] : [], ...refImports ? [refImports] : []].join("\n");
|
|
1998
2122
|
const fileContent = `${header}${getZodSchemaImportStatement(output.override.zod.variant)}\n` + (imports ? `${imports}\n\n` : "\n") + `${rendered.content}\n`;
|
|
1999
|
-
await fs.outputFile(filePath, fileContent);
|
|
2123
|
+
await fs$2.outputFile(filePath, fileContent);
|
|
2000
2124
|
}
|
|
2001
2125
|
if (output.indexFiles && !isSplit && rewritten.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, rewritten.map((e) => e.name), output.namingConvention, true, output.tsconfig);
|
|
2002
2126
|
if (isSplit) {
|
|
@@ -2038,7 +2162,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
2038
2162
|
const [bodyContentType, bodyMedia] = jsonBodyMedia ? ["application/json", jsonBodyMedia] : formDataBodyMedia ? ["multipart/form-data", formDataBodyMedia] : formUrlEncodedBodyMedia ? ["application/x-www-form-urlencoded", formUrlEncodedBodyMedia] : [void 0, void 0];
|
|
2039
2163
|
const bodySchema = bodyMedia?.schema;
|
|
2040
2164
|
const bodySchemas = shouldGenerate.body && bodySchema ? [{
|
|
2041
|
-
name: `${pascal(verbOption.
|
|
2165
|
+
name: `${pascal(verbOption.typeName)}Body`,
|
|
2042
2166
|
schema: useReusableSchemas ? bodySchema : dereference(bodySchema, zodContext),
|
|
2043
2167
|
bodyContentType,
|
|
2044
2168
|
encoding: bodyMedia?.encoding
|
|
@@ -2046,7 +2170,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
2046
2170
|
const parameters = operation.parameters;
|
|
2047
2171
|
const pathParams = parameters?.filter((p) => "in" in p && p.in === "path");
|
|
2048
2172
|
const pathParamsSchemas = useNamedParameters && shouldGenerate.param && pathParams && pathParams.length > 0 ? [{
|
|
2049
|
-
name: `${pascal(verbOption.
|
|
2173
|
+
name: `${pascal(verbOption.typeName)}PathParameters`,
|
|
2050
2174
|
schema: {
|
|
2051
2175
|
type: "object",
|
|
2052
2176
|
properties: Object.fromEntries(pathParams.filter((p) => "schema" in p && p.schema).map((p) => [p.name, useReusableSchemas ? p.schema : dereference(p.schema, zodContext)])),
|
|
@@ -2055,7 +2179,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
2055
2179
|
}] : [];
|
|
2056
2180
|
const queryParams = parameters?.filter((p) => "in" in p && p.in === "query");
|
|
2057
2181
|
const queryParamsSchemas = shouldGenerate.query && queryParams && queryParams.length > 0 ? [{
|
|
2058
|
-
name: `${pascal(verbOption.
|
|
2182
|
+
name: `${pascal(verbOption.typeName)}Params`,
|
|
2059
2183
|
schema: {
|
|
2060
2184
|
type: "object",
|
|
2061
2185
|
properties: Object.fromEntries(queryParams.filter((p) => "schema" in p && p.schema).map((p) => [p.name, useReusableSchemas ? p.schema : dereference(p.schema, zodContext)])),
|
|
@@ -2064,7 +2188,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
2064
2188
|
}] : [];
|
|
2065
2189
|
const headerParams = parameters?.filter((p) => "in" in p && p.in === "header");
|
|
2066
2190
|
const headerParamsSchemas = shouldGenerate.header && headerParams && headerParams.length > 0 ? [{
|
|
2067
|
-
name: `${pascal(verbOption.
|
|
2191
|
+
name: `${pascal(verbOption.typeName)}Headers`,
|
|
2068
2192
|
schema: {
|
|
2069
2193
|
type: "object",
|
|
2070
2194
|
properties: Object.fromEntries(headerParams.filter((p) => "schema" in p && p.schema).map((p) => [p.name, useReusableSchemas ? p.schema : dereference(p.schema, zodContext)])),
|
|
@@ -2118,7 +2242,7 @@ async function writeZodSchemasFromVerbs(verbOptions, schemasPath, fileExtension,
|
|
|
2118
2242
|
const groupedSchemasToWrite = groupSchemasByFilePath(schemasToWrite);
|
|
2119
2243
|
for (const schemaGroup of groupedSchemasToWrite) {
|
|
2120
2244
|
const fileContent = generateZodSchemaFileContent(header, schemaGroup, output.override.zod.variant);
|
|
2121
|
-
await fs.outputFile(schemaGroup[0].filePath, fileContent);
|
|
2245
|
+
await fs$2.outputFile(schemaGroup[0].filePath, fileContent);
|
|
2122
2246
|
}
|
|
2123
2247
|
const writtenSchemaNames = groupedSchemasToWrite.map((schemaGroup) => schemaGroup[0].schemaName);
|
|
2124
2248
|
if (output.indexFiles && !isSplit && uniqueVerbsSchemas.length > 0) await writeZodSchemaIndex(schemasPath, fileExtension, header, writtenSchemaNames, output.namingConvention, true, output.tsconfig);
|
|
@@ -2168,7 +2292,7 @@ function getComparableFilePath(filePath) {
|
|
|
2168
2292
|
const resolvedPath = path.resolve(filePath);
|
|
2169
2293
|
let comparablePath = resolvedPath;
|
|
2170
2294
|
try {
|
|
2171
|
-
comparablePath = fs.realpathSync(resolvedPath);
|
|
2295
|
+
comparablePath = fs$2.realpathSync(resolvedPath);
|
|
2172
2296
|
} catch (error) {
|
|
2173
2297
|
if (error.code !== "ENOENT") throw error;
|
|
2174
2298
|
}
|
|
@@ -2198,12 +2322,12 @@ async function addOperationSchemasReExport(schemaPath, operationSchemasPath, hea
|
|
|
2198
2322
|
const schemaIndexPath = path.join(schemaPath, `index.ts`);
|
|
2199
2323
|
const esmImportPath = upath.getRelativeImportPath(schemaIndexPath, operationSchemasPath);
|
|
2200
2324
|
const exportLine = `export * from '${esmImportPath}';\n`;
|
|
2201
|
-
if (await fs.pathExists(schemaIndexPath)) {
|
|
2202
|
-
const existingContent = await fs.readFile(schemaIndexPath, "utf8");
|
|
2203
|
-
if (!new RegExp(String.raw`export\s*\*\s*from\s*['"]${esmImportPath.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)}['"]`).test(existingContent)) await fs.appendFile(schemaIndexPath, exportLine);
|
|
2325
|
+
if (await fs$2.pathExists(schemaIndexPath)) {
|
|
2326
|
+
const existingContent = await fs$2.readFile(schemaIndexPath, "utf8");
|
|
2327
|
+
if (!new RegExp(String.raw`export\s*\*\s*from\s*['"]${esmImportPath.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)}['"]`).test(existingContent)) await fs$2.appendFile(schemaIndexPath, exportLine);
|
|
2204
2328
|
} else {
|
|
2205
2329
|
const content = header && header.trim().length > 0 ? `${header}\n${exportLine}` : exportLine;
|
|
2206
|
-
await fs.outputFile(schemaIndexPath, content);
|
|
2330
|
+
await fs$2.outputFile(schemaIndexPath, content);
|
|
2207
2331
|
}
|
|
2208
2332
|
}
|
|
2209
2333
|
/**
|
|
@@ -2456,16 +2580,16 @@ async function writeSpecs(builder, workspace, options, projectName) {
|
|
|
2456
2580
|
}
|
|
2457
2581
|
if (output.operationSchemas) imports.push(upath.getRelativeImportPath(indexFile, getFileInfo(output.operationSchemas).dirname));
|
|
2458
2582
|
if (output.indexFiles) {
|
|
2459
|
-
if (await fs.pathExists(indexFile)) {
|
|
2460
|
-
const data = await fs.readFile(indexFile, "utf8");
|
|
2583
|
+
if (await fs$2.pathExists(indexFile)) {
|
|
2584
|
+
const data = await fs$2.readFile(indexFile, "utf8");
|
|
2461
2585
|
const importsNotDeclared = imports.filter((imp) => !data.includes(`export * from '${imp}'`));
|
|
2462
|
-
await fs.appendFile(indexFile, unique(importsNotDeclared).map((imp) => `export * from '${imp}';\n`).join(""));
|
|
2463
|
-
} else await fs.outputFile(indexFile, unique(imports).map((imp) => `export * from '${imp}';`).join("\n") + "\n");
|
|
2586
|
+
await fs$2.appendFile(indexFile, unique(importsNotDeclared).map((imp) => `export * from '${imp}';\n`).join(""));
|
|
2587
|
+
} else await fs$2.outputFile(indexFile, unique(imports).map((imp) => `export * from '${imp}';`).join("\n") + "\n");
|
|
2464
2588
|
implementationPaths = [indexFile, ...implementationPathsForIndex];
|
|
2465
2589
|
}
|
|
2466
2590
|
}
|
|
2467
2591
|
if (builder.extraFiles.length > 0) {
|
|
2468
|
-
await Promise.all(builder.extraFiles.map(async (file) => fs.outputFile(file.path, file.content)));
|
|
2592
|
+
await Promise.all(builder.extraFiles.map(async (file) => fs$2.outputFile(file.path, file.content)));
|
|
2469
2593
|
implementationPaths = [...implementationPaths, ...builder.extraFiles.map((file) => file.path)];
|
|
2470
2594
|
}
|
|
2471
2595
|
const paths = [
|
|
@@ -2568,7 +2692,7 @@ async function generateSpec(workspace, options, projectName) {
|
|
|
2568
2692
|
function findConfigFile(configFilePath) {
|
|
2569
2693
|
if (configFilePath) {
|
|
2570
2694
|
const absolutePath = path.isAbsolute(configFilePath) ? configFilePath : path.resolve(process.cwd(), configFilePath);
|
|
2571
|
-
if (!fs$
|
|
2695
|
+
if (!fs$1.existsSync(absolutePath)) throw new Error(`Config file ${configFilePath} does not exist`);
|
|
2572
2696
|
return absolutePath;
|
|
2573
2697
|
}
|
|
2574
2698
|
const root = process.cwd();
|
|
@@ -2579,7 +2703,7 @@ function findConfigFile(configFilePath) {
|
|
|
2579
2703
|
".mts"
|
|
2580
2704
|
]) {
|
|
2581
2705
|
const fullPath = path.resolve(root, `orval.config${ext}`);
|
|
2582
|
-
if (fs$
|
|
2706
|
+
if (fs$1.existsSync(fullPath)) return fullPath;
|
|
2583
2707
|
}
|
|
2584
2708
|
throw new Error(`No config file found in ${root}`);
|
|
2585
2709
|
}
|
|
@@ -2601,4 +2725,4 @@ async function loadConfigFile(configFilePath) {
|
|
|
2601
2725
|
//#endregion
|
|
2602
2726
|
export { defineConfig as a, description as c, startWatcher as i, name as l, loadConfigFile as n, defineTransformer as o, generateSpec as r, normalizeOptions as s, findConfigFile as t, version as u };
|
|
2603
2727
|
|
|
2604
|
-
//# sourceMappingURL=config-
|
|
2728
|
+
//# sourceMappingURL=config-CotJKggp.mjs.map
|