@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.
- package/bin/gateway-mock/routes/payments-context.mjs +260 -0
- package/bin/gateway-mock/routes/payments.mjs +47 -1
- package/lib/http/bindRuntimeAuth.d.ts +53 -0
- package/lib/http/bindRuntimeAuth.d.ts.map +1 -0
- package/lib/http/bindRuntimeAuth.test.d.ts +2 -0
- package/lib/http/bindRuntimeAuth.test.d.ts.map +1 -0
- package/lib/http/index.d.ts +2 -0
- package/lib/http/index.d.ts.map +1 -1
- package/lib/http/index.js +284 -76
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +282 -76
- package/lib/http/index.mjs.map +1 -1
- package/lib/http/mcpGenerator/mcpGenerator.d.ts.map +1 -1
- package/lib/http/middleware/internal/validationDiagnostic.d.ts +3 -0
- package/lib/http/middleware/internal/validationDiagnostic.d.ts.map +1 -0
- package/lib/http/middleware/request/auth.middleware.d.ts +0 -11
- package/lib/http/middleware/request/auth.middleware.d.ts.map +1 -1
- package/lib/http/middleware/request/parse.middleware.d.ts.map +1 -1
- package/lib/http/middleware/response/enrichExpressLikeSend.middleware.d.ts.map +1 -1
- package/lib/http/middleware/response/parse.middleware.d.ts.map +1 -1
- package/lib/http/openApiV3Generator/openApiV3Generator.d.ts.map +1 -1
- package/lib/http/paymentsClient.d.ts.map +1 -1
- package/lib/http/registerDelegatedSurface.d.ts +28 -0
- package/lib/http/registerDelegatedSurface.d.ts.map +1 -0
- package/lib/http/registerDelegatedSurface.test.d.ts +2 -0
- package/lib/http/registerDelegatedSurface.test.d.ts.map +1 -0
- package/lib/http/router/discriminateBody.d.ts +2 -1
- package/lib/http/router/discriminateBody.d.ts.map +1 -1
- package/lib/http/router/expressLikeRouter.d.ts.map +1 -1
- package/lib/persistence/compliantField.d.ts.map +1 -1
- package/lib/persistence/index.js +1 -6
- package/lib/persistence/index.js.map +1 -1
- package/lib/persistence/index.mjs +1 -6
- package/lib/persistence/index.mjs.map +1 -1
- package/lib/ws/asyncApiV3Generator/asyncApiV3Generator.d.ts.map +1 -1
- package/lib/ws/index.js +94 -8
- package/lib/ws/index.js.map +1 -1
- package/lib/ws/index.mjs +94 -8
- package/lib/ws/index.mjs.map +1 -1
- package/package.json +4 -4
package/lib/http/index.mjs
CHANGED
|
@@ -1,3 +1,65 @@
|
|
|
1
|
+
// src/http/bindRuntimeAuth.ts
|
|
2
|
+
function bindRuntimeAuth(details, binding) {
|
|
3
|
+
const policy = { ...details.auth };
|
|
4
|
+
const keys = Object.keys(binding);
|
|
5
|
+
const credentials = { ...binding };
|
|
6
|
+
const invalid = () => {
|
|
7
|
+
throw new Error("Invalid runtime authentication binding");
|
|
8
|
+
};
|
|
9
|
+
const allowedPolicy = policy.scheme === "jwt" ? [
|
|
10
|
+
"scheme",
|
|
11
|
+
"headerName",
|
|
12
|
+
"tokenPrefix",
|
|
13
|
+
"sessionSchema",
|
|
14
|
+
"allowedRoles",
|
|
15
|
+
"allowedPermissions"
|
|
16
|
+
] : ["scheme", "headerName", "tokenPrefix"];
|
|
17
|
+
if (Object.keys(policy).some((key) => !allowedPolicy.includes(key)))
|
|
18
|
+
invalid();
|
|
19
|
+
for (const key of ["headerName", "tokenPrefix"]) {
|
|
20
|
+
if (policy[key] !== void 0 && (typeof policy[key] !== "string" || !policy[key]))
|
|
21
|
+
invalid();
|
|
22
|
+
}
|
|
23
|
+
if (policy.scheme === "jwt" && policy.allowedRoles === void 0 && policy.allowedPermissions === void 0)
|
|
24
|
+
invalid();
|
|
25
|
+
let authentication;
|
|
26
|
+
if (policy.scheme === "jwt") {
|
|
27
|
+
if (keys.some(
|
|
28
|
+
(key) => !["jwksPublicKeyUrl", "signatureKey", "decodeResource"].includes(key)
|
|
29
|
+
))
|
|
30
|
+
invalid();
|
|
31
|
+
const keyFields = keys.filter((key) => key !== "decodeResource");
|
|
32
|
+
if (keyFields.length !== 1 || typeof credentials[keyFields[0]] !== "string" || !credentials[keyFields[0]])
|
|
33
|
+
invalid();
|
|
34
|
+
const decodeResource = credentials.decodeResource;
|
|
35
|
+
if (decodeResource !== void 0 && typeof decodeResource !== "function")
|
|
36
|
+
invalid();
|
|
37
|
+
delete credentials.decodeResource;
|
|
38
|
+
authentication = {
|
|
39
|
+
jwt: credentials,
|
|
40
|
+
...decodeResource === void 0 ? {} : { decodeResource }
|
|
41
|
+
};
|
|
42
|
+
} else if (policy.scheme === "hmac") {
|
|
43
|
+
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(
|
|
44
|
+
(key) => typeof key !== "string" || !key
|
|
45
|
+
))
|
|
46
|
+
invalid();
|
|
47
|
+
authentication = { hmac: credentials };
|
|
48
|
+
} else {
|
|
49
|
+
return invalid();
|
|
50
|
+
}
|
|
51
|
+
delete policy.scheme;
|
|
52
|
+
for (const key of ["allowedRoles", "allowedPermissions"]) {
|
|
53
|
+
if (policy[key] !== void 0) {
|
|
54
|
+
const values = policy[key];
|
|
55
|
+
if (!Array.isArray(values) || values.some((value) => typeof value !== "string"))
|
|
56
|
+
invalid();
|
|
57
|
+
policy[key] = new Set(values);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { ...details, auth: { ...policy, ...authentication } };
|
|
61
|
+
}
|
|
62
|
+
|
|
1
63
|
// src/http/middleware/request/cors.middleware.ts
|
|
2
64
|
import corsMiddleware from "cors";
|
|
3
65
|
function cors(corsOptions) {
|
|
@@ -473,7 +535,7 @@ function createContext(schemaValidator) {
|
|
|
473
535
|
}
|
|
474
536
|
|
|
475
537
|
// src/http/router/discriminateBody.ts
|
|
476
|
-
function discriminateBody(schemaValidator, body) {
|
|
538
|
+
function discriminateBody(schemaValidator, body, inputProjection) {
|
|
477
539
|
if (body == null) {
|
|
478
540
|
return void 0;
|
|
479
541
|
}
|
|
@@ -523,7 +585,7 @@ function discriminateBody(schemaValidator, body) {
|
|
|
523
585
|
parserType: "text",
|
|
524
586
|
schema: maybeTypedBody
|
|
525
587
|
};
|
|
526
|
-
} else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
|
|
588
|
+
} else if ((inputProjection ?? schemaValidator.openapi(maybeTypedBody)).format === "binary") {
|
|
527
589
|
return {
|
|
528
590
|
contentType: "application/octet-stream",
|
|
529
591
|
parserType: "file",
|
|
@@ -955,15 +1017,23 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
|
|
|
955
1017
|
if (!parsedKeyId || !parsedTimestamp || !parsedNonce || !parsedSignature) {
|
|
956
1018
|
return invalidAuthorizationTokenFormat;
|
|
957
1019
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1020
|
+
let verificationResult = false;
|
|
1021
|
+
const signedPaths = hmacSignedPaths(req);
|
|
1022
|
+
for (const path of signedPaths) {
|
|
1023
|
+
verificationResult = Boolean(await auth.verificationFunction({
|
|
1024
|
+
method: req?.method ?? "",
|
|
1025
|
+
path,
|
|
1026
|
+
body: req?._rawBody ?? req?.body,
|
|
1027
|
+
timestamp: new Date(parsedTimestamp),
|
|
1028
|
+
nonce: parsedNonce,
|
|
1029
|
+
signature: parsedSignature,
|
|
1030
|
+
secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
|
|
1031
|
+
}));
|
|
1032
|
+
if (verificationResult) {
|
|
1033
|
+
if (path !== signedPaths[0]) warnMountRelativeHmacPath();
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
967
1037
|
if (!verificationResult) {
|
|
968
1038
|
return invalidAuthorizationSignature;
|
|
969
1039
|
}
|
|
@@ -1130,6 +1200,25 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
|
|
|
1130
1200
|
}
|
|
1131
1201
|
}
|
|
1132
1202
|
}
|
|
1203
|
+
function acceptMountRelativeHmacPaths() {
|
|
1204
|
+
const value = typeof process === "undefined" ? void 0 : process.env?.HMAC_ACCEPT_MOUNT_RELATIVE_PATHS;
|
|
1205
|
+
return value == null || !["false", "0"].includes(value.trim().toLowerCase());
|
|
1206
|
+
}
|
|
1207
|
+
var warnedMountRelativeHmacPath = false;
|
|
1208
|
+
function warnMountRelativeHmacPath() {
|
|
1209
|
+
if (warnedMountRelativeHmacPath) return;
|
|
1210
|
+
warnedMountRelativeHmacPath = true;
|
|
1211
|
+
console.warn(
|
|
1212
|
+
"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."
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
function hmacSignedPaths(req) {
|
|
1216
|
+
const relative = req?.path ?? "";
|
|
1217
|
+
const wire = (req?.originalUrl ?? req?.url ?? relative).split("?")[0] || relative;
|
|
1218
|
+
const paths = [wire];
|
|
1219
|
+
if (relative !== wire && acceptMountRelativeHmacPaths()) paths.push(relative);
|
|
1220
|
+
return paths;
|
|
1221
|
+
}
|
|
1133
1222
|
async function parseRequestAuth(req, res, next) {
|
|
1134
1223
|
const access = req.contractDetails.access;
|
|
1135
1224
|
if (access === "public") {
|
|
@@ -1367,6 +1456,21 @@ function isRequestShape(maybeResponseShape) {
|
|
|
1367
1456
|
return maybeResponseShape != null && ("body" in maybeResponseShape || "query" in maybeResponseShape || "params" in maybeResponseShape || "headers" in maybeResponseShape);
|
|
1368
1457
|
}
|
|
1369
1458
|
|
|
1459
|
+
// src/http/middleware/internal/validationDiagnostic.ts
|
|
1460
|
+
var sendingDiagnostics = /* @__PURE__ */ new WeakSet();
|
|
1461
|
+
function isValidationDiagnostic(response) {
|
|
1462
|
+
return sendingDiagnostics.has(response);
|
|
1463
|
+
}
|
|
1464
|
+
function sendValidationDiagnostic(response, send) {
|
|
1465
|
+
const alreadySending = sendingDiagnostics.has(response);
|
|
1466
|
+
sendingDiagnostics.add(response);
|
|
1467
|
+
try {
|
|
1468
|
+
return send();
|
|
1469
|
+
} finally {
|
|
1470
|
+
if (!alreadySending) sendingDiagnostics.delete(response);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1370
1474
|
// src/http/middleware/request/parse.middleware.ts
|
|
1371
1475
|
function parse(req, res, next) {
|
|
1372
1476
|
const globalOptions = req._globalOptions?.();
|
|
@@ -1448,14 +1552,13 @@ function parse(req, res, next) {
|
|
|
1448
1552
|
default:
|
|
1449
1553
|
case void 0:
|
|
1450
1554
|
case "error":
|
|
1451
|
-
res.type("
|
|
1555
|
+
res.type("text/plain");
|
|
1452
1556
|
res.status(400);
|
|
1453
1557
|
if (hasSend(res)) {
|
|
1454
|
-
|
|
1455
|
-
`${collectedParseErrors ?? prettyPrintParseErrors2(parsedRequest.errors, "Request")}
|
|
1558
|
+
const diagnostic = `${collectedParseErrors ?? prettyPrintParseErrors2(parsedRequest.errors, "Request")}
|
|
1456
1559
|
|
|
1457
|
-
Correlation id: ${req.context.correlationId ?? "No correlation ID"}
|
|
1458
|
-
);
|
|
1560
|
+
Correlation id: ${req.context.correlationId ?? "No correlation ID"}`;
|
|
1561
|
+
sendValidationDiagnostic(res, () => res.send(diagnostic));
|
|
1459
1562
|
} else {
|
|
1460
1563
|
next?.(new Error("Request is not sendable."));
|
|
1461
1564
|
}
|
|
@@ -1864,13 +1967,25 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
|
1864
1967
|
span: void 0
|
|
1865
1968
|
},
|
|
1866
1969
|
_rawBody: void 0,
|
|
1867
|
-
_parsedVersions: void 0
|
|
1970
|
+
_parsedVersions: void 0,
|
|
1971
|
+
// hyper-express's content parser reads the body through its Request's
|
|
1972
|
+
// body readers; serve the body the call was given.
|
|
1973
|
+
buffer: async () => Buffer.from(
|
|
1974
|
+
typeof req.body === "string" ? req.body : JSON.stringify(req.body)
|
|
1975
|
+
),
|
|
1976
|
+
json: async () => req.body,
|
|
1977
|
+
text: async () => typeof req.body === "string" ? req.body : JSON.stringify(req.body)
|
|
1868
1978
|
};
|
|
1869
1979
|
const res = {
|
|
1870
1980
|
statusCode: 200,
|
|
1981
|
+
// hyper-express's response enrichment reads the status and headers
|
|
1982
|
+
// from these fields, which its own Response keeps.
|
|
1983
|
+
_status_code: 200,
|
|
1984
|
+
_headers: responseHeaders,
|
|
1871
1985
|
status: (code) => {
|
|
1872
1986
|
statusCode = code;
|
|
1873
1987
|
res.statusCode = code;
|
|
1988
|
+
res._status_code = code;
|
|
1874
1989
|
return res;
|
|
1875
1990
|
},
|
|
1876
1991
|
send: (message) => {
|
|
@@ -1890,6 +2005,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
|
1890
2005
|
return res;
|
|
1891
2006
|
},
|
|
1892
2007
|
getHeader: (key) => responseHeaders[key],
|
|
2008
|
+
getHeaders: () => ({ ...responseHeaders }),
|
|
2009
|
+
locals: {},
|
|
1893
2010
|
type: (contentType) => {
|
|
1894
2011
|
responseHeaders["content-type"] = contentType;
|
|
1895
2012
|
return res;
|
|
@@ -1908,31 +2025,59 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
|
1908
2025
|
version
|
|
1909
2026
|
};
|
|
1910
2027
|
const executeMiddlewares = request?.executeMiddlewares ?? false;
|
|
2028
|
+
const cHandler = controllerHandler;
|
|
1911
2029
|
if (executeMiddlewares && middlewares.length > 0) {
|
|
1912
|
-
const
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2030
|
+
const chain = middlewares;
|
|
2031
|
+
await new Promise((resolve, reject) => {
|
|
2032
|
+
let settled = false;
|
|
2033
|
+
let responded = false;
|
|
2034
|
+
let controllerStarted = false;
|
|
2035
|
+
const finish = (error) => {
|
|
2036
|
+
if (settled) return;
|
|
2037
|
+
settled = true;
|
|
2038
|
+
if (error) reject(error);
|
|
2039
|
+
else resolve();
|
|
2040
|
+
};
|
|
2041
|
+
res.on("finish", () => {
|
|
2042
|
+
responded = true;
|
|
2043
|
+
if (!controllerStarted) finish();
|
|
2044
|
+
});
|
|
2045
|
+
const run = async (index) => {
|
|
2046
|
+
try {
|
|
2047
|
+
if (index === chain.length) {
|
|
2048
|
+
controllerStarted = true;
|
|
2049
|
+
await cHandler(req, res, (err) => {
|
|
2050
|
+
if (err) {
|
|
2051
|
+
throw err;
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
finish();
|
|
2055
|
+
return;
|
|
1919
2056
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
2057
|
+
let calledNext = false;
|
|
2058
|
+
await chain[index](req, res, (err) => {
|
|
2059
|
+
calledNext = true;
|
|
2060
|
+
if (err) {
|
|
2061
|
+
throw err;
|
|
2062
|
+
}
|
|
2063
|
+
return run(index + 1);
|
|
2064
|
+
});
|
|
2065
|
+
if (!calledNext && chain[index].length < 3 && !responded) {
|
|
2066
|
+
await run(index + 1);
|
|
2067
|
+
}
|
|
2068
|
+
} catch (error) {
|
|
2069
|
+
finish(error);
|
|
1926
2070
|
}
|
|
1927
|
-
}
|
|
1928
|
-
|
|
2071
|
+
};
|
|
2072
|
+
void run(0);
|
|
2073
|
+
});
|
|
2074
|
+
} else {
|
|
2075
|
+
await cHandler(req, res, (err) => {
|
|
2076
|
+
if (err) {
|
|
2077
|
+
throw err;
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
1929
2080
|
}
|
|
1930
|
-
const cHandler = controllerHandler;
|
|
1931
|
-
await cHandler(req, res, (err) => {
|
|
1932
|
-
if (err) {
|
|
1933
|
-
throw err;
|
|
1934
|
-
}
|
|
1935
|
-
});
|
|
1936
2081
|
return {
|
|
1937
2082
|
code: Number(statusCode),
|
|
1938
2083
|
response: responseMessage,
|
|
@@ -3420,7 +3565,7 @@ function loadStripe() {
|
|
|
3420
3565
|
function stripeGatewayFetch(settings, fetchImpl = fetch) {
|
|
3421
3566
|
const base = new URL(settings.gatewayUrl);
|
|
3422
3567
|
const basePath = base.pathname.replace(/\/+$/, "");
|
|
3423
|
-
|
|
3568
|
+
const signedFetch = async (input, init = {}) => {
|
|
3424
3569
|
const original = new URL(
|
|
3425
3570
|
typeof input === "string" || input instanceof URL ? input : input.url
|
|
3426
3571
|
);
|
|
@@ -3460,6 +3605,13 @@ function stripeGatewayFetch(settings, fetchImpl = fetch) {
|
|
|
3460
3605
|
}
|
|
3461
3606
|
);
|
|
3462
3607
|
};
|
|
3608
|
+
const preconnect = Reflect.get(fetchImpl, "preconnect");
|
|
3609
|
+
if (typeof preconnect === "function") {
|
|
3610
|
+
Object.defineProperty(signedFetch, "preconnect", {
|
|
3611
|
+
value: (_input, options2) => Reflect.apply(preconnect, fetchImpl, [base.origin, options2])
|
|
3612
|
+
});
|
|
3613
|
+
}
|
|
3614
|
+
return signedFetch;
|
|
3463
3615
|
}
|
|
3464
3616
|
function createStripeClient(options2 = {}) {
|
|
3465
3617
|
const StripeClass = options2.Stripe ?? loadStripe();
|
|
@@ -4692,10 +4844,10 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
|
|
|
4692
4844
|
let bodySchema;
|
|
4693
4845
|
let responsesSchemas;
|
|
4694
4846
|
if (route.contractDetails.versions) {
|
|
4695
|
-
const version2 = route.contractDetails.versions[index];
|
|
4847
|
+
const version2 = Object.values(route.contractDetails.versions)[index];
|
|
4848
|
+
responsesSchemas = version2.responses;
|
|
4696
4849
|
if (version2.body && schemaValidator.parse(inputSchema, args).ok) {
|
|
4697
4850
|
bodySchema = version2.body;
|
|
4698
|
-
responsesSchemas = version2.responses;
|
|
4699
4851
|
}
|
|
4700
4852
|
} else {
|
|
4701
4853
|
bodySchema = route.contractDetails.body;
|
|
@@ -4706,7 +4858,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
|
|
|
4706
4858
|
if (discriminatedBody) {
|
|
4707
4859
|
switch (discriminatedBody.parserType) {
|
|
4708
4860
|
case "json": {
|
|
4709
|
-
parsedBody =
|
|
4861
|
+
parsedBody = body === void 0 ? void 0 : JSON.stringify(body);
|
|
4710
4862
|
break;
|
|
4711
4863
|
}
|
|
4712
4864
|
case "text": {
|
|
@@ -4772,11 +4924,11 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
|
|
|
4772
4924
|
).toString();
|
|
4773
4925
|
url += queryString ? `?${queryString}` : "";
|
|
4774
4926
|
}
|
|
4775
|
-
const response = await fetch(
|
|
4927
|
+
const response = await fetch(url, {
|
|
4776
4928
|
method: route.method.toUpperCase(),
|
|
4777
4929
|
headers: {
|
|
4778
4930
|
...headers,
|
|
4779
|
-
...discriminatedBody?.contentType != "multipart/form-data" ? {
|
|
4931
|
+
...discriminatedBody?.contentType != "multipart/form-data" && (contentType ?? discriminatedBody?.contentType) !== void 0 ? {
|
|
4780
4932
|
"Content-Type": contentType ?? discriminatedBody?.contentType
|
|
4781
4933
|
} : {}
|
|
4782
4934
|
},
|
|
@@ -4856,7 +5008,12 @@ function isResponseCompiledSchema(schema) {
|
|
|
4856
5008
|
}
|
|
4857
5009
|
|
|
4858
5010
|
// src/http/middleware/response/parse.middleware.ts
|
|
5011
|
+
var sendingValidationError = /* @__PURE__ */ new WeakSet();
|
|
4859
5012
|
function parse2(req, res, next) {
|
|
5013
|
+
if (sendingValidationError.has(res)) {
|
|
5014
|
+
next?.();
|
|
5015
|
+
return;
|
|
5016
|
+
}
|
|
4860
5017
|
let headers;
|
|
4861
5018
|
let responses;
|
|
4862
5019
|
const globalOptions = req._globalOptions?.();
|
|
@@ -4899,15 +5056,35 @@ function parse2(req, res, next) {
|
|
|
4899
5056
|
responses = responseSchemas.responses;
|
|
4900
5057
|
}
|
|
4901
5058
|
const statusCode = Number(res.statusCode);
|
|
4902
|
-
const
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
5059
|
+
const defaultErrorStatus = [400, 401, 404, 403, 500].includes(statusCode);
|
|
5060
|
+
const missingResponseSchema = !defaultErrorStatus && !Object.prototype.hasOwnProperty.call(responses, statusCode);
|
|
5061
|
+
if (missingResponseSchema && collapsedOptions !== "warning") {
|
|
5062
|
+
throw new Error(`Undocumented response status ${statusCode}`);
|
|
5063
|
+
}
|
|
5064
|
+
const declaredResponse = responses?.[statusCode];
|
|
5065
|
+
const defaultErrorResponse = () => {
|
|
5066
|
+
try {
|
|
5067
|
+
return schemaValidator.parse(
|
|
5068
|
+
schemaValidator.union([schemaValidator.string, declaredResponse]),
|
|
5069
|
+
res.bodyData
|
|
5070
|
+
);
|
|
5071
|
+
} catch (error) {
|
|
5072
|
+
if (declaredResponse === void 0) throw error;
|
|
5073
|
+
const declared = schemaValidator.parse(declaredResponse, res.bodyData);
|
|
5074
|
+
if (declared.ok) return declared;
|
|
5075
|
+
const message = schemaValidator.parse(
|
|
5076
|
+
schemaValidator.string,
|
|
5077
|
+
res.bodyData
|
|
5078
|
+
);
|
|
5079
|
+
return message.ok ? message : declared;
|
|
5080
|
+
}
|
|
5081
|
+
};
|
|
5082
|
+
const parsedResponse = missingResponseSchema ? { ok: true } : defaultErrorStatus ? defaultErrorResponse() : schemaValidator.parse(declaredResponse, res.bodyData);
|
|
4906
5083
|
const parsedHeaders = schemaValidator.parse(
|
|
4907
5084
|
headers ?? schemaValidator.unknown,
|
|
4908
5085
|
res.getHeaders()
|
|
4909
5086
|
);
|
|
4910
|
-
const parseErrors = [];
|
|
5087
|
+
const parseErrors = missingResponseSchema ? [`Response: Undocumented response status ${statusCode}`] : [];
|
|
4911
5088
|
if (!parsedHeaders.ok) {
|
|
4912
5089
|
const headerErrors = prettyPrintParseErrors3(parsedHeaders.errors, "Header");
|
|
4913
5090
|
if (headerErrors) {
|
|
@@ -4924,21 +5101,26 @@ function parse2(req, res, next) {
|
|
|
4924
5101
|
}
|
|
4925
5102
|
}
|
|
4926
5103
|
if (parseErrors.length > 0) {
|
|
4927
|
-
switch (
|
|
5104
|
+
switch (collapsedOptions) {
|
|
4928
5105
|
default:
|
|
4929
5106
|
case void 0:
|
|
4930
5107
|
case "error":
|
|
4931
5108
|
res.type("text/plain");
|
|
4932
5109
|
res.status(500);
|
|
4933
5110
|
if (hasSend(res)) {
|
|
4934
|
-
|
|
4935
|
-
|
|
5111
|
+
sendingValidationError.add(res);
|
|
5112
|
+
try {
|
|
5113
|
+
res.send(
|
|
5114
|
+
`Invalid response:
|
|
4936
5115
|
${parseErrors.join(
|
|
4937
|
-
|
|
4938
|
-
|
|
5116
|
+
"\n\n"
|
|
5117
|
+
)}
|
|
4939
5118
|
|
|
4940
5119
|
Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
|
4941
|
-
|
|
5120
|
+
);
|
|
5121
|
+
} finally {
|
|
5122
|
+
sendingValidationError.delete(res);
|
|
5123
|
+
}
|
|
4942
5124
|
} else {
|
|
4943
5125
|
next?.(new Error("Response is not sendable."));
|
|
4944
5126
|
}
|
|
@@ -4949,8 +5131,6 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
|
|
4949
5131
|
${parseErrors.join("\n\n")}`
|
|
4950
5132
|
);
|
|
4951
5133
|
break;
|
|
4952
|
-
case "none":
|
|
4953
|
-
break;
|
|
4954
5134
|
}
|
|
4955
5135
|
}
|
|
4956
5136
|
next?.();
|
|
@@ -5004,6 +5184,10 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
|
|
|
5004
5184
|
originalSend.call(instance, data);
|
|
5005
5185
|
return;
|
|
5006
5186
|
}
|
|
5187
|
+
if (typeof data === "string" && isValidationDiagnostic(res)) {
|
|
5188
|
+
originalSend.call(instance, data);
|
|
5189
|
+
return;
|
|
5190
|
+
}
|
|
5007
5191
|
let responses;
|
|
5008
5192
|
if (req.contractDetails.responses == null && (req.contractDetails.versions == null || Object.values(req.contractDetails.versions).some(
|
|
5009
5193
|
(version) => version?.responses == null
|
|
@@ -5013,7 +5197,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
|
|
|
5013
5197
|
if (req.contractDetails.responses != null) {
|
|
5014
5198
|
responses = req.contractDetails.responses;
|
|
5015
5199
|
} else {
|
|
5016
|
-
responses = req.contractDetails.versions[req.version]
|
|
5200
|
+
responses = req.contractDetails.versions[req.version]?.responses ?? {};
|
|
5017
5201
|
}
|
|
5018
5202
|
}
|
|
5019
5203
|
const responseBodies = discriminateResponseBodies(
|
|
@@ -5235,7 +5419,16 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
|
|
|
5235
5419
|
for (const key in discriminatedResponseBodiesResult) {
|
|
5236
5420
|
coercedResponses[key] = {
|
|
5237
5421
|
description: httpStatusCodes_default[key],
|
|
5238
|
-
|
|
5422
|
+
// Undefined/void represent no response body, not a JSON null payload.
|
|
5423
|
+
// Check the schema kind rather than its OpenAPI projection: null and
|
|
5424
|
+
// nullish also project to null but still admit an actual JSON body.
|
|
5425
|
+
content: schemaValidator.undefined !== void 0 && typedSchemaValidator.isInstanceOf(
|
|
5426
|
+
discriminatedResponseBodiesResult[key].schema,
|
|
5427
|
+
schemaValidator.undefined
|
|
5428
|
+
) || schemaValidator.void !== void 0 && typedSchemaValidator.isInstanceOf(
|
|
5429
|
+
discriminatedResponseBodiesResult[key].schema,
|
|
5430
|
+
schemaValidator.void
|
|
5431
|
+
) ? void 0 : contentResolver(
|
|
5239
5432
|
schemaValidator,
|
|
5240
5433
|
discriminatedResponseBodiesResult[key].schema,
|
|
5241
5434
|
discriminatedResponseBodiesResult[key].contentType
|
|
@@ -5271,15 +5464,21 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
|
|
|
5271
5464
|
operationObject.parameters?.push({
|
|
5272
5465
|
name: key,
|
|
5273
5466
|
in: "path",
|
|
5467
|
+
required: true,
|
|
5274
5468
|
schema: typedSchemaValidator.openapi(params[key])
|
|
5275
5469
|
});
|
|
5276
5470
|
}
|
|
5277
5471
|
}
|
|
5278
|
-
const
|
|
5472
|
+
const adapter = schemaValidator;
|
|
5473
|
+
const bareProjection = body && adapter.isSchema(body) ? adapter.openapiRequestBody?.(body) : void 0;
|
|
5474
|
+
const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body, bareProjection?.schema) : null;
|
|
5279
5475
|
if (discriminatedBodyResult) {
|
|
5476
|
+
const projection = bareProjection ?? adapter.openapiRequestBody?.(discriminatedBodyResult.schema);
|
|
5280
5477
|
operationObject.requestBody = {
|
|
5281
|
-
required: true,
|
|
5282
|
-
content:
|
|
5478
|
+
required: projection?.required ?? true,
|
|
5479
|
+
content: projection ? {
|
|
5480
|
+
[discriminatedBodyResult.contentType]: { schema: projection.schema }
|
|
5481
|
+
} : contentResolver(
|
|
5283
5482
|
schemaValidator,
|
|
5284
5483
|
discriminatedBodyResult.schema,
|
|
5285
5484
|
discriminatedBodyResult.contentType
|
|
@@ -5314,25 +5513,14 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
|
|
|
5314
5513
|
content: contentResolver(schemaValidator, schemaValidator.string)
|
|
5315
5514
|
};
|
|
5316
5515
|
if ("basic" in auth) {
|
|
5317
|
-
operationObject.security = [
|
|
5318
|
-
{
|
|
5319
|
-
basic: Array.from(
|
|
5320
|
-
"allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
|
|
5321
|
-
)
|
|
5322
|
-
}
|
|
5323
|
-
];
|
|
5516
|
+
operationObject.security = [{ basic: [] }];
|
|
5324
5517
|
securitySchemes["basic"] = {
|
|
5325
5518
|
type: "http",
|
|
5326
5519
|
scheme: "basic"
|
|
5327
5520
|
};
|
|
5328
5521
|
} else if (auth) {
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
[auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
|
|
5332
|
-
"allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
|
|
5333
|
-
)
|
|
5334
|
-
}
|
|
5335
|
-
];
|
|
5522
|
+
const schemeName = auth.headerName || "Authorization";
|
|
5523
|
+
operationObject.security = [{ [schemeName]: [] }];
|
|
5336
5524
|
if (auth.headerName && auth.headerName !== "Authorization") {
|
|
5337
5525
|
securitySchemes[auth.headerName] = {
|
|
5338
5526
|
type: "apiKey",
|
|
@@ -5563,6 +5751,22 @@ var RateLimiter = class {
|
|
|
5563
5751
|
return READ_METHODS.has(method.toUpperCase()) ? "read" : "write";
|
|
5564
5752
|
}
|
|
5565
5753
|
};
|
|
5754
|
+
|
|
5755
|
+
// src/http/registerDelegatedSurface.ts
|
|
5756
|
+
function registerDelegatedSurface(surface, mount, register) {
|
|
5757
|
+
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(
|
|
5758
|
+
(callback) => typeof callback !== "string" || !callback.startsWith("/")
|
|
5759
|
+
)) {
|
|
5760
|
+
throw new Error("Invalid delegated HTTP surface descriptor");
|
|
5761
|
+
}
|
|
5762
|
+
const paths = Array.isArray(mount.paths) ? mount.paths : [mount.paths];
|
|
5763
|
+
if (mount.basePath !== "" && !mount.basePath.startsWith("/") || paths.length === 0 || paths.some(
|
|
5764
|
+
(path) => !path.startsWith("/") || mount.basePath !== "" && !path.startsWith(mount.basePath + "/")
|
|
5765
|
+
)) {
|
|
5766
|
+
throw new Error("Invalid delegated HTTP surface mount");
|
|
5767
|
+
}
|
|
5768
|
+
return register(mount);
|
|
5769
|
+
}
|
|
5566
5770
|
export {
|
|
5567
5771
|
ATTR_API_NAME,
|
|
5568
5772
|
ATTR_APPLICATION_ID,
|
|
@@ -5600,6 +5804,7 @@ export {
|
|
|
5600
5804
|
auditComplianceAccess,
|
|
5601
5805
|
auditEnabled,
|
|
5602
5806
|
auditEventType,
|
|
5807
|
+
bindRuntimeAuth,
|
|
5603
5808
|
createContext,
|
|
5604
5809
|
createEmailClient,
|
|
5605
5810
|
createHmacToken,
|
|
@@ -5653,6 +5858,7 @@ export {
|
|
|
5653
5858
|
put,
|
|
5654
5859
|
recordMetric,
|
|
5655
5860
|
recordRequestAudit,
|
|
5861
|
+
registerDelegatedSurface,
|
|
5656
5862
|
resetProcessHandlersForTesting,
|
|
5657
5863
|
signPlatformEvent,
|
|
5658
5864
|
smsSegments,
|