@codewithagents/openapi-server 1.9.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/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) {
@@ -18969,6 +18974,8 @@ function getQueryParams(operation, spec) {
18969
18974
  key,
18970
18975
  tsType: schemaToTsType(propSchema)
18971
18976
  }));
18977
+ const propFields = param.deepObjectProperties.map((p2) => `${p2.key}?: ${p2.tsType}`).join("; ");
18978
+ param.tsType = `{ ${propFields} }`;
18972
18979
  }
18973
18980
  }
18974
18981
  if (!param.isDeepObject && schema !== void 0 && !isRef3(schema) && schema.type === "array" && resolvedExplode === false) {
@@ -19062,6 +19069,10 @@ function getBodyInfo(operation) {
19062
19069
  }
19063
19070
  return { typeName: void 0, contentType: "multipart/form-data", isSynthesized: false };
19064
19071
  }
19072
+ const octetContent = content["application/octet-stream"];
19073
+ if (octetContent !== void 0) {
19074
+ return { typeName: void 0, contentType: "application/octet-stream", isSynthesized: false };
19075
+ }
19065
19076
  return { typeName: void 0, contentType: "application/json", isSynthesized: false };
19066
19077
  }
19067
19078
 
@@ -19152,6 +19163,11 @@ function collectOperations(spec) {
19152
19163
  const queryParams = getQueryParams(operation, spec);
19153
19164
  const bodyInfo = getBodyInfo(operation);
19154
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
+ }
19155
19171
  operations.push({
19156
19172
  methodName,
19157
19173
  httpMethod: method,
@@ -19165,7 +19181,7 @@ function collectOperations(spec) {
19165
19181
  }
19166
19182
  return operations;
19167
19183
  }
19168
- function buildMethodSignature(op) {
19184
+ function buildMethodSignature(op, options) {
19169
19185
  const args = [];
19170
19186
  for (const p of op.pathParams) {
19171
19187
  args.push(`${sanitizeOperationId2(p)}: string`);
@@ -19180,11 +19196,14 @@ function buildMethodSignature(op) {
19180
19196
  const paramsToken = allOptional ? "params?" : "params";
19181
19197
  args.push(`${paramsToken}: { ${fields} }`);
19182
19198
  }
19199
+ if (options?.contextType !== void 0) {
19200
+ args.push("ctx: Ctx");
19201
+ }
19183
19202
  const returnType = buildReturnType(op.returnInfo);
19184
19203
  const argStr = args.join(", ");
19185
19204
  return `${op.methodName}(${argStr}): ${returnType}`;
19186
19205
  }
19187
- function generateService(spec) {
19206
+ function generateService(spec, options) {
19188
19207
  const serviceName = deriveServiceName(spec);
19189
19208
  const operations = collectOperations(spec);
19190
19209
  const importTypes = /* @__PURE__ */ new Set();
@@ -19204,10 +19223,11 @@ function generateService(spec) {
19204
19223
  lines.push(`import type { ${sortedImports.join(", ")} } from './models.js'`);
19205
19224
  lines.push("");
19206
19225
  }
19207
- lines.push(`export interface ${serviceName} {`);
19226
+ const interfaceDecl = options?.contextType !== void 0 ? `export interface ${serviceName}<Ctx = never> {` : `export interface ${serviceName} {`;
19227
+ lines.push(interfaceDecl);
19208
19228
  for (const op of operations) {
19209
19229
  lines.push(` /** ${op.httpMethod.toUpperCase()} ${op.path} */`);
19210
- lines.push(` ${buildMethodSignature(op)}`);
19230
+ lines.push(` ${buildMethodSignature(op, options)}`);
19211
19231
  }
19212
19232
  lines.push("}");
19213
19233
  lines.push("");
@@ -19272,7 +19292,7 @@ function queryParamDeepObjectZodBase(param) {
19272
19292
  return `z.object({ ${propFields.join(", ")} })`;
19273
19293
  }
19274
19294
  function queryParamNumberZodBase(param) {
19275
- let base = "z.number()";
19295
+ let base = "z.coerce.number()";
19276
19296
  if (param.minimum !== void 0) base += `.min(${param.minimum})`;
19277
19297
  if (param.maximum !== void 0) base += `.max(${param.maximum})`;
19278
19298
  if (param.exclusiveMinimum !== void 0) base += `.gt(${param.exclusiveMinimum})`;
@@ -19338,6 +19358,19 @@ function getPathParamValidations(operation, spec, rawPathParamNames) {
19338
19358
  }
19339
19359
  return result;
19340
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
+ }
19341
19374
  function getHeaderParams(operation, spec) {
19342
19375
  const parameters = operation.parameters;
19343
19376
  if (parameters === void 0) return [];
@@ -19361,6 +19394,29 @@ function getHeaderParams(operation, spec) {
19361
19394
  }
19362
19395
  return result;
19363
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
+ }
19364
19420
  function queryParamHasConstraints(q) {
19365
19421
  const constraintFields = [
19366
19422
  q.enum,
@@ -19410,7 +19466,7 @@ function emitPathValidation(lines, validations, indent, framework) {
19410
19466
  if (framework === "hono") {
19411
19467
  access = `c.req.param(${JSON.stringify(v.rawName)})`;
19412
19468
  } else if (framework === "express") {
19413
- access = `req.params[${JSON.stringify(v.rawName)}]`;
19469
+ access = `req.params[${JSON.stringify(v.rawName)}] as string`;
19414
19470
  } else {
19415
19471
  access = /[^a-zA-Z0-9_$]/.test(v.rawName) ? `req.params[${JSON.stringify(v.rawName)}]` : `req.params.${v.rawName}`;
19416
19472
  }
@@ -19433,13 +19489,14 @@ function emitHeaderValidation(lines, headerParams, indent, framework) {
19433
19489
  }).join(",\n");
19434
19490
  const rawFields = headerParams.map((h) => {
19435
19491
  const key = JSON.stringify(h.rawName);
19492
+ const lookupKey = JSON.stringify(h.rawName.toLowerCase());
19436
19493
  let access;
19437
19494
  if (framework === "hono") {
19438
19495
  access = `c.req.header(${key})`;
19439
19496
  } else if (framework === "express") {
19440
- access = `req.headers[${key}] as string | undefined`;
19497
+ access = `req.headers[${lookupKey}] as string | undefined`;
19441
19498
  } else {
19442
- access = `req.headers[${key}]`;
19499
+ access = `req.headers[${lookupKey}]`;
19443
19500
  }
19444
19501
  return `${fieldIndent}${key}: ${access}`;
19445
19502
  }).join(",\n");
@@ -19450,6 +19507,33 @@ function emitHeaderValidation(lines, headerParams, indent, framework) {
19450
19507
  lines.push(rawFields);
19451
19508
  lines.push(`${inner}})`);
19452
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
+ }
19453
19537
  function response200IsVoid(resp) {
19454
19538
  if (isRef3(resp)) return false;
19455
19539
  const r = resp;
@@ -19514,6 +19598,34 @@ function getResponseStatus(operation, httpMethod) {
19514
19598
  }
19515
19599
  return httpMethod === "delete" ? { status: 204, isVoid: true, responseContentType: "application/json" } : { status: 200, isVoid: false, responseContentType: "application/json" };
19516
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;
19628
+ }
19517
19629
  function collectOperations2(spec) {
19518
19630
  const paths = spec.paths;
19519
19631
  if (paths === void 0) return [];
@@ -19527,8 +19639,10 @@ function collectOperations2(spec) {
19527
19639
  const pathParamValidations = getPathParamValidations(operation, spec, pathParams);
19528
19640
  const queryParams = getQueryParams(operation, spec);
19529
19641
  const headerParams = getHeaderParams(operation, spec);
19642
+ const cookieParams = getCookieParams(operation, spec);
19530
19643
  const bodyInfo = getBodyInfo(operation);
19531
19644
  const responseStatus = getResponseStatus(operation, method);
19645
+ const responseTypeInfo = getResponseTypeName(operation);
19532
19646
  operations.push({
19533
19647
  methodName,
19534
19648
  httpMethod: method,
@@ -19538,8 +19652,11 @@ function collectOperations2(spec) {
19538
19652
  pathParamValidations,
19539
19653
  queryParams,
19540
19654
  headerParams,
19655
+ cookieParams,
19541
19656
  bodyInfo,
19542
- responseStatus
19657
+ responseStatus,
19658
+ responseTypeName: responseTypeInfo?.typeName,
19659
+ responseIsArray: responseTypeInfo?.isArray
19543
19660
  });
19544
19661
  }
19545
19662
  }
@@ -19564,13 +19681,27 @@ function collectUsedSchemaNames(operations, schemaNames) {
19564
19681
  }
19565
19682
  return used;
19566
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
+ }
19567
19694
  function collectGeneratorSetup(operations, options) {
19568
19695
  const sortedBodyTypes = collectSortedBodyTypes(operations);
19569
19696
  const usedSchemaNames = options?.schemaNames !== void 0 ? collectUsedSchemaNames(operations, options.schemaNames) : /* @__PURE__ */ new Set();
19570
- const needsZod = usedSchemaNames.size > 0 && options?.schemaImportPath !== void 0 || operationsNeedZodForParams(operations);
19571
- return { sortedBodyTypes, usedSchemaNames, needsZod };
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 };
19572
19703
  }
19573
- function buildRouteHandler(op, indent, schemaNames) {
19704
+ function buildRouteHandler(op, indent, schemaNames, contextType) {
19574
19705
  const lines = [];
19575
19706
  lines.push(`${indent}app.${op.httpMethod}(${JSON.stringify(op.honoPath)}, async (c) => {`);
19576
19707
  if (op.pathParamValidations.length > 0) {
@@ -19628,6 +19759,14 @@ function buildRouteHandler(op, indent, schemaNames) {
19628
19759
  );
19629
19760
  lines.push(`${indent} }`);
19630
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
+ }
19631
19770
  let bodyVarName = "body";
19632
19771
  if (op.bodyInfo !== void 0) {
19633
19772
  const typeDecl = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? op.bodyInfo.typeName : "unknown";
@@ -19665,7 +19804,7 @@ function buildRouteHandler(op, indent, schemaNames) {
19665
19804
  `${indent} return c.json({ error: 'Invalid request body', issues: parseResult.error.issues }, 422)`
19666
19805
  );
19667
19806
  lines.push(`${indent} }`);
19668
- lines.push(`${indent} const validatedBody = parseResult.data`);
19807
+ lines.push(`${indent} const validatedBody = parseResult.data as ${typeDecl}`);
19669
19808
  bodyVarName = "validatedBody";
19670
19809
  }
19671
19810
  }
@@ -19677,7 +19816,10 @@ function buildRouteHandler(op, indent, schemaNames) {
19677
19816
  serviceArgs.push(bodyVarName);
19678
19817
  }
19679
19818
  if (op.queryParams.length > 0) {
19680
- serviceArgs.push("params");
19819
+ serviceArgs.push(queryParamsNeedValidation(op.queryParams) ? "_qv.data" : "params");
19820
+ }
19821
+ if (contextType !== void 0) {
19822
+ serviceArgs.push("c");
19681
19823
  }
19682
19824
  const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
19683
19825
  lines.push(`${indent} try {`);
@@ -19721,7 +19863,7 @@ function buildRouteHandler(op, indent, schemaNames) {
19721
19863
  lines.push(`${indent}})`);
19722
19864
  return lines.join("\n");
19723
19865
  }
19724
- function buildExpressRouteHandler(op, indent, schemaNames) {
19866
+ function buildExpressRouteHandler(op, indent, schemaNames, contextType) {
19725
19867
  const lines = [];
19726
19868
  lines.push(
19727
19869
  `${indent}router.${op.httpMethod}(${JSON.stringify(op.honoPath)}, async (req: Request, res: Response) => {`
@@ -19771,6 +19913,14 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
19771
19913
  );
19772
19914
  lines.push(`${indent} }`);
19773
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
+ }
19774
19924
  let bodyVarName = "body";
19775
19925
  if (op.bodyInfo !== void 0) {
19776
19926
  if (op.bodyInfo.contentType === "multipart/form-data") {
@@ -19789,7 +19939,8 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
19789
19939
  `${indent} return void res.status(422).json({ error: 'Invalid request body', issues: parseResult.error.issues })`
19790
19940
  );
19791
19941
  lines.push(`${indent} }`);
19792
- lines.push(`${indent} const validatedBody = parseResult.data`);
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}`);
19793
19944
  bodyVarName = "validatedBody";
19794
19945
  } else {
19795
19946
  const typeAnnotation = op.bodyInfo.typeName !== void 0 && !op.bodyInfo.isSynthesized ? ` as ${op.bodyInfo.typeName}` : "";
@@ -19799,13 +19950,16 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
19799
19950
  }
19800
19951
  const serviceArgs = [];
19801
19952
  for (const p of op.pathParams) {
19802
- serviceArgs.push(`req.params['${p}']!`);
19953
+ serviceArgs.push(`(req.params['${p}'] as string)`);
19803
19954
  }
19804
19955
  if (op.bodyInfo !== void 0) {
19805
19956
  serviceArgs.push(bodyVarName);
19806
19957
  }
19807
19958
  if (op.queryParams.length > 0) {
19808
- serviceArgs.push("params");
19959
+ serviceArgs.push(queryParamsNeedValidation(op.queryParams) ? "_qv.data" : "params");
19960
+ }
19961
+ if (contextType !== void 0) {
19962
+ serviceArgs.push("req");
19809
19963
  }
19810
19964
  const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
19811
19965
  lines.push(`${indent} try {`);
@@ -19847,7 +20001,25 @@ function buildExpressRouteHandler(op, indent, schemaNames) {
19847
20001
  lines.push(`${indent}})`);
19848
20002
  return lines.join("\n");
19849
20003
  }
19850
- function buildFastifyRouteHandler(op, indent, schemaNames) {
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) {
19851
20023
  const lines = [];
19852
20024
  const genericParts = [];
19853
20025
  if (op.queryParams.length > 0) {
@@ -19877,13 +20049,14 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
19877
20049
  genericParts.push(`Params: { ${paramFields} }`);
19878
20050
  }
19879
20051
  const generic = genericParts.length > 0 ? `<{ ${genericParts.join("; ")} }>` : "";
20052
+ const routeOpts = buildFastifyRouteOptions(op, schemaNames);
19880
20053
  lines.push(
19881
- `${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) => {`
19882
20055
  );
19883
20056
  if (op.pathParamValidations.length > 0) {
19884
20057
  emitPathValidation(lines, op.pathParamValidations, indent, "fastify");
19885
20058
  lines.push(`${indent} if (!_pv.success) {`);
19886
- lines.push(`${indent} return reply.status(422).send({`);
20059
+ lines.push(`${indent} return (reply as FastifyReply).status(422).send({`);
19887
20060
  lines.push(`${indent} error: 'Invalid path parameters',`);
19888
20061
  lines.push(`${indent} issues: _pv.error.issues,`);
19889
20062
  lines.push(`${indent} })`);
@@ -19918,6 +20091,9 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
19918
20091
  const delim = JSON.stringify(delimiterChar(q.delimiterStyle));
19919
20092
  return ` ${q.name}: typeof _dq['${q.rawName}'] === 'string' ? _dq['${q.rawName}']!.split(${delim}) : undefined`;
19920
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
+ }
19921
20097
  return hasDeepOrDelimited ? ` ${q.name}: _dq['${q.rawName}']` : ` ${q.name}: req.query.${q.name}`;
19922
20098
  }).join(",\n");
19923
20099
  lines.push(`${indent} const params = {`);
@@ -19926,7 +20102,7 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
19926
20102
  if (queryParamsNeedValidation(op.queryParams)) {
19927
20103
  emitQueryValidation(lines, op.queryParams, indent);
19928
20104
  lines.push(`${indent} if (!_qv.success) {`);
19929
- lines.push(`${indent} return reply.status(422).send({`);
20105
+ lines.push(`${indent} return (reply as FastifyReply).status(422).send({`);
19930
20106
  lines.push(`${indent} error: 'Invalid query parameters',`);
19931
20107
  lines.push(`${indent} issues: _qv.error.issues,`);
19932
20108
  lines.push(`${indent} })`);
@@ -19936,17 +20112,30 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
19936
20112
  if (op.headerParams.length > 0) {
19937
20113
  emitHeaderValidation(lines, op.headerParams, indent, "fastify");
19938
20114
  lines.push(`${indent} if (!_hv.success) {`);
19939
- lines.push(`${indent} return reply.status(422).send({`);
20115
+ lines.push(`${indent} return (reply as FastifyReply).status(422).send({`);
19940
20116
  lines.push(`${indent} error: 'Invalid request headers',`);
19941
20117
  lines.push(`${indent} issues: _hv.error.issues,`);
19942
20118
  lines.push(`${indent} })`);
19943
20119
  lines.push(`${indent} }`);
19944
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
+ }
19945
20130
  let bodyVarName = "req.body";
19946
20131
  if (op.bodyInfo !== void 0) {
19947
20132
  if (op.bodyInfo.contentType === "multipart/form-data") {
19948
20133
  lines.push(
19949
- `${indent} // multipart/form-data: assumes @fastify/multipart plugin has populated req.body.`
20134
+ `${indent} // multipart/form-data: requires @fastify/multipart registered with { attachFieldsToBody: true }.`
20135
+ );
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.`
19950
20139
  );
19951
20140
  } else {
19952
20141
  const schemaName = op.bodyInfo.typeName !== void 0 ? `${op.bodyInfo.typeName}Schema` : void 0;
@@ -19956,10 +20145,12 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
19956
20145
  lines.push(`${indent} const parseResult = ${schemaName}.safeParse(req.body)`);
19957
20146
  lines.push(`${indent} if (!parseResult.success) {`);
19958
20147
  lines.push(
19959
- `${indent} return reply.status(422).send({ error: 'Invalid request body', issues: parseResult.error.issues })`
20148
+ `${indent} return (reply as FastifyReply).status(422).send({ error: 'Invalid request body', issues: parseResult.error.issues })`
19960
20149
  );
19961
20150
  lines.push(`${indent} }`);
19962
- bodyVarName = "parseResult.data";
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";
19963
20154
  }
19964
20155
  }
19965
20156
  }
@@ -19971,7 +20162,10 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
19971
20162
  serviceArgs.push(bodyVarName);
19972
20163
  }
19973
20164
  if (op.queryParams.length > 0) {
19974
- serviceArgs.push("params");
20165
+ serviceArgs.push(queryParamsNeedValidation(op.queryParams) ? "_qv.data" : "params");
20166
+ }
20167
+ if (contextType !== void 0) {
20168
+ serviceArgs.push("req");
19975
20169
  }
19976
20170
  const serviceCall = `service.${op.methodName}(${serviceArgs.join(", ")})`;
19977
20171
  lines.push(`${indent} try {`);
@@ -19994,14 +20188,14 @@ function buildFastifyRouteHandler(op, indent, schemaNames) {
19994
20188
  lines.push(`${indent} return reply.status(${op.responseStatus.status}).type('application/octet-stream').send(Buffer.from(await ${serviceCall}))`);
19995
20189
  }
19996
20190
  } else if (op.responseStatus.status === 200) {
19997
- lines.push(`${indent} return ${serviceCall}`);
20191
+ lines.push(`${indent} return await ${serviceCall}`);
19998
20192
  } else {
19999
20193
  lines.push(`${indent} reply.status(${op.responseStatus.status})`);
20000
- lines.push(`${indent} return ${serviceCall}`);
20194
+ lines.push(`${indent} return await ${serviceCall}`);
20001
20195
  }
20002
20196
  lines.push(`${indent} } catch (err) {`);
20003
20197
  lines.push(`${indent} if (err instanceof HttpError) {`);
20004
- lines.push(`${indent} return reply.status(err.status).send({ error: err.message })`);
20198
+ lines.push(`${indent} return (reply as FastifyReply).status(err.status).send({ error: err.message })`);
20005
20199
  lines.push(`${indent} }`);
20006
20200
  lines.push(`${indent} throw err`);
20007
20201
  lines.push(`${indent} }`);
@@ -20023,6 +20217,7 @@ function operationsNeedZodForParams(operations) {
20023
20217
  if (op.pathParamValidations.length > 0) return true;
20024
20218
  if (queryParamsNeedValidation(op.queryParams)) return true;
20025
20219
  if (op.headerParams.length > 0) return true;
20220
+ if (op.cookieParams.length > 0) return true;
20026
20221
  }
20027
20222
  return false;
20028
20223
  }
@@ -20041,6 +20236,8 @@ function generateExpressRouter(spec, options) {
20041
20236
  if (sortedBodyTypes.length > 0) {
20042
20237
  lines.push(`import type { ${sortedBodyTypes.join(", ")} } from './models.js'`);
20043
20238
  }
20239
+ const ctx = options?.contextType;
20240
+ const serviceRef = ctx !== void 0 ? `${serviceName}<${ctx}>` : serviceName;
20044
20241
  lines.push(`import type { ${serviceName} } from './service.js'`);
20045
20242
  if (needsZod) {
20046
20243
  lines.push(`import { z } from 'zod'`);
@@ -20052,11 +20249,11 @@ function generateExpressRouter(spec, options) {
20052
20249
  lines.push("");
20053
20250
  for (const l of httpErrorClassLines()) lines.push(l);
20054
20251
  lines.push("");
20055
- lines.push(`export function createRouter(service: ${serviceName}): Router {`);
20252
+ lines.push(`export function createRouter(service: ${serviceRef}): Router {`);
20056
20253
  lines.push(" const router = Router()");
20057
20254
  lines.push("");
20058
20255
  for (const op of operations) {
20059
- lines.push(buildExpressRouteHandler(op, " ", options?.schemaNames));
20256
+ lines.push(buildExpressRouteHandler(op, " ", options?.schemaNames, ctx));
20060
20257
  lines.push("");
20061
20258
  }
20062
20259
  lines.push(" return router");
@@ -20070,11 +20267,26 @@ function generateExpressRouter(spec, options) {
20070
20267
  function generateFastifyRouter(spec, options) {
20071
20268
  const serviceName = deriveServiceName(spec);
20072
20269
  const operations = collectOperations2(spec);
20073
- 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
+ );
20074
20275
  const lines = [];
20075
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
+ );
20076
20286
  lines.push("");
20077
- lines.push("import type { FastifyInstance } from 'fastify'");
20287
+ const ctx = options?.contextType;
20288
+ const serviceRef = ctx !== void 0 ? `${serviceName}<${ctx}>` : serviceName;
20289
+ lines.push("import type { FastifyInstance, FastifyReply } from 'fastify'");
20078
20290
  if (sortedBodyTypes.length > 0) {
20079
20291
  lines.push(`import type { ${sortedBodyTypes.join(", ")} } from './models.js'`);
20080
20292
  }
@@ -20082,17 +20294,41 @@ function generateFastifyRouter(spec, options) {
20082
20294
  if (needsZod) {
20083
20295
  lines.push(`import { z } from 'zod'`);
20084
20296
  }
20085
- if (usedSchemaNames.size > 0 && options?.schemaImportPath !== void 0) {
20086
- const sortedUsedSchemas = Array.from(usedSchemaNames).sort();
20297
+ if (allUsedSchemaNames.size > 0 && options?.schemaImportPath !== void 0) {
20298
+ const sortedUsedSchemas = Array.from(allUsedSchemaNames).sort();
20087
20299
  lines.push(`import { ${sortedUsedSchemas.join(", ")} } from '${options.schemaImportPath}'`);
20088
20300
  }
20089
20301
  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("");
20090
20308
  for (const l of httpErrorClassLines()) lines.push(l);
20091
20309
  lines.push("");
20092
- lines.push(`export function createRouter(app: FastifyInstance, service: ${serviceName}): void {`);
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
+ }
20093
20329
  for (const op of operations) {
20094
20330
  lines.push("");
20095
- lines.push(buildFastifyRouteHandler(op, " ", options?.schemaNames));
20331
+ lines.push(buildFastifyRouteHandler(op, " ", options?.schemaNames, ctx));
20096
20332
  }
20097
20333
  lines.push("}");
20098
20334
  lines.push("");
@@ -20108,7 +20344,13 @@ function generateRouter(spec, options) {
20108
20344
  const lines = [];
20109
20345
  lines.push("// This file is auto-generated. Do not edit manually.");
20110
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);
20111
20350
  lines.push("import { Hono } from 'hono'");
20351
+ if (needsGetCookie) {
20352
+ lines.push("import { getCookie } from 'hono/cookie'");
20353
+ }
20112
20354
  if (sortedBodyTypes.length > 0) {
20113
20355
  lines.push(`import type { ${sortedBodyTypes.join(", ")} } from './models.js'`);
20114
20356
  }
@@ -20123,11 +20365,11 @@ function generateRouter(spec, options) {
20123
20365
  lines.push("");
20124
20366
  for (const l of httpErrorClassLines()) lines.push(l);
20125
20367
  lines.push("");
20126
- lines.push(`export function createRouter(service: ${serviceName}): Hono {`);
20368
+ lines.push(`export function createRouter(service: ${serviceRef}): Hono {`);
20127
20369
  lines.push(" const app = new Hono()");
20128
20370
  lines.push("");
20129
20371
  for (const op of operations) {
20130
- lines.push(buildRouteHandler(op, " ", options?.schemaNames));
20372
+ lines.push(buildRouteHandler(op, " ", options?.schemaNames, ctx));
20131
20373
  lines.push("");
20132
20374
  }
20133
20375
  lines.push(" return app");
@@ -20157,9 +20399,14 @@ async function generateOne(cwd, config, label) {
20157
20399
  const prefix = label !== void 0 ? `[${label}] ` : "";
20158
20400
  console.log(`${prefix}Parsing spec: ${inputPath}`);
20159
20401
  const spec = await parseSpec(inputPath);
20160
- const generatedFiles = [generateService(spec)];
20402
+ const serviceOptions = config.context_type !== void 0 ? { contextType: config.context_type } : void 0;
20403
+ const generatedFiles = [generateService(spec, serviceOptions)];
20161
20404
  if (framework !== "none") {
20162
- generatedFiles.push(buildRouterFile(spec, framework));
20405
+ generatedFiles.push(
20406
+ buildRouterFile(spec, framework, {
20407
+ contextType: config.context_type
20408
+ })
20409
+ );
20163
20410
  }
20164
20411
  console.log(`${prefix}Writing output to: ${outputDir}`);
20165
20412
  await (0, import_promises2.mkdir)(outputDir, { recursive: true });
@@ -20192,7 +20439,8 @@ async function generateSchemaEnhancedRouter(cwd, config, spec, framework, output
20192
20439
  const schemaImportPathJs = schemaImportPath.replace(/\.ts$/, ".js");
20193
20440
  const routerFile = buildRouterFile(spec, framework, {
20194
20441
  schemaNames: exportedSchemas,
20195
- schemaImportPath: schemaImportPathJs
20442
+ schemaImportPath: schemaImportPathJs,
20443
+ contextType: config.context_type
20196
20444
  });
20197
20445
  const routerPath = (0, import_node_path2.join)(outputDir, routerFile.filename);
20198
20446
  await (0, import_promises2.writeFile)(routerPath, await formatTs(routerFile.content, routerPath), "utf-8");