@codewithagents/openapi-server 1.8.0 → 1.10.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/README.md +239 -2
- package/dist/cli.cjs +835 -141
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +6 -2
- package/dist/generator.js.map +1 -1
- package/dist/plugins/router.d.ts +13 -0
- package/dist/plugins/router.d.ts.map +1 -1
- package/dist/plugins/router.js +934 -122
- package/dist/plugins/router.js.map +1 -1
- package/dist/plugins/service.d.ts +17 -1
- package/dist/plugins/service.d.ts.map +1 -1
- package/dist/plugins/service.js +90 -28
- package/dist/plugins/service.js.map +1 -1
- package/dist/plugins/shared.d.ts +46 -0
- package/dist/plugins/shared.d.ts.map +1 -1
- package/dist/plugins/shared.js +135 -12
- package/dist/plugins/shared.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -18844,10 +18844,14 @@ function parseServerConfig(raw, base) {
|
|
|
18844
18844
|
if (raw["input_schema"] !== void 0 && (typeof raw["input_schema"] !== "string" || !raw["input_schema"])) {
|
|
18845
18845
|
throw new Error('"input_schema" must be a non-empty string path to your Zod schema file');
|
|
18846
18846
|
}
|
|
18847
|
+
if (raw["context_type"] !== void 0 && (typeof raw["context_type"] !== "string" || !raw["context_type"])) {
|
|
18848
|
+
throw new Error('"context_type" must be a non-empty string TypeScript type name');
|
|
18849
|
+
}
|
|
18847
18850
|
return {
|
|
18848
18851
|
...base,
|
|
18849
18852
|
framework,
|
|
18850
|
-
input_schema: raw["input_schema"]
|
|
18853
|
+
input_schema: raw["input_schema"],
|
|
18854
|
+
context_type: raw["context_type"]
|
|
18851
18855
|
};
|
|
18852
18856
|
}
|
|
18853
18857
|
async function loadConfigs2(cwd, configPath) {
|
|
@@ -18943,6 +18947,7 @@ function schemaToTsType(schema) {
|
|
|
18943
18947
|
const s = schema;
|
|
18944
18948
|
if (s.type === "number" || s.type === "integer") return "number";
|
|
18945
18949
|
if (s.type === "boolean") return "boolean";
|
|
18950
|
+
if (s.type === "array") return "string[]";
|
|
18946
18951
|
return "string";
|
|
18947
18952
|
}
|
|
18948
18953
|
function getQueryParams(operation, spec) {
|
|
@@ -18953,35 +18958,137 @@ function getQueryParams(operation, spec) {
|
|
|
18953
18958
|
const resolved = resolveParam(p, spec);
|
|
18954
18959
|
if (resolved === void 0 || resolved.in !== "query") continue;
|
|
18955
18960
|
const schema = resolved.schema;
|
|
18956
|
-
|
|
18961
|
+
const resolvedStyle = resolved.style;
|
|
18962
|
+
const resolvedExplode = resolved.explode;
|
|
18963
|
+
const param = {
|
|
18957
18964
|
name: normalizeParamName(resolved.name),
|
|
18965
|
+
rawName: resolved.name,
|
|
18958
18966
|
tsType: schemaToTsType(schema),
|
|
18959
18967
|
required: resolved.required === true
|
|
18960
|
-
}
|
|
18968
|
+
};
|
|
18969
|
+
if (resolvedStyle === "deepObject" && schema !== void 0 && !isRef3(schema)) {
|
|
18970
|
+
const s = schema;
|
|
18971
|
+
if (s.type === "object" && s.properties !== void 0) {
|
|
18972
|
+
param.isDeepObject = true;
|
|
18973
|
+
param.deepObjectProperties = Object.entries(s.properties).map(([key, propSchema]) => ({
|
|
18974
|
+
key,
|
|
18975
|
+
tsType: schemaToTsType(propSchema)
|
|
18976
|
+
}));
|
|
18977
|
+
const propFields = param.deepObjectProperties.map((p2) => `${p2.key}?: ${p2.tsType}`).join("; ");
|
|
18978
|
+
param.tsType = `{ ${propFields} }`;
|
|
18979
|
+
}
|
|
18980
|
+
}
|
|
18981
|
+
if (!param.isDeepObject && schema !== void 0 && !isRef3(schema) && schema.type === "array" && resolvedExplode === false) {
|
|
18982
|
+
if (resolvedStyle === "spaceDelimited") {
|
|
18983
|
+
param.delimiterStyle = "ssv";
|
|
18984
|
+
} else if (resolvedStyle === "pipeDelimited") {
|
|
18985
|
+
param.delimiterStyle = "psv";
|
|
18986
|
+
} else {
|
|
18987
|
+
param.delimiterStyle = "csv";
|
|
18988
|
+
}
|
|
18989
|
+
}
|
|
18990
|
+
if (schema !== void 0 && !isRef3(schema)) {
|
|
18991
|
+
const s = schema;
|
|
18992
|
+
if (Array.isArray(s.enum)) param.enum = s.enum;
|
|
18993
|
+
if (typeof s.minimum === "number") param.minimum = s.minimum;
|
|
18994
|
+
if (typeof s.maximum === "number") param.maximum = s.maximum;
|
|
18995
|
+
if (typeof s.exclusiveMinimum === "number") param.exclusiveMinimum = s.exclusiveMinimum;
|
|
18996
|
+
if (typeof s.exclusiveMaximum === "number") param.exclusiveMaximum = s.exclusiveMaximum;
|
|
18997
|
+
if (typeof s.minLength === "number") param.minLength = s.minLength;
|
|
18998
|
+
if (typeof s.maxLength === "number") param.maxLength = s.maxLength;
|
|
18999
|
+
if (typeof s.pattern === "string") param.pattern = s.pattern;
|
|
19000
|
+
}
|
|
19001
|
+
result.push(param);
|
|
18961
19002
|
}
|
|
18962
19003
|
return result;
|
|
18963
19004
|
}
|
|
18964
19005
|
function getBodyInfo(operation) {
|
|
18965
19006
|
const requestBody = operation.requestBody;
|
|
18966
19007
|
if (requestBody === void 0) return void 0;
|
|
18967
|
-
if (isRef3(requestBody))
|
|
19008
|
+
if (isRef3(requestBody)) {
|
|
19009
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
19010
|
+
}
|
|
18968
19011
|
const rb = requestBody;
|
|
18969
19012
|
const content = rb.content;
|
|
18970
|
-
if (content === void 0)
|
|
19013
|
+
if (content === void 0) {
|
|
19014
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
19015
|
+
}
|
|
18971
19016
|
const jsonContent = content["application/json"];
|
|
18972
|
-
if (jsonContent
|
|
18973
|
-
|
|
18974
|
-
|
|
18975
|
-
|
|
19017
|
+
if (jsonContent !== void 0 && jsonContent.schema !== void 0) {
|
|
19018
|
+
const schema = jsonContent.schema;
|
|
19019
|
+
if (isRef3(schema)) {
|
|
19020
|
+
return {
|
|
19021
|
+
typeName: refToName(schema.$ref),
|
|
19022
|
+
contentType: "application/json",
|
|
19023
|
+
isSynthesized: false
|
|
19024
|
+
};
|
|
19025
|
+
}
|
|
19026
|
+
const operationId = operation.operationId;
|
|
19027
|
+
if (operationId !== void 0 && operationId.length > 0) {
|
|
19028
|
+
return { typeName: toTypeName(operationId), contentType: "application/json", isSynthesized: true };
|
|
19029
|
+
}
|
|
19030
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
19031
|
+
}
|
|
19032
|
+
const formContent = content["application/x-www-form-urlencoded"];
|
|
19033
|
+
if (formContent !== void 0) {
|
|
19034
|
+
const schema = formContent.schema;
|
|
19035
|
+
if (schema !== void 0 && isRef3(schema)) {
|
|
19036
|
+
return {
|
|
19037
|
+
typeName: refToName(schema.$ref),
|
|
19038
|
+
contentType: "application/x-www-form-urlencoded",
|
|
19039
|
+
isSynthesized: false
|
|
19040
|
+
};
|
|
19041
|
+
}
|
|
19042
|
+
const operationId = operation.operationId;
|
|
19043
|
+
if (operationId !== void 0 && operationId.length > 0) {
|
|
19044
|
+
return {
|
|
19045
|
+
typeName: toTypeName(operationId),
|
|
19046
|
+
contentType: "application/x-www-form-urlencoded",
|
|
19047
|
+
isSynthesized: true
|
|
19048
|
+
};
|
|
19049
|
+
}
|
|
19050
|
+
return { typeName: void 0, contentType: "application/x-www-form-urlencoded", isSynthesized: false };
|
|
19051
|
+
}
|
|
19052
|
+
const multipartContent = content["multipart/form-data"];
|
|
19053
|
+
if (multipartContent !== void 0) {
|
|
19054
|
+
const schema = multipartContent.schema;
|
|
19055
|
+
if (schema !== void 0 && isRef3(schema)) {
|
|
19056
|
+
return {
|
|
19057
|
+
typeName: refToName(schema.$ref),
|
|
19058
|
+
contentType: "multipart/form-data",
|
|
19059
|
+
isSynthesized: false
|
|
19060
|
+
};
|
|
19061
|
+
}
|
|
19062
|
+
const operationId = operation.operationId;
|
|
19063
|
+
if (operationId !== void 0 && operationId.length > 0) {
|
|
19064
|
+
return {
|
|
19065
|
+
typeName: toTypeName(operationId),
|
|
19066
|
+
contentType: "multipart/form-data",
|
|
19067
|
+
isSynthesized: true
|
|
19068
|
+
};
|
|
19069
|
+
}
|
|
19070
|
+
return { typeName: void 0, contentType: "multipart/form-data", isSynthesized: false };
|
|
19071
|
+
}
|
|
19072
|
+
const octetContent = content["application/octet-stream"];
|
|
19073
|
+
if (octetContent !== void 0) {
|
|
19074
|
+
return { typeName: void 0, contentType: "application/octet-stream", isSynthesized: false };
|
|
18976
19075
|
}
|
|
18977
|
-
return { typeName: void 0 };
|
|
19076
|
+
return { typeName: void 0, contentType: "application/json", isSynthesized: false };
|
|
18978
19077
|
}
|
|
18979
19078
|
|
|
18980
19079
|
// src/plugins/service.ts
|
|
19080
|
+
function collectContentfulTwoxxCodes(responses) {
|
|
19081
|
+
return Object.keys(responses).filter((k) => /^2\d\d$/.test(k) && k !== "204").sort();
|
|
19082
|
+
}
|
|
18981
19083
|
function getReturnInfo(operation) {
|
|
18982
19084
|
const responses = operation.responses;
|
|
18983
19085
|
if (responses === void 0) return { typeName: void 0, isArray: false, isVoid: true };
|
|
18984
|
-
|
|
19086
|
+
const contentfulCodes = collectContentfulTwoxxCodes(responses);
|
|
19087
|
+
const isMultiStatus = contentfulCodes.length > 1;
|
|
19088
|
+
const twoxxCodes = ["200", "201", ...Object.keys(responses).filter(
|
|
19089
|
+
(k) => /^2\d\d$/.test(k) && k !== "200" && k !== "201" && k !== "204"
|
|
19090
|
+
)];
|
|
19091
|
+
for (const code of twoxxCodes) {
|
|
18985
19092
|
const response = responses[code];
|
|
18986
19093
|
if (response === void 0) continue;
|
|
18987
19094
|
if (isRef3(response)) continue;
|
|
@@ -18989,28 +19096,37 @@ function getReturnInfo(operation) {
|
|
|
18989
19096
|
const content = resp.content;
|
|
18990
19097
|
if (content === void 0) continue;
|
|
18991
19098
|
const jsonContent = content["application/json"];
|
|
18992
|
-
if (jsonContent
|
|
18993
|
-
|
|
18994
|
-
|
|
18995
|
-
return {
|
|
18996
|
-
typeName: refToName(schema.$ref),
|
|
18997
|
-
isArray: false,
|
|
18998
|
-
isVoid: false
|
|
18999
|
-
};
|
|
19000
|
-
}
|
|
19001
|
-
const s = schema;
|
|
19002
|
-
if (s.type === "array") {
|
|
19003
|
-
const items = s.items;
|
|
19004
|
-
if (items !== void 0 && isRef3(items)) {
|
|
19099
|
+
if (jsonContent !== void 0 && jsonContent.schema !== void 0) {
|
|
19100
|
+
const schema = jsonContent.schema;
|
|
19101
|
+
if (isRef3(schema)) {
|
|
19005
19102
|
return {
|
|
19006
|
-
typeName: refToName(
|
|
19007
|
-
isArray:
|
|
19008
|
-
isVoid: false
|
|
19103
|
+
typeName: refToName(schema.$ref),
|
|
19104
|
+
isArray: false,
|
|
19105
|
+
isVoid: false,
|
|
19106
|
+
isMultiStatus
|
|
19009
19107
|
};
|
|
19010
19108
|
}
|
|
19011
|
-
|
|
19109
|
+
const s = schema;
|
|
19110
|
+
if (s.type === "array") {
|
|
19111
|
+
const items = s.items;
|
|
19112
|
+
if (items !== void 0 && isRef3(items)) {
|
|
19113
|
+
return {
|
|
19114
|
+
typeName: refToName(items.$ref),
|
|
19115
|
+
isArray: true,
|
|
19116
|
+
isVoid: false,
|
|
19117
|
+
isMultiStatus
|
|
19118
|
+
};
|
|
19119
|
+
}
|
|
19120
|
+
return { typeName: void 0, isArray: true, isVoid: false, isMultiStatus };
|
|
19121
|
+
}
|
|
19122
|
+
return { typeName: void 0, isArray: false, isVoid: false, isMultiStatus };
|
|
19123
|
+
}
|
|
19124
|
+
if (content["text/plain"] !== void 0) {
|
|
19125
|
+
return { typeName: void 0, isArray: false, isVoid: false, primitiveType: "string" };
|
|
19126
|
+
}
|
|
19127
|
+
if (content["application/octet-stream"] !== void 0) {
|
|
19128
|
+
return { typeName: void 0, isArray: false, isVoid: false, primitiveType: "Uint8Array" };
|
|
19012
19129
|
}
|
|
19013
|
-
return { typeName: void 0, isArray: false, isVoid: false };
|
|
19014
19130
|
}
|
|
19015
19131
|
if (responses["204"] !== void 0) {
|
|
19016
19132
|
return { typeName: void 0, isArray: false, isVoid: true };
|
|
@@ -19019,6 +19135,16 @@ function getReturnInfo(operation) {
|
|
|
19019
19135
|
}
|
|
19020
19136
|
function buildReturnType(info) {
|
|
19021
19137
|
if (info.isVoid) return "Promise<void>";
|
|
19138
|
+
if (info.primitiveType !== void 0) return `Promise<${info.primitiveType}>`;
|
|
19139
|
+
if (info.isMultiStatus === true) {
|
|
19140
|
+
let bodyType;
|
|
19141
|
+
if (info.typeName !== void 0) {
|
|
19142
|
+
bodyType = info.isArray ? `${info.typeName}[]` : info.typeName;
|
|
19143
|
+
} else {
|
|
19144
|
+
bodyType = info.isArray ? "unknown[]" : "unknown";
|
|
19145
|
+
}
|
|
19146
|
+
return `Promise<{ status: number; body: ${bodyType} }>`;
|
|
19147
|
+
}
|
|
19022
19148
|
if (info.typeName !== void 0) {
|
|
19023
19149
|
return info.isArray ? `Promise<${info.typeName}[]>` : `Promise<${info.typeName}>`;
|
|
19024
19150
|
}
|
|
@@ -19037,6 +19163,11 @@ function collectOperations(spec) {
|
|
|
19037
19163
|
const queryParams = getQueryParams(operation, spec);
|
|
19038
19164
|
const bodyInfo = getBodyInfo(operation);
|
|
19039
19165
|
const returnInfo = getReturnInfo(operation);
|
|
19166
|
+
if (returnInfo.typeName === void 0 && !returnInfo.isVoid && returnInfo.primitiveType === void 0) {
|
|
19167
|
+
console.warn(
|
|
19168
|
+
`${methodName} (${method.toUpperCase()} ${path}): response type is unknown, no named response schema could be resolved from the spec. Add a named $ref response schema to get a typed return type and enable runtime validation.`
|
|
19169
|
+
);
|
|
19170
|
+
}
|
|
19040
19171
|
operations.push({
|
|
19041
19172
|
methodName,
|
|
19042
19173
|
httpMethod: method,
|
|
@@ -19050,13 +19181,13 @@ function collectOperations(spec) {
|
|
|
19050
19181
|
}
|
|
19051
19182
|
return operations;
|
|
19052
19183
|
}
|
|
19053
|
-
function buildMethodSignature(op) {
|
|
19184
|
+
function buildMethodSignature(op, options) {
|
|
19054
19185
|
const args = [];
|
|
19055
19186
|
for (const p of op.pathParams) {
|
|
19056
19187
|
args.push(`${sanitizeOperationId2(p)}: string`);
|
|
19057
19188
|
}
|
|
19058
19189
|
if (op.bodyInfo !== void 0) {
|
|
19059
|
-
const typeName = op.bodyInfo.typeName
|
|
19190
|
+
const typeName = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? op.bodyInfo.typeName : "unknown";
|
|
19060
19191
|
args.push(`body: ${typeName}`);
|
|
19061
19192
|
}
|
|
19062
19193
|
if (op.queryParams.length > 0) {
|
|
@@ -19065,16 +19196,19 @@ function buildMethodSignature(op) {
|
|
|
19065
19196
|
const paramsToken = allOptional ? "params?" : "params";
|
|
19066
19197
|
args.push(`${paramsToken}: { ${fields} }`);
|
|
19067
19198
|
}
|
|
19199
|
+
if (options?.contextType !== void 0) {
|
|
19200
|
+
args.push("ctx: Ctx");
|
|
19201
|
+
}
|
|
19068
19202
|
const returnType = buildReturnType(op.returnInfo);
|
|
19069
19203
|
const argStr = args.join(", ");
|
|
19070
19204
|
return `${op.methodName}(${argStr}): ${returnType}`;
|
|
19071
19205
|
}
|
|
19072
|
-
function generateService(spec) {
|
|
19206
|
+
function generateService(spec, options) {
|
|
19073
19207
|
const serviceName = deriveServiceName(spec);
|
|
19074
19208
|
const operations = collectOperations(spec);
|
|
19075
19209
|
const importTypes = /* @__PURE__ */ new Set();
|
|
19076
19210
|
for (const op of operations) {
|
|
19077
|
-
if (op.bodyInfo?.typeName !== void 0) {
|
|
19211
|
+
if (op.bodyInfo?.typeName !== void 0 && !op.bodyInfo.isSynthesized) {
|
|
19078
19212
|
importTypes.add(op.bodyInfo.typeName);
|
|
19079
19213
|
}
|
|
19080
19214
|
if (op.returnInfo.typeName !== void 0) {
|
|
@@ -19089,10 +19223,11 @@ function generateService(spec) {
|
|
|
19089
19223
|
lines.push(`import type { ${sortedImports.join(", ")} } from './models.js'`);
|
|
19090
19224
|
lines.push("");
|
|
19091
19225
|
}
|
|
19092
|
-
|
|
19226
|
+
const interfaceDecl = options?.contextType !== void 0 ? `export interface ${serviceName}<Ctx = never> {` : `export interface ${serviceName} {`;
|
|
19227
|
+
lines.push(interfaceDecl);
|
|
19093
19228
|
for (const op of operations) {
|
|
19094
19229
|
lines.push(` /** ${op.httpMethod.toUpperCase()} ${op.path} */`);
|
|
19095
|
-
lines.push(` ${buildMethodSignature(op)}`);
|
|
19230
|
+
lines.push(` ${buildMethodSignature(op, options)}`);
|
|
19096
19231
|
}
|
|
19097
19232
|
lines.push("}");
|
|
19098
19233
|
lines.push("");
|
|
@@ -19124,6 +19259,21 @@ function formatToZodModifier(format) {
|
|
|
19124
19259
|
function pathParamZodExpr(schema) {
|
|
19125
19260
|
if (schema === void 0 || isRef3(schema)) return void 0;
|
|
19126
19261
|
const s = schema;
|
|
19262
|
+
if (s.type === "integer" || s.type === "number") {
|
|
19263
|
+
const hasMin = typeof s.minimum === "number";
|
|
19264
|
+
const hasMax = typeof s.maximum === "number";
|
|
19265
|
+
const hasExcMin = typeof s.exclusiveMinimum === "number";
|
|
19266
|
+
const hasExcMax = typeof s.exclusiveMaximum === "number";
|
|
19267
|
+
if (hasMin || hasMax || hasExcMin || hasExcMax) {
|
|
19268
|
+
let expr = "z.coerce.number()";
|
|
19269
|
+
if (hasMin) expr += `.min(${s.minimum})`;
|
|
19270
|
+
if (hasMax) expr += `.max(${s.maximum})`;
|
|
19271
|
+
if (hasExcMin) expr += `.gt(${s.exclusiveMinimum})`;
|
|
19272
|
+
if (hasExcMax) expr += `.lt(${s.exclusiveMaximum})`;
|
|
19273
|
+
return expr;
|
|
19274
|
+
}
|
|
19275
|
+
return void 0;
|
|
19276
|
+
}
|
|
19127
19277
|
if (s.type !== "string") return void 0;
|
|
19128
19278
|
const format = s.format;
|
|
19129
19279
|
if (format === void 0) return void 0;
|
|
@@ -19131,23 +19281,64 @@ function pathParamZodExpr(schema) {
|
|
|
19131
19281
|
if (modifier === "") return void 0;
|
|
19132
19282
|
return `z.string()${modifier}`;
|
|
19133
19283
|
}
|
|
19134
|
-
function
|
|
19284
|
+
function queryParamDelimitedZodBase(_param) {
|
|
19285
|
+
return "z.array(z.string())";
|
|
19286
|
+
}
|
|
19287
|
+
function queryParamDeepObjectZodBase(param) {
|
|
19288
|
+
const propFields = (param.deepObjectProperties ?? []).map((p) => {
|
|
19289
|
+
const coerced = p.tsType === "number" ? "z.coerce.number()" : "z.string()";
|
|
19290
|
+
return `${p.key}: ${coerced}.optional()`;
|
|
19291
|
+
});
|
|
19292
|
+
return `z.object({ ${propFields.join(", ")} })`;
|
|
19293
|
+
}
|
|
19294
|
+
function queryParamNumberZodBase(param) {
|
|
19295
|
+
let base = "z.coerce.number()";
|
|
19296
|
+
if (param.minimum !== void 0) base += `.min(${param.minimum})`;
|
|
19297
|
+
if (param.maximum !== void 0) base += `.max(${param.maximum})`;
|
|
19298
|
+
if (param.exclusiveMinimum !== void 0) base += `.gt(${param.exclusiveMinimum})`;
|
|
19299
|
+
if (param.exclusiveMaximum !== void 0) base += `.lt(${param.exclusiveMaximum})`;
|
|
19300
|
+
return base;
|
|
19301
|
+
}
|
|
19302
|
+
function queryParamStringZodBase(param) {
|
|
19303
|
+
let base;
|
|
19304
|
+
if (param.enum !== void 0 && param.enum.length > 0) {
|
|
19305
|
+
const members = param.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
19306
|
+
base = `z.enum([${members}])`;
|
|
19307
|
+
} else {
|
|
19308
|
+
base = "z.string()";
|
|
19309
|
+
}
|
|
19310
|
+
if (param.minLength !== void 0) base += `.min(${param.minLength})`;
|
|
19311
|
+
if (param.maxLength !== void 0) base += `.max(${param.maxLength})`;
|
|
19312
|
+
if (param.pattern !== void 0) base += `.regex(/${param.pattern}/)`;
|
|
19313
|
+
return base;
|
|
19314
|
+
}
|
|
19315
|
+
function queryParamZodExpr(param) {
|
|
19135
19316
|
let base;
|
|
19136
|
-
if (
|
|
19137
|
-
base =
|
|
19138
|
-
} else if (
|
|
19317
|
+
if (param.delimiterStyle !== void 0) {
|
|
19318
|
+
base = queryParamDelimitedZodBase(param);
|
|
19319
|
+
} else if (param.isDeepObject === true && param.deepObjectProperties !== void 0) {
|
|
19320
|
+
base = queryParamDeepObjectZodBase(param);
|
|
19321
|
+
} else if (param.tsType === "number") {
|
|
19322
|
+
base = queryParamNumberZodBase(param);
|
|
19323
|
+
} else if (param.tsType === "boolean") {
|
|
19139
19324
|
base = "z.boolean()";
|
|
19140
19325
|
} else {
|
|
19141
|
-
|
|
19142
|
-
const s = schema;
|
|
19143
|
-
const format = s.format;
|
|
19144
|
-
const modifier = format !== void 0 ? formatToZodModifier(format) : "";
|
|
19145
|
-
base = `z.string()${modifier}`;
|
|
19146
|
-
} else {
|
|
19147
|
-
base = "z.string()";
|
|
19148
|
-
}
|
|
19326
|
+
base = queryParamStringZodBase(param);
|
|
19149
19327
|
}
|
|
19150
|
-
return required ? base : `${base}.optional()`;
|
|
19328
|
+
return param.required ? base : `${base}.optional()`;
|
|
19329
|
+
}
|
|
19330
|
+
function headerParamZodExpr(param) {
|
|
19331
|
+
let base;
|
|
19332
|
+
if (param.enum !== void 0 && param.enum.length > 0) {
|
|
19333
|
+
const members = param.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
19334
|
+
base = `z.enum([${members}])`;
|
|
19335
|
+
} else {
|
|
19336
|
+
base = "z.string()";
|
|
19337
|
+
}
|
|
19338
|
+
if (param.minLength !== void 0) base += `.min(${param.minLength})`;
|
|
19339
|
+
if (param.maxLength !== void 0) base += `.max(${param.maxLength})`;
|
|
19340
|
+
if (param.pattern !== void 0) base += `.regex(/${param.pattern}/)`;
|
|
19341
|
+
return param.required ? base : `${base}.optional()`;
|
|
19151
19342
|
}
|
|
19152
19343
|
function getPathParamValidations(operation, spec, rawPathParamNames) {
|
|
19153
19344
|
const parameters = operation.parameters;
|
|
@@ -19167,6 +19358,19 @@ function getPathParamValidations(operation, spec, rawPathParamNames) {
|
|
|
19167
19358
|
}
|
|
19168
19359
|
return result;
|
|
19169
19360
|
}
|
|
19361
|
+
function cookieParamZodExpr(param) {
|
|
19362
|
+
let base;
|
|
19363
|
+
if (param.enum !== void 0 && param.enum.length > 0) {
|
|
19364
|
+
const members = param.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
19365
|
+
base = `z.enum([${members}])`;
|
|
19366
|
+
} else {
|
|
19367
|
+
base = "z.string()";
|
|
19368
|
+
}
|
|
19369
|
+
if (param.minLength !== void 0) base += `.min(${param.minLength})`;
|
|
19370
|
+
if (param.maxLength !== void 0) base += `.max(${param.maxLength})`;
|
|
19371
|
+
if (param.pattern !== void 0) base += `.regex(/${param.pattern}/)`;
|
|
19372
|
+
return param.required ? base : `${base}.optional()`;
|
|
19373
|
+
}
|
|
19170
19374
|
function getHeaderParams(operation, spec) {
|
|
19171
19375
|
const parameters = operation.parameters;
|
|
19172
19376
|
if (parameters === void 0) return [];
|
|
@@ -19174,21 +19378,74 @@ function getHeaderParams(operation, spec) {
|
|
|
19174
19378
|
for (const p of parameters) {
|
|
19175
19379
|
const resolved = resolveParam(p, spec);
|
|
19176
19380
|
if (resolved === void 0 || resolved.in !== "header") continue;
|
|
19177
|
-
|
|
19381
|
+
const param = {
|
|
19178
19382
|
rawName: resolved.name,
|
|
19179
19383
|
required: resolved.required === true
|
|
19180
|
-
}
|
|
19384
|
+
};
|
|
19385
|
+
const schema = resolved.schema;
|
|
19386
|
+
if (schema !== void 0 && !isRef3(schema)) {
|
|
19387
|
+
const s = schema;
|
|
19388
|
+
if (Array.isArray(s.enum)) param.enum = s.enum;
|
|
19389
|
+
if (typeof s.minLength === "number") param.minLength = s.minLength;
|
|
19390
|
+
if (typeof s.maxLength === "number") param.maxLength = s.maxLength;
|
|
19391
|
+
if (typeof s.pattern === "string") param.pattern = s.pattern;
|
|
19392
|
+
}
|
|
19393
|
+
result.push(param);
|
|
19181
19394
|
}
|
|
19182
19395
|
return result;
|
|
19183
19396
|
}
|
|
19397
|
+
function getCookieParams(operation, spec) {
|
|
19398
|
+
const parameters = operation.parameters;
|
|
19399
|
+
if (parameters === void 0) return [];
|
|
19400
|
+
const result = [];
|
|
19401
|
+
for (const p of parameters) {
|
|
19402
|
+
const resolved = resolveParam(p, spec);
|
|
19403
|
+
if (resolved === void 0 || resolved.in !== "cookie") continue;
|
|
19404
|
+
const param = {
|
|
19405
|
+
rawName: resolved.name,
|
|
19406
|
+
required: resolved.required === true
|
|
19407
|
+
};
|
|
19408
|
+
const schema = resolved.schema;
|
|
19409
|
+
if (schema !== void 0 && !isRef3(schema)) {
|
|
19410
|
+
const s = schema;
|
|
19411
|
+
if (Array.isArray(s.enum)) param.enum = s.enum;
|
|
19412
|
+
if (typeof s.minLength === "number") param.minLength = s.minLength;
|
|
19413
|
+
if (typeof s.maxLength === "number") param.maxLength = s.maxLength;
|
|
19414
|
+
if (typeof s.pattern === "string") param.pattern = s.pattern;
|
|
19415
|
+
}
|
|
19416
|
+
result.push(param);
|
|
19417
|
+
}
|
|
19418
|
+
return result;
|
|
19419
|
+
}
|
|
19420
|
+
function queryParamHasConstraints(q) {
|
|
19421
|
+
const constraintFields = [
|
|
19422
|
+
q.enum,
|
|
19423
|
+
q.minimum,
|
|
19424
|
+
q.maximum,
|
|
19425
|
+
q.exclusiveMinimum,
|
|
19426
|
+
q.exclusiveMaximum,
|
|
19427
|
+
q.minLength,
|
|
19428
|
+
q.maxLength,
|
|
19429
|
+
q.pattern,
|
|
19430
|
+
q.delimiterStyle
|
|
19431
|
+
];
|
|
19432
|
+
return constraintFields.some((f) => f !== void 0) || q.isDeepObject === true;
|
|
19433
|
+
}
|
|
19184
19434
|
function queryParamsNeedValidation(queryParams) {
|
|
19185
|
-
return queryParams.some(
|
|
19435
|
+
return queryParams.some(
|
|
19436
|
+
(q) => q.required || q.tsType !== "string" || queryParamHasConstraints(q)
|
|
19437
|
+
);
|
|
19438
|
+
}
|
|
19439
|
+
function delimiterChar(style) {
|
|
19440
|
+
if (style === "ssv") return " ";
|
|
19441
|
+
if (style === "psv") return "|";
|
|
19442
|
+
return ",";
|
|
19186
19443
|
}
|
|
19187
19444
|
function emitQueryValidation(lines, queryParams, indent) {
|
|
19188
19445
|
const inner = `${indent} `;
|
|
19189
19446
|
const fieldIndent = `${indent} `;
|
|
19190
19447
|
const fields = queryParams.map((q) => {
|
|
19191
|
-
const expr =
|
|
19448
|
+
const expr = queryParamZodExpr(q);
|
|
19192
19449
|
return `${fieldIndent}${q.name}: ${expr}`;
|
|
19193
19450
|
}).join(",\n");
|
|
19194
19451
|
lines.push(`${inner}// Validate query parameters: returns 422 with Zod issues on failure`);
|
|
@@ -19209,7 +19466,7 @@ function emitPathValidation(lines, validations, indent, framework) {
|
|
|
19209
19466
|
if (framework === "hono") {
|
|
19210
19467
|
access = `c.req.param(${JSON.stringify(v.rawName)})`;
|
|
19211
19468
|
} else if (framework === "express") {
|
|
19212
|
-
access = `req.params[${JSON.stringify(v.rawName)}]`;
|
|
19469
|
+
access = `req.params[${JSON.stringify(v.rawName)}] as string`;
|
|
19213
19470
|
} else {
|
|
19214
19471
|
access = /[^a-zA-Z0-9_$]/.test(v.rawName) ? `req.params[${JSON.stringify(v.rawName)}]` : `req.params.${v.rawName}`;
|
|
19215
19472
|
}
|
|
@@ -19227,18 +19484,19 @@ function emitHeaderValidation(lines, headerParams, indent, framework) {
|
|
|
19227
19484
|
const fieldIndent = `${indent} `;
|
|
19228
19485
|
const schemaFields = headerParams.map((h) => {
|
|
19229
19486
|
const key = JSON.stringify(h.rawName);
|
|
19230
|
-
const expr = h
|
|
19487
|
+
const expr = headerParamZodExpr(h);
|
|
19231
19488
|
return `${fieldIndent}${key}: ${expr}`;
|
|
19232
19489
|
}).join(",\n");
|
|
19233
19490
|
const rawFields = headerParams.map((h) => {
|
|
19234
19491
|
const key = JSON.stringify(h.rawName);
|
|
19492
|
+
const lookupKey = JSON.stringify(h.rawName.toLowerCase());
|
|
19235
19493
|
let access;
|
|
19236
19494
|
if (framework === "hono") {
|
|
19237
19495
|
access = `c.req.header(${key})`;
|
|
19238
19496
|
} else if (framework === "express") {
|
|
19239
|
-
access = `req.headers[${
|
|
19497
|
+
access = `req.headers[${lookupKey}] as string | undefined`;
|
|
19240
19498
|
} else {
|
|
19241
|
-
access = `req.headers[${
|
|
19499
|
+
access = `req.headers[${lookupKey}]`;
|
|
19242
19500
|
}
|
|
19243
19501
|
return `${fieldIndent}${key}: ${access}`;
|
|
19244
19502
|
}).join(",\n");
|
|
@@ -19249,24 +19507,124 @@ function emitHeaderValidation(lines, headerParams, indent, framework) {
|
|
|
19249
19507
|
lines.push(rawFields);
|
|
19250
19508
|
lines.push(`${inner}})`);
|
|
19251
19509
|
}
|
|
19510
|
+
function emitCookieValidation(lines, cookieParams, indent, framework) {
|
|
19511
|
+
const inner = `${indent} `;
|
|
19512
|
+
const fieldIndent = `${indent} `;
|
|
19513
|
+
const schemaFields = cookieParams.map((ck) => {
|
|
19514
|
+
const key = JSON.stringify(ck.rawName);
|
|
19515
|
+
const expr = cookieParamZodExpr(ck);
|
|
19516
|
+
return `${fieldIndent}${key}: ${expr}`;
|
|
19517
|
+
}).join(",\n");
|
|
19518
|
+
const rawFields = cookieParams.map((ck) => {
|
|
19519
|
+
const key = JSON.stringify(ck.rawName);
|
|
19520
|
+
let access;
|
|
19521
|
+
if (framework === "hono") {
|
|
19522
|
+
access = `getCookie(c, ${key})`;
|
|
19523
|
+
} else if (framework === "express") {
|
|
19524
|
+
access = `req.cookies[${key}] as string | undefined`;
|
|
19525
|
+
} else {
|
|
19526
|
+
access = `req.cookies[${key}]`;
|
|
19527
|
+
}
|
|
19528
|
+
return `${fieldIndent}${key}: ${access}`;
|
|
19529
|
+
}).join(",\n");
|
|
19530
|
+
lines.push(`${inner}// Validate request cookies: returns 422 with Zod issues on failure`);
|
|
19531
|
+
lines.push(`${inner}const _ckv = z.object({`);
|
|
19532
|
+
lines.push(schemaFields);
|
|
19533
|
+
lines.push(`${inner}}).safeParse({`);
|
|
19534
|
+
lines.push(rawFields);
|
|
19535
|
+
lines.push(`${inner}})`);
|
|
19536
|
+
}
|
|
19252
19537
|
function response200IsVoid(resp) {
|
|
19253
19538
|
if (isRef3(resp)) return false;
|
|
19254
19539
|
const r = resp;
|
|
19255
19540
|
const content = r.content;
|
|
19256
19541
|
return content === void 0 || Object.keys(content).length === 0;
|
|
19257
19542
|
}
|
|
19543
|
+
function detectResponseContentType(resp) {
|
|
19544
|
+
if (isRef3(resp)) return "application/json";
|
|
19545
|
+
const r = resp;
|
|
19546
|
+
const content = r.content;
|
|
19547
|
+
if (content === void 0) return "application/json";
|
|
19548
|
+
if ("text/plain" in content) return "text/plain";
|
|
19549
|
+
if ("application/octet-stream" in content) return "application/octet-stream";
|
|
19550
|
+
return "application/json";
|
|
19551
|
+
}
|
|
19258
19552
|
function getResponseStatus(operation, httpMethod) {
|
|
19259
19553
|
const responses = operation.responses;
|
|
19260
19554
|
if (responses === void 0) {
|
|
19261
|
-
return httpMethod === "delete" ? { status: 204, isVoid: true } : { status: 200, isVoid: false };
|
|
19555
|
+
return httpMethod === "delete" ? { status: 204, isVoid: true, responseContentType: "application/json" } : { status: 200, isVoid: false, responseContentType: "application/json" };
|
|
19556
|
+
}
|
|
19557
|
+
const contentfulTwoxxKeys = Object.keys(responses).filter((k) => /^2\d\d$/.test(k) && k !== "204").sort();
|
|
19558
|
+
if (contentfulTwoxxKeys.length > 1) {
|
|
19559
|
+
return {
|
|
19560
|
+
status: 200,
|
|
19561
|
+
isVoid: false,
|
|
19562
|
+
responseContentType: "application/json",
|
|
19563
|
+
isMultiStatus: true
|
|
19564
|
+
};
|
|
19565
|
+
}
|
|
19566
|
+
if (responses["201"] !== void 0) {
|
|
19567
|
+
return {
|
|
19568
|
+
status: 201,
|
|
19569
|
+
isVoid: false,
|
|
19570
|
+
responseContentType: detectResponseContentType(responses["201"])
|
|
19571
|
+
};
|
|
19572
|
+
}
|
|
19573
|
+
if (responses["204"] !== void 0) {
|
|
19574
|
+
return { status: 204, isVoid: true, responseContentType: "application/json" };
|
|
19262
19575
|
}
|
|
19263
|
-
if (responses["201"] !== void 0) return { status: 201, isVoid: false };
|
|
19264
|
-
if (responses["204"] !== void 0) return { status: 204, isVoid: true };
|
|
19265
19576
|
if (responses["200"] !== void 0) {
|
|
19266
|
-
if (response200IsVoid(responses["200"]))
|
|
19267
|
-
|
|
19577
|
+
if (response200IsVoid(responses["200"])) {
|
|
19578
|
+
return { status: 204, isVoid: true, responseContentType: "application/json" };
|
|
19579
|
+
}
|
|
19580
|
+
return {
|
|
19581
|
+
status: 200,
|
|
19582
|
+
isVoid: false,
|
|
19583
|
+
responseContentType: detectResponseContentType(responses["200"])
|
|
19584
|
+
};
|
|
19268
19585
|
}
|
|
19269
|
-
|
|
19586
|
+
const twoxxKeys = Object.keys(responses).filter(
|
|
19587
|
+
(k) => /^2\d\d$/.test(k) && k !== "200" && k !== "201" && k !== "204"
|
|
19588
|
+
);
|
|
19589
|
+
if (twoxxKeys.length === 1) {
|
|
19590
|
+
const code = parseInt(twoxxKeys[0], 10);
|
|
19591
|
+
const resp = responses[twoxxKeys[0]];
|
|
19592
|
+
const isVoid = isRef3(resp) ? false : (() => {
|
|
19593
|
+
const r = resp;
|
|
19594
|
+
const content = r.content;
|
|
19595
|
+
return content === void 0 || Object.keys(content).length === 0;
|
|
19596
|
+
})();
|
|
19597
|
+
return { status: code, isVoid, responseContentType: detectResponseContentType(resp) };
|
|
19598
|
+
}
|
|
19599
|
+
return httpMethod === "delete" ? { status: 204, isVoid: true, responseContentType: "application/json" } : { status: 200, isVoid: false, responseContentType: "application/json" };
|
|
19600
|
+
}
|
|
19601
|
+
function getResponseTypeName(operation) {
|
|
19602
|
+
const responses = operation.responses;
|
|
19603
|
+
if (responses === void 0) return void 0;
|
|
19604
|
+
const priority = ["200", "201", ...Object.keys(responses).filter(
|
|
19605
|
+
(k) => /^2\d\d$/.test(k) && k !== "200" && k !== "201" && k !== "204"
|
|
19606
|
+
)];
|
|
19607
|
+
for (const code of priority) {
|
|
19608
|
+
const response = responses[code];
|
|
19609
|
+
if (response === void 0 || isRef3(response)) continue;
|
|
19610
|
+
const resp = response;
|
|
19611
|
+
const content = resp.content;
|
|
19612
|
+
if (content === void 0) continue;
|
|
19613
|
+
const jsonContent = content["application/json"];
|
|
19614
|
+
if (jsonContent === void 0 || jsonContent.schema === void 0) continue;
|
|
19615
|
+
const schema = jsonContent.schema;
|
|
19616
|
+
if (isRef3(schema)) {
|
|
19617
|
+
return { typeName: refToName(schema.$ref), isArray: false };
|
|
19618
|
+
}
|
|
19619
|
+
const s = schema;
|
|
19620
|
+
if (s.type === "array" && s.items !== void 0 && isRef3(s.items)) {
|
|
19621
|
+
return {
|
|
19622
|
+
typeName: refToName(s.items.$ref),
|
|
19623
|
+
isArray: true
|
|
19624
|
+
};
|
|
19625
|
+
}
|
|
19626
|
+
}
|
|
19627
|
+
return void 0;
|
|
19270
19628
|
}
|
|
19271
19629
|
function collectOperations2(spec) {
|
|
19272
19630
|
const paths = spec.paths;
|
|
@@ -19281,8 +19639,10 @@ function collectOperations2(spec) {
|
|
|
19281
19639
|
const pathParamValidations = getPathParamValidations(operation, spec, pathParams);
|
|
19282
19640
|
const queryParams = getQueryParams(operation, spec);
|
|
19283
19641
|
const headerParams = getHeaderParams(operation, spec);
|
|
19642
|
+
const cookieParams = getCookieParams(operation, spec);
|
|
19284
19643
|
const bodyInfo = getBodyInfo(operation);
|
|
19285
19644
|
const responseStatus = getResponseStatus(operation, method);
|
|
19645
|
+
const responseTypeInfo = getResponseTypeName(operation);
|
|
19286
19646
|
operations.push({
|
|
19287
19647
|
methodName,
|
|
19288
19648
|
httpMethod: method,
|
|
@@ -19292,8 +19652,11 @@ function collectOperations2(spec) {
|
|
|
19292
19652
|
pathParamValidations,
|
|
19293
19653
|
queryParams,
|
|
19294
19654
|
headerParams,
|
|
19655
|
+
cookieParams,
|
|
19295
19656
|
bodyInfo,
|
|
19296
|
-
responseStatus
|
|
19657
|
+
responseStatus,
|
|
19658
|
+
responseTypeName: responseTypeInfo?.typeName,
|
|
19659
|
+
responseIsArray: responseTypeInfo?.isArray
|
|
19297
19660
|
});
|
|
19298
19661
|
}
|
|
19299
19662
|
}
|
|
@@ -19302,7 +19665,9 @@ function collectOperations2(spec) {
|
|
|
19302
19665
|
function collectSortedBodyTypes(operations) {
|
|
19303
19666
|
const bodyTypes = /* @__PURE__ */ new Set();
|
|
19304
19667
|
for (const op of operations) {
|
|
19305
|
-
if (op.bodyInfo?.typeName !== void 0
|
|
19668
|
+
if (op.bodyInfo?.typeName !== void 0 && !op.bodyInfo.isSynthesized) {
|
|
19669
|
+
bodyTypes.add(op.bodyInfo.typeName);
|
|
19670
|
+
}
|
|
19306
19671
|
}
|
|
19307
19672
|
return Array.from(bodyTypes).sort();
|
|
19308
19673
|
}
|
|
@@ -19316,13 +19681,27 @@ function collectUsedSchemaNames(operations, schemaNames) {
|
|
|
19316
19681
|
}
|
|
19317
19682
|
return used;
|
|
19318
19683
|
}
|
|
19684
|
+
function collectUsedResponseSchemaNames(operations, schemaNames) {
|
|
19685
|
+
const used = /* @__PURE__ */ new Set();
|
|
19686
|
+
for (const op of operations) {
|
|
19687
|
+
if (op.responseTypeName === void 0) continue;
|
|
19688
|
+
if (op.responseStatus.isMultiStatus === true) continue;
|
|
19689
|
+
const schemaName = `${op.responseTypeName}Schema`;
|
|
19690
|
+
if (schemaNames.has(schemaName)) used.add(schemaName);
|
|
19691
|
+
}
|
|
19692
|
+
return used;
|
|
19693
|
+
}
|
|
19319
19694
|
function collectGeneratorSetup(operations, options) {
|
|
19320
19695
|
const sortedBodyTypes = collectSortedBodyTypes(operations);
|
|
19321
19696
|
const usedSchemaNames = options?.schemaNames !== void 0 ? collectUsedSchemaNames(operations, options.schemaNames) : /* @__PURE__ */ new Set();
|
|
19322
|
-
const
|
|
19323
|
-
|
|
19697
|
+
const usedResponseSchemaNames = options?.schemaNames !== void 0 ? collectUsedResponseSchemaNames(operations, options.schemaNames) : /* @__PURE__ */ new Set();
|
|
19698
|
+
const hasArrayResponseSchema = usedResponseSchemaNames.size > 0 && operations.some(
|
|
19699
|
+
(op) => op.responseIsArray === true && op.responseTypeName !== void 0 && usedResponseSchemaNames.has(`${op.responseTypeName}Schema`)
|
|
19700
|
+
);
|
|
19701
|
+
const needsZod = usedSchemaNames.size > 0 && options?.schemaImportPath !== void 0 || operationsNeedZodForParams(operations) || usedResponseSchemaNames.size > 0 && options?.schemaImportPath !== void 0 && hasArrayResponseSchema;
|
|
19702
|
+
return { sortedBodyTypes, usedSchemaNames, usedResponseSchemaNames, needsZod };
|
|
19324
19703
|
}
|
|
19325
|
-
function buildRouteHandler(op, indent, schemaNames) {
|
|
19704
|
+
function buildRouteHandler(op, indent, schemaNames, contextType) {
|
|
19326
19705
|
const lines = [];
|
|
19327
19706
|
lines.push(`${indent}app.${op.httpMethod}(${JSON.stringify(op.honoPath)}, async (c) => {`);
|
|
19328
19707
|
if (op.pathParamValidations.length > 0) {
|
|
@@ -19334,7 +19713,27 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
19334
19713
|
lines.push(`${indent} }`);
|
|
19335
19714
|
}
|
|
19336
19715
|
if (op.queryParams.length > 0) {
|
|
19716
|
+
const deepObjectParams = op.queryParams.filter((q) => q.isDeepObject === true);
|
|
19717
|
+
if (deepObjectParams.length > 0) {
|
|
19718
|
+
lines.push(`${indent} const _dq = c.req.queries()`);
|
|
19719
|
+
for (const q of deepObjectParams) {
|
|
19720
|
+
const prefixLen = q.rawName.length + 1;
|
|
19721
|
+
const bracketPrefix = q.rawName + "[";
|
|
19722
|
+
lines.push(`${indent} const ${q.name} = Object.fromEntries(`);
|
|
19723
|
+
lines.push(
|
|
19724
|
+
`${indent} Object.entries(_dq).filter(([k]) => k.startsWith('${bracketPrefix}') && k.endsWith(']')).map(([k, vs]) => [k.slice(${prefixLen}, -1), vs[0]])`
|
|
19725
|
+
);
|
|
19726
|
+
lines.push(`${indent} )`);
|
|
19727
|
+
}
|
|
19728
|
+
}
|
|
19337
19729
|
const fields = op.queryParams.map((q) => {
|
|
19730
|
+
if (q.isDeepObject === true) {
|
|
19731
|
+
return ` ${q.name}`;
|
|
19732
|
+
}
|
|
19733
|
+
if (q.delimiterStyle !== void 0) {
|
|
19734
|
+
const delim = JSON.stringify(delimiterChar(q.delimiterStyle));
|
|
19735
|
+
return ` ${q.name}: c.req.query('${q.rawName}') !== undefined ? c.req.query('${q.rawName}')!.split(${delim}) : undefined`;
|
|
19736
|
+
}
|
|
19338
19737
|
if (q.tsType === "number") {
|
|
19339
19738
|
return ` ${q.name}: c.req.query('${q.name}') !== undefined ? Number(c.req.query('${q.name}')) : undefined`;
|
|
19340
19739
|
}
|
|
@@ -19360,10 +19759,42 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
19360
19759
|
);
|
|
19361
19760
|
lines.push(`${indent} }`);
|
|
19362
19761
|
}
|
|
19762
|
+
if (op.cookieParams.length > 0) {
|
|
19763
|
+
emitCookieValidation(lines, op.cookieParams, indent, "hono");
|
|
19764
|
+
lines.push(`${indent} if (!_ckv.success) {`);
|
|
19765
|
+
lines.push(
|
|
19766
|
+
`${indent} return c.json({ error: 'Invalid request cookies', issues: _ckv.error.issues }, 422)`
|
|
19767
|
+
);
|
|
19768
|
+
lines.push(`${indent} }`);
|
|
19769
|
+
}
|
|
19363
19770
|
let bodyVarName = "body";
|
|
19364
19771
|
if (op.bodyInfo !== void 0) {
|
|
19365
|
-
const
|
|
19366
|
-
|
|
19772
|
+
const typeDecl = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? op.bodyInfo.typeName : "unknown";
|
|
19773
|
+
if (op.bodyInfo.contentType === "application/x-www-form-urlencoded") {
|
|
19774
|
+
lines.push(`${indent} const _ct = c.req.header('content-type') ?? ''`);
|
|
19775
|
+
lines.push(
|
|
19776
|
+
`${indent} if (!_ct.toLowerCase().startsWith('application/x-www-form-urlencoded')) {`
|
|
19777
|
+
);
|
|
19778
|
+
lines.push(`${indent} return c.json({ error: 'Unsupported Media Type' }, 415)`);
|
|
19779
|
+
lines.push(`${indent} }`);
|
|
19780
|
+
lines.push(`${indent} const body: unknown = await c.req.parseBody()`);
|
|
19781
|
+
} else if (op.bodyInfo.contentType === "multipart/form-data") {
|
|
19782
|
+
lines.push(
|
|
19783
|
+
`${indent} // multipart/form-data: parseBody({ all: true }) collects repeated keys into arrays.`
|
|
19784
|
+
);
|
|
19785
|
+
lines.push(`${indent} const body: unknown = await c.req.parseBody({ all: true })`);
|
|
19786
|
+
} else {
|
|
19787
|
+
lines.push(`${indent} const _ct = c.req.header('content-type') ?? ''`);
|
|
19788
|
+
lines.push(`${indent} if (!_ct.toLowerCase().startsWith('application/json')) {`);
|
|
19789
|
+
lines.push(`${indent} return c.json({ error: 'Unsupported Media Type' }, 415)`);
|
|
19790
|
+
lines.push(`${indent} }`);
|
|
19791
|
+
lines.push(`${indent} let body: ${typeDecl}`);
|
|
19792
|
+
lines.push(`${indent} try {`);
|
|
19793
|
+
lines.push(`${indent} body = JSON.parse(await c.req.text()) as ${typeDecl}`);
|
|
19794
|
+
lines.push(`${indent} } catch {`);
|
|
19795
|
+
lines.push(`${indent} return c.json({ error: 'Invalid JSON body' }, 400)`);
|
|
19796
|
+
lines.push(`${indent} }`);
|
|
19797
|
+
}
|
|
19367
19798
|
const schemaName = op.bodyInfo.typeName !== void 0 ? `${op.bodyInfo.typeName}Schema` : void 0;
|
|
19368
19799
|
if (schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName)) {
|
|
19369
19800
|
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
@@ -19373,7 +19804,7 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
19373
19804
|
`${indent} return c.json({ error: 'Invalid request body', issues: parseResult.error.issues }, 422)`
|
|
19374
19805
|
);
|
|
19375
19806
|
lines.push(`${indent} }`);
|
|
19376
|
-
lines.push(`${indent} const validatedBody = parseResult.data`);
|
|
19807
|
+
lines.push(`${indent} const validatedBody = parseResult.data as ${typeDecl}`);
|
|
19377
19808
|
bodyVarName = "validatedBody";
|
|
19378
19809
|
}
|
|
19379
19810
|
}
|
|
@@ -19385,21 +19816,54 @@ function buildRouteHandler(op, indent, schemaNames) {
|
|
|
19385
19816
|
serviceArgs.push(bodyVarName);
|
|
19386
19817
|
}
|
|
19387
19818
|
if (op.queryParams.length > 0) {
|
|
19388
|
-
serviceArgs.push("params");
|
|
19819
|
+
serviceArgs.push(queryParamsNeedValidation(op.queryParams) ? "_qv.data" : "params");
|
|
19820
|
+
}
|
|
19821
|
+
if (contextType !== void 0) {
|
|
19822
|
+
serviceArgs.push("c");
|
|
19389
19823
|
}
|
|
19390
19824
|
const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
|
|
19825
|
+
lines.push(`${indent} try {`);
|
|
19391
19826
|
if (op.responseStatus.isVoid) {
|
|
19392
|
-
lines.push(`${indent}
|
|
19393
|
-
lines.push(`${indent}
|
|
19394
|
-
} else if (op.responseStatus.
|
|
19395
|
-
lines.push(`${indent}
|
|
19827
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
19828
|
+
lines.push(`${indent} return new Response(null, { status: ${op.responseStatus.status} })`);
|
|
19829
|
+
} else if (op.responseStatus.isMultiStatus === true) {
|
|
19830
|
+
lines.push(`${indent} const _envelope = await ${serviceCall}`);
|
|
19831
|
+
lines.push(`${indent} return c.json(_envelope.body, _envelope.status as any)`);
|
|
19832
|
+
} else if (op.responseStatus.responseContentType === "text/plain") {
|
|
19833
|
+
if (op.responseStatus.status === 200) {
|
|
19834
|
+
lines.push(`${indent} return c.text(await ${serviceCall})`);
|
|
19835
|
+
} else {
|
|
19836
|
+
lines.push(`${indent} return c.text(await ${serviceCall}, ${op.responseStatus.status})`);
|
|
19837
|
+
}
|
|
19838
|
+
} else if (op.responseStatus.responseContentType === "application/octet-stream") {
|
|
19839
|
+
if (op.responseStatus.status === 200) {
|
|
19840
|
+
lines.push(`${indent} const _result = await ${serviceCall}`);
|
|
19841
|
+
lines.push(
|
|
19842
|
+
`${indent} return new Response(_result, { headers: { 'content-type': 'application/octet-stream' } })`
|
|
19843
|
+
);
|
|
19844
|
+
} else {
|
|
19845
|
+
lines.push(`${indent} const _result = await ${serviceCall}`);
|
|
19846
|
+
lines.push(
|
|
19847
|
+
`${indent} return new Response(_result, { status: ${op.responseStatus.status}, headers: { 'content-type': 'application/octet-stream' } })`
|
|
19848
|
+
);
|
|
19849
|
+
}
|
|
19850
|
+
} else if (op.responseStatus.status === 200) {
|
|
19851
|
+
lines.push(`${indent} return c.json(await ${serviceCall})`);
|
|
19396
19852
|
} else {
|
|
19397
|
-
lines.push(`${indent}
|
|
19853
|
+
lines.push(`${indent} return c.json(await ${serviceCall}, ${op.responseStatus.status})`);
|
|
19398
19854
|
}
|
|
19855
|
+
lines.push(`${indent} } catch (err) {`);
|
|
19856
|
+
lines.push(`${indent} if (err instanceof HttpError) {`);
|
|
19857
|
+
lines.push(
|
|
19858
|
+
`${indent} return new Response(JSON.stringify({ error: err.message }), { status: err.status, headers: { 'content-type': 'application/json' } })`
|
|
19859
|
+
);
|
|
19860
|
+
lines.push(`${indent} }`);
|
|
19861
|
+
lines.push(`${indent} throw err`);
|
|
19862
|
+
lines.push(`${indent} }`);
|
|
19399
19863
|
lines.push(`${indent}})`);
|
|
19400
19864
|
return lines.join("\n");
|
|
19401
19865
|
}
|
|
19402
|
-
function buildExpressRouteHandler(op, indent, schemaNames) {
|
|
19866
|
+
function buildExpressRouteHandler(op, indent, schemaNames, contextType) {
|
|
19403
19867
|
const lines = [];
|
|
19404
19868
|
lines.push(
|
|
19405
19869
|
`${indent}router.${op.httpMethod}(${JSON.stringify(op.honoPath)}, async (req: Request, res: Response) => {`
|
|
@@ -19414,6 +19878,13 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
|
|
|
19414
19878
|
}
|
|
19415
19879
|
if (op.queryParams.length > 0) {
|
|
19416
19880
|
const fields = op.queryParams.map((q) => {
|
|
19881
|
+
if (q.isDeepObject === true) {
|
|
19882
|
+
return ` ${q.name}: (req.query['${q.rawName}'] ?? {}) as Record<string, string | undefined>`;
|
|
19883
|
+
}
|
|
19884
|
+
if (q.delimiterStyle !== void 0) {
|
|
19885
|
+
const delim = JSON.stringify(delimiterChar(q.delimiterStyle));
|
|
19886
|
+
return ` ${q.name}: typeof req.query['${q.rawName}'] === 'string' ? (req.query['${q.rawName}'] as string).split(${delim}) : undefined`;
|
|
19887
|
+
}
|
|
19417
19888
|
if (q.tsType === "number") {
|
|
19418
19889
|
return ` ${q.name}: Number(req.query['${q.name}'] as string)`;
|
|
19419
19890
|
}
|
|
@@ -19442,59 +19913,133 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
|
|
|
19442
19913
|
);
|
|
19443
19914
|
lines.push(`${indent} }`);
|
|
19444
19915
|
}
|
|
19916
|
+
if (op.cookieParams.length > 0) {
|
|
19917
|
+
emitCookieValidation(lines, op.cookieParams, indent, "express");
|
|
19918
|
+
lines.push(`${indent} if (!_ckv.success) {`);
|
|
19919
|
+
lines.push(
|
|
19920
|
+
`${indent} return void res.status(422).json({ error: 'Invalid request cookies', issues: _ckv.error.issues })`
|
|
19921
|
+
);
|
|
19922
|
+
lines.push(`${indent} }`);
|
|
19923
|
+
}
|
|
19445
19924
|
let bodyVarName = "body";
|
|
19446
19925
|
if (op.bodyInfo !== void 0) {
|
|
19447
|
-
|
|
19448
|
-
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
19449
|
-
if (useZod) {
|
|
19450
|
-
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
19451
|
-
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
19452
|
-
lines.push(`${indent} if (!parseResult.success) {`);
|
|
19926
|
+
if (op.bodyInfo.contentType === "multipart/form-data") {
|
|
19453
19927
|
lines.push(
|
|
19454
|
-
`${indent}
|
|
19928
|
+
`${indent} // multipart/form-data: assumes multer middleware has populated req.files + req.body.`
|
|
19455
19929
|
);
|
|
19456
|
-
lines.push(`${indent} }`);
|
|
19457
|
-
lines.push(`${indent} const validatedBody = parseResult.data`);
|
|
19458
|
-
bodyVarName = "validatedBody";
|
|
19930
|
+
lines.push(`${indent} const body = { ...req.body, ...(req as any).files } as unknown`);
|
|
19459
19931
|
} else {
|
|
19460
|
-
const
|
|
19461
|
-
|
|
19932
|
+
const schemaName = op.bodyInfo.typeName !== void 0 ? `${op.bodyInfo.typeName}Schema` : void 0;
|
|
19933
|
+
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
19934
|
+
if (useZod) {
|
|
19935
|
+
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
19936
|
+
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
19937
|
+
lines.push(`${indent} if (!parseResult.success) {`);
|
|
19938
|
+
lines.push(
|
|
19939
|
+
`${indent} return void res.status(422).json({ error: 'Invalid request body', issues: parseResult.error.issues })`
|
|
19940
|
+
);
|
|
19941
|
+
lines.push(`${indent} }`);
|
|
19942
|
+
const typeDecl = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? op.bodyInfo.typeName : "unknown";
|
|
19943
|
+
lines.push(`${indent} const validatedBody = parseResult.data as ${typeDecl}`);
|
|
19944
|
+
bodyVarName = "validatedBody";
|
|
19945
|
+
} else {
|
|
19946
|
+
const typeAnnotation = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? ` as ${op.bodyInfo.typeName}` : "";
|
|
19947
|
+
lines.push(`${indent} const body = req.body${typeAnnotation}`);
|
|
19948
|
+
}
|
|
19462
19949
|
}
|
|
19463
19950
|
}
|
|
19464
19951
|
const serviceArgs = [];
|
|
19465
19952
|
for (const p of op.pathParams) {
|
|
19466
|
-
serviceArgs.push(`req.params['${p}']
|
|
19953
|
+
serviceArgs.push(`(req.params['${p}'] as string)`);
|
|
19467
19954
|
}
|
|
19468
19955
|
if (op.bodyInfo !== void 0) {
|
|
19469
19956
|
serviceArgs.push(bodyVarName);
|
|
19470
19957
|
}
|
|
19471
19958
|
if (op.queryParams.length > 0) {
|
|
19472
|
-
serviceArgs.push("params");
|
|
19959
|
+
serviceArgs.push(queryParamsNeedValidation(op.queryParams) ? "_qv.data" : "params");
|
|
19960
|
+
}
|
|
19961
|
+
if (contextType !== void 0) {
|
|
19962
|
+
serviceArgs.push("req");
|
|
19473
19963
|
}
|
|
19474
19964
|
const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
|
|
19965
|
+
lines.push(`${indent} try {`);
|
|
19475
19966
|
if (op.responseStatus.isVoid) {
|
|
19476
|
-
lines.push(`${indent}
|
|
19477
|
-
lines.push(`${indent}
|
|
19478
|
-
} else if (op.responseStatus.
|
|
19479
|
-
lines.push(`${indent}
|
|
19967
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
19968
|
+
lines.push(`${indent} res.status(${op.responseStatus.status}).end()`);
|
|
19969
|
+
} else if (op.responseStatus.isMultiStatus === true) {
|
|
19970
|
+
lines.push(`${indent} const _envelope = await ${serviceCall}`);
|
|
19971
|
+
lines.push(`${indent} res.status(_envelope.status).json(_envelope.body)`);
|
|
19972
|
+
} else if (op.responseStatus.responseContentType === "text/plain") {
|
|
19973
|
+
if (op.responseStatus.status === 200) {
|
|
19974
|
+
lines.push(`${indent} res.type('text/plain').send(await ${serviceCall})`);
|
|
19975
|
+
} else {
|
|
19976
|
+
lines.push(
|
|
19977
|
+
`${indent} res.status(${op.responseStatus.status}).type('text/plain').send(await ${serviceCall})`
|
|
19978
|
+
);
|
|
19979
|
+
}
|
|
19980
|
+
} else if (op.responseStatus.responseContentType === "application/octet-stream") {
|
|
19981
|
+
if (op.responseStatus.status === 200) {
|
|
19982
|
+
lines.push(
|
|
19983
|
+
`${indent} res.setHeader('Content-Type', 'application/octet-stream').send(Buffer.from(await ${serviceCall}))`
|
|
19984
|
+
);
|
|
19985
|
+
} else {
|
|
19986
|
+
lines.push(
|
|
19987
|
+
`${indent} res.status(${op.responseStatus.status}).setHeader('Content-Type', 'application/octet-stream').send(Buffer.from(await ${serviceCall}))`
|
|
19988
|
+
);
|
|
19989
|
+
}
|
|
19990
|
+
} else if (op.responseStatus.status === 200) {
|
|
19991
|
+
lines.push(`${indent} res.json(await ${serviceCall})`);
|
|
19480
19992
|
} else {
|
|
19481
|
-
lines.push(`${indent}
|
|
19482
|
-
}
|
|
19993
|
+
lines.push(`${indent} res.status(${op.responseStatus.status}).json(await ${serviceCall})`);
|
|
19994
|
+
}
|
|
19995
|
+
lines.push(`${indent} } catch (err) {`);
|
|
19996
|
+
lines.push(`${indent} if (err instanceof HttpError) {`);
|
|
19997
|
+
lines.push(`${indent} return void res.status(err.status).json({ error: err.message })`);
|
|
19998
|
+
lines.push(`${indent} }`);
|
|
19999
|
+
lines.push(`${indent} throw err`);
|
|
20000
|
+
lines.push(`${indent} }`);
|
|
19483
20001
|
lines.push(`${indent}})`);
|
|
19484
20002
|
return lines.join("\n");
|
|
19485
20003
|
}
|
|
19486
|
-
function
|
|
20004
|
+
function buildFastifyRouteOptions(op, schemaNames) {
|
|
20005
|
+
const parts = [];
|
|
20006
|
+
const responseSchemaExpr = buildFastifyResponseSchemaExpr(op, schemaNames);
|
|
20007
|
+
if (responseSchemaExpr !== void 0) {
|
|
20008
|
+
parts.push(`schema: { response: { ${op.responseStatus.status}: ${responseSchemaExpr} } }`);
|
|
20009
|
+
}
|
|
20010
|
+
parts.push(`config: { operationId: '${op.methodName}' }`);
|
|
20011
|
+
return `{ ${parts.join(", ")} }`;
|
|
20012
|
+
}
|
|
20013
|
+
function buildFastifyResponseSchemaExpr(op, schemaNames) {
|
|
20014
|
+
if (schemaNames === void 0) return void 0;
|
|
20015
|
+
if (op.responseTypeName === void 0) return void 0;
|
|
20016
|
+
if (op.responseStatus.isMultiStatus === true) return void 0;
|
|
20017
|
+
if (op.responseStatus.isVoid) return void 0;
|
|
20018
|
+
const schemaName = `${op.responseTypeName}Schema`;
|
|
20019
|
+
if (!schemaNames.has(schemaName)) return void 0;
|
|
20020
|
+
return op.responseIsArray === true ? `z.array(${schemaName})` : schemaName;
|
|
20021
|
+
}
|
|
20022
|
+
function buildFastifyRouteHandler(op, indent, schemaNames, contextType) {
|
|
19487
20023
|
const lines = [];
|
|
19488
20024
|
const genericParts = [];
|
|
19489
20025
|
if (op.queryParams.length > 0) {
|
|
19490
|
-
const
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
20026
|
+
const hasDeepOrDelimited = op.queryParams.some(
|
|
20027
|
+
(q) => q.isDeepObject === true || q.delimiterStyle !== void 0
|
|
20028
|
+
);
|
|
20029
|
+
let querystringType;
|
|
20030
|
+
if (hasDeepOrDelimited) {
|
|
20031
|
+
querystringType = "Record<string, string | string[] | undefined>";
|
|
20032
|
+
} else {
|
|
20033
|
+
const queryFields = op.queryParams.map((q) => {
|
|
20034
|
+
if (q.tsType === "number") return `${q.name}?: number`;
|
|
20035
|
+
if (q.tsType === "boolean") return `${q.name}?: boolean`;
|
|
20036
|
+
return `${q.name}?: string`;
|
|
20037
|
+
}).join("; ");
|
|
20038
|
+
querystringType = `{ ${queryFields} }`;
|
|
20039
|
+
}
|
|
20040
|
+
genericParts.push(`Querystring: ${querystringType}`);
|
|
20041
|
+
}
|
|
20042
|
+
if (op.bodyInfo !== void 0 && op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized) {
|
|
19498
20043
|
genericParts.push(`Body: ${op.bodyInfo.typeName}`);
|
|
19499
20044
|
} else if (op.bodyInfo !== void 0) {
|
|
19500
20045
|
genericParts.push("Body: unknown");
|
|
@@ -19504,27 +20049,60 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
|
19504
20049
|
genericParts.push(`Params: { ${paramFields} }`);
|
|
19505
20050
|
}
|
|
19506
20051
|
const generic = genericParts.length > 0 ? `<{ ${genericParts.join("; ")} }>` : "";
|
|
20052
|
+
const routeOpts = buildFastifyRouteOptions(op, schemaNames);
|
|
19507
20053
|
lines.push(
|
|
19508
|
-
`${indent}app.${op.httpMethod}${generic}(${JSON.stringify(op.honoPath)}, async (req, reply) => {`
|
|
20054
|
+
`${indent}app.${op.httpMethod}${generic}(${JSON.stringify(op.honoPath)}, ${routeOpts}, async (req, reply) => {`
|
|
19509
20055
|
);
|
|
19510
20056
|
if (op.pathParamValidations.length > 0) {
|
|
19511
20057
|
emitPathValidation(lines, op.pathParamValidations, indent, "fastify");
|
|
19512
20058
|
lines.push(`${indent} if (!_pv.success) {`);
|
|
19513
|
-
lines.push(`${indent} return reply.status(422).send({`);
|
|
20059
|
+
lines.push(`${indent} return (reply as FastifyReply).status(422).send({`);
|
|
19514
20060
|
lines.push(`${indent} error: 'Invalid path parameters',`);
|
|
19515
20061
|
lines.push(`${indent} issues: _pv.error.issues,`);
|
|
19516
20062
|
lines.push(`${indent} })`);
|
|
19517
20063
|
lines.push(`${indent} }`);
|
|
19518
20064
|
}
|
|
19519
20065
|
if (op.queryParams.length > 0) {
|
|
19520
|
-
const
|
|
20066
|
+
const deepObjectParams = op.queryParams.filter((q) => q.isDeepObject === true);
|
|
20067
|
+
const hasDeepOrDelimited = op.queryParams.some(
|
|
20068
|
+
(q) => q.isDeepObject === true || q.delimiterStyle !== void 0
|
|
20069
|
+
);
|
|
20070
|
+
if (hasDeepOrDelimited) {
|
|
20071
|
+
lines.push(
|
|
20072
|
+
`${indent} const _dq = req.query as unknown as Record<string, string | undefined>`
|
|
20073
|
+
);
|
|
20074
|
+
}
|
|
20075
|
+
if (deepObjectParams.length > 0) {
|
|
20076
|
+
for (const q of deepObjectParams) {
|
|
20077
|
+
const prefixLen = q.rawName.length + 1;
|
|
20078
|
+
const bracketPrefix = q.rawName + "[";
|
|
20079
|
+
lines.push(`${indent} const ${q.name} = Object.fromEntries(`);
|
|
20080
|
+
lines.push(
|
|
20081
|
+
`${indent} Object.entries(_dq).filter(([k]) => k.startsWith('${bracketPrefix}') && k.endsWith(']')).map(([k, v]) => [k.slice(${prefixLen}, -1), v])`
|
|
20082
|
+
);
|
|
20083
|
+
lines.push(`${indent} )`);
|
|
20084
|
+
}
|
|
20085
|
+
}
|
|
20086
|
+
const fields = op.queryParams.map((q) => {
|
|
20087
|
+
if (q.isDeepObject === true) {
|
|
20088
|
+
return ` ${q.name}`;
|
|
20089
|
+
}
|
|
20090
|
+
if (q.delimiterStyle !== void 0) {
|
|
20091
|
+
const delim = JSON.stringify(delimiterChar(q.delimiterStyle));
|
|
20092
|
+
return ` ${q.name}: typeof _dq['${q.rawName}'] === 'string' ? _dq['${q.rawName}']!.split(${delim}) : undefined`;
|
|
20093
|
+
}
|
|
20094
|
+
if (q.tsType === "boolean") {
|
|
20095
|
+
return hasDeepOrDelimited ? ` ${q.name}: _dq['${q.rawName}'] === 'true'` : ` ${q.name}: (req.query.${q.name} as unknown as string) === 'true'`;
|
|
20096
|
+
}
|
|
20097
|
+
return hasDeepOrDelimited ? ` ${q.name}: _dq['${q.rawName}']` : ` ${q.name}: req.query.${q.name}`;
|
|
20098
|
+
}).join(",\n");
|
|
19521
20099
|
lines.push(`${indent} const params = {`);
|
|
19522
20100
|
lines.push(fields);
|
|
19523
20101
|
lines.push(`${indent} }`);
|
|
19524
20102
|
if (queryParamsNeedValidation(op.queryParams)) {
|
|
19525
20103
|
emitQueryValidation(lines, op.queryParams, indent);
|
|
19526
20104
|
lines.push(`${indent} if (!_qv.success) {`);
|
|
19527
|
-
lines.push(`${indent} return reply.status(422).send({`);
|
|
20105
|
+
lines.push(`${indent} return (reply as FastifyReply).status(422).send({`);
|
|
19528
20106
|
lines.push(`${indent} error: 'Invalid query parameters',`);
|
|
19529
20107
|
lines.push(`${indent} issues: _qv.error.issues,`);
|
|
19530
20108
|
lines.push(`${indent} })`);
|
|
@@ -19534,25 +20112,46 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
|
19534
20112
|
if (op.headerParams.length > 0) {
|
|
19535
20113
|
emitHeaderValidation(lines, op.headerParams, indent, "fastify");
|
|
19536
20114
|
lines.push(`${indent} if (!_hv.success) {`);
|
|
19537
|
-
lines.push(`${indent} return reply.status(422).send({`);
|
|
20115
|
+
lines.push(`${indent} return (reply as FastifyReply).status(422).send({`);
|
|
19538
20116
|
lines.push(`${indent} error: 'Invalid request headers',`);
|
|
19539
20117
|
lines.push(`${indent} issues: _hv.error.issues,`);
|
|
19540
20118
|
lines.push(`${indent} })`);
|
|
19541
20119
|
lines.push(`${indent} }`);
|
|
19542
20120
|
}
|
|
20121
|
+
if (op.cookieParams.length > 0) {
|
|
20122
|
+
emitCookieValidation(lines, op.cookieParams, indent, "fastify");
|
|
20123
|
+
lines.push(`${indent} if (!_ckv.success) {`);
|
|
20124
|
+
lines.push(`${indent} return (reply as FastifyReply).status(422).send({`);
|
|
20125
|
+
lines.push(`${indent} error: 'Invalid request cookies',`);
|
|
20126
|
+
lines.push(`${indent} issues: _ckv.error.issues,`);
|
|
20127
|
+
lines.push(`${indent} })`);
|
|
20128
|
+
lines.push(`${indent} }`);
|
|
20129
|
+
}
|
|
19543
20130
|
let bodyVarName = "req.body";
|
|
19544
20131
|
if (op.bodyInfo !== void 0) {
|
|
19545
|
-
|
|
19546
|
-
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
19547
|
-
if (useZod) {
|
|
19548
|
-
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
19549
|
-
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
19550
|
-
lines.push(`${indent} if (!parseResult.success) {`);
|
|
20132
|
+
if (op.bodyInfo.contentType === "multipart/form-data") {
|
|
19551
20133
|
lines.push(
|
|
19552
|
-
`${indent}
|
|
20134
|
+
`${indent} // multipart/form-data: requires @fastify/multipart registered with { attachFieldsToBody: true }.`
|
|
19553
20135
|
);
|
|
19554
|
-
|
|
19555
|
-
|
|
20136
|
+
} else if (op.bodyInfo.contentType === "application/octet-stream") {
|
|
20137
|
+
lines.push(
|
|
20138
|
+
`${indent} // application/octet-stream: req.body is a Buffer from the registered content-type parser.`
|
|
20139
|
+
);
|
|
20140
|
+
} else {
|
|
20141
|
+
const schemaName = op.bodyInfo.typeName !== void 0 ? `${op.bodyInfo.typeName}Schema` : void 0;
|
|
20142
|
+
const useZod = schemaName !== void 0 && schemaNames !== void 0 && schemaNames.has(schemaName);
|
|
20143
|
+
if (useZod) {
|
|
20144
|
+
lines.push(`${indent} // Validate request body: returns 422 with Zod issues on failure`);
|
|
20145
|
+
lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
|
|
20146
|
+
lines.push(`${indent} if (!parseResult.success) {`);
|
|
20147
|
+
lines.push(
|
|
20148
|
+
`${indent} return (reply as FastifyReply).status(422).send({ error: 'Invalid request body', issues: parseResult.error.issues })`
|
|
20149
|
+
);
|
|
20150
|
+
lines.push(`${indent} }`);
|
|
20151
|
+
const bodyCastType = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? op.bodyInfo.typeName : "unknown";
|
|
20152
|
+
lines.push(`${indent} const validatedBody = parseResult.data as ${bodyCastType}`);
|
|
20153
|
+
bodyVarName = "validatedBody";
|
|
20154
|
+
}
|
|
19556
20155
|
}
|
|
19557
20156
|
}
|
|
19558
20157
|
const serviceArgs = [];
|
|
@@ -19563,26 +20162,62 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
|
|
|
19563
20162
|
serviceArgs.push(bodyVarName);
|
|
19564
20163
|
}
|
|
19565
20164
|
if (op.queryParams.length > 0) {
|
|
19566
|
-
serviceArgs.push("params");
|
|
20165
|
+
serviceArgs.push(queryParamsNeedValidation(op.queryParams) ? "_qv.data" : "params");
|
|
20166
|
+
}
|
|
20167
|
+
if (contextType !== void 0) {
|
|
20168
|
+
serviceArgs.push("req");
|
|
19567
20169
|
}
|
|
19568
20170
|
const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
|
|
20171
|
+
lines.push(`${indent} try {`);
|
|
19569
20172
|
if (op.responseStatus.isVoid) {
|
|
19570
|
-
lines.push(`${indent}
|
|
19571
|
-
lines.push(`${indent}
|
|
19572
|
-
} else if (op.responseStatus.
|
|
19573
|
-
lines.push(`${indent}
|
|
19574
|
-
lines.push(`${indent}
|
|
20173
|
+
lines.push(`${indent} await ${serviceCall}`);
|
|
20174
|
+
lines.push(`${indent} reply.status(${op.responseStatus.status}).send()`);
|
|
20175
|
+
} else if (op.responseStatus.isMultiStatus === true) {
|
|
20176
|
+
lines.push(`${indent} const _envelope = await ${serviceCall}`);
|
|
20177
|
+
lines.push(`${indent} return reply.status(_envelope.status).send(_envelope.body)`);
|
|
20178
|
+
} else if (op.responseStatus.responseContentType === "text/plain") {
|
|
20179
|
+
if (op.responseStatus.status === 200) {
|
|
20180
|
+
lines.push(`${indent} return reply.type('text/plain').send(await ${serviceCall})`);
|
|
20181
|
+
} else {
|
|
20182
|
+
lines.push(`${indent} return reply.status(${op.responseStatus.status}).type('text/plain').send(await ${serviceCall})`);
|
|
20183
|
+
}
|
|
20184
|
+
} else if (op.responseStatus.responseContentType === "application/octet-stream") {
|
|
20185
|
+
if (op.responseStatus.status === 200) {
|
|
20186
|
+
lines.push(`${indent} return reply.type('application/octet-stream').send(Buffer.from(await ${serviceCall}))`);
|
|
20187
|
+
} else {
|
|
20188
|
+
lines.push(`${indent} return reply.status(${op.responseStatus.status}).type('application/octet-stream').send(Buffer.from(await ${serviceCall}))`);
|
|
20189
|
+
}
|
|
20190
|
+
} else if (op.responseStatus.status === 200) {
|
|
20191
|
+
lines.push(`${indent} return await ${serviceCall}`);
|
|
19575
20192
|
} else {
|
|
19576
|
-
lines.push(`${indent}
|
|
19577
|
-
|
|
20193
|
+
lines.push(`${indent} reply.status(${op.responseStatus.status})`);
|
|
20194
|
+
lines.push(`${indent} return await ${serviceCall}`);
|
|
20195
|
+
}
|
|
20196
|
+
lines.push(`${indent} } catch (err) {`);
|
|
20197
|
+
lines.push(`${indent} if (err instanceof HttpError) {`);
|
|
20198
|
+
lines.push(`${indent} return (reply as FastifyReply).status(err.status).send({ error: err.message })`);
|
|
20199
|
+
lines.push(`${indent} }`);
|
|
20200
|
+
lines.push(`${indent} throw err`);
|
|
20201
|
+
lines.push(`${indent} }`);
|
|
19578
20202
|
lines.push(`${indent}})`);
|
|
19579
20203
|
return lines.join("\n");
|
|
19580
20204
|
}
|
|
20205
|
+
function httpErrorClassLines() {
|
|
20206
|
+
return [
|
|
20207
|
+
"export class HttpError extends Error {",
|
|
20208
|
+
" constructor(public readonly status: number, message: string) {",
|
|
20209
|
+
" super(message)",
|
|
20210
|
+
" this.name = 'HttpError'",
|
|
20211
|
+
" }",
|
|
20212
|
+
"}"
|
|
20213
|
+
];
|
|
20214
|
+
}
|
|
19581
20215
|
function operationsNeedZodForParams(operations) {
|
|
19582
20216
|
for (const op of operations) {
|
|
19583
20217
|
if (op.pathParamValidations.length > 0) return true;
|
|
19584
20218
|
if (queryParamsNeedValidation(op.queryParams)) return true;
|
|
19585
20219
|
if (op.headerParams.length > 0) return true;
|
|
20220
|
+
if (op.cookieParams.length > 0) return true;
|
|
19586
20221
|
}
|
|
19587
20222
|
return false;
|
|
19588
20223
|
}
|
|
@@ -19601,6 +20236,8 @@ function generateExpressRouter(spec, options) {
|
|
|
19601
20236
|
if (sortedBodyTypes.length > 0) {
|
|
19602
20237
|
lines.push(`import type { ${sortedBodyTypes.join(", ")} } from './models.js'`);
|
|
19603
20238
|
}
|
|
20239
|
+
const ctx = options?.contextType;
|
|
20240
|
+
const serviceRef = ctx !== void 0 ? `${serviceName}<${ctx}>` : serviceName;
|
|
19604
20241
|
lines.push(`import type { ${serviceName} } from './service.js'`);
|
|
19605
20242
|
if (needsZod) {
|
|
19606
20243
|
lines.push(`import { z } from 'zod'`);
|
|
@@ -19610,11 +20247,13 @@ function generateExpressRouter(spec, options) {
|
|
|
19610
20247
|
lines.push(`import { ${sortedUsedSchemas.join(", ")} } from '${options.schemaImportPath}'`);
|
|
19611
20248
|
}
|
|
19612
20249
|
lines.push("");
|
|
19613
|
-
|
|
20250
|
+
for (const l of httpErrorClassLines()) lines.push(l);
|
|
20251
|
+
lines.push("");
|
|
20252
|
+
lines.push(`export function createRouter(service: ${serviceRef}): Router {`);
|
|
19614
20253
|
lines.push(" const router = Router()");
|
|
19615
20254
|
lines.push("");
|
|
19616
20255
|
for (const op of operations) {
|
|
19617
|
-
lines.push(buildExpressRouteHandler(op, " ", options?.schemaNames));
|
|
20256
|
+
lines.push(buildExpressRouteHandler(op, " ", options?.schemaNames, ctx));
|
|
19618
20257
|
lines.push("");
|
|
19619
20258
|
}
|
|
19620
20259
|
lines.push(" return router");
|
|
@@ -19628,11 +20267,26 @@ function generateExpressRouter(spec, options) {
|
|
|
19628
20267
|
function generateFastifyRouter(spec, options) {
|
|
19629
20268
|
const serviceName = deriveServiceName(spec);
|
|
19630
20269
|
const operations = collectOperations2(spec);
|
|
19631
|
-
const { sortedBodyTypes, usedSchemaNames, needsZod } = collectGeneratorSetup(operations, options);
|
|
20270
|
+
const { sortedBodyTypes, usedSchemaNames, usedResponseSchemaNames, needsZod } = collectGeneratorSetup(operations, options);
|
|
20271
|
+
const allUsedSchemaNames = /* @__PURE__ */ new Set([...usedSchemaNames, ...usedResponseSchemaNames]);
|
|
20272
|
+
const hasOctetStreamRequestBody = operations.some(
|
|
20273
|
+
(op) => op.bodyInfo?.contentType === "application/octet-stream"
|
|
20274
|
+
);
|
|
19632
20275
|
const lines = [];
|
|
19633
20276
|
lines.push("// This file is auto-generated. Do not edit manually.");
|
|
20277
|
+
lines.push(
|
|
20278
|
+
"// Fastify natively parses only application/json and text/plain request bodies."
|
|
20279
|
+
);
|
|
20280
|
+
lines.push(
|
|
20281
|
+
"// For application/x-www-form-urlencoded bodies, register @fastify/formbody before this router."
|
|
20282
|
+
);
|
|
20283
|
+
lines.push(
|
|
20284
|
+
"// For multipart/form-data bodies, register @fastify/multipart with { attachFieldsToBody: true } before this router."
|
|
20285
|
+
);
|
|
19634
20286
|
lines.push("");
|
|
19635
|
-
|
|
20287
|
+
const ctx = options?.contextType;
|
|
20288
|
+
const serviceRef = ctx !== void 0 ? `${serviceName}<${ctx}>` : serviceName;
|
|
20289
|
+
lines.push("import type { FastifyInstance, FastifyReply } from 'fastify'");
|
|
19636
20290
|
if (sortedBodyTypes.length > 0) {
|
|
19637
20291
|
lines.push(`import type { ${sortedBodyTypes.join(", ")} } from './models.js'`);
|
|
19638
20292
|
}
|
|
@@ -19640,15 +20294,41 @@ function generateFastifyRouter(spec, options) {
|
|
|
19640
20294
|
if (needsZod) {
|
|
19641
20295
|
lines.push(`import { z } from 'zod'`);
|
|
19642
20296
|
}
|
|
19643
|
-
if (
|
|
19644
|
-
const sortedUsedSchemas = Array.from(
|
|
20297
|
+
if (allUsedSchemaNames.size > 0 && options?.schemaImportPath !== void 0) {
|
|
20298
|
+
const sortedUsedSchemas = Array.from(allUsedSchemaNames).sort();
|
|
19645
20299
|
lines.push(`import { ${sortedUsedSchemas.join(", ")} } from '${options.schemaImportPath}'`);
|
|
19646
20300
|
}
|
|
19647
20301
|
lines.push("");
|
|
19648
|
-
lines.push(
|
|
20302
|
+
lines.push("declare module 'fastify' {");
|
|
20303
|
+
lines.push(" interface FastifyContextConfig {");
|
|
20304
|
+
lines.push(" operationId?: string");
|
|
20305
|
+
lines.push(" }");
|
|
20306
|
+
lines.push("}");
|
|
20307
|
+
lines.push("");
|
|
20308
|
+
for (const l of httpErrorClassLines()) lines.push(l);
|
|
20309
|
+
lines.push("");
|
|
20310
|
+
lines.push(`export function createRouter(app: FastifyInstance, service: ${serviceRef}): void {`);
|
|
20311
|
+
if (hasOctetStreamRequestBody) {
|
|
20312
|
+
lines.push(
|
|
20313
|
+
" app.addContentTypeParser('application/octet-stream', { parseAs: 'buffer' }, (req, body, done) => done(null, body))"
|
|
20314
|
+
);
|
|
20315
|
+
}
|
|
20316
|
+
if (usedResponseSchemaNames.size > 0 && options?.schemaImportPath !== void 0) {
|
|
20317
|
+
lines.push(" app.setSerializerCompiler(({ schema }) => {");
|
|
20318
|
+
lines.push(
|
|
20319
|
+
" if (schema !== null && typeof schema === 'object' && 'parse' in schema) {"
|
|
20320
|
+
);
|
|
20321
|
+
lines.push(
|
|
20322
|
+
" const zodSchema = schema as { parse: (data: unknown) => unknown }"
|
|
20323
|
+
);
|
|
20324
|
+
lines.push(" return (data: unknown) => JSON.stringify(zodSchema.parse(data))");
|
|
20325
|
+
lines.push(" }");
|
|
20326
|
+
lines.push(" return (data: unknown) => JSON.stringify(data)");
|
|
20327
|
+
lines.push(" })");
|
|
20328
|
+
}
|
|
19649
20329
|
for (const op of operations) {
|
|
19650
20330
|
lines.push("");
|
|
19651
|
-
lines.push(buildFastifyRouteHandler(op, " ", options?.schemaNames));
|
|
20331
|
+
lines.push(buildFastifyRouteHandler(op, " ", options?.schemaNames, ctx));
|
|
19652
20332
|
}
|
|
19653
20333
|
lines.push("}");
|
|
19654
20334
|
lines.push("");
|
|
@@ -19664,7 +20344,13 @@ function generateRouter(spec, options) {
|
|
|
19664
20344
|
const lines = [];
|
|
19665
20345
|
lines.push("// This file is auto-generated. Do not edit manually.");
|
|
19666
20346
|
lines.push("");
|
|
20347
|
+
const ctx = options?.contextType;
|
|
20348
|
+
const serviceRef = ctx !== void 0 ? `${serviceName}<${ctx}>` : serviceName;
|
|
20349
|
+
const needsGetCookie = operations.some((op) => op.cookieParams.length > 0);
|
|
19667
20350
|
lines.push("import { Hono } from 'hono'");
|
|
20351
|
+
if (needsGetCookie) {
|
|
20352
|
+
lines.push("import { getCookie } from 'hono/cookie'");
|
|
20353
|
+
}
|
|
19668
20354
|
if (sortedBodyTypes.length > 0) {
|
|
19669
20355
|
lines.push(`import type { ${sortedBodyTypes.join(", ")} } from './models.js'`);
|
|
19670
20356
|
}
|
|
@@ -19677,11 +20363,13 @@ function generateRouter(spec, options) {
|
|
|
19677
20363
|
lines.push(`import { ${sortedUsedSchemas.join(", ")} } from '${options.schemaImportPath}'`);
|
|
19678
20364
|
}
|
|
19679
20365
|
lines.push("");
|
|
19680
|
-
|
|
20366
|
+
for (const l of httpErrorClassLines()) lines.push(l);
|
|
20367
|
+
lines.push("");
|
|
20368
|
+
lines.push(`export function createRouter(service: ${serviceRef}): Hono {`);
|
|
19681
20369
|
lines.push(" const app = new Hono()");
|
|
19682
20370
|
lines.push("");
|
|
19683
20371
|
for (const op of operations) {
|
|
19684
|
-
lines.push(buildRouteHandler(op, " ", options?.schemaNames));
|
|
20372
|
+
lines.push(buildRouteHandler(op, " ", options?.schemaNames, ctx));
|
|
19685
20373
|
lines.push("");
|
|
19686
20374
|
}
|
|
19687
20375
|
lines.push(" return app");
|
|
@@ -19711,9 +20399,14 @@ async function generateOne(cwd, config, label) {
|
|
|
19711
20399
|
const prefix = label !== void 0 ? `[${label}] ` : "";
|
|
19712
20400
|
console.log(`${prefix}Parsing spec: ${inputPath}`);
|
|
19713
20401
|
const spec = await parseSpec(inputPath);
|
|
19714
|
-
const
|
|
20402
|
+
const serviceOptions = config.context_type !== void 0 ? { contextType: config.context_type } : void 0;
|
|
20403
|
+
const generatedFiles = [generateService(spec, serviceOptions)];
|
|
19715
20404
|
if (framework !== "none") {
|
|
19716
|
-
generatedFiles.push(
|
|
20405
|
+
generatedFiles.push(
|
|
20406
|
+
buildRouterFile(spec, framework, {
|
|
20407
|
+
contextType: config.context_type
|
|
20408
|
+
})
|
|
20409
|
+
);
|
|
19717
20410
|
}
|
|
19718
20411
|
console.log(`${prefix}Writing output to: ${outputDir}`);
|
|
19719
20412
|
await (0, import_promises2.mkdir)(outputDir, { recursive: true });
|
|
@@ -19746,7 +20439,8 @@ async function generateSchemaEnhancedRouter(cwd, config, spec, framework, output
|
|
|
19746
20439
|
const schemaImportPathJs = schemaImportPath.replace(/\.ts$/, ".js");
|
|
19747
20440
|
const routerFile = buildRouterFile(spec, framework, {
|
|
19748
20441
|
schemaNames: exportedSchemas,
|
|
19749
|
-
schemaImportPath: schemaImportPathJs
|
|
20442
|
+
schemaImportPath: schemaImportPathJs,
|
|
20443
|
+
contextType: config.context_type
|
|
19750
20444
|
});
|
|
19751
20445
|
const routerPath = (0, import_node_path2.join)(outputDir, routerFile.filename);
|
|
19752
20446
|
await (0, import_promises2.writeFile)(routerPath, await formatTs(routerFile.content, routerPath), "utf-8");
|