@forklaunch/core 3.2.1 → 3.3.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.
Files changed (40) hide show
  1. package/bin/gateway-mock/routes/payments-context.mjs +260 -0
  2. package/bin/gateway-mock/routes/payments.mjs +47 -1
  3. package/lib/http/bindRuntimeAuth.d.ts +53 -0
  4. package/lib/http/bindRuntimeAuth.d.ts.map +1 -0
  5. package/lib/http/bindRuntimeAuth.test.d.ts +2 -0
  6. package/lib/http/bindRuntimeAuth.test.d.ts.map +1 -0
  7. package/lib/http/index.d.ts +2 -0
  8. package/lib/http/index.d.ts.map +1 -1
  9. package/lib/http/index.js +284 -76
  10. package/lib/http/index.js.map +1 -1
  11. package/lib/http/index.mjs +282 -76
  12. package/lib/http/index.mjs.map +1 -1
  13. package/lib/http/mcpGenerator/mcpGenerator.d.ts.map +1 -1
  14. package/lib/http/middleware/internal/validationDiagnostic.d.ts +3 -0
  15. package/lib/http/middleware/internal/validationDiagnostic.d.ts.map +1 -0
  16. package/lib/http/middleware/request/auth.middleware.d.ts +0 -11
  17. package/lib/http/middleware/request/auth.middleware.d.ts.map +1 -1
  18. package/lib/http/middleware/request/parse.middleware.d.ts.map +1 -1
  19. package/lib/http/middleware/response/enrichExpressLikeSend.middleware.d.ts.map +1 -1
  20. package/lib/http/middleware/response/parse.middleware.d.ts.map +1 -1
  21. package/lib/http/openApiV3Generator/openApiV3Generator.d.ts.map +1 -1
  22. package/lib/http/paymentsClient.d.ts.map +1 -1
  23. package/lib/http/registerDelegatedSurface.d.ts +28 -0
  24. package/lib/http/registerDelegatedSurface.d.ts.map +1 -0
  25. package/lib/http/registerDelegatedSurface.test.d.ts +2 -0
  26. package/lib/http/registerDelegatedSurface.test.d.ts.map +1 -0
  27. package/lib/http/router/discriminateBody.d.ts +2 -1
  28. package/lib/http/router/discriminateBody.d.ts.map +1 -1
  29. package/lib/http/router/expressLikeRouter.d.ts.map +1 -1
  30. package/lib/persistence/compliantField.d.ts.map +1 -1
  31. package/lib/persistence/index.js +1 -6
  32. package/lib/persistence/index.js.map +1 -1
  33. package/lib/persistence/index.mjs +1 -6
  34. package/lib/persistence/index.mjs.map +1 -1
  35. package/lib/ws/asyncApiV3Generator/asyncApiV3Generator.d.ts.map +1 -1
  36. package/lib/ws/index.js +94 -8
  37. package/lib/ws/index.js.map +1 -1
  38. package/lib/ws/index.mjs +94 -8
  39. package/lib/ws/index.mjs.map +1 -1
  40. package/package.json +4 -4
package/lib/http/index.js CHANGED
@@ -66,6 +66,7 @@ __export(http_exports, {
66
66
  auditComplianceAccess: () => auditComplianceAccess,
67
67
  auditEnabled: () => auditEnabled,
68
68
  auditEventType: () => auditEventType,
69
+ bindRuntimeAuth: () => bindRuntimeAuth,
69
70
  createContext: () => createContext,
70
71
  createEmailClient: () => createEmailClient,
71
72
  createHmacToken: () => createHmacToken,
@@ -119,6 +120,7 @@ __export(http_exports, {
119
120
  put: () => put,
120
121
  recordMetric: () => recordMetric,
121
122
  recordRequestAudit: () => recordRequestAudit,
123
+ registerDelegatedSurface: () => registerDelegatedSurface,
122
124
  resetProcessHandlersForTesting: () => resetProcessHandlersForTesting,
123
125
  signPlatformEvent: () => signPlatformEvent,
124
126
  smsSegments: () => smsSegments,
@@ -134,6 +136,68 @@ __export(http_exports, {
134
136
  });
135
137
  module.exports = __toCommonJS(http_exports);
136
138
 
139
+ // src/http/bindRuntimeAuth.ts
140
+ function bindRuntimeAuth(details, binding) {
141
+ const policy = { ...details.auth };
142
+ const keys = Object.keys(binding);
143
+ const credentials = { ...binding };
144
+ const invalid = () => {
145
+ throw new Error("Invalid runtime authentication binding");
146
+ };
147
+ const allowedPolicy = policy.scheme === "jwt" ? [
148
+ "scheme",
149
+ "headerName",
150
+ "tokenPrefix",
151
+ "sessionSchema",
152
+ "allowedRoles",
153
+ "allowedPermissions"
154
+ ] : ["scheme", "headerName", "tokenPrefix"];
155
+ if (Object.keys(policy).some((key) => !allowedPolicy.includes(key)))
156
+ invalid();
157
+ for (const key of ["headerName", "tokenPrefix"]) {
158
+ if (policy[key] !== void 0 && (typeof policy[key] !== "string" || !policy[key]))
159
+ invalid();
160
+ }
161
+ if (policy.scheme === "jwt" && policy.allowedRoles === void 0 && policy.allowedPermissions === void 0)
162
+ invalid();
163
+ let authentication;
164
+ if (policy.scheme === "jwt") {
165
+ if (keys.some(
166
+ (key) => !["jwksPublicKeyUrl", "signatureKey", "decodeResource"].includes(key)
167
+ ))
168
+ invalid();
169
+ const keyFields = keys.filter((key) => key !== "decodeResource");
170
+ if (keyFields.length !== 1 || typeof credentials[keyFields[0]] !== "string" || !credentials[keyFields[0]])
171
+ invalid();
172
+ const decodeResource = credentials.decodeResource;
173
+ if (decodeResource !== void 0 && typeof decodeResource !== "function")
174
+ invalid();
175
+ delete credentials.decodeResource;
176
+ authentication = {
177
+ jwt: credentials,
178
+ ...decodeResource === void 0 ? {} : { decodeResource }
179
+ };
180
+ } else if (policy.scheme === "hmac") {
181
+ if (keys.length !== 1 || keys[0] !== "secretKeys" || !credentials.secretKeys || typeof credentials.secretKeys !== "object" || Array.isArray(credentials.secretKeys) || Object.keys(credentials.secretKeys).length === 0 || Object.values(credentials.secretKeys).some(
182
+ (key) => typeof key !== "string" || !key
183
+ ))
184
+ invalid();
185
+ authentication = { hmac: credentials };
186
+ } else {
187
+ return invalid();
188
+ }
189
+ delete policy.scheme;
190
+ for (const key of ["allowedRoles", "allowedPermissions"]) {
191
+ if (policy[key] !== void 0) {
192
+ const values = policy[key];
193
+ if (!Array.isArray(values) || values.some((value) => typeof value !== "string"))
194
+ invalid();
195
+ policy[key] = new Set(values);
196
+ }
197
+ }
198
+ return { ...details, auth: { ...policy, ...authentication } };
199
+ }
200
+
137
201
  // src/http/middleware/request/cors.middleware.ts
138
202
  var import_cors = __toESM(require("cors"));
139
203
  function cors(corsOptions) {
@@ -594,7 +658,7 @@ function createContext(schemaValidator) {
594
658
  }
595
659
 
596
660
  // src/http/router/discriminateBody.ts
597
- function discriminateBody(schemaValidator, body) {
661
+ function discriminateBody(schemaValidator, body, inputProjection) {
598
662
  if (body == null) {
599
663
  return void 0;
600
664
  }
@@ -644,7 +708,7 @@ function discriminateBody(schemaValidator, body) {
644
708
  parserType: "text",
645
709
  schema: maybeTypedBody
646
710
  };
647
- } else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
711
+ } else if ((inputProjection ?? schemaValidator.openapi(maybeTypedBody)).format === "binary") {
648
712
  return {
649
713
  contentType: "application/octet-stream",
650
714
  parserType: "file",
@@ -1074,15 +1138,23 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
1074
1138
  if (!parsedKeyId || !parsedTimestamp || !parsedNonce || !parsedSignature) {
1075
1139
  return invalidAuthorizationTokenFormat;
1076
1140
  }
1077
- const verificationResult = await auth.verificationFunction({
1078
- method: req?.method ?? "",
1079
- path: req?.path ?? "",
1080
- body: req?._rawBody ?? req?.body,
1081
- timestamp: new Date(parsedTimestamp),
1082
- nonce: parsedNonce,
1083
- signature: parsedSignature,
1084
- secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
1085
- });
1141
+ let verificationResult = false;
1142
+ const signedPaths = hmacSignedPaths(req);
1143
+ for (const path of signedPaths) {
1144
+ verificationResult = Boolean(await auth.verificationFunction({
1145
+ method: req?.method ?? "",
1146
+ path,
1147
+ body: req?._rawBody ?? req?.body,
1148
+ timestamp: new Date(parsedTimestamp),
1149
+ nonce: parsedNonce,
1150
+ signature: parsedSignature,
1151
+ secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
1152
+ }));
1153
+ if (verificationResult) {
1154
+ if (path !== signedPaths[0]) warnMountRelativeHmacPath();
1155
+ break;
1156
+ }
1157
+ }
1086
1158
  if (!verificationResult) {
1087
1159
  return invalidAuthorizationSignature;
1088
1160
  }
@@ -1249,6 +1321,25 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
1249
1321
  }
1250
1322
  }
1251
1323
  }
1324
+ function acceptMountRelativeHmacPaths() {
1325
+ const value = typeof process === "undefined" ? void 0 : process.env?.HMAC_ACCEPT_MOUNT_RELATIVE_PATHS;
1326
+ return value == null || !["false", "0"].includes(value.trim().toLowerCase());
1327
+ }
1328
+ var warnedMountRelativeHmacPath = false;
1329
+ function warnMountRelativeHmacPath() {
1330
+ if (warnedMountRelativeHmacPath) return;
1331
+ warnedMountRelativeHmacPath = true;
1332
+ console.warn(
1333
+ "Accepted an HMAC signature over the router-relative path. Sign the full request path instead; set HMAC_ACCEPT_MOUNT_RELATIVE_PATHS=false to reject the deprecated form."
1334
+ );
1335
+ }
1336
+ function hmacSignedPaths(req) {
1337
+ const relative = req?.path ?? "";
1338
+ const wire = (req?.originalUrl ?? req?.url ?? relative).split("?")[0] || relative;
1339
+ const paths = [wire];
1340
+ if (relative !== wire && acceptMountRelativeHmacPaths()) paths.push(relative);
1341
+ return paths;
1342
+ }
1252
1343
  async function parseRequestAuth(req, res, next) {
1253
1344
  const access = req.contractDetails.access;
1254
1345
  if (access === "public") {
@@ -1482,6 +1573,21 @@ function isRequestShape(maybeResponseShape) {
1482
1573
  return maybeResponseShape != null && ("body" in maybeResponseShape || "query" in maybeResponseShape || "params" in maybeResponseShape || "headers" in maybeResponseShape);
1483
1574
  }
1484
1575
 
1576
+ // src/http/middleware/internal/validationDiagnostic.ts
1577
+ var sendingDiagnostics = /* @__PURE__ */ new WeakSet();
1578
+ function isValidationDiagnostic(response) {
1579
+ return sendingDiagnostics.has(response);
1580
+ }
1581
+ function sendValidationDiagnostic(response, send) {
1582
+ const alreadySending = sendingDiagnostics.has(response);
1583
+ sendingDiagnostics.add(response);
1584
+ try {
1585
+ return send();
1586
+ } finally {
1587
+ if (!alreadySending) sendingDiagnostics.delete(response);
1588
+ }
1589
+ }
1590
+
1485
1591
  // src/http/middleware/request/parse.middleware.ts
1486
1592
  function parse(req, res, next) {
1487
1593
  const globalOptions = req._globalOptions?.();
@@ -1563,14 +1669,13 @@ function parse(req, res, next) {
1563
1669
  default:
1564
1670
  case void 0:
1565
1671
  case "error":
1566
- res.type("application/json");
1672
+ res.type("text/plain");
1567
1673
  res.status(400);
1568
1674
  if (hasSend(res)) {
1569
- res.send(
1570
- `${collectedParseErrors ?? (0, import_validator2.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
1675
+ const diagnostic = `${collectedParseErrors ?? (0, import_validator2.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
1571
1676
 
1572
- Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
1573
- );
1677
+ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`;
1678
+ sendValidationDiagnostic(res, () => res.send(diagnostic));
1574
1679
  } else {
1575
1680
  next?.(new Error("Request is not sendable."));
1576
1681
  }
@@ -1979,13 +2084,25 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1979
2084
  span: void 0
1980
2085
  },
1981
2086
  _rawBody: void 0,
1982
- _parsedVersions: void 0
2087
+ _parsedVersions: void 0,
2088
+ // hyper-express's content parser reads the body through its Request's
2089
+ // body readers; serve the body the call was given.
2090
+ buffer: async () => Buffer.from(
2091
+ typeof req.body === "string" ? req.body : JSON.stringify(req.body)
2092
+ ),
2093
+ json: async () => req.body,
2094
+ text: async () => typeof req.body === "string" ? req.body : JSON.stringify(req.body)
1983
2095
  };
1984
2096
  const res = {
1985
2097
  statusCode: 200,
2098
+ // hyper-express's response enrichment reads the status and headers
2099
+ // from these fields, which its own Response keeps.
2100
+ _status_code: 200,
2101
+ _headers: responseHeaders,
1986
2102
  status: (code) => {
1987
2103
  statusCode = code;
1988
2104
  res.statusCode = code;
2105
+ res._status_code = code;
1989
2106
  return res;
1990
2107
  },
1991
2108
  send: (message) => {
@@ -2005,6 +2122,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
2005
2122
  return res;
2006
2123
  },
2007
2124
  getHeader: (key) => responseHeaders[key],
2125
+ getHeaders: () => ({ ...responseHeaders }),
2126
+ locals: {},
2008
2127
  type: (contentType) => {
2009
2128
  responseHeaders["content-type"] = contentType;
2010
2129
  return res;
@@ -2023,31 +2142,59 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
2023
2142
  version
2024
2143
  };
2025
2144
  const executeMiddlewares = request?.executeMiddlewares ?? false;
2145
+ const cHandler = controllerHandler;
2026
2146
  if (executeMiddlewares && middlewares.length > 0) {
2027
- const allHandlers = [...middlewares];
2028
- let cursor = allHandlers.shift();
2029
- if (cursor) {
2030
- for (const fn of allHandlers) {
2031
- await cursor(req, res, (err) => {
2032
- if (err) {
2033
- throw err;
2147
+ const chain = middlewares;
2148
+ await new Promise((resolve, reject) => {
2149
+ let settled = false;
2150
+ let responded = false;
2151
+ let controllerStarted = false;
2152
+ const finish = (error) => {
2153
+ if (settled) return;
2154
+ settled = true;
2155
+ if (error) reject(error);
2156
+ else resolve();
2157
+ };
2158
+ res.on("finish", () => {
2159
+ responded = true;
2160
+ if (!controllerStarted) finish();
2161
+ });
2162
+ const run = async (index) => {
2163
+ try {
2164
+ if (index === chain.length) {
2165
+ controllerStarted = true;
2166
+ await cHandler(req, res, (err) => {
2167
+ if (err) {
2168
+ throw err;
2169
+ }
2170
+ });
2171
+ finish();
2172
+ return;
2034
2173
  }
2035
- cursor = fn;
2036
- });
2037
- }
2038
- await cursor(req, res, async (err) => {
2039
- if (err) {
2040
- throw err;
2174
+ let calledNext = false;
2175
+ await chain[index](req, res, (err) => {
2176
+ calledNext = true;
2177
+ if (err) {
2178
+ throw err;
2179
+ }
2180
+ return run(index + 1);
2181
+ });
2182
+ if (!calledNext && chain[index].length < 3 && !responded) {
2183
+ await run(index + 1);
2184
+ }
2185
+ } catch (error) {
2186
+ finish(error);
2041
2187
  }
2042
- });
2043
- }
2188
+ };
2189
+ void run(0);
2190
+ });
2191
+ } else {
2192
+ await cHandler(req, res, (err) => {
2193
+ if (err) {
2194
+ throw err;
2195
+ }
2196
+ });
2044
2197
  }
2045
- const cHandler = controllerHandler;
2046
- await cHandler(req, res, (err) => {
2047
- if (err) {
2048
- throw err;
2049
- }
2050
- });
2051
2198
  return {
2052
2199
  code: Number(statusCode),
2053
2200
  response: responseMessage,
@@ -3536,7 +3683,7 @@ function loadStripe() {
3536
3683
  function stripeGatewayFetch(settings, fetchImpl = fetch) {
3537
3684
  const base = new URL(settings.gatewayUrl);
3538
3685
  const basePath = base.pathname.replace(/\/+$/, "");
3539
- return async (input, init = {}) => {
3686
+ const signedFetch = async (input, init = {}) => {
3540
3687
  const original = new URL(
3541
3688
  typeof input === "string" || input instanceof URL ? input : input.url
3542
3689
  );
@@ -3576,6 +3723,13 @@ function stripeGatewayFetch(settings, fetchImpl = fetch) {
3576
3723
  }
3577
3724
  );
3578
3725
  };
3726
+ const preconnect = Reflect.get(fetchImpl, "preconnect");
3727
+ if (typeof preconnect === "function") {
3728
+ Object.defineProperty(signedFetch, "preconnect", {
3729
+ value: (_input, options2) => Reflect.apply(preconnect, fetchImpl, [base.origin, options2])
3730
+ });
3731
+ }
3732
+ return signedFetch;
3579
3733
  }
3580
3734
  function createStripeClient(options2 = {}) {
3581
3735
  const StripeClass = options2.Stripe ?? loadStripe();
@@ -4808,10 +4962,10 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4808
4962
  let bodySchema;
4809
4963
  let responsesSchemas;
4810
4964
  if (route.contractDetails.versions) {
4811
- const version2 = route.contractDetails.versions[index];
4965
+ const version2 = Object.values(route.contractDetails.versions)[index];
4966
+ responsesSchemas = version2.responses;
4812
4967
  if (version2.body && schemaValidator.parse(inputSchema, args).ok) {
4813
4968
  bodySchema = version2.body;
4814
- responsesSchemas = version2.responses;
4815
4969
  }
4816
4970
  } else {
4817
4971
  bodySchema = route.contractDetails.body;
@@ -4822,7 +4976,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4822
4976
  if (discriminatedBody) {
4823
4977
  switch (discriminatedBody.parserType) {
4824
4978
  case "json": {
4825
- parsedBody = (0, import_common13.safeStringify)(body);
4979
+ parsedBody = body === void 0 ? void 0 : JSON.stringify(body);
4826
4980
  break;
4827
4981
  }
4828
4982
  case "text": {
@@ -4888,11 +5042,11 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4888
5042
  ).toString();
4889
5043
  url += queryString ? `?${queryString}` : "";
4890
5044
  }
4891
- const response = await fetch(encodeURI(url), {
5045
+ const response = await fetch(url, {
4892
5046
  method: route.method.toUpperCase(),
4893
5047
  headers: {
4894
5048
  ...headers,
4895
- ...discriminatedBody?.contentType != "multipart/form-data" ? {
5049
+ ...discriminatedBody?.contentType != "multipart/form-data" && (contentType ?? discriminatedBody?.contentType) !== void 0 ? {
4896
5050
  "Content-Type": contentType ?? discriminatedBody?.contentType
4897
5051
  } : {}
4898
5052
  },
@@ -4970,7 +5124,12 @@ function isResponseCompiledSchema(schema) {
4970
5124
  }
4971
5125
 
4972
5126
  // src/http/middleware/response/parse.middleware.ts
5127
+ var sendingValidationError = /* @__PURE__ */ new WeakSet();
4973
5128
  function parse2(req, res, next) {
5129
+ if (sendingValidationError.has(res)) {
5130
+ next?.();
5131
+ return;
5132
+ }
4974
5133
  let headers;
4975
5134
  let responses;
4976
5135
  const globalOptions = req._globalOptions?.();
@@ -5013,15 +5172,35 @@ function parse2(req, res, next) {
5013
5172
  responses = responseSchemas.responses;
5014
5173
  }
5015
5174
  const statusCode = Number(res.statusCode);
5016
- const parsedResponse = schemaValidator.parse(
5017
- [400, 401, 404, 403, 500].includes(statusCode) ? schemaValidator.union([schemaValidator.string, responses?.[statusCode]]) : responses?.[statusCode],
5018
- res.bodyData
5019
- );
5175
+ const defaultErrorStatus = [400, 401, 404, 403, 500].includes(statusCode);
5176
+ const missingResponseSchema = !defaultErrorStatus && !Object.prototype.hasOwnProperty.call(responses, statusCode);
5177
+ if (missingResponseSchema && collapsedOptions !== "warning") {
5178
+ throw new Error(`Undocumented response status ${statusCode}`);
5179
+ }
5180
+ const declaredResponse = responses?.[statusCode];
5181
+ const defaultErrorResponse = () => {
5182
+ try {
5183
+ return schemaValidator.parse(
5184
+ schemaValidator.union([schemaValidator.string, declaredResponse]),
5185
+ res.bodyData
5186
+ );
5187
+ } catch (error) {
5188
+ if (declaredResponse === void 0) throw error;
5189
+ const declared = schemaValidator.parse(declaredResponse, res.bodyData);
5190
+ if (declared.ok) return declared;
5191
+ const message = schemaValidator.parse(
5192
+ schemaValidator.string,
5193
+ res.bodyData
5194
+ );
5195
+ return message.ok ? message : declared;
5196
+ }
5197
+ };
5198
+ const parsedResponse = missingResponseSchema ? { ok: true } : defaultErrorStatus ? defaultErrorResponse() : schemaValidator.parse(declaredResponse, res.bodyData);
5020
5199
  const parsedHeaders = schemaValidator.parse(
5021
5200
  headers ?? schemaValidator.unknown,
5022
5201
  res.getHeaders()
5023
5202
  );
5024
- const parseErrors = [];
5203
+ const parseErrors = missingResponseSchema ? [`Response: Undocumented response status ${statusCode}`] : [];
5025
5204
  if (!parsedHeaders.ok) {
5026
5205
  const headerErrors = (0, import_validator3.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
5027
5206
  if (headerErrors) {
@@ -5038,21 +5217,26 @@ function parse2(req, res, next) {
5038
5217
  }
5039
5218
  }
5040
5219
  if (parseErrors.length > 0) {
5041
- switch (req.contractDetails.options?.responseValidation) {
5220
+ switch (collapsedOptions) {
5042
5221
  default:
5043
5222
  case void 0:
5044
5223
  case "error":
5045
5224
  res.type("text/plain");
5046
5225
  res.status(500);
5047
5226
  if (hasSend(res)) {
5048
- res.send(
5049
- `Invalid response:
5227
+ sendingValidationError.add(res);
5228
+ try {
5229
+ res.send(
5230
+ `Invalid response:
5050
5231
  ${parseErrors.join(
5051
- "\n\n"
5052
- )}
5232
+ "\n\n"
5233
+ )}
5053
5234
 
5054
5235
  Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
5055
- );
5236
+ );
5237
+ } finally {
5238
+ sendingValidationError.delete(res);
5239
+ }
5056
5240
  } else {
5057
5241
  next?.(new Error("Response is not sendable."));
5058
5242
  }
@@ -5063,8 +5247,6 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
5063
5247
  ${parseErrors.join("\n\n")}`
5064
5248
  );
5065
5249
  break;
5066
- case "none":
5067
- break;
5068
5250
  }
5069
5251
  }
5070
5252
  next?.();
@@ -5106,6 +5288,10 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
5106
5288
  originalSend.call(instance, data);
5107
5289
  return;
5108
5290
  }
5291
+ if (typeof data === "string" && isValidationDiagnostic(res)) {
5292
+ originalSend.call(instance, data);
5293
+ return;
5294
+ }
5109
5295
  let responses;
5110
5296
  if (req.contractDetails.responses == null && (req.contractDetails.versions == null || Object.values(req.contractDetails.versions).some(
5111
5297
  (version) => version?.responses == null
@@ -5115,7 +5301,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
5115
5301
  if (req.contractDetails.responses != null) {
5116
5302
  responses = req.contractDetails.responses;
5117
5303
  } else {
5118
- responses = req.contractDetails.versions[req.version].responses;
5304
+ responses = req.contractDetails.versions[req.version]?.responses ?? {};
5119
5305
  }
5120
5306
  }
5121
5307
  const responseBodies = discriminateResponseBodies(
@@ -5337,7 +5523,16 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5337
5523
  for (const key in discriminatedResponseBodiesResult) {
5338
5524
  coercedResponses[key] = {
5339
5525
  description: httpStatusCodes_default[key],
5340
- content: contentResolver(
5526
+ // Undefined/void represent no response body, not a JSON null payload.
5527
+ // Check the schema kind rather than its OpenAPI projection: null and
5528
+ // nullish also project to null but still admit an actual JSON body.
5529
+ content: schemaValidator.undefined !== void 0 && typedSchemaValidator.isInstanceOf(
5530
+ discriminatedResponseBodiesResult[key].schema,
5531
+ schemaValidator.undefined
5532
+ ) || schemaValidator.void !== void 0 && typedSchemaValidator.isInstanceOf(
5533
+ discriminatedResponseBodiesResult[key].schema,
5534
+ schemaValidator.void
5535
+ ) ? void 0 : contentResolver(
5341
5536
  schemaValidator,
5342
5537
  discriminatedResponseBodiesResult[key].schema,
5343
5538
  discriminatedResponseBodiesResult[key].contentType
@@ -5373,15 +5568,21 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5373
5568
  operationObject.parameters?.push({
5374
5569
  name: key,
5375
5570
  in: "path",
5571
+ required: true,
5376
5572
  schema: typedSchemaValidator.openapi(params[key])
5377
5573
  });
5378
5574
  }
5379
5575
  }
5380
- const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body) : null;
5576
+ const adapter = schemaValidator;
5577
+ const bareProjection = body && adapter.isSchema(body) ? adapter.openapiRequestBody?.(body) : void 0;
5578
+ const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body, bareProjection?.schema) : null;
5381
5579
  if (discriminatedBodyResult) {
5580
+ const projection = bareProjection ?? adapter.openapiRequestBody?.(discriminatedBodyResult.schema);
5382
5581
  operationObject.requestBody = {
5383
- required: true,
5384
- content: contentResolver(
5582
+ required: projection?.required ?? true,
5583
+ content: projection ? {
5584
+ [discriminatedBodyResult.contentType]: { schema: projection.schema }
5585
+ } : contentResolver(
5385
5586
  schemaValidator,
5386
5587
  discriminatedBodyResult.schema,
5387
5588
  discriminatedBodyResult.contentType
@@ -5416,25 +5617,14 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5416
5617
  content: contentResolver(schemaValidator, schemaValidator.string)
5417
5618
  };
5418
5619
  if ("basic" in auth) {
5419
- operationObject.security = [
5420
- {
5421
- basic: Array.from(
5422
- "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
5423
- )
5424
- }
5425
- ];
5620
+ operationObject.security = [{ basic: [] }];
5426
5621
  securitySchemes["basic"] = {
5427
5622
  type: "http",
5428
5623
  scheme: "basic"
5429
5624
  };
5430
5625
  } else if (auth) {
5431
- operationObject.security = [
5432
- {
5433
- [auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
5434
- "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
5435
- )
5436
- }
5437
- ];
5626
+ const schemeName = auth.headerName || "Authorization";
5627
+ operationObject.security = [{ [schemeName]: [] }];
5438
5628
  if (auth.headerName && auth.headerName !== "Authorization") {
5439
5629
  securitySchemes[auth.headerName] = {
5440
5630
  type: "apiKey",
@@ -5665,6 +5855,22 @@ var RateLimiter = class {
5665
5855
  return READ_METHODS.has(method.toUpperCase()) ? "read" : "write";
5666
5856
  }
5667
5857
  };
5858
+
5859
+ // src/http/registerDelegatedSurface.ts
5860
+ function registerDelegatedSurface(surface, mount, register) {
5861
+ if (!surface.id || !surface.provider || surface.coverage !== "provider-owned" || !surface.client.package || !surface.client.export || surface.runtime.basePath !== "runtime-resolved" || typeof surface.runtime.openApi !== "boolean" || !Array.isArray(surface.callbacks) || surface.callbacks.some(
5862
+ (callback) => typeof callback !== "string" || !callback.startsWith("/")
5863
+ )) {
5864
+ throw new Error("Invalid delegated HTTP surface descriptor");
5865
+ }
5866
+ const paths = Array.isArray(mount.paths) ? mount.paths : [mount.paths];
5867
+ if (mount.basePath !== "" && !mount.basePath.startsWith("/") || paths.length === 0 || paths.some(
5868
+ (path) => !path.startsWith("/") || mount.basePath !== "" && !path.startsWith(mount.basePath + "/")
5869
+ )) {
5870
+ throw new Error("Invalid delegated HTTP surface mount");
5871
+ }
5872
+ return register(mount);
5873
+ }
5668
5874
  // Annotate the CommonJS export names for ESM import in node:
5669
5875
  0 && (module.exports = {
5670
5876
  ATTR_API_NAME,
@@ -5703,6 +5909,7 @@ var RateLimiter = class {
5703
5909
  auditComplianceAccess,
5704
5910
  auditEnabled,
5705
5911
  auditEventType,
5912
+ bindRuntimeAuth,
5706
5913
  createContext,
5707
5914
  createEmailClient,
5708
5915
  createHmacToken,
@@ -5756,6 +5963,7 @@ var RateLimiter = class {
5756
5963
  put,
5757
5964
  recordMetric,
5758
5965
  recordRequestAudit,
5966
+ registerDelegatedSurface,
5759
5967
  resetProcessHandlersForTesting,
5760
5968
  signPlatformEvent,
5761
5969
  smsSegments,