@codewithagents/openapi-server 1.7.0 → 1.9.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/cli.cjs +755 -350
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +35 -21
- package/dist/config.js.map +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +58 -63
- package/dist/generator.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/router.d.ts.map +1 -1
- package/dist/plugins/router.js +544 -260
- package/dist/plugins/router.js.map +1 -1
- package/dist/plugins/service.d.ts.map +1 -1
- package/dist/plugins/service.js +68 -193
- package/dist/plugins/service.js.map +1 -1
- package/dist/plugins/shared.d.ts +90 -0
- package/dist/plugins/shared.d.ts.map +1 -0
- package/dist/plugins/shared.js +290 -0
- package/dist/plugins/shared.js.map +1 -0
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -18475,44 +18475,46 @@ function validateInputPath(resolvedInput) {
|
|
|
18475
18475
|
}
|
|
18476
18476
|
}
|
|
18477
18477
|
}
|
|
18478
|
-
async function
|
|
18479
|
-
|
|
18480
|
-
|
|
18481
|
-
|
|
18478
|
+
async function loadJsConfig(resolvedConfigPath) {
|
|
18479
|
+
let mod;
|
|
18480
|
+
try {
|
|
18481
|
+
mod = await import((0, import_node_url.pathToFileURL)(resolvedConfigPath).href);
|
|
18482
|
+
} catch (err) {
|
|
18483
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18484
|
+
throw new Error(`Failed to load JS config file: ${resolvedConfigPath}
|
|
18485
|
+
${message}`);
|
|
18486
|
+
}
|
|
18487
|
+
const exported = mod["default"] ?? mod;
|
|
18488
|
+
if (typeof exported !== "object" || exported === null) {
|
|
18489
|
+
throw new Error("Config must be a JSON object");
|
|
18482
18490
|
}
|
|
18483
|
-
|
|
18491
|
+
return exported;
|
|
18492
|
+
}
|
|
18493
|
+
async function loadJsonConfig(resolvedConfigPath) {
|
|
18494
|
+
let fileContents;
|
|
18495
|
+
try {
|
|
18496
|
+
fileContents = await (0, import_promises.readFile)(resolvedConfigPath, "utf-8");
|
|
18497
|
+
} catch {
|
|
18498
|
+
throw new Error(`Config file not found: ${resolvedConfigPath}`);
|
|
18499
|
+
}
|
|
18500
|
+
let parsed2;
|
|
18501
|
+
try {
|
|
18502
|
+
parsed2 = JSON.parse(fileContents);
|
|
18503
|
+
} catch {
|
|
18504
|
+
throw new Error(`Config file is not valid JSON: ${resolvedConfigPath}`);
|
|
18505
|
+
}
|
|
18506
|
+
if (typeof parsed2 !== "object" || parsed2 === null) {
|
|
18507
|
+
throw new Error("Config must be a JSON object");
|
|
18508
|
+
}
|
|
18509
|
+
return parsed2;
|
|
18510
|
+
}
|
|
18511
|
+
async function loadRawConfig(resolvedConfigPath) {
|
|
18484
18512
|
if (isJsConfigPath(resolvedConfigPath)) {
|
|
18485
|
-
|
|
18486
|
-
try {
|
|
18487
|
-
mod = await import((0, import_node_url.pathToFileURL)(resolvedConfigPath).href);
|
|
18488
|
-
} catch (err) {
|
|
18489
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
18490
|
-
throw new Error(`Failed to load JS config file: ${resolvedConfigPath}
|
|
18491
|
-
${message}`);
|
|
18492
|
-
}
|
|
18493
|
-
const exported = mod["default"] ?? mod;
|
|
18494
|
-
if (typeof exported !== "object" || exported === null) {
|
|
18495
|
-
throw new Error("Config must be a JSON object");
|
|
18496
|
-
}
|
|
18497
|
-
raw = exported;
|
|
18498
|
-
} else {
|
|
18499
|
-
let fileContents;
|
|
18500
|
-
try {
|
|
18501
|
-
fileContents = await (0, import_promises.readFile)(resolvedConfigPath, "utf-8");
|
|
18502
|
-
} catch {
|
|
18503
|
-
throw new Error(`Config file not found: ${resolvedConfigPath}`);
|
|
18504
|
-
}
|
|
18505
|
-
let parsed2;
|
|
18506
|
-
try {
|
|
18507
|
-
parsed2 = JSON.parse(fileContents);
|
|
18508
|
-
} catch {
|
|
18509
|
-
throw new Error(`Config file is not valid JSON: ${resolvedConfigPath}`);
|
|
18510
|
-
}
|
|
18511
|
-
if (typeof parsed2 !== "object" || parsed2 === null) {
|
|
18512
|
-
throw new Error("Config must be a JSON object");
|
|
18513
|
-
}
|
|
18514
|
-
raw = parsed2;
|
|
18513
|
+
return loadJsConfig(resolvedConfigPath);
|
|
18515
18514
|
}
|
|
18515
|
+
return loadJsonConfig(resolvedConfigPath);
|
|
18516
|
+
}
|
|
18517
|
+
function parseBaseConfig(raw, cwd) {
|
|
18516
18518
|
if (typeof raw["input_openapi"] !== "string" || !raw["input_openapi"]) {
|
|
18517
18519
|
throw new Error('Config missing required field: "input_openapi" (path to OpenAPI 3.1 spec)');
|
|
18518
18520
|
}
|
|
@@ -18521,34 +18523,80 @@ ${message}`);
|
|
|
18521
18523
|
}
|
|
18522
18524
|
const input_openapi = raw["input_openapi"];
|
|
18523
18525
|
const output = raw["output"];
|
|
18524
|
-
validateInputPath((0, import_node_path.resolve)(
|
|
18525
|
-
validateOutputPath((0, import_node_path.resolve)(
|
|
18526
|
-
|
|
18527
|
-
return opts.parse(raw, base, opts.cwd);
|
|
18526
|
+
validateInputPath((0, import_node_path.resolve)(cwd, input_openapi));
|
|
18527
|
+
validateOutputPath((0, import_node_path.resolve)(cwd, output));
|
|
18528
|
+
return { input_openapi, output };
|
|
18528
18529
|
}
|
|
18529
|
-
|
|
18530
|
-
|
|
18531
|
-
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18538
|
-
|
|
18539
|
-
|
|
18540
|
-
|
|
18541
|
-
|
|
18542
|
-
|
|
18543
|
-
|
|
18544
|
-
|
|
18545
|
-
|
|
18546
|
-
|
|
18547
|
-
|
|
18548
|
-
|
|
18549
|
-
|
|
18530
|
+
async function prepareRaw(opts) {
|
|
18531
|
+
const resolvedConfigPath = opts.configPath ?? (0, import_node_path.join)(opts.cwd, opts.defaultFileName);
|
|
18532
|
+
if (opts.configPath !== void 0) {
|
|
18533
|
+
validateConfigPath(opts.configPath);
|
|
18534
|
+
}
|
|
18535
|
+
return { raw: await loadRawConfig(resolvedConfigPath) };
|
|
18536
|
+
}
|
|
18537
|
+
function parseProjectEntry(entry, index, opts) {
|
|
18538
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
18539
|
+
throw new Error(`projects[${index}]: entry must be a config object`);
|
|
18540
|
+
}
|
|
18541
|
+
const projectRaw = entry;
|
|
18542
|
+
let base;
|
|
18543
|
+
try {
|
|
18544
|
+
base = parseBaseConfig(projectRaw, opts.cwd);
|
|
18545
|
+
} catch (err) {
|
|
18546
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18547
|
+
throw new Error(`projects[${index}]: ${message}`);
|
|
18548
|
+
}
|
|
18549
|
+
try {
|
|
18550
|
+
return opts.parse(projectRaw, base, opts.cwd);
|
|
18551
|
+
} catch (err) {
|
|
18552
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18553
|
+
throw new Error(`projects[${index}]: ${message}`);
|
|
18554
|
+
}
|
|
18555
|
+
}
|
|
18556
|
+
async function loadConfigsFile(opts) {
|
|
18557
|
+
const { raw } = await prepareRaw(opts);
|
|
18558
|
+
if ("projects" in raw) {
|
|
18559
|
+
return parseProjectsArray(raw, opts);
|
|
18560
|
+
}
|
|
18561
|
+
const base = parseBaseConfig(raw, opts.cwd);
|
|
18562
|
+
return [opts.parse(raw, base, opts.cwd)];
|
|
18563
|
+
}
|
|
18564
|
+
function parseProjectsArray(raw, opts) {
|
|
18565
|
+
const hasTopLevelInput = "input_openapi" in raw && raw["input_openapi"] !== void 0;
|
|
18566
|
+
const hasTopLevelOutput = "output" in raw && raw["output"] !== void 0;
|
|
18567
|
+
if (hasTopLevelInput || hasTopLevelOutput) {
|
|
18568
|
+
throw new Error('Config cannot have both top-level "input_openapi"/"output" and a "projects" array. Use one form or the other.');
|
|
18569
|
+
}
|
|
18570
|
+
const projects = raw["projects"];
|
|
18571
|
+
if (!Array.isArray(projects)) {
|
|
18572
|
+
throw new Error('"projects" must be an array of config objects');
|
|
18573
|
+
}
|
|
18574
|
+
if (projects.length === 0) {
|
|
18575
|
+
throw new Error('"projects" array must contain at least one config entry');
|
|
18576
|
+
}
|
|
18577
|
+
return projects.map((entry, index) => parseProjectEntry(entry, index, opts));
|
|
18578
|
+
}
|
|
18579
|
+
async function runProjects(configs, generateOne2) {
|
|
18580
|
+
if (configs.length === 0) {
|
|
18581
|
+
throw new Error("runProjects requires at least one config entry");
|
|
18582
|
+
}
|
|
18583
|
+
if (configs.length === 1) {
|
|
18584
|
+
await generateOne2(configs[0]);
|
|
18585
|
+
return;
|
|
18586
|
+
}
|
|
18587
|
+
for (let i = 0; i < configs.length; i++) {
|
|
18588
|
+
const label = `${i + 1}/${configs.length}`;
|
|
18589
|
+
console.log(`
|
|
18590
|
+
[${label}] generating ${configs[i].input_openapi}...`);
|
|
18591
|
+
try {
|
|
18592
|
+
await generateOne2(configs[i], label);
|
|
18593
|
+
} catch (err) {
|
|
18594
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18595
|
+
throw new Error(`[${label}] Project failed (${configs[i].input_openapi}): ${message}`);
|
|
18550
18596
|
}
|
|
18551
|
-
}
|
|
18597
|
+
}
|
|
18598
|
+
console.log(`
|
|
18599
|
+
All ${configs.length} projects generated successfully.`);
|
|
18552
18600
|
}
|
|
18553
18601
|
|
|
18554
18602
|
// ../openapi-zod-ts/dist/parser.js
|
|
@@ -18787,7 +18835,31 @@ function toTypeName(name) {
|
|
|
18787
18835
|
return safe.length > 0 ? safe : "_";
|
|
18788
18836
|
}
|
|
18789
18837
|
|
|
18790
|
-
// src/
|
|
18838
|
+
// src/config.ts
|
|
18839
|
+
function parseServerConfig(raw, base) {
|
|
18840
|
+
const framework = raw["framework"];
|
|
18841
|
+
if (framework !== void 0 && framework !== "hono" && framework !== "express" && framework !== "fastify" && framework !== "none") {
|
|
18842
|
+
throw new Error('"framework" must be one of: "hono", "express", "fastify", or "none"');
|
|
18843
|
+
}
|
|
18844
|
+
if (raw["input_schema"] !== void 0 && (typeof raw["input_schema"] !== "string" || !raw["input_schema"])) {
|
|
18845
|
+
throw new Error('"input_schema" must be a non-empty string path to your Zod schema file');
|
|
18846
|
+
}
|
|
18847
|
+
return {
|
|
18848
|
+
...base,
|
|
18849
|
+
framework,
|
|
18850
|
+
input_schema: raw["input_schema"]
|
|
18851
|
+
};
|
|
18852
|
+
}
|
|
18853
|
+
async function loadConfigs2(cwd, configPath) {
|
|
18854
|
+
return loadConfigsFile({
|
|
18855
|
+
cwd,
|
|
18856
|
+
configPath,
|
|
18857
|
+
defaultFileName: "openapi-server.config.json",
|
|
18858
|
+
parse: parseServerConfig
|
|
18859
|
+
});
|
|
18860
|
+
}
|
|
18861
|
+
|
|
18862
|
+
// src/plugins/shared.ts
|
|
18791
18863
|
var SUPPORTED_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
18792
18864
|
function isRef3(obj) {
|
|
18793
18865
|
return typeof obj === "object" && obj !== null && "$ref" in obj;
|
|
@@ -18799,7 +18871,7 @@ function refToName(ref) {
|
|
|
18799
18871
|
function extractPathParamsFromPath(path) {
|
|
18800
18872
|
const matches = path.match(/\{([^}]+)\}/g);
|
|
18801
18873
|
if (matches === null) return [];
|
|
18802
|
-
return matches.map((m) =>
|
|
18874
|
+
return matches.map((m) => m.slice(1, -1));
|
|
18803
18875
|
}
|
|
18804
18876
|
function resolveParam(p, spec) {
|
|
18805
18877
|
if (!isRef3(p)) return p;
|
|
@@ -18866,6 +18938,13 @@ function normalizeParamName(name) {
|
|
|
18866
18938
|
const camel = parts.map((part, i) => i === 0 ? part : part[0].toUpperCase() + part.slice(1)).join("");
|
|
18867
18939
|
return /^[^a-zA-Z_$]/.test(camel) ? `_${camel}` : camel;
|
|
18868
18940
|
}
|
|
18941
|
+
function schemaToTsType(schema) {
|
|
18942
|
+
if (schema === void 0 || isRef3(schema)) return "string";
|
|
18943
|
+
const s = schema;
|
|
18944
|
+
if (s.type === "number" || s.type === "integer") return "number";
|
|
18945
|
+
if (s.type === "boolean") return "boolean";
|
|
18946
|
+
return "string";
|
|
18947
|
+
}
|
|
18869
18948
|
function getQueryParams(operation, spec) {
|
|
18870
18949
|
const parameters = operation.parameters;
|
|
18871
18950
|
if (parameters === void 0) return [];
|
|
@@ -18874,39 +18953,131 @@ function getQueryParams(operation, spec) {
|
|
|
18874
18953
|
const resolved = resolveParam(p, spec);
|
|
18875
18954
|
if (resolved === void 0 || resolved.in !== "query") continue;
|
|
18876
18955
|
const schema = resolved.schema;
|
|
18877
|
-
|
|
18956
|
+
const resolvedStyle = resolved.style;
|
|
18957
|
+
const resolvedExplode = resolved.explode;
|
|
18958
|
+
const param = {
|
|
18959
|
+
name: normalizeParamName(resolved.name),
|
|
18960
|
+
rawName: resolved.name,
|
|
18961
|
+
tsType: schemaToTsType(schema),
|
|
18962
|
+
required: resolved.required === true
|
|
18963
|
+
};
|
|
18964
|
+
if (resolvedStyle === "deepObject" && schema !== void 0 && !isRef3(schema)) {
|
|
18965
|
+
const s = schema;
|
|
18966
|
+
if (s.type === "object" && s.properties !== void 0) {
|
|
18967
|
+
param.isDeepObject = true;
|
|
18968
|
+
param.deepObjectProperties = Object.entries(s.properties).map(([key, propSchema]) => ({
|
|
18969
|
+
key,
|
|
18970
|
+
tsType: schemaToTsType(propSchema)
|
|
18971
|
+
}));
|
|
18972
|
+
}
|
|
18973
|
+
}
|
|
18974
|
+
if (!param.isDeepObject && schema !== void 0 && !isRef3(schema) && schema.type === "array" && resolvedExplode === false) {
|
|
18975
|
+
if (resolvedStyle === "spaceDelimited") {
|
|
18976
|
+
param.delimiterStyle = "ssv";
|
|
18977
|
+
} else if (resolvedStyle === "pipeDelimited") {
|
|
18978
|
+
param.delimiterStyle = "psv";
|
|
18979
|
+
} else {
|
|
18980
|
+
param.delimiterStyle = "csv";
|
|
18981
|
+
}
|
|
18982
|
+
}
|
|
18878
18983
|
if (schema !== void 0 && !isRef3(schema)) {
|
|
18879
18984
|
const s = schema;
|
|
18880
|
-
if (
|
|
18881
|
-
|
|
18985
|
+
if (Array.isArray(s.enum)) param.enum = s.enum;
|
|
18986
|
+
if (typeof s.minimum === "number") param.minimum = s.minimum;
|
|
18987
|
+
if (typeof s.maximum === "number") param.maximum = s.maximum;
|
|
18988
|
+
if (typeof s.exclusiveMinimum === "number") param.exclusiveMinimum = s.exclusiveMinimum;
|
|
18989
|
+
if (typeof s.exclusiveMaximum === "number") param.exclusiveMaximum = s.exclusiveMaximum;
|
|
18990
|
+
if (typeof s.minLength === "number") param.minLength = s.minLength;
|
|
18991
|
+
if (typeof s.maxLength === "number") param.maxLength = s.maxLength;
|
|
18992
|
+
if (typeof s.pattern === "string") param.pattern = s.pattern;
|
|
18882
18993
|
}
|
|
18883
|
-
result.push(
|
|
18884
|
-
name: normalizeParamName(resolved.name),
|
|
18885
|
-
tsType,
|
|
18886
|
-
required: resolved.required === true
|
|
18887
|
-
});
|
|
18994
|
+
result.push(param);
|
|
18888
18995
|
}
|
|
18889
18996
|
return result;
|
|
18890
18997
|
}
|
|
18891
18998
|
function getBodyInfo(operation) {
|
|
18892
18999
|
const requestBody = operation.requestBody;
|
|
18893
19000
|
if (requestBody === void 0) return void 0;
|
|
18894
|
-
if (isRef3(requestBody))
|
|
19001
|
+
if (isRef3(requestBody)) {
|
|
19002
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
19003
|
+
}
|
|
18895
19004
|
const rb = requestBody;
|
|
18896
19005
|
const content = rb.content;
|
|
18897
|
-
if (content === void 0)
|
|
19006
|
+
if (content === void 0) {
|
|
19007
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
19008
|
+
}
|
|
18898
19009
|
const jsonContent = content["application/json"];
|
|
18899
|
-
if (jsonContent
|
|
18900
|
-
|
|
18901
|
-
|
|
18902
|
-
|
|
19010
|
+
if (jsonContent !== void 0 && jsonContent.schema !== void 0) {
|
|
19011
|
+
const schema = jsonContent.schema;
|
|
19012
|
+
if (isRef3(schema)) {
|
|
19013
|
+
return {
|
|
19014
|
+
typeName: refToName(schema.$ref),
|
|
19015
|
+
contentType: "application/json",
|
|
19016
|
+
isSynthesized: false
|
|
19017
|
+
};
|
|
19018
|
+
}
|
|
19019
|
+
const operationId = operation.operationId;
|
|
19020
|
+
if (operationId !== void 0 && operationId.length > 0) {
|
|
19021
|
+
return { typeName: toTypeName(operationId), contentType: "application/json", isSynthesized: true };
|
|
19022
|
+
}
|
|
19023
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
19024
|
+
}
|
|
19025
|
+
const formContent = content["application/x-www-form-urlencoded"];
|
|
19026
|
+
if (formContent !== void 0) {
|
|
19027
|
+
const schema = formContent.schema;
|
|
19028
|
+
if (schema !== void 0 && isRef3(schema)) {
|
|
19029
|
+
return {
|
|
19030
|
+
typeName: refToName(schema.$ref),
|
|
19031
|
+
contentType: "application/x-www-form-urlencoded",
|
|
19032
|
+
isSynthesized: false
|
|
19033
|
+
};
|
|
19034
|
+
}
|
|
19035
|
+
const operationId = operation.operationId;
|
|
19036
|
+
if (operationId !== void 0 && operationId.length > 0) {
|
|
19037
|
+
return {
|
|
19038
|
+
typeName: toTypeName(operationId),
|
|
19039
|
+
contentType: "application/x-www-form-urlencoded",
|
|
19040
|
+
isSynthesized: true
|
|
19041
|
+
};
|
|
19042
|
+
}
|
|
19043
|
+
return { typeName: void 0, contentType: "application/x-www-form-urlencoded", isSynthesized: false };
|
|
18903
19044
|
}
|
|
18904
|
-
|
|
19045
|
+
const multipartContent = content["multipart/form-data"];
|
|
19046
|
+
if (multipartContent !== void 0) {
|
|
19047
|
+
const schema = multipartContent.schema;
|
|
19048
|
+
if (schema !== void 0 && isRef3(schema)) {
|
|
19049
|
+
return {
|
|
19050
|
+
typeName: refToName(schema.$ref),
|
|
19051
|
+
contentType: "multipart/form-data",
|
|
19052
|
+
isSynthesized: false
|
|
19053
|
+
};
|
|
19054
|
+
}
|
|
19055
|
+
const operationId = operation.operationId;
|
|
19056
|
+
if (operationId !== void 0 && operationId.length > 0) {
|
|
19057
|
+
return {
|
|
19058
|
+
typeName: toTypeName(operationId),
|
|
19059
|
+
contentType: "multipart/form-data",
|
|
19060
|
+
isSynthesized: true
|
|
19061
|
+
};
|
|
19062
|
+
}
|
|
19063
|
+
return { typeName: void 0, contentType: "multipart/form-data", isSynthesized: false };
|
|
19064
|
+
}
|
|
19065
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
19066
|
+
}
|
|
19067
|
+
|
|
19068
|
+
// src/plugins/service.ts
|
|
19069
|
+
function collectContentfulTwoxxCodes(responses) {
|
|
19070
|
+
return Object.keys(responses).filter((k) => /^2\d\d$/.test(k) && k !== "204").sort();
|
|
18905
19071
|
}
|
|
18906
19072
|
function getReturnInfo(operation) {
|
|
18907
19073
|
const responses = operation.responses;
|
|
18908
19074
|
if (responses === void 0) return { typeName: void 0, isArray: false, isVoid: true };
|
|
18909
|
-
|
|
19075
|
+
const contentfulCodes = collectContentfulTwoxxCodes(responses);
|
|
19076
|
+
const isMultiStatus = contentfulCodes.length > 1;
|
|
19077
|
+
const twoxxCodes = ["200", "201", ...Object.keys(responses).filter(
|
|
19078
|
+
(k) => /^2\d\d$/.test(k) && k !== "200" && k !== "201" && k !== "204"
|
|
19079
|
+
)];
|
|
19080
|
+
for (const code of twoxxCodes) {
|
|
18910
19081
|
const response = responses[code];
|
|
18911
19082
|
if (response === void 0) continue;
|
|
18912
19083
|
if (isRef3(response)) continue;
|
|
@@ -18914,28 +19085,37 @@ function getReturnInfo(operation) {
|
|
|
18914
19085
|
const content = resp.content;
|
|
18915
19086
|
if (content === void 0) continue;
|
|
18916
19087
|
const jsonContent = content["application/json"];
|
|
18917
|
-
if (jsonContent
|
|
18918
|
-
|
|
18919
|
-
|
|
18920
|
-
return {
|
|
18921
|
-
typeName: refToName(schema.$ref),
|
|
18922
|
-
isArray: false,
|
|
18923
|
-
isVoid: false
|
|
18924
|
-
};
|
|
18925
|
-
}
|
|
18926
|
-
const s = schema;
|
|
18927
|
-
if (s.type === "array") {
|
|
18928
|
-
const items = s.items;
|
|
18929
|
-
if (items !== void 0 && isRef3(items)) {
|
|
19088
|
+
if (jsonContent !== void 0 && jsonContent.schema !== void 0) {
|
|
19089
|
+
const schema = jsonContent.schema;
|
|
19090
|
+
if (isRef3(schema)) {
|
|
18930
19091
|
return {
|
|
18931
|
-
typeName: refToName(
|
|
18932
|
-
isArray:
|
|
18933
|
-
isVoid: false
|
|
19092
|
+
typeName: refToName(schema.$ref),
|
|
19093
|
+
isArray: false,
|
|
19094
|
+
isVoid: false,
|
|
19095
|
+
isMultiStatus
|
|
18934
19096
|
};
|
|
18935
19097
|
}
|
|
18936
|
-
|
|
19098
|
+
const s = schema;
|
|
19099
|
+
if (s.type === "array") {
|
|
19100
|
+
const items = s.items;
|
|
19101
|
+
if (items !== void 0 && isRef3(items)) {
|
|
19102
|
+
return {
|
|
19103
|
+
typeName: refToName(items.$ref),
|
|
19104
|
+
isArray: true,
|
|
19105
|
+
isVoid: false,
|
|
19106
|
+
isMultiStatus
|
|
19107
|
+
};
|
|
19108
|
+
}
|
|
19109
|
+
return { typeName: void 0, isArray: true, isVoid: false, isMultiStatus };
|
|
19110
|
+
}
|
|
19111
|
+
return { typeName: void 0, isArray: false, isVoid: false, isMultiStatus };
|
|
19112
|
+
}
|
|
19113
|
+
if (content["text/plain"] !== void 0) {
|
|
19114
|
+
return { typeName: void 0, isArray: false, isVoid: false, primitiveType: "string" };
|
|
19115
|
+
}
|
|
19116
|
+
if (content["application/octet-stream"] !== void 0) {
|
|
19117
|
+
return { typeName: void 0, isArray: false, isVoid: false, primitiveType: "Uint8Array" };
|
|
18937
19118
|
}
|
|
18938
|
-
return { typeName: void 0, isArray: false, isVoid: false };
|
|
18939
19119
|
}
|
|
18940
19120
|
if (responses["204"] !== void 0) {
|
|
18941
19121
|
return { typeName: void 0, isArray: false, isVoid: true };
|
|
@@ -18944,6 +19124,16 @@ function getReturnInfo(operation) {
|
|
|
18944
19124
|
}
|
|
18945
19125
|
function buildReturnType(info) {
|
|
18946
19126
|
if (info.isVoid) return "Promise<void>";
|
|
19127
|
+
if (info.primitiveType !== void 0) return `Promise<${info.primitiveType}>`;
|
|
19128
|
+
if (info.isMultiStatus === true) {
|
|
19129
|
+
let bodyType;
|
|
19130
|
+
if (info.typeName !== void 0) {
|
|
19131
|
+
bodyType = info.isArray ? `${info.typeName}[]` : info.typeName;
|
|
19132
|
+
} else {
|
|
19133
|
+
bodyType = info.isArray ? "unknown[]" : "unknown";
|
|
19134
|
+
}
|
|
19135
|
+
return `Promise<{ status: number; body: ${bodyType} }>`;
|
|
19136
|
+
}
|
|
18947
19137
|
if (info.typeName !== void 0) {
|
|
18948
19138
|
return info.isArray ? `Promise<${info.typeName}[]>` : `Promise<${info.typeName}>`;
|
|
18949
19139
|
}
|
|
@@ -18978,10 +19168,10 @@ function collectOperations(spec) {
|
|
|
18978
19168
|
function buildMethodSignature(op) {
|
|
18979
19169
|
const args = [];
|
|
18980
19170
|
for (const p of op.pathParams) {
|
|
18981
|
-
args.push(`${p}: string`);
|
|
19171
|
+
args.push(`${sanitizeOperationId2(p)}: string`);
|
|
18982
19172
|
}
|
|
18983
19173
|
if (op.bodyInfo !== void 0) {
|
|
18984
|
-
const typeName = op.bodyInfo.typeName
|
|
19174
|
+
const typeName = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? op.bodyInfo.typeName : "unknown";
|
|
18985
19175
|
args.push(`body: ${typeName}`);
|
|
18986
19176
|
}
|
|
18987
19177
|
if (op.queryParams.length > 0) {
|
|
@@ -18999,7 +19189,7 @@ function generateService(spec) {
|
|
|
18999
19189
|
const operations = collectOperations(spec);
|
|
19000
19190
|
const importTypes = /* @__PURE__ */ new Set();
|
|
19001
19191
|
for (const op of operations) {
|
|
19002
|
-
if (op.bodyInfo?.typeName !== void 0) {
|
|
19192
|
+
if (op.bodyInfo?.typeName !== void 0 && !op.bodyInfo.isSynthesized) {
|
|
19003
19193
|
importTypes.add(op.bodyInfo.typeName);
|
|
19004
19194
|
}
|
|
19005
19195
|
if (op.returnInfo.typeName !== void 0) {
|
|
@@ -19028,110 +19218,9 @@ function generateService(spec) {
|
|
|
19028
19218
|
}
|
|
19029
19219
|
|
|
19030
19220
|
// src/plugins/router.ts
|
|
19031
|
-
var SUPPORTED_METHODS2 = ["get", "post", "put", "patch", "delete"];
|
|
19032
|
-
function isRef4(obj) {
|
|
19033
|
-
return typeof obj === "object" && obj !== null && "$ref" in obj;
|
|
19034
|
-
}
|
|
19035
|
-
function refToName2(ref) {
|
|
19036
|
-
const parts = ref.split("/");
|
|
19037
|
-
return toTypeName(parts[parts.length - 1]);
|
|
19038
|
-
}
|
|
19039
|
-
function extractPathParamsFromPath2(path) {
|
|
19040
|
-
const matches = path.match(/\{([^}]+)\}/g);
|
|
19041
|
-
if (matches === null) return [];
|
|
19042
|
-
return matches.map((m) => m.slice(1, -1));
|
|
19043
|
-
}
|
|
19044
19221
|
function toHonoPath(openapiPath) {
|
|
19045
19222
|
return openapiPath.replace(/\{([^}]+)\}/g, ":$1");
|
|
19046
19223
|
}
|
|
19047
|
-
function resolveParam2(p, spec) {
|
|
19048
|
-
if (!isRef4(p)) return p;
|
|
19049
|
-
const refStr = p.$ref;
|
|
19050
|
-
const name = refToName2(refStr);
|
|
19051
|
-
const components = spec.components;
|
|
19052
|
-
if (components?.parameters === void 0) return void 0;
|
|
19053
|
-
const resolved = components.parameters[name];
|
|
19054
|
-
if (resolved === void 0 || isRef4(resolved)) return void 0;
|
|
19055
|
-
return resolved;
|
|
19056
|
-
}
|
|
19057
|
-
function deriveServiceName2(spec) {
|
|
19058
|
-
const title = spec.info?.title ?? "";
|
|
19059
|
-
const pascal = title.replace(/[^a-zA-Z0-9 ]/g, "").split(/\s+/).filter((s) => s.length > 0).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
19060
|
-
if (pascal.length === 0) return "ApiService";
|
|
19061
|
-
const safePascal = /^[0-9]/.test(pascal) ? `_${pascal}` : pascal;
|
|
19062
|
-
if (safePascal.endsWith("Service")) return safePascal;
|
|
19063
|
-
return `${safePascal}Service`;
|
|
19064
|
-
}
|
|
19065
|
-
function sanitizeOperationId3(id) {
|
|
19066
|
-
const parts = id.replace(/'/g, "").split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
19067
|
-
if (parts.length === 0) return "unknown";
|
|
19068
|
-
const [first = "", ...rest] = parts;
|
|
19069
|
-
const camel = first.charAt(0).toLowerCase() + first.slice(1) + rest.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
|
|
19070
|
-
return /^[0-9]/.test(camel) ? `_${camel}` : camel;
|
|
19071
|
-
}
|
|
19072
|
-
function deriveMethodName2(operationId, method, path) {
|
|
19073
|
-
if (operationId !== void 0 && operationId.length > 0) {
|
|
19074
|
-
return sanitizeOperationId3(operationId);
|
|
19075
|
-
}
|
|
19076
|
-
return deriveOperationName3(method, path);
|
|
19077
|
-
}
|
|
19078
|
-
function deriveOperationName3(method, path) {
|
|
19079
|
-
const prefixMap = {
|
|
19080
|
-
get: "get",
|
|
19081
|
-
post: "create",
|
|
19082
|
-
put: "update",
|
|
19083
|
-
patch: "patch",
|
|
19084
|
-
delete: "delete"
|
|
19085
|
-
};
|
|
19086
|
-
const prefix = prefixMap[method] ?? method;
|
|
19087
|
-
const segments = path.replace(/^\/api\/v\d+\//, "").replace(/^\//, "");
|
|
19088
|
-
const parts = segments.split("/").map((seg) => {
|
|
19089
|
-
const paramMatches = seg.match(/\{([^}]+)\}/g);
|
|
19090
|
-
if (paramMatches !== null && !(seg.startsWith("{") && seg.endsWith("}"))) {
|
|
19091
|
-
return paramMatches.map((m) => {
|
|
19092
|
-
const name = sanitizeOperationId3(m.slice(1, -1));
|
|
19093
|
-
return "By" + name.charAt(0).toUpperCase() + name.slice(1);
|
|
19094
|
-
}).join("");
|
|
19095
|
-
}
|
|
19096
|
-
if (seg.startsWith("{") && seg.endsWith("}")) {
|
|
19097
|
-
const name = seg.slice(1, -1);
|
|
19098
|
-
const sanitized = sanitizeOperationId3(name);
|
|
19099
|
-
return "By" + sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
|
|
19100
|
-
}
|
|
19101
|
-
return toTypeName(seg);
|
|
19102
|
-
});
|
|
19103
|
-
return prefix + parts.join("");
|
|
19104
|
-
}
|
|
19105
|
-
function normalizeParamName2(name) {
|
|
19106
|
-
const stripped = name.replace(/\[\]$/, "").replace(/'/g, "");
|
|
19107
|
-
const parts = stripped.split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
19108
|
-
if (parts.length === 0) return "_";
|
|
19109
|
-
const camel = parts.map((part, i) => i === 0 ? part : part[0].toUpperCase() + part.slice(1)).join("");
|
|
19110
|
-
return /^[^a-zA-Z_$]/.test(camel) ? `_${camel}` : camel;
|
|
19111
|
-
}
|
|
19112
|
-
function schemaToTsType(schema) {
|
|
19113
|
-
if (schema === void 0 || isRef4(schema)) return "string";
|
|
19114
|
-
const s = schema;
|
|
19115
|
-
if (s.type === "number" || s.type === "integer") return "number";
|
|
19116
|
-
if (s.type === "boolean") return "boolean";
|
|
19117
|
-
return "string";
|
|
19118
|
-
}
|
|
19119
|
-
function getQueryParams2(operation, spec) {
|
|
19120
|
-
const parameters = operation.parameters;
|
|
19121
|
-
if (parameters === void 0) return [];
|
|
19122
|
-
const result = [];
|
|
19123
|
-
for (const p of parameters) {
|
|
19124
|
-
const resolved = resolveParam2(p, spec);
|
|
19125
|
-
if (resolved === void 0 || resolved.in !== "query") continue;
|
|
19126
|
-
const schema = resolved.schema;
|
|
19127
|
-
result.push({
|
|
19128
|
-
name: normalizeParamName2(resolved.name),
|
|
19129
|
-
tsType: schemaToTsType(schema),
|
|
19130
|
-
required: resolved.required === true
|
|
19131
|
-
});
|
|
19132
|
-
}
|
|
19133
|
-
return result;
|
|
19134
|
-
}
|
|
19135
19224
|
function formatToZodModifier(format) {
|
|
19136
19225
|
switch (format) {
|
|
19137
19226
|
case "uuid":
|
|
@@ -19148,8 +19237,23 @@ function formatToZodModifier(format) {
|
|
|
19148
19237
|
}
|
|
19149
19238
|
}
|
|
19150
19239
|
function pathParamZodExpr(schema) {
|
|
19151
|
-
if (schema === void 0 ||
|
|
19240
|
+
if (schema === void 0 || isRef3(schema)) return void 0;
|
|
19152
19241
|
const s = schema;
|
|
19242
|
+
if (s.type === "integer" || s.type === "number") {
|
|
19243
|
+
const hasMin = typeof s.minimum === "number";
|
|
19244
|
+
const hasMax = typeof s.maximum === "number";
|
|
19245
|
+
const hasExcMin = typeof s.exclusiveMinimum === "number";
|
|
19246
|
+
const hasExcMax = typeof s.exclusiveMaximum === "number";
|
|
19247
|
+
if (hasMin || hasMax || hasExcMin || hasExcMax) {
|
|
19248
|
+
let expr = "z.coerce.number()";
|
|
19249
|
+
if (hasMin) expr += `.min(${s.minimum})`;
|
|
19250
|
+
if (hasMax) expr += `.max(${s.maximum})`;
|
|
19251
|
+
if (hasExcMin) expr += `.gt(${s.exclusiveMinimum})`;
|
|
19252
|
+
if (hasExcMax) expr += `.lt(${s.exclusiveMaximum})`;
|
|
19253
|
+
return expr;
|
|
19254
|
+
}
|
|
19255
|
+
return void 0;
|
|
19256
|
+
}
|
|
19153
19257
|
if (s.type !== "string") return void 0;
|
|
19154
19258
|
const format = s.format;
|
|
19155
19259
|
if (format === void 0) return void 0;
|
|
@@ -19157,30 +19261,71 @@ function pathParamZodExpr(schema) {
|
|
|
19157
19261
|
if (modifier === "") return void 0;
|
|
19158
19262
|
return `z.string()${modifier}`;
|
|
19159
19263
|
}
|
|
19160
|
-
function
|
|
19264
|
+
function queryParamDelimitedZodBase(_param) {
|
|
19265
|
+
return "z.array(z.string())";
|
|
19266
|
+
}
|
|
19267
|
+
function queryParamDeepObjectZodBase(param) {
|
|
19268
|
+
const propFields = (param.deepObjectProperties ?? []).map((p) => {
|
|
19269
|
+
const coerced = p.tsType === "number" ? "z.coerce.number()" : "z.string()";
|
|
19270
|
+
return `${p.key}: ${coerced}.optional()`;
|
|
19271
|
+
});
|
|
19272
|
+
return `z.object({ ${propFields.join(", ")} })`;
|
|
19273
|
+
}
|
|
19274
|
+
function queryParamNumberZodBase(param) {
|
|
19275
|
+
let base = "z.number()";
|
|
19276
|
+
if (param.minimum !== void 0) base += `.min(${param.minimum})`;
|
|
19277
|
+
if (param.maximum !== void 0) base += `.max(${param.maximum})`;
|
|
19278
|
+
if (param.exclusiveMinimum !== void 0) base += `.gt(${param.exclusiveMinimum})`;
|
|
19279
|
+
if (param.exclusiveMaximum !== void 0) base += `.lt(${param.exclusiveMaximum})`;
|
|
19280
|
+
return base;
|
|
19281
|
+
}
|
|
19282
|
+
function queryParamStringZodBase(param) {
|
|
19283
|
+
let base;
|
|
19284
|
+
if (param.enum !== void 0 && param.enum.length > 0) {
|
|
19285
|
+
const members = param.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
19286
|
+
base = `z.enum([${members}])`;
|
|
19287
|
+
} else {
|
|
19288
|
+
base = "z.string()";
|
|
19289
|
+
}
|
|
19290
|
+
if (param.minLength !== void 0) base += `.min(${param.minLength})`;
|
|
19291
|
+
if (param.maxLength !== void 0) base += `.max(${param.maxLength})`;
|
|
19292
|
+
if (param.pattern !== void 0) base += `.regex(/${param.pattern}/)`;
|
|
19293
|
+
return base;
|
|
19294
|
+
}
|
|
19295
|
+
function queryParamZodExpr(param) {
|
|
19161
19296
|
let base;
|
|
19162
|
-
if (
|
|
19163
|
-
base =
|
|
19164
|
-
} else if (
|
|
19297
|
+
if (param.delimiterStyle !== void 0) {
|
|
19298
|
+
base = queryParamDelimitedZodBase(param);
|
|
19299
|
+
} else if (param.isDeepObject === true && param.deepObjectProperties !== void 0) {
|
|
19300
|
+
base = queryParamDeepObjectZodBase(param);
|
|
19301
|
+
} else if (param.tsType === "number") {
|
|
19302
|
+
base = queryParamNumberZodBase(param);
|
|
19303
|
+
} else if (param.tsType === "boolean") {
|
|
19165
19304
|
base = "z.boolean()";
|
|
19166
19305
|
} else {
|
|
19167
|
-
|
|
19168
|
-
const s = schema;
|
|
19169
|
-
const format = s.format;
|
|
19170
|
-
const modifier = format !== void 0 ? formatToZodModifier(format) : "";
|
|
19171
|
-
base = `z.string()${modifier}`;
|
|
19172
|
-
} else {
|
|
19173
|
-
base = "z.string()";
|
|
19174
|
-
}
|
|
19306
|
+
base = queryParamStringZodBase(param);
|
|
19175
19307
|
}
|
|
19176
|
-
return required ? base : `${base}.optional()`;
|
|
19308
|
+
return param.required ? base : `${base}.optional()`;
|
|
19309
|
+
}
|
|
19310
|
+
function headerParamZodExpr(param) {
|
|
19311
|
+
let base;
|
|
19312
|
+
if (param.enum !== void 0 && param.enum.length > 0) {
|
|
19313
|
+
const members = param.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
19314
|
+
base = `z.enum([${members}])`;
|
|
19315
|
+
} else {
|
|
19316
|
+
base = "z.string()";
|
|
19317
|
+
}
|
|
19318
|
+
if (param.minLength !== void 0) base += `.min(${param.minLength})`;
|
|
19319
|
+
if (param.maxLength !== void 0) base += `.max(${param.maxLength})`;
|
|
19320
|
+
if (param.pattern !== void 0) base += `.regex(/${param.pattern}/)`;
|
|
19321
|
+
return param.required ? base : `${base}.optional()`;
|
|
19177
19322
|
}
|
|
19178
19323
|
function getPathParamValidations(operation, spec, rawPathParamNames) {
|
|
19179
19324
|
const parameters = operation.parameters;
|
|
19180
19325
|
if (parameters === void 0) return [];
|
|
19181
19326
|
const zodByName = /* @__PURE__ */ new Map();
|
|
19182
19327
|
for (const p of parameters) {
|
|
19183
|
-
const resolved =
|
|
19328
|
+
const resolved = resolveParam(p, spec);
|
|
19184
19329
|
if (resolved === void 0 || resolved.in !== "path") continue;
|
|
19185
19330
|
const schema = resolved.schema;
|
|
19186
19331
|
const zodExpr = pathParamZodExpr(schema);
|
|
@@ -19198,23 +19343,53 @@ function getHeaderParams(operation, spec) {
|
|
|
19198
19343
|
if (parameters === void 0) return [];
|
|
19199
19344
|
const result = [];
|
|
19200
19345
|
for (const p of parameters) {
|
|
19201
|
-
const resolved =
|
|
19346
|
+
const resolved = resolveParam(p, spec);
|
|
19202
19347
|
if (resolved === void 0 || resolved.in !== "header") continue;
|
|
19203
|
-
|
|
19348
|
+
const param = {
|
|
19204
19349
|
rawName: resolved.name,
|
|
19205
19350
|
required: resolved.required === true
|
|
19206
|
-
}
|
|
19351
|
+
};
|
|
19352
|
+
const schema = resolved.schema;
|
|
19353
|
+
if (schema !== void 0 && !isRef3(schema)) {
|
|
19354
|
+
const s = schema;
|
|
19355
|
+
if (Array.isArray(s.enum)) param.enum = s.enum;
|
|
19356
|
+
if (typeof s.minLength === "number") param.minLength = s.minLength;
|
|
19357
|
+
if (typeof s.maxLength === "number") param.maxLength = s.maxLength;
|
|
19358
|
+
if (typeof s.pattern === "string") param.pattern = s.pattern;
|
|
19359
|
+
}
|
|
19360
|
+
result.push(param);
|
|
19207
19361
|
}
|
|
19208
19362
|
return result;
|
|
19209
19363
|
}
|
|
19364
|
+
function queryParamHasConstraints(q) {
|
|
19365
|
+
const constraintFields = [
|
|
19366
|
+
q.enum,
|
|
19367
|
+
q.minimum,
|
|
19368
|
+
q.maximum,
|
|
19369
|
+
q.exclusiveMinimum,
|
|
19370
|
+
q.exclusiveMaximum,
|
|
19371
|
+
q.minLength,
|
|
19372
|
+
q.maxLength,
|
|
19373
|
+
q.pattern,
|
|
19374
|
+
q.delimiterStyle
|
|
19375
|
+
];
|
|
19376
|
+
return constraintFields.some((f) => f !== void 0) || q.isDeepObject === true;
|
|
19377
|
+
}
|
|
19210
19378
|
function queryParamsNeedValidation(queryParams) {
|
|
19211
|
-
return queryParams.some(
|
|
19379
|
+
return queryParams.some(
|
|
19380
|
+
(q) => q.required || q.tsType !== "string" || queryParamHasConstraints(q)
|
|
19381
|
+
);
|
|
19382
|
+
}
|
|
19383
|
+
function delimiterChar(style) {
|
|
19384
|
+
if (style === "ssv") return " ";
|
|
19385
|
+
if (style === "psv") return "|";
|
|
19386
|
+
return ",";
|
|
19212
19387
|
}
|
|
19213
19388
|
function emitQueryValidation(lines, queryParams, indent) {
|
|
19214
19389
|
const inner = `${indent} `;
|
|
19215
19390
|
const fieldIndent = `${indent} `;
|
|
19216
19391
|
const fields = queryParams.map((q) => {
|
|
19217
|
-
const expr =
|
|
19392
|
+
const expr = queryParamZodExpr(q);
|
|
19218
19393
|
return `${fieldIndent}${q.name}: ${expr}`;
|
|
19219
19394
|
}).join(",\n");
|
|
19220
19395
|
lines.push(`${inner}// Validate query parameters: returns 422 with Zod issues on failure`);
|
|
@@ -19253,7 +19428,7 @@ function emitHeaderValidation(lines, headerParams, indent, framework) {
|
|
|
19253
19428
|
const fieldIndent = `${indent} `;
|
|
19254
19429
|
const schemaFields = headerParams.map((h) => {
|
|
19255
19430
|
const key = JSON.stringify(h.rawName);
|
|
19256
|
-
const expr = h
|
|
19431
|
+
const expr = headerParamZodExpr(h);
|
|
19257
19432
|
return `${fieldIndent}${key}: ${expr}`;
|
|
19258
19433
|
}).join(",\n");
|
|
19259
19434
|
const rawFields = headerParams.map((h) => {
|
|
@@ -19275,54 +19450,84 @@ function emitHeaderValidation(lines, headerParams, indent, framework) {
|
|
|
19275
19450
|
lines.push(rawFields);
|
|
19276
19451
|
lines.push(`${inner}})`);
|
|
19277
19452
|
}
|
|
19278
|
-
function getBodyInfo2(operation) {
|
|
19279
|
-
const requestBody = operation.requestBody;
|
|
19280
|
-
if (requestBody === void 0) return void 0;
|
|
19281
|
-
if (isRef4(requestBody)) return { typeName: void 0 };
|
|
19282
|
-
const rb = requestBody;
|
|
19283
|
-
const content = rb.content;
|
|
19284
|
-
if (content === void 0) return { typeName: void 0 };
|
|
19285
|
-
const jsonContent = content["application/json"];
|
|
19286
|
-
if (jsonContent === void 0 || jsonContent.schema === void 0) return { typeName: void 0 };
|
|
19287
|
-
const schema = jsonContent.schema;
|
|
19288
|
-
if (isRef4(schema)) {
|
|
19289
|
-
return { typeName: refToName2(schema.$ref) };
|
|
19290
|
-
}
|
|
19291
|
-
return { typeName: void 0 };
|
|
19292
|
-
}
|
|
19293
19453
|
function response200IsVoid(resp) {
|
|
19294
|
-
if (
|
|
19454
|
+
if (isRef3(resp)) return false;
|
|
19295
19455
|
const r = resp;
|
|
19296
19456
|
const content = r.content;
|
|
19297
19457
|
return content === void 0 || Object.keys(content).length === 0;
|
|
19298
19458
|
}
|
|
19459
|
+
function detectResponseContentType(resp) {
|
|
19460
|
+
if (isRef3(resp)) return "application/json";
|
|
19461
|
+
const r = resp;
|
|
19462
|
+
const content = r.content;
|
|
19463
|
+
if (content === void 0) return "application/json";
|
|
19464
|
+
if ("text/plain" in content) return "text/plain";
|
|
19465
|
+
if ("application/octet-stream" in content) return "application/octet-stream";
|
|
19466
|
+
return "application/json";
|
|
19467
|
+
}
|
|
19299
19468
|
function getResponseStatus(operation, httpMethod) {
|
|
19300
19469
|
const responses = operation.responses;
|
|
19301
19470
|
if (responses === void 0) {
|
|
19302
|
-
return httpMethod === "delete" ? { status: 204, isVoid: true } : { status: 200, isVoid: false };
|
|
19471
|
+
return httpMethod === "delete" ? { status: 204, isVoid: true, responseContentType: "application/json" } : { status: 200, isVoid: false, responseContentType: "application/json" };
|
|
19472
|
+
}
|
|
19473
|
+
const contentfulTwoxxKeys = Object.keys(responses).filter((k) => /^2\d\d$/.test(k) && k !== "204").sort();
|
|
19474
|
+
if (contentfulTwoxxKeys.length > 1) {
|
|
19475
|
+
return {
|
|
19476
|
+
status: 200,
|
|
19477
|
+
isVoid: false,
|
|
19478
|
+
responseContentType: "application/json",
|
|
19479
|
+
isMultiStatus: true
|
|
19480
|
+
};
|
|
19481
|
+
}
|
|
19482
|
+
if (responses["201"] !== void 0) {
|
|
19483
|
+
return {
|
|
19484
|
+
status: 201,
|
|
19485
|
+
isVoid: false,
|
|
19486
|
+
responseContentType: detectResponseContentType(responses["201"])
|
|
19487
|
+
};
|
|
19488
|
+
}
|
|
19489
|
+
if (responses["204"] !== void 0) {
|
|
19490
|
+
return { status: 204, isVoid: true, responseContentType: "application/json" };
|
|
19303
19491
|
}
|
|
19304
|
-
if (responses["201"] !== void 0) return { status: 201, isVoid: false };
|
|
19305
|
-
if (responses["204"] !== void 0) return { status: 204, isVoid: true };
|
|
19306
19492
|
if (responses["200"] !== void 0) {
|
|
19307
|
-
if (response200IsVoid(responses["200"]))
|
|
19308
|
-
|
|
19493
|
+
if (response200IsVoid(responses["200"])) {
|
|
19494
|
+
return { status: 204, isVoid: true, responseContentType: "application/json" };
|
|
19495
|
+
}
|
|
19496
|
+
return {
|
|
19497
|
+
status: 200,
|
|
19498
|
+
isVoid: false,
|
|
19499
|
+
responseContentType: detectResponseContentType(responses["200"])
|
|
19500
|
+
};
|
|
19309
19501
|
}
|
|
19310
|
-
|
|
19502
|
+
const twoxxKeys = Object.keys(responses).filter(
|
|
19503
|
+
(k) => /^2\d\d$/.test(k) && k !== "200" && k !== "201" && k !== "204"
|
|
19504
|
+
);
|
|
19505
|
+
if (twoxxKeys.length === 1) {
|
|
19506
|
+
const code = parseInt(twoxxKeys[0], 10);
|
|
19507
|
+
const resp = responses[twoxxKeys[0]];
|
|
19508
|
+
const isVoid = isRef3(resp) ? false : (() => {
|
|
19509
|
+
const r = resp;
|
|
19510
|
+
const content = r.content;
|
|
19511
|
+
return content === void 0 || Object.keys(content).length === 0;
|
|
19512
|
+
})();
|
|
19513
|
+
return { status: code, isVoid, responseContentType: detectResponseContentType(resp) };
|
|
19514
|
+
}
|
|
19515
|
+
return httpMethod === "delete" ? { status: 204, isVoid: true, responseContentType: "application/json" } : { status: 200, isVoid: false, responseContentType: "application/json" };
|
|
19311
19516
|
}
|
|
19312
19517
|
function collectOperations2(spec) {
|
|
19313
19518
|
const paths = spec.paths;
|
|
19314
19519
|
if (paths === void 0) return [];
|
|
19315
19520
|
const operations = [];
|
|
19316
19521
|
for (const [path, pathItem] of Object.entries(paths)) {
|
|
19317
|
-
for (const method of
|
|
19522
|
+
for (const method of SUPPORTED_METHODS) {
|
|
19318
19523
|
const operation = pathItem[method];
|
|
19319
19524
|
if (operation === void 0) continue;
|
|
19320
|
-
const methodName =
|
|
19321
|
-
const pathParams =
|
|
19525
|
+
const methodName = deriveMethodName(operation.operationId, method, path);
|
|
19526
|
+
const pathParams = extractPathParamsFromPath(path);
|
|
19322
19527
|
const pathParamValidations = getPathParamValidations(operation, spec, pathParams);
|
|
19323
|
-
const queryParams =
|
|
19528
|
+
const queryParams = getQueryParams(operation, spec);
|
|
19324
19529
|
const headerParams = getHeaderParams(operation, spec);
|
|
19325
|
-
const bodyInfo =
|
|
19530
|
+
const bodyInfo = getBodyInfo(operation);
|
|
19326
19531
|
const responseStatus = getResponseStatus(operation, method);
|
|
19327
19532
|
operations.push({
|
|
19328
19533
|
methodName,
|
|
@@ -19343,7 +19548,9 @@ function collectOperations2(spec) {
|
|
|
19343
19548
|
function collectSortedBodyTypes(operations) {
|
|
19344
19549
|
const bodyTypes = /* @__PURE__ */ new Set();
|
|
19345
19550
|
for (const op of operations) {
|
|
19346
|
-
if (op.bodyInfo?.typeName !== void 0
|
|
19551
|
+
if (op.bodyInfo?.typeName !== void 0 && !op.bodyInfo.isSynthesized) {
|
|
19552
|
+
bodyTypes.add(op.bodyInfo.typeName);
|
|
19553
|
+
}
|
|
19347
19554
|
}
|
|
19348
19555
|
return Array.from(bodyTypes).sort();
|
|
19349
19556
|
}
|
|
@@ -19375,7 +19582,27 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
19375
19582
|
lines.push(`${indent} }`);
|
|
19376
19583
|
}
|
|
19377
19584
|
if (op.queryParams.length > 0) {
|
|
19585
|
+
const deepObjectParams = op.queryParams.filter((q) => q.isDeepObject === true);
|
|
19586
|
+
if (deepObjectParams.length > 0) {
|
|
19587
|
+
lines.push(`${indent} const _dq = c.req.queries()`);
|
|
19588
|
+
for (const q of deepObjectParams) {
|
|
19589
|
+
const prefixLen = q.rawName.length + 1;
|
|
19590
|
+
const bracketPrefix = q.rawName + "[";
|
|
19591
|
+
lines.push(`${indent} const ${q.name} = Object.fromEntries(`);
|
|
19592
|
+
lines.push(
|
|
19593
|
+
`${indent} Object.entries(_dq).filter(([k]) => k.startsWith('${bracketPrefix}') && k.endsWith(']')).map(([k, vs]) => [k.slice(${prefixLen}, -1), vs[0]])`
|
|
19594
|
+
);
|
|
19595
|
+
lines.push(`${indent} )`);
|
|
19596
|
+
}
|
|
19597
|
+
}
|
|
19378
19598
|
const fields = op.queryParams.map((q) => {
|
|
19599
|
+
if (q.isDeepObject === true) {
|
|
19600
|
+
return ` ${q.name}`;
|
|
19601
|
+
}
|
|
19602
|
+
if (q.delimiterStyle !== void 0) {
|
|
19603
|
+
const delim = JSON.stringify(delimiterChar(q.delimiterStyle));
|
|
19604
|
+
return ` ${q.name}: c.req.query('${q.rawName}') !== undefined ? c.req.query('${q.rawName}')!.split(${delim}) : undefined`;
|
|
19605
|
+
}
|
|
19379
19606
|
if (q.tsType === "number") {
|
|
19380
19607
|
return ` ${q.name}: c.req.query('${q.name}') !== undefined ? Number(c.req.query('${q.name}')) : undefined`;
|
|
19381
19608
|
}
|
|
@@ -19403,8 +19630,32 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
19403
19630
|
}
|
|
19404
19631
|
let bodyVarName = "body";
|
|
19405
19632
|
if (op.bodyInfo !== void 0) {
|
|
19406
|
-
const
|
|
19407
|
-
|
|
19633
|
+
const typeDecl = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? op.bodyInfo.typeName : "unknown";
|
|
19634
|
+
if (op.bodyInfo.contentType === "application/x-www-form-urlencoded") {
|
|
19635
|
+
lines.push(`${indent} const _ct = c.req.header('content-type') ?? ''`);
|
|
19636
|
+
lines.push(
|
|
19637
|
+
`${indent} if (!_ct.toLowerCase().startsWith('application/x-www-form-urlencoded')) {`
|
|
19638
|
+
);
|
|
19639
|
+
lines.push(`${indent} return c.json({ error: 'Unsupported Media Type' }, 415)`);
|
|
19640
|
+
lines.push(`${indent} }`);
|
|
19641
|
+
lines.push(`${indent} const body: unknown = await c.req.parseBody()`);
|
|
19642
|
+
} else if (op.bodyInfo.contentType === "multipart/form-data") {
|
|
19643
|
+
lines.push(
|
|
19644
|
+
`${indent} // multipart/form-data: parseBody({ all: true }) collects repeated keys into arrays.`
|
|
19645
|
+
);
|
|
19646
|
+
lines.push(`${indent} const body: unknown = await c.req.parseBody({ all: true })`);
|
|
19647
|
+
} else {
|
|
19648
|
+
lines.push(`${indent} const _ct = c.req.header('content-type') ?? ''`);
|
|
19649
|
+
lines.push(`${indent} if (!_ct.toLowerCase().startsWith('application/json')) {`);
|
|
19650
|
+
lines.push(`${indent} return c.json({ error: 'Unsupported Media Type' }, 415)`);
|
|
19651
|
+
lines.push(`${indent} }`);
|
|
19652
|
+
lines.push(`${indent} let body: ${typeDecl}`);
|
|
19653
|
+
lines.push(`${indent} try {`);
|
|
19654
|
+
lines.push(`${indent} body = JSON.parse(await c.req.text()) as ${typeDecl}`);
|
|
19655
|
+
lines.push(`${indent} } catch {`);
|
|
19656
|
+
lines.push(`${indent} return c.json({ error: 'Invalid JSON body' }, 400)`);
|
|
19657
|
+
lines.push(`${indent} }`);
|
|
19658
|
+
}
|
|
19408
19659
|
const schemaName = op.bodyInfo.typeName !== void 0 ? `${op.bodyInfo.typeName}Schema` : void 0;
|
|
19409
19660
|
if (schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName)) {
|
|
19410
19661
|
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
@@ -19429,14 +19680,44 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
19429
19680
|
serviceArgs.push("params");
|
|
19430
19681
|
}
|
|
19431
19682
|
const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
|
|
19683
|
+
lines.push(`${indent} try {`);
|
|
19432
19684
|
if (op.responseStatus.isVoid) {
|
|
19433
|
-
lines.push(`${indent}
|
|
19434
|
-
lines.push(`${indent}
|
|
19435
|
-
} else if (op.responseStatus.
|
|
19436
|
-
lines.push(`${indent}
|
|
19685
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
19686
|
+
lines.push(`${indent} return new Response(null, { status: ${op.responseStatus.status} })`);
|
|
19687
|
+
} else if (op.responseStatus.isMultiStatus === true) {
|
|
19688
|
+
lines.push(`${indent} const _envelope = await ${serviceCall}`);
|
|
19689
|
+
lines.push(`${indent} return c.json(_envelope.body, _envelope.status as any)`);
|
|
19690
|
+
} else if (op.responseStatus.responseContentType === "text/plain") {
|
|
19691
|
+
if (op.responseStatus.status === 200) {
|
|
19692
|
+
lines.push(`${indent} return c.text(await ${serviceCall})`);
|
|
19693
|
+
} else {
|
|
19694
|
+
lines.push(`${indent} return c.text(await ${serviceCall}, ${op.responseStatus.status})`);
|
|
19695
|
+
}
|
|
19696
|
+
} else if (op.responseStatus.responseContentType === "application/octet-stream") {
|
|
19697
|
+
if (op.responseStatus.status === 200) {
|
|
19698
|
+
lines.push(`${indent} const _result = await ${serviceCall}`);
|
|
19699
|
+
lines.push(
|
|
19700
|
+
`${indent} return new Response(_result, { headers: { 'content-type': 'application/octet-stream' } })`
|
|
19701
|
+
);
|
|
19702
|
+
} else {
|
|
19703
|
+
lines.push(`${indent} const _result = await ${serviceCall}`);
|
|
19704
|
+
lines.push(
|
|
19705
|
+
`${indent} return new Response(_result, { status: ${op.responseStatus.status}, headers: { 'content-type': 'application/octet-stream' } })`
|
|
19706
|
+
);
|
|
19707
|
+
}
|
|
19708
|
+
} else if (op.responseStatus.status === 200) {
|
|
19709
|
+
lines.push(`${indent} return c.json(await ${serviceCall})`);
|
|
19437
19710
|
} else {
|
|
19438
|
-
lines.push(`${indent}
|
|
19711
|
+
lines.push(`${indent} return c.json(await ${serviceCall}, ${op.responseStatus.status})`);
|
|
19439
19712
|
}
|
|
19713
|
+
lines.push(`${indent} } catch (err) {`);
|
|
19714
|
+
lines.push(`${indent} if (err instanceof HttpError) {`);
|
|
19715
|
+
lines.push(
|
|
19716
|
+
`${indent} return new Response(JSON.stringify({ error: err.message }), { status: err.status, headers: { 'content-type': 'application/json' } })`
|
|
19717
|
+
);
|
|
19718
|
+
lines.push(`${indent} }`);
|
|
19719
|
+
lines.push(`${indent} throw err`);
|
|
19720
|
+
lines.push(`${indent} }`);
|
|
19440
19721
|
lines.push(`${indent}})`);
|
|
19441
19722
|
return lines.join("\n");
|
|
19442
19723
|
}
|
|
@@ -19455,6 +19736,13 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
|
|
|
19455
19736
|
}
|
|
19456
19737
|
if (op.queryParams.length > 0) {
|
|
19457
19738
|
const fields = op.queryParams.map((q) => {
|
|
19739
|
+
if (q.isDeepObject === true) {
|
|
19740
|
+
return ` ${q.name}: (req.query['${q.rawName}'] ?? {}) as Record<string, string | undefined>`;
|
|
19741
|
+
}
|
|
19742
|
+
if (q.delimiterStyle !== void 0) {
|
|
19743
|
+
const delim = JSON.stringify(delimiterChar(q.delimiterStyle));
|
|
19744
|
+
return ` ${q.name}: typeof req.query['${q.rawName}'] === 'string' ? (req.query['${q.rawName}'] as string).split(${delim}) : undefined`;
|
|
19745
|
+
}
|
|
19458
19746
|
if (q.tsType === "number") {
|
|
19459
19747
|
return ` ${q.name}: Number(req.query['${q.name}'] as string)`;
|
|
19460
19748
|
}
|
|
@@ -19485,21 +19773,28 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
|
|
|
19485
19773
|
}
|
|
19486
19774
|
let bodyVarName = "body";
|
|
19487
19775
|
if (op.bodyInfo !== void 0) {
|
|
19488
|
-
|
|
19489
|
-
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
19490
|
-
if (useZod) {
|
|
19491
|
-
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
19492
|
-
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
19493
|
-
lines.push(`${indent} if (!parseResult.success) {`);
|
|
19776
|
+
if (op.bodyInfo.contentType === "multipart/form-data") {
|
|
19494
19777
|
lines.push(
|
|
19495
|
-
`${indent}
|
|
19778
|
+
`${indent} // multipart/form-data: assumes multer middleware has populated req.files + req.body.`
|
|
19496
19779
|
);
|
|
19497
|
-
lines.push(`${indent} }`);
|
|
19498
|
-
lines.push(`${indent} const validatedBody = parseResult.data`);
|
|
19499
|
-
bodyVarName = "validatedBody";
|
|
19780
|
+
lines.push(`${indent} const body = { ...req.body, ...(req as any).files } as unknown`);
|
|
19500
19781
|
} else {
|
|
19501
|
-
const
|
|
19502
|
-
|
|
19782
|
+
const schemaName = op.bodyInfo.typeName !== void 0 ? `${op.bodyInfo.typeName}Schema` : void 0;
|
|
19783
|
+
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
19784
|
+
if (useZod) {
|
|
19785
|
+
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
19786
|
+
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
19787
|
+
lines.push(`${indent} if (!parseResult.success) {`);
|
|
19788
|
+
lines.push(
|
|
19789
|
+
`${indent} return void res.status(422).json({ error: 'Invalid request body', issues: parseResult.error.issues })`
|
|
19790
|
+
);
|
|
19791
|
+
lines.push(`${indent} }`);
|
|
19792
|
+
lines.push(`${indent} const validatedBody = parseResult.data`);
|
|
19793
|
+
bodyVarName = "validatedBody";
|
|
19794
|
+
} else {
|
|
19795
|
+
const typeAnnotation = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? ` as ${op.bodyInfo.typeName}` : "";
|
|
19796
|
+
lines.push(`${indent} const body = req.body${typeAnnotation}`);
|
|
19797
|
+
}
|
|
19503
19798
|
}
|
|
19504
19799
|
}
|
|
19505
19800
|
const serviceArgs = [];
|
|
@@ -19513,14 +19808,42 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
|
|
|
19513
19808
|
serviceArgs.push("params");
|
|
19514
19809
|
}
|
|
19515
19810
|
const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
|
|
19811
|
+
lines.push(`${indent} try {`);
|
|
19516
19812
|
if (op.responseStatus.isVoid) {
|
|
19517
|
-
lines.push(`${indent}
|
|
19518
|
-
lines.push(`${indent}
|
|
19519
|
-
} else if (op.responseStatus.
|
|
19520
|
-
lines.push(`${indent}
|
|
19813
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
19814
|
+
lines.push(`${indent} res.status(${op.responseStatus.status}).end()`);
|
|
19815
|
+
} else if (op.responseStatus.isMultiStatus === true) {
|
|
19816
|
+
lines.push(`${indent} const _envelope = await ${serviceCall}`);
|
|
19817
|
+
lines.push(`${indent} res.status(_envelope.status).json(_envelope.body)`);
|
|
19818
|
+
} else if (op.responseStatus.responseContentType === "text/plain") {
|
|
19819
|
+
if (op.responseStatus.status === 200) {
|
|
19820
|
+
lines.push(`${indent} res.type('text/plain').send(await ${serviceCall})`);
|
|
19821
|
+
} else {
|
|
19822
|
+
lines.push(
|
|
19823
|
+
`${indent} res.status(${op.responseStatus.status}).type('text/plain').send(await ${serviceCall})`
|
|
19824
|
+
);
|
|
19825
|
+
}
|
|
19826
|
+
} else if (op.responseStatus.responseContentType === "application/octet-stream") {
|
|
19827
|
+
if (op.responseStatus.status === 200) {
|
|
19828
|
+
lines.push(
|
|
19829
|
+
`${indent} res.setHeader('Content-Type', 'application/octet-stream').send(Buffer.from(await ${serviceCall}))`
|
|
19830
|
+
);
|
|
19831
|
+
} else {
|
|
19832
|
+
lines.push(
|
|
19833
|
+
`${indent} res.status(${op.responseStatus.status}).setHeader('Content-Type', 'application/octet-stream').send(Buffer.from(await ${serviceCall}))`
|
|
19834
|
+
);
|
|
19835
|
+
}
|
|
19836
|
+
} else if (op.responseStatus.status === 200) {
|
|
19837
|
+
lines.push(`${indent} res.json(await ${serviceCall})`);
|
|
19521
19838
|
} else {
|
|
19522
|
-
lines.push(`${indent}
|
|
19523
|
-
}
|
|
19839
|
+
lines.push(`${indent} res.status(${op.responseStatus.status}).json(await ${serviceCall})`);
|
|
19840
|
+
}
|
|
19841
|
+
lines.push(`${indent} } catch (err) {`);
|
|
19842
|
+
lines.push(`${indent} if (err instanceof HttpError) {`);
|
|
19843
|
+
lines.push(`${indent} return void res.status(err.status).json({ error: err.message })`);
|
|
19844
|
+
lines.push(`${indent} }`);
|
|
19845
|
+
lines.push(`${indent} throw err`);
|
|
19846
|
+
lines.push(`${indent} }`);
|
|
19524
19847
|
lines.push(`${indent}})`);
|
|
19525
19848
|
return lines.join("\n");
|
|
19526
19849
|
}
|
|
@@ -19528,14 +19851,23 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
|
19528
19851
|
const lines = [];
|
|
19529
19852
|
const genericParts = [];
|
|
19530
19853
|
if (op.queryParams.length > 0) {
|
|
19531
|
-
const
|
|
19532
|
-
|
|
19533
|
-
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
|
|
19854
|
+
const hasDeepOrDelimited = op.queryParams.some(
|
|
19855
|
+
(q) => q.isDeepObject === true || q.delimiterStyle !== void 0
|
|
19856
|
+
);
|
|
19857
|
+
let querystringType;
|
|
19858
|
+
if (hasDeepOrDelimited) {
|
|
19859
|
+
querystringType = "Record<string, string | string[] | undefined>";
|
|
19860
|
+
} else {
|
|
19861
|
+
const queryFields = op.queryParams.map((q) => {
|
|
19862
|
+
if (q.tsType === "number") return `${q.name}?: number`;
|
|
19863
|
+
if (q.tsType === "boolean") return `${q.name}?: boolean`;
|
|
19864
|
+
return `${q.name}?: string`;
|
|
19865
|
+
}).join("; ");
|
|
19866
|
+
querystringType = `{ ${queryFields} }`;
|
|
19867
|
+
}
|
|
19868
|
+
genericParts.push(`Querystring: ${querystringType}`);
|
|
19869
|
+
}
|
|
19870
|
+
if (op.bodyInfo !== void 0 && op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized) {
|
|
19539
19871
|
genericParts.push(`Body: ${op.bodyInfo.typeName}`);
|
|
19540
19872
|
} else if (op.bodyInfo !== void 0) {
|
|
19541
19873
|
genericParts.push("Body: unknown");
|
|
@@ -19558,7 +19890,36 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
|
19558
19890
|
lines.push(`${indent} }`);
|
|
19559
19891
|
}
|
|
19560
19892
|
if (op.queryParams.length > 0) {
|
|
19561
|
-
const
|
|
19893
|
+
const deepObjectParams = op.queryParams.filter((q) => q.isDeepObject === true);
|
|
19894
|
+
const hasDeepOrDelimited = op.queryParams.some(
|
|
19895
|
+
(q) => q.isDeepObject === true || q.delimiterStyle !== void 0
|
|
19896
|
+
);
|
|
19897
|
+
if (hasDeepOrDelimited) {
|
|
19898
|
+
lines.push(
|
|
19899
|
+
`${indent} const _dq = req.query as unknown as Record<string, string | undefined>`
|
|
19900
|
+
);
|
|
19901
|
+
}
|
|
19902
|
+
if (deepObjectParams.length > 0) {
|
|
19903
|
+
for (const q of deepObjectParams) {
|
|
19904
|
+
const prefixLen = q.rawName.length + 1;
|
|
19905
|
+
const bracketPrefix = q.rawName + "[";
|
|
19906
|
+
lines.push(`${indent} const ${q.name} = Object.fromEntries(`);
|
|
19907
|
+
lines.push(
|
|
19908
|
+
`${indent} Object.entries(_dq).filter(([k]) => k.startsWith('${bracketPrefix}') && k.endsWith(']')).map(([k, v]) => [k.slice(${prefixLen}, -1), v])`
|
|
19909
|
+
);
|
|
19910
|
+
lines.push(`${indent} )`);
|
|
19911
|
+
}
|
|
19912
|
+
}
|
|
19913
|
+
const fields = op.queryParams.map((q) => {
|
|
19914
|
+
if (q.isDeepObject === true) {
|
|
19915
|
+
return ` ${q.name}`;
|
|
19916
|
+
}
|
|
19917
|
+
if (q.delimiterStyle !== void 0) {
|
|
19918
|
+
const delim = JSON.stringify(delimiterChar(q.delimiterStyle));
|
|
19919
|
+
return ` ${q.name}: typeof _dq['${q.rawName}'] === 'string' ? _dq['${q.rawName}']!.split(${delim}) : undefined`;
|
|
19920
|
+
}
|
|
19921
|
+
return hasDeepOrDelimited ? ` ${q.name}: _dq['${q.rawName}']` : ` ${q.name}: req.query.${q.name}`;
|
|
19922
|
+
}).join(",\n");
|
|
19562
19923
|
lines.push(`${indent} const params = {`);
|
|
19563
19924
|
lines.push(fields);
|
|
19564
19925
|
lines.push(`${indent} }`);
|
|
@@ -19583,17 +19944,23 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
|
19583
19944
|
}
|
|
19584
19945
|
let bodyVarName = "req.body";
|
|
19585
19946
|
if (op.bodyInfo !== void 0) {
|
|
19586
|
-
|
|
19587
|
-
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
19588
|
-
if (useZod) {
|
|
19589
|
-
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
19590
|
-
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
19591
|
-
lines.push(`${indent} if (!parseResult.success) {`);
|
|
19947
|
+
if (op.bodyInfo.contentType === "multipart/form-data") {
|
|
19592
19948
|
lines.push(
|
|
19593
|
-
`${indent}
|
|
19949
|
+
`${indent} // multipart/form-data: assumes @fastify/multipart plugin has populated req.body.`
|
|
19594
19950
|
);
|
|
19595
|
-
|
|
19596
|
-
|
|
19951
|
+
} else {
|
|
19952
|
+
const schemaName = op.bodyInfo.typeName !== void 0 ? `${op.bodyInfo.typeName}Schema` : void 0;
|
|
19953
|
+
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
19954
|
+
if (useZod) {
|
|
19955
|
+
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
19956
|
+
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
19957
|
+
lines.push(`${indent} if (!parseResult.success) {`);
|
|
19958
|
+
lines.push(
|
|
19959
|
+
`${indent} return reply.status(422).send({ error: 'Invalid request body', issues: parseResult.error.issues })`
|
|
19960
|
+
);
|
|
19961
|
+
lines.push(`${indent} }`);
|
|
19962
|
+
bodyVarName = "parseResult.data";
|
|
19963
|
+
}
|
|
19597
19964
|
}
|
|
19598
19965
|
}
|
|
19599
19966
|
const serviceArgs = [];
|
|
@@ -19607,18 +19974,50 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
|
19607
19974
|
serviceArgs.push("params");
|
|
19608
19975
|
}
|
|
19609
19976
|
const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
|
|
19977
|
+
lines.push(`${indent} try {`);
|
|
19610
19978
|
if (op.responseStatus.isVoid) {
|
|
19611
|
-
lines.push(`${indent}
|
|
19612
|
-
lines.push(`${indent}
|
|
19613
|
-
} else if (op.responseStatus.
|
|
19614
|
-
lines.push(`${indent}
|
|
19615
|
-
lines.push(`${indent}
|
|
19979
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
19980
|
+
lines.push(`${indent} reply.status(${op.responseStatus.status}).send()`);
|
|
19981
|
+
} else if (op.responseStatus.isMultiStatus === true) {
|
|
19982
|
+
lines.push(`${indent} const _envelope = await ${serviceCall}`);
|
|
19983
|
+
lines.push(`${indent} return reply.status(_envelope.status).send(_envelope.body)`);
|
|
19984
|
+
} else if (op.responseStatus.responseContentType === "text/plain") {
|
|
19985
|
+
if (op.responseStatus.status === 200) {
|
|
19986
|
+
lines.push(`${indent} return reply.type('text/plain').send(await ${serviceCall})`);
|
|
19987
|
+
} else {
|
|
19988
|
+
lines.push(`${indent} return reply.status(${op.responseStatus.status}).type('text/plain').send(await ${serviceCall})`);
|
|
19989
|
+
}
|
|
19990
|
+
} else if (op.responseStatus.responseContentType === "application/octet-stream") {
|
|
19991
|
+
if (op.responseStatus.status === 200) {
|
|
19992
|
+
lines.push(`${indent} return reply.type('application/octet-stream').send(Buffer.from(await ${serviceCall}))`);
|
|
19993
|
+
} else {
|
|
19994
|
+
lines.push(`${indent} return reply.status(${op.responseStatus.status}).type('application/octet-stream').send(Buffer.from(await ${serviceCall}))`);
|
|
19995
|
+
}
|
|
19996
|
+
} else if (op.responseStatus.status === 200) {
|
|
19997
|
+
lines.push(`${indent} return ${serviceCall}`);
|
|
19616
19998
|
} else {
|
|
19617
|
-
lines.push(`${indent}
|
|
19618
|
-
|
|
19999
|
+
lines.push(`${indent} reply.status(${op.responseStatus.status})`);
|
|
20000
|
+
lines.push(`${indent} return ${serviceCall}`);
|
|
20001
|
+
}
|
|
20002
|
+
lines.push(`${indent} } catch (err) {`);
|
|
20003
|
+
lines.push(`${indent} if (err instanceof HttpError) {`);
|
|
20004
|
+
lines.push(`${indent} return reply.status(err.status).send({ error: err.message })`);
|
|
20005
|
+
lines.push(`${indent} }`);
|
|
20006
|
+
lines.push(`${indent} throw err`);
|
|
20007
|
+
lines.push(`${indent} }`);
|
|
19619
20008
|
lines.push(`${indent}})`);
|
|
19620
20009
|
return lines.join("\n");
|
|
19621
20010
|
}
|
|
20011
|
+
function httpErrorClassLines() {
|
|
20012
|
+
return [
|
|
20013
|
+
"export class HttpError extends Error {",
|
|
20014
|
+
" constructor(public readonly status: number, message: string) {",
|
|
20015
|
+
" super(message)",
|
|
20016
|
+
" this.name = 'HttpError'",
|
|
20017
|
+
" }",
|
|
20018
|
+
"}"
|
|
20019
|
+
];
|
|
20020
|
+
}
|
|
19622
20021
|
function operationsNeedZodForParams(operations) {
|
|
19623
20022
|
for (const op of operations) {
|
|
19624
20023
|
if (op.pathParamValidations.length > 0) return true;
|
|
@@ -19628,7 +20027,7 @@ function operationsNeedZodForParams(operations) {
|
|
|
19628
20027
|
return false;
|
|
19629
20028
|
}
|
|
19630
20029
|
function generateExpressRouter(spec, options) {
|
|
19631
|
-
const serviceName =
|
|
20030
|
+
const serviceName = deriveServiceName(spec);
|
|
19632
20031
|
const operations = collectOperations2(spec);
|
|
19633
20032
|
const { sortedBodyTypes, usedSchemaNames, needsZod } = collectGeneratorSetup(operations, options);
|
|
19634
20033
|
const lines = [];
|
|
@@ -19651,6 +20050,8 @@ function generateExpressRouter(spec, options) {
|
|
|
19651
20050
|
lines.push(`import { ${sortedUsedSchemas.join(", ")} } from '${options.schemaImportPath}'`);
|
|
19652
20051
|
}
|
|
19653
20052
|
lines.push("");
|
|
20053
|
+
for (const l of httpErrorClassLines()) lines.push(l);
|
|
20054
|
+
lines.push("");
|
|
19654
20055
|
lines.push(`export function createRouter(service: ${serviceName}): Router {`);
|
|
19655
20056
|
lines.push(" const router = Router()");
|
|
19656
20057
|
lines.push("");
|
|
@@ -19667,7 +20068,7 @@ function generateExpressRouter(spec, options) {
|
|
|
19667
20068
|
};
|
|
19668
20069
|
}
|
|
19669
20070
|
function generateFastifyRouter(spec, options) {
|
|
19670
|
-
const serviceName =
|
|
20071
|
+
const serviceName = deriveServiceName(spec);
|
|
19671
20072
|
const operations = collectOperations2(spec);
|
|
19672
20073
|
const { sortedBodyTypes, usedSchemaNames, needsZod } = collectGeneratorSetup(operations, options);
|
|
19673
20074
|
const lines = [];
|
|
@@ -19686,6 +20087,8 @@ function generateFastifyRouter(spec, options) {
|
|
|
19686
20087
|
lines.push(`import { ${sortedUsedSchemas.join(", ")} } from '${options.schemaImportPath}'`);
|
|
19687
20088
|
}
|
|
19688
20089
|
lines.push("");
|
|
20090
|
+
for (const l of httpErrorClassLines()) lines.push(l);
|
|
20091
|
+
lines.push("");
|
|
19689
20092
|
lines.push(`export function createRouter(app: FastifyInstance, service: ${serviceName}): void {`);
|
|
19690
20093
|
for (const op of operations) {
|
|
19691
20094
|
lines.push("");
|
|
@@ -19699,7 +20102,7 @@ function generateFastifyRouter(spec, options) {
|
|
|
19699
20102
|
};
|
|
19700
20103
|
}
|
|
19701
20104
|
function generateRouter(spec, options) {
|
|
19702
|
-
const serviceName =
|
|
20105
|
+
const serviceName = deriveServiceName(spec);
|
|
19703
20106
|
const operations = collectOperations2(spec);
|
|
19704
20107
|
const { sortedBodyTypes, usedSchemaNames, needsZod } = collectGeneratorSetup(operations, options);
|
|
19705
20108
|
const lines = [];
|
|
@@ -19718,6 +20121,8 @@ function generateRouter(spec, options) {
|
|
|
19718
20121
|
lines.push(`import { ${sortedUsedSchemas.join(", ")} } from '${options.schemaImportPath}'`);
|
|
19719
20122
|
}
|
|
19720
20123
|
lines.push("");
|
|
20124
|
+
for (const l of httpErrorClassLines()) lines.push(l);
|
|
20125
|
+
lines.push("");
|
|
19721
20126
|
lines.push(`export function createRouter(service: ${serviceName}): Hono {`);
|
|
19722
20127
|
lines.push(" const app = new Hono()");
|
|
19723
20128
|
lines.push("");
|
|
@@ -19740,63 +20145,63 @@ async function formatTs(content, filePath) {
|
|
|
19740
20145
|
const config = await resolveConfig(filePath);
|
|
19741
20146
|
return format(content, { ...config, parser: "typescript" });
|
|
19742
20147
|
}
|
|
19743
|
-
|
|
19744
|
-
|
|
19745
|
-
|
|
20148
|
+
function buildRouterFile(spec, framework, options) {
|
|
20149
|
+
if (framework === "hono") return generateRouter(spec, options);
|
|
20150
|
+
if (framework === "express") return generateExpressRouter(spec, options);
|
|
20151
|
+
return generateFastifyRouter(spec, options);
|
|
20152
|
+
}
|
|
20153
|
+
async function generateOne(cwd, config, label) {
|
|
19746
20154
|
const inputPath = (0, import_node_path2.resolve)(cwd, config.input_openapi);
|
|
19747
20155
|
const outputDir = (0, import_node_path2.resolve)(cwd, config.output);
|
|
19748
20156
|
const framework = config.framework ?? "none";
|
|
19749
|
-
|
|
20157
|
+
const prefix = label !== void 0 ? `[${label}] ` : "";
|
|
20158
|
+
console.log(`${prefix}Parsing spec: ${inputPath}`);
|
|
19750
20159
|
const spec = await parseSpec(inputPath);
|
|
19751
|
-
const generatedFiles = [];
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
|
|
19755
|
-
}
|
|
19756
|
-
generatedFiles.push(generateExpressRouter(spec));
|
|
19757
|
-
} else if (framework === "fastify") {
|
|
19758
|
-
generatedFiles.push(generateFastifyRouter(spec));
|
|
19759
|
-
}
|
|
19760
|
-
console.log(`Writing output to: ${outputDir}`);
|
|
20160
|
+
const generatedFiles = [generateService(spec)];
|
|
20161
|
+
if (framework !== "none") {
|
|
20162
|
+
generatedFiles.push(buildRouterFile(spec, framework));
|
|
20163
|
+
}
|
|
20164
|
+
console.log(`${prefix}Writing output to: ${outputDir}`);
|
|
19761
20165
|
await (0, import_promises2.mkdir)(outputDir, { recursive: true });
|
|
19762
20166
|
for (const file of generatedFiles) {
|
|
19763
20167
|
const filePath = (0, import_node_path2.join)(outputDir, file.filename);
|
|
19764
20168
|
await (0, import_promises2.writeFile)(filePath, await formatTs(file.content, filePath), "utf-8");
|
|
19765
|
-
console.log(
|
|
20169
|
+
console.log(`${prefix} \u2713 ${file.filename}`);
|
|
19766
20170
|
}
|
|
19767
|
-
if (
|
|
19768
|
-
|
|
19769
|
-
let schemaContent;
|
|
19770
|
-
try {
|
|
19771
|
-
schemaContent = await (0, import_promises2.readFile)(schemaPath, "utf-8");
|
|
19772
|
-
} catch {
|
|
19773
|
-
console.log(` \u2139 input_schema not found at ${schemaPath}, skipping Zod validation`);
|
|
19774
|
-
console.log(`Done! Generated ${generatedFiles.length} file(s).`);
|
|
19775
|
-
return;
|
|
19776
|
-
}
|
|
19777
|
-
const exportedSchemas = /* @__PURE__ */ new Set();
|
|
19778
|
-
for (const match of schemaContent.matchAll(/^export\s+const\s+(\w+Schema)\b/gm)) {
|
|
19779
|
-
exportedSchemas.add(match[1]);
|
|
19780
|
-
}
|
|
19781
|
-
if (exportedSchemas.size > 0) {
|
|
19782
|
-
const relPath = (0, import_node_path2.relative)(outputDir, schemaPath).replace(/\\/g, "/");
|
|
19783
|
-
const schemaImportPath = relPath.startsWith(".") ? relPath : `./${relPath}`;
|
|
19784
|
-
const schemaImportPathJs = schemaImportPath.replace(/\.ts$/, ".js");
|
|
19785
|
-
const routerOptions = { schemaNames: exportedSchemas, schemaImportPath: schemaImportPathJs };
|
|
19786
|
-
let routerFile;
|
|
19787
|
-
if (framework === "hono") {
|
|
19788
|
-
routerFile = generateRouter(spec, routerOptions);
|
|
19789
|
-
} else if (framework === "express") {
|
|
19790
|
-
routerFile = generateExpressRouter(spec, routerOptions);
|
|
19791
|
-
} else {
|
|
19792
|
-
routerFile = generateFastifyRouter(spec, routerOptions);
|
|
19793
|
-
}
|
|
19794
|
-
const routerPath = (0, import_node_path2.join)(outputDir, routerFile.filename);
|
|
19795
|
-
await (0, import_promises2.writeFile)(routerPath, await formatTs(routerFile.content, routerPath), "utf-8");
|
|
19796
|
-
console.log(` \u2713 router.ts (with Zod validation for ${exportedSchemas.size} schema(s))`);
|
|
19797
|
-
}
|
|
20171
|
+
if (framework !== "none" && config.input_schema !== void 0) {
|
|
20172
|
+
await generateSchemaEnhancedRouter(cwd, config, spec, framework, outputDir, prefix);
|
|
19798
20173
|
}
|
|
19799
|
-
console.log(
|
|
20174
|
+
console.log(`${prefix}Done! Generated ${generatedFiles.length} file(s).`);
|
|
20175
|
+
}
|
|
20176
|
+
async function generateSchemaEnhancedRouter(cwd, config, spec, framework, outputDir, prefix) {
|
|
20177
|
+
const schemaPath = (0, import_node_path2.resolve)(cwd, config.input_schema);
|
|
20178
|
+
let schemaContent;
|
|
20179
|
+
try {
|
|
20180
|
+
schemaContent = await (0, import_promises2.readFile)(schemaPath, "utf-8");
|
|
20181
|
+
} catch {
|
|
20182
|
+
console.log(`${prefix} input_schema not found at ${schemaPath}, skipping Zod validation`);
|
|
20183
|
+
return;
|
|
20184
|
+
}
|
|
20185
|
+
const exportedSchemas = /* @__PURE__ */ new Set();
|
|
20186
|
+
for (const match of schemaContent.matchAll(/^export\s+const\s+(\w+Schema)\b/gm)) {
|
|
20187
|
+
exportedSchemas.add(match[1]);
|
|
20188
|
+
}
|
|
20189
|
+
if (exportedSchemas.size === 0) return;
|
|
20190
|
+
const relPath = (0, import_node_path2.relative)(outputDir, schemaPath).replace(/\\/g, "/");
|
|
20191
|
+
const schemaImportPath = relPath.startsWith(".") ? relPath : `./${relPath}`;
|
|
20192
|
+
const schemaImportPathJs = schemaImportPath.replace(/\.ts$/, ".js");
|
|
20193
|
+
const routerFile = buildRouterFile(spec, framework, {
|
|
20194
|
+
schemaNames: exportedSchemas,
|
|
20195
|
+
schemaImportPath: schemaImportPathJs
|
|
20196
|
+
});
|
|
20197
|
+
const routerPath = (0, import_node_path2.join)(outputDir, routerFile.filename);
|
|
20198
|
+
await (0, import_promises2.writeFile)(routerPath, await formatTs(routerFile.content, routerPath), "utf-8");
|
|
20199
|
+
console.log(`${prefix} \u2713 router.ts (with Zod validation for ${exportedSchemas.size} schema(s))`);
|
|
20200
|
+
}
|
|
20201
|
+
async function generate2(cwd, configPath) {
|
|
20202
|
+
console.log("Loading config...");
|
|
20203
|
+
const configs = await loadConfigs2(cwd, configPath);
|
|
20204
|
+
await runProjects(configs, (config, label) => generateOne(cwd, config, label));
|
|
19800
20205
|
}
|
|
19801
20206
|
|
|
19802
20207
|
// ../openapi-zod-ts/dist/cli-core.js
|