@forklaunch/core 0.11.6 → 0.12.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/lib/http/index.d.mts +569 -292
- package/lib/http/index.d.ts +569 -292
- package/lib/http/index.js +738 -267
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +738 -269
- package/lib/http/index.mjs.map +1 -1
- package/package.json +16 -16
package/lib/http/index.js
CHANGED
@@ -39,6 +39,7 @@ __export(http_exports, {
|
|
39
39
|
ForklaunchExpressLikeApplication: () => ForklaunchExpressLikeApplication,
|
40
40
|
ForklaunchExpressLikeRouter: () => ForklaunchExpressLikeRouter,
|
41
41
|
HTTPStatuses: () => HTTPStatuses,
|
42
|
+
OPENAPI_DEFAULT_VERSION: () => OPENAPI_DEFAULT_VERSION,
|
42
43
|
OpenTelemetryCollector: () => OpenTelemetryCollector,
|
43
44
|
delete_: () => delete_,
|
44
45
|
discriminateBody: () => discriminateBody,
|
@@ -46,7 +47,7 @@ __export(http_exports, {
|
|
46
47
|
enrichExpressLikeSend: () => enrichExpressLikeSend,
|
47
48
|
evaluateTelemetryOptions: () => evaluateTelemetryOptions,
|
48
49
|
generateMcpServer: () => generateMcpServer,
|
49
|
-
|
50
|
+
generateOpenApiSpecs: () => generateOpenApiSpecs,
|
50
51
|
get: () => get,
|
51
52
|
getCodeForStatus: () => getCodeForStatus,
|
52
53
|
head: () => head,
|
@@ -69,6 +70,8 @@ __export(http_exports, {
|
|
69
70
|
post: () => post,
|
70
71
|
put: () => put,
|
71
72
|
recordMetric: () => recordMetric,
|
73
|
+
sdkClient: () => sdkClient,
|
74
|
+
sdkRouter: () => sdkRouter,
|
72
75
|
trace: () => trace3,
|
73
76
|
typedAuthHandler: () => typedAuthHandler,
|
74
77
|
typedHandler: () => typedHandler
|
@@ -87,13 +90,22 @@ function cors(corsOptions) {
|
|
87
90
|
return res.getHeaders()[key];
|
88
91
|
};
|
89
92
|
}
|
90
|
-
(0, import_cors.default)(corsOptions)(
|
91
|
-
|
93
|
+
(0, import_cors.default)(corsOptions)(
|
94
|
+
req,
|
95
|
+
res,
|
96
|
+
next ?? (() => {
|
97
|
+
})
|
98
|
+
);
|
92
99
|
};
|
93
100
|
}
|
94
101
|
|
95
102
|
// src/http/router/expressLikeRouter.ts
|
96
|
-
var
|
103
|
+
var import_common7 = require("@forklaunch/common");
|
104
|
+
|
105
|
+
// src/http/guards/hasVersionedSchema.ts
|
106
|
+
function hasVersionedSchema(contractDetails) {
|
107
|
+
return typeof contractDetails === "object" && contractDetails !== null && "versions" in contractDetails && contractDetails.versions !== null;
|
108
|
+
}
|
97
109
|
|
98
110
|
// src/http/guards/isForklaunchRouter.ts
|
99
111
|
function isForklaunchRouter(maybeForklaunchRouter) {
|
@@ -125,12 +137,16 @@ function isForklaunchExpressLikeRouter(maybeForklaunchExpressLikeRouter) {
|
|
125
137
|
|
126
138
|
// src/http/guards/isPathParamContractDetails.ts
|
127
139
|
function isPathParamHttpContractDetails(maybePathParamHttpContractDetails) {
|
128
|
-
return maybePathParamHttpContractDetails != null && typeof maybePathParamHttpContractDetails === "object" && "name" in maybePathParamHttpContractDetails && "summary" in maybePathParamHttpContractDetails && "responses" in maybePathParamHttpContractDetails && maybePathParamHttpContractDetails.
|
140
|
+
return maybePathParamHttpContractDetails != null && typeof maybePathParamHttpContractDetails === "object" && "name" in maybePathParamHttpContractDetails && "summary" in maybePathParamHttpContractDetails && maybePathParamHttpContractDetails.name != null && maybePathParamHttpContractDetails.summary != null && ("responses" in maybePathParamHttpContractDetails && maybePathParamHttpContractDetails.responses != null || "versions" in maybePathParamHttpContractDetails && typeof maybePathParamHttpContractDetails.versions === "object" && maybePathParamHttpContractDetails.versions != null && Object.values(maybePathParamHttpContractDetails.versions).every(
|
141
|
+
(version) => "responses" in version && version.responses != null
|
142
|
+
));
|
129
143
|
}
|
130
144
|
|
131
145
|
// src/http/guards/isHttpContractDetails.ts
|
132
146
|
function isHttpContractDetails(maybeContractDetails) {
|
133
|
-
return isPathParamHttpContractDetails(maybeContractDetails) && "body" in maybeContractDetails && maybeContractDetails.body != null
|
147
|
+
return isPathParamHttpContractDetails(maybeContractDetails) && ("body" in maybeContractDetails && maybeContractDetails.body != null || "versions" in maybeContractDetails && typeof maybeContractDetails.versions === "object" && maybeContractDetails.versions != null && Object.values(maybeContractDetails.versions).every(
|
148
|
+
(version) => "body" in version && version.body != null
|
149
|
+
));
|
134
150
|
}
|
135
151
|
|
136
152
|
// src/http/guards/isTypedHandler.ts
|
@@ -279,7 +295,10 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
279
295
|
if (!authorizationMethod.mapRoles) {
|
280
296
|
return [500, "No role mapping function provided."];
|
281
297
|
}
|
282
|
-
const resourceRoles = await authorizationMethod.mapRoles(
|
298
|
+
const resourceRoles = await authorizationMethod.mapRoles(
|
299
|
+
resourceId,
|
300
|
+
req
|
301
|
+
);
|
283
302
|
if ("allowedRoles" in authorizationMethod && authorizationMethod.allowedRoles) {
|
284
303
|
if (resourceRoles.intersection(authorizationMethod.allowedRoles).size === 0) {
|
285
304
|
return invalidAuthorizationTokenRoles;
|
@@ -300,6 +319,7 @@ async function parseRequestAuth(req, res, next) {
|
|
300
319
|
const [error, message] = await checkAuthorizationToken(
|
301
320
|
auth,
|
302
321
|
req.headers[auth.headerName ?? "Authorization"] || req.headers[auth.headerName ?? "authorization"],
|
322
|
+
// we can safely cast here because we know that the user will supply resolution for the request
|
303
323
|
req
|
304
324
|
) ?? [];
|
305
325
|
if (error != null) {
|
@@ -610,6 +630,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
|
|
610
630
|
}
|
611
631
|
|
612
632
|
// src/http/middleware/request/parse.middleware.ts
|
633
|
+
var import_common6 = require("@forklaunch/common");
|
613
634
|
var import_validator = require("@forklaunch/validator");
|
614
635
|
|
615
636
|
// src/http/guards/hasSend.ts
|
@@ -630,10 +651,49 @@ function parse(req, res, next) {
|
|
630
651
|
headers: req.headers,
|
631
652
|
body: req.body
|
632
653
|
};
|
633
|
-
const
|
634
|
-
|
635
|
-
|
636
|
-
|
654
|
+
const schemaValidator = req.schemaValidator;
|
655
|
+
let matchedVersions;
|
656
|
+
let parsedRequest;
|
657
|
+
let collectedParseErrors;
|
658
|
+
if (req.contractDetails.versions) {
|
659
|
+
if ((0, import_common6.isRecord)(req.requestSchema)) {
|
660
|
+
let runningParseErrors = "";
|
661
|
+
matchedVersions = [];
|
662
|
+
Object.entries(req.requestSchema).forEach(([version, schema]) => {
|
663
|
+
const parsingResult = schemaValidator.parse(schema, request);
|
664
|
+
if (parsingResult.ok) {
|
665
|
+
parsedRequest = parsingResult;
|
666
|
+
matchedVersions.push(version);
|
667
|
+
req.version = version;
|
668
|
+
res.version = req.version;
|
669
|
+
} else {
|
670
|
+
runningParseErrors += (0, import_validator.prettyPrintParseErrors)(
|
671
|
+
parsingResult.errors,
|
672
|
+
`Version ${version} request`
|
673
|
+
);
|
674
|
+
}
|
675
|
+
});
|
676
|
+
if (!parsedRequest) {
|
677
|
+
parsedRequest = {
|
678
|
+
ok: false,
|
679
|
+
errors: []
|
680
|
+
};
|
681
|
+
collectedParseErrors = runningParseErrors;
|
682
|
+
}
|
683
|
+
} else {
|
684
|
+
req.version = Object.keys(req.contractDetails.versions).pop();
|
685
|
+
res.version = req.version;
|
686
|
+
parsedRequest = {
|
687
|
+
ok: true,
|
688
|
+
value: request
|
689
|
+
};
|
690
|
+
matchedVersions = Object.keys(req.contractDetails.versions);
|
691
|
+
}
|
692
|
+
} else {
|
693
|
+
const parsingResult = schemaValidator.parse(req.requestSchema, request);
|
694
|
+
parsedRequest = parsingResult;
|
695
|
+
matchedVersions = 0;
|
696
|
+
}
|
637
697
|
if (parsedRequest.ok && isRequestShape(parsedRequest.value)) {
|
638
698
|
req.body = parsedRequest.value.body;
|
639
699
|
req.params = parsedRequest.value.params;
|
@@ -664,10 +724,7 @@ function parse(req, res, next) {
|
|
664
724
|
res.status(400);
|
665
725
|
if (hasSend(res)) {
|
666
726
|
res.send(
|
667
|
-
`${(0, import_validator.prettyPrintParseErrors)(
|
668
|
-
parsedRequest.errors,
|
669
|
-
"Request"
|
670
|
-
)}
|
727
|
+
`${collectedParseErrors ?? (0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
|
671
728
|
|
672
729
|
Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
673
730
|
);
|
@@ -677,13 +734,14 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
|
677
734
|
return;
|
678
735
|
case "warning":
|
679
736
|
req.openTelemetryCollector.warn(
|
680
|
-
(0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request")
|
737
|
+
collectedParseErrors ?? (0, import_validator.prettyPrintParseErrors)(parsedRequest.errors, "Request")
|
681
738
|
);
|
682
739
|
break;
|
683
740
|
case "none":
|
684
741
|
break;
|
685
742
|
}
|
686
743
|
}
|
744
|
+
req._parsedVersions = matchedVersions;
|
687
745
|
next?.();
|
688
746
|
}
|
689
747
|
|
@@ -821,13 +879,12 @@ function discriminateResponseBodies(schemaValidator, responses) {
|
|
821
879
|
|
822
880
|
// src/http/router/expressLikeRouter.ts
|
823
881
|
var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
824
|
-
constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector
|
882
|
+
constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
|
825
883
|
this.basePath = basePath;
|
826
884
|
this.schemaValidator = schemaValidator;
|
827
885
|
this.internal = internal;
|
828
886
|
this.postEnrichMiddleware = postEnrichMiddleware;
|
829
887
|
this.openTelemetryCollector = openTelemetryCollector;
|
830
|
-
this.sdkName = sdkName;
|
831
888
|
if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
|
832
889
|
process.on("uncaughtException", (err) => {
|
833
890
|
this.openTelemetryCollector.error(`Uncaught exception: ${err}`);
|
@@ -850,8 +907,9 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
850
907
|
requestHandler;
|
851
908
|
routers = [];
|
852
909
|
routes = [];
|
853
|
-
|
910
|
+
_fetchMap = {};
|
854
911
|
sdk = {};
|
912
|
+
sdkPaths = {};
|
855
913
|
/**
|
856
914
|
* Resolves middlewares based on the contract details.
|
857
915
|
*
|
@@ -921,50 +979,112 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
921
979
|
}
|
922
980
|
return controllerHandler;
|
923
981
|
}
|
924
|
-
#
|
982
|
+
#processContractDetailsIO(contractDetailsIO, params) {
|
925
983
|
const schemaValidator = this.schemaValidator;
|
926
|
-
|
927
|
-
if (isHttpContractDetails(contractDetails)) {
|
928
|
-
body = discriminateBody(this.schemaValidator, contractDetails.body);
|
929
|
-
}
|
930
|
-
const requestSchema = schemaValidator.compile(
|
931
|
-
schemaValidator.schemify({
|
932
|
-
...contractDetails.params ? { params: contractDetails.params } : {},
|
933
|
-
...contractDetails.requestHeaders ? { headers: contractDetails.requestHeaders } : {},
|
934
|
-
...contractDetails.query ? { query: contractDetails.query } : {},
|
935
|
-
...body != null ? { body: body.schema } : {}
|
936
|
-
})
|
937
|
-
);
|
938
|
-
const responseEntries = {
|
984
|
+
const responseSchemas = {
|
939
985
|
400: schemaValidator.string,
|
940
986
|
401: schemaValidator.string,
|
941
987
|
403: schemaValidator.string,
|
942
988
|
404: schemaValidator.string,
|
943
989
|
500: schemaValidator.string,
|
944
|
-
...
|
990
|
+
...Object.fromEntries(
|
945
991
|
Object.entries(
|
946
992
|
discriminateResponseBodies(
|
947
993
|
this.schemaValidator,
|
948
|
-
|
994
|
+
contractDetailsIO.responses
|
949
995
|
)
|
950
996
|
).map(([key, value]) => {
|
951
|
-
return [key, value.schema];
|
997
|
+
return [Number(key), value.schema];
|
952
998
|
})
|
953
|
-
)
|
999
|
+
)
|
954
1000
|
};
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
schemaValidator.
|
1001
|
+
return {
|
1002
|
+
requestSchema: schemaValidator.compile(
|
1003
|
+
schemaValidator.schemify({
|
1004
|
+
...params != null ? { params } : { params: schemaValidator.unknown },
|
1005
|
+
...contractDetailsIO.requestHeaders != null ? { headers: contractDetailsIO.requestHeaders } : { headers: schemaValidator.unknown },
|
1006
|
+
...contractDetailsIO.query != null ? { query: contractDetailsIO.query } : { query: schemaValidator.unknown },
|
1007
|
+
...contractDetailsIO.body != null ? {
|
1008
|
+
body: discriminateBody(
|
1009
|
+
this.schemaValidator,
|
1010
|
+
contractDetailsIO.body
|
1011
|
+
)?.schema
|
1012
|
+
} : { body: schemaValidator.unknown }
|
1013
|
+
})
|
1014
|
+
),
|
1015
|
+
responseSchemas: {
|
1016
|
+
...contractDetailsIO.responseHeaders != null ? {
|
1017
|
+
headers: schemaValidator.compile(
|
1018
|
+
schemaValidator.schemify(contractDetailsIO.responseHeaders)
|
1019
|
+
)
|
1020
|
+
} : { headers: schemaValidator.unknown },
|
1021
|
+
responses: Object.fromEntries(
|
1022
|
+
Object.entries(responseSchemas).map(([key, value]) => {
|
1023
|
+
return [
|
1024
|
+
key,
|
1025
|
+
schemaValidator.compile(schemaValidator.schemify(value))
|
1026
|
+
];
|
1027
|
+
})
|
960
1028
|
)
|
961
|
-
}
|
1029
|
+
}
|
962
1030
|
};
|
963
|
-
|
964
|
-
|
965
|
-
|
1031
|
+
}
|
1032
|
+
#compile(contractDetails) {
|
1033
|
+
const schemaValidator = this.schemaValidator;
|
1034
|
+
let requestSchema;
|
1035
|
+
let responseSchemas;
|
1036
|
+
if (hasVersionedSchema(contractDetails)) {
|
1037
|
+
requestSchema = {};
|
1038
|
+
responseSchemas = {};
|
1039
|
+
Object.entries(contractDetails.versions ?? {}).forEach(
|
1040
|
+
([version, versionedContractDetails]) => {
|
1041
|
+
const {
|
1042
|
+
requestSchema: versionedRequestSchema,
|
1043
|
+
responseSchemas: versionedResponseSchemas
|
1044
|
+
} = this.#processContractDetailsIO(
|
1045
|
+
versionedContractDetails,
|
1046
|
+
contractDetails.params
|
1047
|
+
);
|
1048
|
+
if ((0, import_common7.isRecord)(requestSchema)) {
|
1049
|
+
requestSchema = {
|
1050
|
+
...requestSchema,
|
1051
|
+
[version]: versionedRequestSchema
|
1052
|
+
};
|
1053
|
+
}
|
1054
|
+
if ((0, import_common7.isRecord)(responseSchemas)) {
|
1055
|
+
responseSchemas = {
|
1056
|
+
...responseSchemas,
|
1057
|
+
[version]: versionedResponseSchemas
|
1058
|
+
};
|
1059
|
+
}
|
1060
|
+
}
|
966
1061
|
);
|
967
|
-
}
|
1062
|
+
} else {
|
1063
|
+
const {
|
1064
|
+
requestSchema: unversionedRequestSchema,
|
1065
|
+
responseSchemas: unversionedResponseSchemas
|
1066
|
+
} = this.#processContractDetailsIO(
|
1067
|
+
{
|
1068
|
+
..."params" in contractDetails && contractDetails.params != null ? { params: contractDetails.params } : { params: schemaValidator.unknown },
|
1069
|
+
..."requestHeaders" in contractDetails && contractDetails.requestHeaders != null ? { requestHeaders: contractDetails.requestHeaders } : {
|
1070
|
+
requestHeaders: schemaValidator.unknown
|
1071
|
+
},
|
1072
|
+
..."responseHeaders" in contractDetails && contractDetails.responseHeaders != null ? { responseHeaders: contractDetails.responseHeaders } : {
|
1073
|
+
responseHeaders: schemaValidator.unknown
|
1074
|
+
},
|
1075
|
+
..."query" in contractDetails && contractDetails.query != null ? { query: contractDetails.query } : {
|
1076
|
+
query: schemaValidator.unknown
|
1077
|
+
},
|
1078
|
+
..."body" in contractDetails && contractDetails.body != null ? { body: contractDetails.body } : {
|
1079
|
+
body: schemaValidator.unknown
|
1080
|
+
},
|
1081
|
+
responses: "responses" in contractDetails && contractDetails.responses != null ? contractDetails.responses : schemaValidator.unknown
|
1082
|
+
},
|
1083
|
+
contractDetails.params
|
1084
|
+
);
|
1085
|
+
requestSchema = unversionedRequestSchema;
|
1086
|
+
responseSchemas = unversionedResponseSchemas;
|
1087
|
+
}
|
968
1088
|
return {
|
969
1089
|
requestSchema,
|
970
1090
|
responseSchemas
|
@@ -973,15 +1093,18 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
973
1093
|
/**
|
974
1094
|
* Fetches a route from the route map and executes it with the given parameters.
|
975
1095
|
*
|
976
|
-
* @template Path - The path type that extends keyof
|
1096
|
+
* @template Path - The path type that extends keyof _fetchMap and string.
|
977
1097
|
* @param {Path} path - The route path
|
978
|
-
* @param {Parameters<
|
979
|
-
* @returns {Promise<ReturnType<
|
1098
|
+
* @param {Parameters<_fetchMap[Path]>[1]} [requestInit] - Optional request initialization parameters.
|
1099
|
+
* @returns {Promise<ReturnType<_fetchMap[Path]>>} - The result of executing the route handler.
|
980
1100
|
*/
|
981
1101
|
fetch = async (path, ...reqInit) => {
|
982
|
-
|
1102
|
+
const method = reqInit[0]?.method;
|
1103
|
+
const version = reqInit[0] != null && "version" in reqInit[0] ? reqInit[0].version : void 0;
|
1104
|
+
return (version ? this._fetchMap[path][method ?? "GET"][version] : this._fetchMap[path][method ?? "GET"])(
|
983
1105
|
path,
|
984
1106
|
reqInit[0]
|
1107
|
+
// reqInit
|
985
1108
|
);
|
986
1109
|
};
|
987
1110
|
/**
|
@@ -991,7 +1114,7 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
991
1114
|
* @param controllerHandler
|
992
1115
|
* @returns
|
993
1116
|
*/
|
994
|
-
#localParamRequest(handlers, controllerHandler) {
|
1117
|
+
#localParamRequest(handlers, controllerHandler, version) {
|
995
1118
|
return async (route, request) => {
|
996
1119
|
let statusCode;
|
997
1120
|
let responseMessage;
|
@@ -1001,7 +1124,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1001
1124
|
query: request?.query ?? {},
|
1002
1125
|
headers: request?.headers ?? {},
|
1003
1126
|
body: discriminateBody(this.schemaValidator, request?.body)?.schema ?? {},
|
1004
|
-
path: route
|
1127
|
+
path: route,
|
1128
|
+
version
|
1005
1129
|
};
|
1006
1130
|
const res = {
|
1007
1131
|
status: (code) => {
|
@@ -1022,7 +1146,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1022
1146
|
},
|
1023
1147
|
sseEmitter: (generator) => {
|
1024
1148
|
responseMessage = generator();
|
1025
|
-
}
|
1149
|
+
},
|
1150
|
+
version
|
1026
1151
|
};
|
1027
1152
|
let cursor = handlers.shift();
|
1028
1153
|
if (cursor) {
|
@@ -1056,20 +1181,20 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1056
1181
|
registerRoute(method, path, registrationMethod, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareAndTypedHandler) {
|
1057
1182
|
if (isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
|
1058
1183
|
const { contractDetails, handlers } = contractDetailsOrMiddlewareOrTypedHandler;
|
1059
|
-
this.registerRoute(method, path, registrationMethod, contractDetails, ...handlers);
|
1060
|
-
return
|
1184
|
+
const router = this.registerRoute(method, path, registrationMethod, contractDetails, ...handlers);
|
1185
|
+
return router;
|
1061
1186
|
} else {
|
1062
1187
|
const maybeTypedHandler = middlewareOrMiddlewareAndTypedHandler[middlewareOrMiddlewareAndTypedHandler.length - 1];
|
1063
1188
|
if (isTypedHandler(maybeTypedHandler)) {
|
1064
1189
|
const { contractDetails, handlers } = maybeTypedHandler;
|
1065
|
-
this.registerRoute(
|
1190
|
+
const router = this.registerRoute(
|
1066
1191
|
method,
|
1067
1192
|
path,
|
1068
1193
|
registrationMethod,
|
1069
1194
|
contractDetails,
|
1070
1195
|
...middlewareOrMiddlewareAndTypedHandler.concat(handlers)
|
1071
1196
|
);
|
1072
|
-
return
|
1197
|
+
return router;
|
1073
1198
|
} else {
|
1074
1199
|
if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler) || isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
|
1075
1200
|
throw new Error("Contract details are not defined");
|
@@ -1083,6 +1208,17 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1083
1208
|
"Contract details are malformed for route definition"
|
1084
1209
|
);
|
1085
1210
|
}
|
1211
|
+
if (contractDetails.versions) {
|
1212
|
+
const parserTypes = Object.values(contractDetails.versions).map(
|
1213
|
+
(version) => discriminateBody(this.schemaValidator, version.body)?.parserType
|
1214
|
+
);
|
1215
|
+
const allParserTypesSame = parserTypes.length === 0 || parserTypes.every((pt) => pt === parserTypes[0]);
|
1216
|
+
if (!allParserTypesSame) {
|
1217
|
+
throw new Error(
|
1218
|
+
"All versioned contractDetails must have the same parsing type for body."
|
1219
|
+
);
|
1220
|
+
}
|
1221
|
+
}
|
1086
1222
|
this.routes.push({
|
1087
1223
|
basePath: this.basePath,
|
1088
1224
|
path,
|
@@ -1091,22 +1227,39 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1091
1227
|
});
|
1092
1228
|
const { requestSchema, responseSchemas } = this.#compile(contractDetails);
|
1093
1229
|
const controllerHandler = this.#extractControllerHandler(handlers);
|
1230
|
+
const resolvedMiddlewares = this.#resolveMiddlewares(
|
1231
|
+
path,
|
1232
|
+
contractDetails,
|
1233
|
+
requestSchema,
|
1234
|
+
responseSchemas
|
1235
|
+
).concat(handlers);
|
1094
1236
|
registrationMethod.bind(this.internal)(
|
1095
1237
|
path,
|
1096
|
-
...
|
1097
|
-
path,
|
1098
|
-
contractDetails,
|
1099
|
-
requestSchema,
|
1100
|
-
responseSchemas
|
1101
|
-
).concat(handlers),
|
1238
|
+
...resolvedMiddlewares,
|
1102
1239
|
this.#parseAndRunControllerHandler(controllerHandler)
|
1103
1240
|
);
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1241
|
+
(0, import_common7.toRecord)(this._fetchMap)[(0, import_common7.sanitizePathSlashes)(`${this.basePath}${path}`)] = {
|
1242
|
+
...this._fetchMap[(0, import_common7.sanitizePathSlashes)(`${this.basePath}${path}`)] ?? {},
|
1243
|
+
[method.toUpperCase()]: contractDetails.versions ? Object.fromEntries(
|
1244
|
+
Object.keys(contractDetails.versions).map((version) => [
|
1245
|
+
version,
|
1246
|
+
this.#localParamRequest(handlers, controllerHandler, version)
|
1247
|
+
])
|
1248
|
+
) : this.#localParamRequest(handlers, controllerHandler)
|
1249
|
+
};
|
1250
|
+
(0, import_common7.toRecord)(this.sdk)[(0, import_common7.toPrettyCamelCase)(contractDetails.name)] = contractDetails.versions ? Object.fromEntries(
|
1251
|
+
Object.keys(contractDetails.versions).map((version) => [
|
1252
|
+
version,
|
1253
|
+
(req) => this.#localParamRequest(
|
1254
|
+
handlers,
|
1255
|
+
controllerHandler,
|
1256
|
+
version
|
1257
|
+
)(`${this.basePath}${path}`, req)
|
1258
|
+
])
|
1259
|
+
) : (req) => this.#localParamRequest(handlers, controllerHandler)(
|
1260
|
+
`${this.basePath}${path}`,
|
1261
|
+
req
|
1107
1262
|
);
|
1108
|
-
(0, import_common6.toRecord)(this.fetchMap)[(0, import_common6.sanitizePathSlashes)(`${this.basePath}${path}`)] = localParamRequest;
|
1109
|
-
(0, import_common6.toRecord)(this.sdk)[(0, import_common6.toPrettyCamelCase)(contractDetails.name ?? this.basePath)] = (req) => localParamRequest(`${this.basePath}${path}`, req);
|
1110
1263
|
return this;
|
1111
1264
|
}
|
1112
1265
|
}
|
@@ -1192,11 +1345,11 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1192
1345
|
return this;
|
1193
1346
|
}
|
1194
1347
|
addRouterToSdk(router) {
|
1195
|
-
Object.entries(router.
|
1196
|
-
([key, value]) => (0,
|
1348
|
+
Object.entries(router._fetchMap).map(
|
1349
|
+
([key, value]) => (0, import_common7.toRecord)(this._fetchMap)[(0, import_common7.sanitizePathSlashes)(`${this.basePath}${key}`)] = value
|
1197
1350
|
);
|
1198
|
-
const existingSdk = this.sdk[router.sdkName ?? (0,
|
1199
|
-
(0,
|
1351
|
+
const existingSdk = this.sdk[router.sdkName ?? (0, import_common7.toPrettyCamelCase)(router.basePath)];
|
1352
|
+
(0, import_common7.toRecord)(this.sdk)[router.sdkName ?? (0, import_common7.toPrettyCamelCase)(router.basePath)] = {
|
1200
1353
|
...typeof existingSdk === "object" ? existingSdk : {},
|
1201
1354
|
...router.sdk
|
1202
1355
|
};
|
@@ -1453,8 +1606,9 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1453
1606
|
cloneInternals(clone) {
|
1454
1607
|
clone.routers = [...this.routers];
|
1455
1608
|
clone.routes = [...this.routes];
|
1456
|
-
clone.
|
1609
|
+
clone._fetchMap = { ...this._fetchMap };
|
1457
1610
|
clone.sdk = { ...this.sdk };
|
1611
|
+
clone.sdkPaths = { ...this.sdkPaths };
|
1458
1612
|
}
|
1459
1613
|
clone() {
|
1460
1614
|
const clone = new _ForklaunchExpressLikeRouter(
|
@@ -1462,8 +1616,7 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1462
1616
|
this.schemaValidator,
|
1463
1617
|
this.internal,
|
1464
1618
|
this.postEnrichMiddleware,
|
1465
|
-
this.openTelemetryCollector
|
1466
|
-
this.sdkName
|
1619
|
+
this.openTelemetryCollector
|
1467
1620
|
);
|
1468
1621
|
this.cloneInternals(clone);
|
1469
1622
|
return clone;
|
@@ -1518,9 +1671,13 @@ function typedHandler(_schemaValidator, pathOrContractMethod, contractMethodOrCo
|
|
1518
1671
|
throw new Error("Invalid definition for handler");
|
1519
1672
|
}
|
1520
1673
|
}
|
1674
|
+
if (isPath(pathOrContractMethod) && typeof contractMethodOrContractDetails !== "string") {
|
1675
|
+
throw new Error("Contract method not supplied, bailing");
|
1676
|
+
}
|
1521
1677
|
return {
|
1522
1678
|
_typedHandler: true,
|
1523
1679
|
_path: isPath(pathOrContractMethod) ? pathOrContractMethod : void 0,
|
1680
|
+
_method: isPath(pathOrContractMethod) ? contractMethodOrContractDetails : pathOrContractMethod,
|
1524
1681
|
contractDetails,
|
1525
1682
|
handlers
|
1526
1683
|
};
|
@@ -2564,33 +2721,53 @@ var import_common8 = require("@forklaunch/common");
|
|
2564
2721
|
var import_zod = require("@forklaunch/validator/zod");
|
2565
2722
|
var import_fastmcp = require("fastmcp");
|
2566
2723
|
|
2724
|
+
// src/http/guards/isVersionedInputSchema.ts
|
2725
|
+
function isUnionable(schema) {
|
2726
|
+
return schema.length > 1;
|
2727
|
+
}
|
2728
|
+
|
2567
2729
|
// src/http/router/unpackRouters.ts
|
2568
|
-
|
2569
|
-
function unpackRouters(routers, recursiveBasePath = [], recursiveSdkPath = []) {
|
2730
|
+
function unpackRouters(routers, recursiveBasePath = []) {
|
2570
2731
|
return routers.reduce((acc, router) => {
|
2571
2732
|
acc.push({
|
2572
2733
|
fullPath: [...recursiveBasePath, router.basePath].join(""),
|
2573
|
-
sdkPath: [
|
2574
|
-
...recursiveSdkPath,
|
2575
|
-
(0, import_common7.toPrettyCamelCase)(router.sdkName ?? router.basePath)
|
2576
|
-
].join("."),
|
2577
2734
|
router
|
2578
2735
|
});
|
2579
2736
|
acc.push(
|
2580
|
-
...unpackRouters(
|
2581
|
-
|
2582
|
-
|
2583
|
-
|
2584
|
-
...recursiveSdkPath,
|
2585
|
-
(0, import_common7.toPrettyCamelCase)(router.sdkName ?? router.basePath)
|
2586
|
-
]
|
2587
|
-
)
|
2737
|
+
...unpackRouters(router.routers, [
|
2738
|
+
...recursiveBasePath,
|
2739
|
+
router.basePath
|
2740
|
+
])
|
2588
2741
|
);
|
2589
2742
|
return acc;
|
2590
2743
|
}, []);
|
2591
2744
|
}
|
2592
2745
|
|
2593
2746
|
// src/http/mcpGenerator/mcpGenerator.ts
|
2747
|
+
function generateInputSchema(schemaValidator, body, params, query, requestHeaders, auth) {
|
2748
|
+
let discriminatedBody;
|
2749
|
+
if (body) {
|
2750
|
+
discriminatedBody = discriminateBody(schemaValidator, body);
|
2751
|
+
}
|
2752
|
+
return schemaValidator.schemify({
|
2753
|
+
...discriminatedBody && body ? {
|
2754
|
+
..."contentType" in body ? { contentType: body.contentType } : {},
|
2755
|
+
body: schemaValidator.schemify(discriminatedBody.schema)
|
2756
|
+
} : {},
|
2757
|
+
...params ? { params: schemaValidator.schemify(params) } : {},
|
2758
|
+
...query ? { query: schemaValidator.schemify(query) } : {},
|
2759
|
+
...requestHeaders ? {
|
2760
|
+
headers: schemaValidator.schemify({
|
2761
|
+
...requestHeaders,
|
2762
|
+
...auth ? {
|
2763
|
+
[auth.headerName ?? "authorization"]: import_zod.string.startsWith(
|
2764
|
+
auth.tokenPrefix ?? ("basic" in auth ? "Basic " : "Bearer ")
|
2765
|
+
)
|
2766
|
+
} : {}
|
2767
|
+
})
|
2768
|
+
} : {}
|
2769
|
+
});
|
2770
|
+
}
|
2594
2771
|
function generateMcpServer(schemaValidator, protocol, host, port, version, routers, options2, contentTypeMap) {
|
2595
2772
|
if (!(schemaValidator instanceof import_zod.ZodSchemaValidator)) {
|
2596
2773
|
throw new Error(
|
@@ -2604,35 +2781,36 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2604
2781
|
});
|
2605
2782
|
unpackRouters(routers).forEach(({ fullPath, router }) => {
|
2606
2783
|
router.routes.forEach((route) => {
|
2607
|
-
|
2608
|
-
if (
|
2609
|
-
|
2610
|
-
|
2611
|
-
|
2784
|
+
const inputSchemas = [];
|
2785
|
+
if (route.contractDetails.versions) {
|
2786
|
+
Object.values(route.contractDetails.versions).forEach((version2) => {
|
2787
|
+
inputSchemas.push(
|
2788
|
+
generateInputSchema(
|
2789
|
+
schemaValidator,
|
2790
|
+
version2.body,
|
2791
|
+
route.contractDetails.params,
|
2792
|
+
version2.query,
|
2793
|
+
version2.requestHeaders,
|
2794
|
+
route.contractDetails.auth
|
2795
|
+
)
|
2796
|
+
);
|
2797
|
+
});
|
2798
|
+
} else {
|
2799
|
+
inputSchemas.push(
|
2800
|
+
generateInputSchema(
|
2801
|
+
schemaValidator,
|
2802
|
+
route.contractDetails.body,
|
2803
|
+
route.contractDetails.params,
|
2804
|
+
route.contractDetails.query,
|
2805
|
+
route.contractDetails.requestHeaders,
|
2806
|
+
route.contractDetails.auth
|
2807
|
+
)
|
2612
2808
|
);
|
2613
2809
|
}
|
2614
|
-
const inputSchema = schemaValidator.schemify({
|
2615
|
-
...discriminatedBody && "body" in route.contractDetails ? {
|
2616
|
-
..."contentType" in route.contractDetails.body ? { contentType: route.contractDetails.body.contentType } : {},
|
2617
|
-
body: schemaValidator.schemify(discriminatedBody.schema)
|
2618
|
-
} : {},
|
2619
|
-
...route.contractDetails.params ? { params: schemaValidator.schemify(route.contractDetails.params) } : {},
|
2620
|
-
...route.contractDetails.query ? { query: schemaValidator.schemify(route.contractDetails.query) } : {},
|
2621
|
-
...route.contractDetails.requestHeaders ? {
|
2622
|
-
headers: schemaValidator.schemify({
|
2623
|
-
...route.contractDetails.requestHeaders,
|
2624
|
-
...route.contractDetails.auth ? {
|
2625
|
-
[route.contractDetails.auth.headerName ?? "authorization"]: import_zod.string.startsWith(
|
2626
|
-
route.contractDetails.auth.tokenPrefix ?? ("basic" in route.contractDetails.auth ? "Basic " : "Bearer ")
|
2627
|
-
)
|
2628
|
-
} : {}
|
2629
|
-
})
|
2630
|
-
} : {}
|
2631
|
-
});
|
2632
2810
|
mcpServer.addTool({
|
2633
2811
|
name: route.contractDetails.name,
|
2634
2812
|
description: route.contractDetails.summary,
|
2635
|
-
parameters:
|
2813
|
+
parameters: isUnionable(inputSchemas) ? schemaValidator.union(inputSchemas) : inputSchemas[0],
|
2636
2814
|
execute: async (args) => {
|
2637
2815
|
const { contentType, body, params, query, headers } = args;
|
2638
2816
|
let url = `${protocol}://${host}:${port}${fullPath}${route.path}`;
|
@@ -2644,6 +2822,22 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2644
2822
|
);
|
2645
2823
|
}
|
2646
2824
|
}
|
2825
|
+
let bodySchema;
|
2826
|
+
let responsesSchemas;
|
2827
|
+
if (route.contractDetails.versions) {
|
2828
|
+
Object.values(route.contractDetails.versions).forEach(
|
2829
|
+
(version2, index) => {
|
2830
|
+
if (version2.body && schemaValidator.parse(inputSchemas[index], args).ok) {
|
2831
|
+
bodySchema = version2.body;
|
2832
|
+
responsesSchemas = version2.responses;
|
2833
|
+
}
|
2834
|
+
}
|
2835
|
+
);
|
2836
|
+
} else {
|
2837
|
+
bodySchema = route.contractDetails.body;
|
2838
|
+
responsesSchemas = route.contractDetails.responses;
|
2839
|
+
}
|
2840
|
+
const discriminatedBody = bodySchema ? discriminateBody(schemaValidator, bodySchema) : void 0;
|
2647
2841
|
let parsedBody;
|
2648
2842
|
if (discriminatedBody) {
|
2649
2843
|
switch (discriminatedBody.parserType) {
|
@@ -2719,9 +2913,12 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2719
2913
|
`Error received while proxying request to ${url}: ${await response.text()}`
|
2720
2914
|
);
|
2721
2915
|
}
|
2916
|
+
if (!responsesSchemas) {
|
2917
|
+
throw new Error("No responses schemas found");
|
2918
|
+
}
|
2722
2919
|
const contractContentType = discriminateResponseBodies(
|
2723
2920
|
schemaValidator,
|
2724
|
-
|
2921
|
+
responsesSchemas
|
2725
2922
|
)[response.status].contentType;
|
2726
2923
|
switch (contentTypeMap && contentTypeMap[contractContentType] ? contentTypeMap[contractContentType] : contractContentType) {
|
2727
2924
|
case "application/json":
|
@@ -2775,15 +2972,56 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2775
2972
|
|
2776
2973
|
// src/http/middleware/response/parse.middleware.ts
|
2777
2974
|
var import_validator2 = require("@forklaunch/validator");
|
2975
|
+
|
2976
|
+
// src/http/guards/isResponseCompiledSchema.ts
|
2977
|
+
function isResponseCompiledSchema(schema) {
|
2978
|
+
return typeof schema === "object" && schema !== null && "responses" in schema;
|
2979
|
+
}
|
2980
|
+
|
2981
|
+
// src/http/middleware/response/parse.middleware.ts
|
2778
2982
|
function parse2(req, res, next) {
|
2779
|
-
|
2983
|
+
let headers;
|
2984
|
+
let responses;
|
2985
|
+
const responseSchemas = res.responseSchemas;
|
2986
|
+
const schemaValidator = req.schemaValidator;
|
2987
|
+
if (!isResponseCompiledSchema(responseSchemas)) {
|
2988
|
+
const parsedVersions = req._parsedVersions;
|
2989
|
+
if (typeof parsedVersions === "number") {
|
2990
|
+
throw new Error("Request failed to parse given version map");
|
2991
|
+
}
|
2992
|
+
const mappedHeaderSchemas = parsedVersions.map(
|
2993
|
+
(version) => responseSchemas[version].headers
|
2994
|
+
);
|
2995
|
+
const mappedResponseSchemas = parsedVersions.map(
|
2996
|
+
(version) => responseSchemas[version].responses
|
2997
|
+
);
|
2998
|
+
const collapsedResponseSchemas = mappedResponseSchemas.reduce((acc, responseSchema) => {
|
2999
|
+
Object.entries(responseSchema).forEach(([status, schema]) => {
|
3000
|
+
if (!acc[Number(status)]) {
|
3001
|
+
acc[Number(status)] = [];
|
3002
|
+
}
|
3003
|
+
acc[Number(status)].push(schema);
|
3004
|
+
});
|
3005
|
+
return acc;
|
3006
|
+
}, {});
|
3007
|
+
headers = schemaValidator.union(mappedHeaderSchemas);
|
3008
|
+
responses = Object.fromEntries(
|
3009
|
+
Object.entries(collapsedResponseSchemas).map(([status, schemas]) => [
|
3010
|
+
status,
|
3011
|
+
schemaValidator.union(schemas)
|
3012
|
+
])
|
3013
|
+
);
|
3014
|
+
} else {
|
3015
|
+
headers = responseSchemas.headers;
|
3016
|
+
responses = responseSchemas.responses;
|
3017
|
+
}
|
2780
3018
|
const statusCode = Number(res.statusCode);
|
2781
|
-
const parsedResponse =
|
2782
|
-
responses?.[statusCode],
|
3019
|
+
const parsedResponse = schemaValidator.parse(
|
3020
|
+
[400, 401, 404, 403, 500].includes(statusCode) ? schemaValidator.union([schemaValidator.string, responses?.[statusCode]]) : responses?.[statusCode],
|
2783
3021
|
res.bodyData
|
2784
3022
|
);
|
2785
|
-
const parsedHeaders =
|
2786
|
-
headers ??
|
3023
|
+
const parsedHeaders = schemaValidator.parse(
|
3024
|
+
headers ?? schemaValidator.unknown,
|
2787
3025
|
res.getHeaders()
|
2788
3026
|
);
|
2789
3027
|
const parseErrors = [];
|
@@ -2870,9 +3108,21 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
|
|
2870
3108
|
originalSend.call(instance, "Not Found");
|
2871
3109
|
errorSent = true;
|
2872
3110
|
}
|
3111
|
+
let responses;
|
3112
|
+
if (req.contractDetails.responses == null && (req.contractDetails.versions == null || Object.values(req.contractDetails.versions).some(
|
3113
|
+
(version) => version?.responses == null
|
3114
|
+
))) {
|
3115
|
+
throw new Error("Responses schema definitions are required");
|
3116
|
+
} else {
|
3117
|
+
if (req.contractDetails.responses != null) {
|
3118
|
+
responses = req.contractDetails.responses;
|
3119
|
+
} else {
|
3120
|
+
responses = req.contractDetails.versions[req.version].responses;
|
3121
|
+
}
|
3122
|
+
}
|
2873
3123
|
const responseBodies = discriminateResponseBodies(
|
2874
3124
|
req.schemaValidator,
|
2875
|
-
|
3125
|
+
responses
|
2876
3126
|
);
|
2877
3127
|
if (responseBodies != null && responseBodies[Number(res.statusCode)] != null) {
|
2878
3128
|
res.type(responseBodies[Number(res.statusCode)].contentType);
|
@@ -2999,6 +3249,7 @@ ${res.locals.errorMessage}`;
|
|
2999
3249
|
|
3000
3250
|
// src/http/openApiV3Generator/openApiV3Generator.ts
|
3001
3251
|
var import_common11 = require("@forklaunch/common");
|
3252
|
+
var OPENAPI_DEFAULT_VERSION = Symbol("default");
|
3002
3253
|
function toUpperCase(str) {
|
3003
3254
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
3004
3255
|
}
|
@@ -3008,25 +3259,50 @@ function transformBasePath(basePath) {
|
|
3008
3259
|
}
|
3009
3260
|
return `/${basePath}`;
|
3010
3261
|
}
|
3011
|
-
function generateOpenApiDocument(protocol, host, port,
|
3262
|
+
function generateOpenApiDocument(protocol, host, port, versionedTags, versionedPaths, versionedSecuritySchemes, otherServers) {
|
3012
3263
|
return {
|
3013
|
-
|
3014
|
-
|
3015
|
-
|
3016
|
-
|
3017
|
-
|
3018
|
-
components: {
|
3019
|
-
securitySchemes
|
3020
|
-
},
|
3021
|
-
tags,
|
3022
|
-
servers: [
|
3023
|
-
{
|
3024
|
-
url: `${protocol}://${host}:${port}`,
|
3025
|
-
description: "Main Server"
|
3264
|
+
[OPENAPI_DEFAULT_VERSION]: {
|
3265
|
+
openapi: "3.1.0",
|
3266
|
+
info: {
|
3267
|
+
title: process.env.API_TITLE || "API Definition",
|
3268
|
+
version: process.env.VERSION || "latest"
|
3026
3269
|
},
|
3027
|
-
|
3028
|
-
|
3029
|
-
|
3270
|
+
components: {
|
3271
|
+
securitySchemes: versionedSecuritySchemes[OPENAPI_DEFAULT_VERSION]
|
3272
|
+
},
|
3273
|
+
tags: versionedTags[OPENAPI_DEFAULT_VERSION],
|
3274
|
+
servers: [
|
3275
|
+
{
|
3276
|
+
url: `${protocol}://${host}:${port}`,
|
3277
|
+
description: "Main Server"
|
3278
|
+
},
|
3279
|
+
...otherServers || []
|
3280
|
+
],
|
3281
|
+
paths: versionedPaths[OPENAPI_DEFAULT_VERSION]
|
3282
|
+
},
|
3283
|
+
...Object.fromEntries(
|
3284
|
+
Object.entries(versionedPaths).map(([version, paths]) => [
|
3285
|
+
version,
|
3286
|
+
{
|
3287
|
+
openapi: "3.1.0",
|
3288
|
+
info: {
|
3289
|
+
title: process.env.API_TITLE || "API Definition",
|
3290
|
+
version
|
3291
|
+
},
|
3292
|
+
components: {
|
3293
|
+
securitySchemes: versionedSecuritySchemes[version]
|
3294
|
+
},
|
3295
|
+
tags: versionedTags[version],
|
3296
|
+
servers: [
|
3297
|
+
{
|
3298
|
+
url: `${protocol}://${host}:${port}`,
|
3299
|
+
description: "Main Server"
|
3300
|
+
}
|
3301
|
+
],
|
3302
|
+
paths
|
3303
|
+
}
|
3304
|
+
])
|
3305
|
+
)
|
3030
3306
|
};
|
3031
3307
|
}
|
3032
3308
|
function contentResolver(schemaValidator, body, contentType) {
|
@@ -3045,143 +3321,232 @@ function contentResolver(schemaValidator, body, contentType) {
|
|
3045
3321
|
}
|
3046
3322
|
};
|
3047
3323
|
}
|
3048
|
-
function
|
3049
|
-
const
|
3050
|
-
const
|
3051
|
-
const
|
3052
|
-
|
3324
|
+
function generateOperationObject(schemaValidator, path, method, controllerName, sdkPaths, securitySchemes, name, summary, responses, params, responseHeaders, requestHeaders, query, body, auth) {
|
3325
|
+
const typedSchemaValidator = schemaValidator;
|
3326
|
+
const coercedResponses = {};
|
3327
|
+
const discriminatedResponseBodiesResult = discriminateResponseBodies(
|
3328
|
+
schemaValidator,
|
3329
|
+
responses
|
3330
|
+
);
|
3331
|
+
for (const key in discriminatedResponseBodiesResult) {
|
3332
|
+
coercedResponses[key] = {
|
3333
|
+
description: httpStatusCodes_default[key],
|
3334
|
+
content: contentResolver(
|
3335
|
+
schemaValidator,
|
3336
|
+
discriminatedResponseBodiesResult[key].schema,
|
3337
|
+
discriminatedResponseBodiesResult[key].contentType
|
3338
|
+
),
|
3339
|
+
headers: responseHeaders ? Object.fromEntries(
|
3340
|
+
Object.entries(responseHeaders).map(([key2, value]) => [
|
3341
|
+
key2,
|
3342
|
+
{
|
3343
|
+
schema: typedSchemaValidator.openapi(value)
|
3344
|
+
}
|
3345
|
+
])
|
3346
|
+
) : void 0
|
3347
|
+
};
|
3348
|
+
}
|
3349
|
+
const commonErrors = [400, 404, 500];
|
3350
|
+
for (const error of commonErrors) {
|
3351
|
+
if (!(error in responses)) {
|
3352
|
+
coercedResponses[error] = {
|
3353
|
+
description: httpStatusCodes_default[error],
|
3354
|
+
content: contentResolver(schemaValidator, schemaValidator.string)
|
3355
|
+
};
|
3356
|
+
}
|
3357
|
+
}
|
3358
|
+
const operationObject = {
|
3359
|
+
tags: [controllerName],
|
3360
|
+
summary: `${name}: ${summary}`,
|
3361
|
+
parameters: [],
|
3362
|
+
responses: coercedResponses,
|
3363
|
+
operationId: sdkPaths[[method, path].join(".")]
|
3364
|
+
};
|
3365
|
+
if (params) {
|
3366
|
+
for (const key in params) {
|
3367
|
+
operationObject.parameters?.push({
|
3368
|
+
name: key,
|
3369
|
+
in: "path",
|
3370
|
+
schema: typedSchemaValidator.openapi(params[key])
|
3371
|
+
});
|
3372
|
+
}
|
3373
|
+
}
|
3374
|
+
const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body) : null;
|
3375
|
+
if (discriminatedBodyResult) {
|
3376
|
+
operationObject.requestBody = {
|
3377
|
+
required: true,
|
3378
|
+
content: contentResolver(
|
3379
|
+
schemaValidator,
|
3380
|
+
discriminatedBodyResult.schema,
|
3381
|
+
discriminatedBodyResult.contentType
|
3382
|
+
)
|
3383
|
+
};
|
3384
|
+
}
|
3385
|
+
if (requestHeaders) {
|
3386
|
+
for (const key in requestHeaders) {
|
3387
|
+
operationObject.parameters?.push({
|
3388
|
+
name: key,
|
3389
|
+
in: "header",
|
3390
|
+
schema: typedSchemaValidator.openapi(requestHeaders[key])
|
3391
|
+
});
|
3392
|
+
}
|
3393
|
+
}
|
3394
|
+
if (query) {
|
3395
|
+
for (const key in query) {
|
3396
|
+
operationObject.parameters?.push({
|
3397
|
+
name: key,
|
3398
|
+
in: "query",
|
3399
|
+
schema: typedSchemaValidator.openapi(query[key])
|
3400
|
+
});
|
3401
|
+
}
|
3402
|
+
}
|
3403
|
+
if (auth) {
|
3404
|
+
responses[401] = {
|
3405
|
+
description: httpStatusCodes_default[401],
|
3406
|
+
content: contentResolver(schemaValidator, schemaValidator.string)
|
3407
|
+
};
|
3408
|
+
responses[403] = {
|
3409
|
+
description: httpStatusCodes_default[403],
|
3410
|
+
content: contentResolver(schemaValidator, schemaValidator.string)
|
3411
|
+
};
|
3412
|
+
if ("basic" in auth) {
|
3413
|
+
operationObject.security = [
|
3414
|
+
{
|
3415
|
+
basic: Array.from(
|
3416
|
+
"allowedPermissions" in auth ? auth.allowedPermissions?.values() || [] : []
|
3417
|
+
)
|
3418
|
+
}
|
3419
|
+
];
|
3420
|
+
securitySchemes["basic"] = {
|
3421
|
+
type: "http",
|
3422
|
+
scheme: "basic"
|
3423
|
+
};
|
3424
|
+
} else if (auth) {
|
3425
|
+
operationObject.security = [
|
3426
|
+
{
|
3427
|
+
[auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
|
3428
|
+
"allowedPermissions" in auth ? auth.allowedPermissions?.values() || [] : []
|
3429
|
+
)
|
3430
|
+
}
|
3431
|
+
];
|
3432
|
+
if (auth.headerName && auth.headerName !== "Authorization") {
|
3433
|
+
securitySchemes[auth.headerName] = {
|
3434
|
+
type: "apiKey",
|
3435
|
+
in: "header",
|
3436
|
+
name: auth.headerName
|
3437
|
+
};
|
3438
|
+
} else {
|
3439
|
+
securitySchemes["Authorization"] = {
|
3440
|
+
type: "http",
|
3441
|
+
scheme: "bearer",
|
3442
|
+
bearerFormat: "JWT"
|
3443
|
+
};
|
3444
|
+
}
|
3445
|
+
}
|
3446
|
+
}
|
3447
|
+
return operationObject;
|
3448
|
+
}
|
3449
|
+
function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, otherServers) {
|
3450
|
+
const versionedPaths = {
|
3451
|
+
[OPENAPI_DEFAULT_VERSION]: {}
|
3452
|
+
};
|
3453
|
+
const versionedTags = {
|
3454
|
+
[OPENAPI_DEFAULT_VERSION]: []
|
3455
|
+
};
|
3456
|
+
const versionedSecuritySchemes = {
|
3457
|
+
[OPENAPI_DEFAULT_VERSION]: {}
|
3458
|
+
};
|
3459
|
+
unpackRouters(routers).forEach(({ fullPath, router }) => {
|
3053
3460
|
const controllerName = transformBasePath(fullPath);
|
3054
|
-
tags.push({
|
3055
|
-
name: controllerName,
|
3056
|
-
description: `${toUpperCase(controllerName)} Operations`
|
3057
|
-
});
|
3058
3461
|
router.routes.forEach((route) => {
|
3059
3462
|
const openApiPath = (0, import_common11.openApiCompliantPath)(
|
3060
3463
|
`${fullPath}${route.path === "/" ? "" : route.path}`
|
3061
3464
|
);
|
3062
|
-
|
3063
|
-
|
3064
|
-
|
3065
|
-
|
3066
|
-
|
3067
|
-
|
3068
|
-
|
3069
|
-
|
3070
|
-
|
3071
|
-
|
3072
|
-
|
3073
|
-
|
3074
|
-
|
3465
|
+
const { name, summary, params, versions, auth } = route.contractDetails;
|
3466
|
+
if (versions) {
|
3467
|
+
for (const version of Object.keys(versions)) {
|
3468
|
+
if (!versionedPaths[version]) {
|
3469
|
+
versionedPaths[version] = {};
|
3470
|
+
}
|
3471
|
+
if (!versionedPaths[version][openApiPath]) {
|
3472
|
+
versionedPaths[version][openApiPath] = {};
|
3473
|
+
}
|
3474
|
+
if (!versionedTags[version]) {
|
3475
|
+
versionedTags[version] = [];
|
3476
|
+
}
|
3477
|
+
if (!versionedTags[version].find((tag) => tag.name === controllerName)) {
|
3478
|
+
versionedTags[version].push({
|
3479
|
+
name: controllerName,
|
3480
|
+
description: `${toUpperCase(controllerName)} Operations`
|
3481
|
+
});
|
3482
|
+
}
|
3483
|
+
if (!versionedSecuritySchemes[version]) {
|
3484
|
+
versionedSecuritySchemes[version] = {};
|
3485
|
+
}
|
3486
|
+
const { query, requestHeaders, body, responses, responseHeaders } = versions[version];
|
3487
|
+
const operationObject = generateOperationObject(
|
3075
3488
|
schemaValidator,
|
3076
|
-
|
3077
|
-
|
3078
|
-
|
3079
|
-
|
3080
|
-
|
3081
|
-
|
3082
|
-
|
3083
|
-
|
3084
|
-
|
3085
|
-
|
3086
|
-
|
3087
|
-
|
3489
|
+
route.path,
|
3490
|
+
route.method,
|
3491
|
+
controllerName,
|
3492
|
+
router.sdkPaths,
|
3493
|
+
versionedSecuritySchemes[version],
|
3494
|
+
name,
|
3495
|
+
summary,
|
3496
|
+
responses,
|
3497
|
+
params,
|
3498
|
+
responseHeaders,
|
3499
|
+
requestHeaders,
|
3500
|
+
query,
|
3501
|
+
body,
|
3502
|
+
auth
|
3503
|
+
);
|
3504
|
+
if (route.method !== "middleware") {
|
3505
|
+
versionedPaths[version][openApiPath][route.method] = operationObject;
|
3506
|
+
}
|
3088
3507
|
}
|
3089
|
-
}
|
3090
|
-
|
3091
|
-
|
3092
|
-
summary: `${name}: ${summary}`,
|
3093
|
-
parameters: [],
|
3094
|
-
responses,
|
3095
|
-
operationId: `${sdkPath}.${(0, import_common11.toPrettyCamelCase)(name)}`
|
3096
|
-
};
|
3097
|
-
if (route.contractDetails.params) {
|
3098
|
-
for (const key in route.contractDetails.params) {
|
3099
|
-
operationObject.parameters?.push({
|
3100
|
-
name: key,
|
3101
|
-
in: "path",
|
3102
|
-
schema: schemaValidator.openapi(
|
3103
|
-
route.contractDetails.params[key]
|
3104
|
-
)
|
3105
|
-
});
|
3508
|
+
} else {
|
3509
|
+
if (!versionedPaths[OPENAPI_DEFAULT_VERSION]) {
|
3510
|
+
versionedPaths[OPENAPI_DEFAULT_VERSION] = {};
|
3106
3511
|
}
|
3107
|
-
|
3108
|
-
|
3109
|
-
if (discriminatedBodyResult) {
|
3110
|
-
operationObject.requestBody = {
|
3111
|
-
required: true,
|
3112
|
-
content: contentResolver(
|
3113
|
-
schemaValidator,
|
3114
|
-
discriminatedBodyResult.schema,
|
3115
|
-
discriminatedBodyResult.contentType
|
3116
|
-
)
|
3117
|
-
};
|
3118
|
-
}
|
3119
|
-
if (requestHeaders) {
|
3120
|
-
for (const key in requestHeaders) {
|
3121
|
-
operationObject.parameters?.push({
|
3122
|
-
name: key,
|
3123
|
-
in: "header",
|
3124
|
-
schema: schemaValidator.openapi(
|
3125
|
-
requestHeaders[key]
|
3126
|
-
)
|
3127
|
-
});
|
3512
|
+
if (!versionedPaths[OPENAPI_DEFAULT_VERSION][openApiPath]) {
|
3513
|
+
versionedPaths[OPENAPI_DEFAULT_VERSION][openApiPath] = {};
|
3128
3514
|
}
|
3129
|
-
|
3130
|
-
|
3131
|
-
|
3132
|
-
|
3133
|
-
|
3134
|
-
|
3135
|
-
|
3515
|
+
if (!versionedTags[OPENAPI_DEFAULT_VERSION]) {
|
3516
|
+
versionedTags[OPENAPI_DEFAULT_VERSION] = [];
|
3517
|
+
}
|
3518
|
+
if (!versionedTags[OPENAPI_DEFAULT_VERSION].find(
|
3519
|
+
(tag) => tag.name === controllerName
|
3520
|
+
)) {
|
3521
|
+
versionedTags[OPENAPI_DEFAULT_VERSION].push({
|
3522
|
+
name: controllerName,
|
3523
|
+
description: `${toUpperCase(controllerName)} Operations`
|
3136
3524
|
});
|
3137
3525
|
}
|
3138
|
-
|
3139
|
-
|
3140
|
-
|
3141
|
-
|
3142
|
-
|
3143
|
-
|
3144
|
-
|
3145
|
-
|
3146
|
-
|
3147
|
-
|
3148
|
-
|
3149
|
-
|
3150
|
-
|
3151
|
-
|
3152
|
-
|
3153
|
-
|
3154
|
-
|
3155
|
-
|
3156
|
-
|
3157
|
-
|
3158
|
-
|
3159
|
-
|
3160
|
-
|
3161
|
-
operationObject.security = [
|
3162
|
-
{
|
3163
|
-
[route.contractDetails.auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
|
3164
|
-
"allowedPermissions" in route.contractDetails.auth ? route.contractDetails.auth.allowedPermissions?.values() || [] : []
|
3165
|
-
)
|
3166
|
-
}
|
3167
|
-
];
|
3168
|
-
if (route.contractDetails.auth.headerName && route.contractDetails.auth.headerName !== "Authorization") {
|
3169
|
-
securitySchemes[route.contractDetails.auth.headerName] = {
|
3170
|
-
type: "apiKey",
|
3171
|
-
in: "header",
|
3172
|
-
name: route.contractDetails.auth.headerName
|
3173
|
-
};
|
3174
|
-
} else {
|
3175
|
-
securitySchemes["Authorization"] = {
|
3176
|
-
type: "http",
|
3177
|
-
scheme: "bearer",
|
3178
|
-
bearerFormat: "JWT"
|
3179
|
-
};
|
3180
|
-
}
|
3526
|
+
if (!versionedSecuritySchemes[OPENAPI_DEFAULT_VERSION]) {
|
3527
|
+
versionedSecuritySchemes[OPENAPI_DEFAULT_VERSION] = {};
|
3528
|
+
}
|
3529
|
+
const { query, requestHeaders, body, responses, responseHeaders } = route.contractDetails;
|
3530
|
+
const operationObject = generateOperationObject(
|
3531
|
+
schemaValidator,
|
3532
|
+
route.path,
|
3533
|
+
route.method,
|
3534
|
+
controllerName,
|
3535
|
+
router.sdkPaths,
|
3536
|
+
versionedSecuritySchemes[OPENAPI_DEFAULT_VERSION],
|
3537
|
+
name,
|
3538
|
+
summary,
|
3539
|
+
responses,
|
3540
|
+
params,
|
3541
|
+
responseHeaders,
|
3542
|
+
requestHeaders,
|
3543
|
+
query,
|
3544
|
+
body,
|
3545
|
+
auth
|
3546
|
+
);
|
3547
|
+
if (route.method !== "middleware") {
|
3548
|
+
versionedPaths[OPENAPI_DEFAULT_VERSION][openApiPath][route.method] = operationObject;
|
3181
3549
|
}
|
3182
|
-
}
|
3183
|
-
if (route.method !== "middleware") {
|
3184
|
-
paths[openApiPath][route.method] = operationObject;
|
3185
3550
|
}
|
3186
3551
|
});
|
3187
3552
|
});
|
@@ -3189,13 +3554,116 @@ function generateSwaggerDocument(schemaValidator, protocol, host, port, routers,
|
|
3189
3554
|
protocol,
|
3190
3555
|
host,
|
3191
3556
|
port,
|
3192
|
-
|
3193
|
-
|
3194
|
-
|
3557
|
+
versionedTags,
|
3558
|
+
versionedPaths,
|
3559
|
+
versionedSecuritySchemes,
|
3195
3560
|
otherServers
|
3196
3561
|
);
|
3197
3562
|
}
|
3198
3563
|
|
3564
|
+
// src/http/sdk/sdkClient.ts
|
3565
|
+
var import_common12 = require("@forklaunch/common");
|
3566
|
+
|
3567
|
+
// src/http/guards/isSdkRouter.ts
|
3568
|
+
function isSdkRouter(value) {
|
3569
|
+
return typeof value === "object" && value !== null && "sdk" in value && "_fetchMap" in value && "sdkPaths" in value;
|
3570
|
+
}
|
3571
|
+
|
3572
|
+
// src/http/sdk/sdkClient.ts
|
3573
|
+
function mapToSdk(schemaValidator, routerMap, runningPath = void 0) {
|
3574
|
+
const routerUniquenessCache = /* @__PURE__ */ new Set();
|
3575
|
+
return Object.fromEntries(
|
3576
|
+
Object.entries(routerMap).map(([key, value]) => {
|
3577
|
+
if (routerUniquenessCache.has((0, import_common12.hashString)((0, import_common12.safeStringify)(value)))) {
|
3578
|
+
throw new Error(
|
3579
|
+
`SdkClient: Cannot use the same router pointer twice. Please clone the duplicate router with .clone() or only use the router once.`
|
3580
|
+
);
|
3581
|
+
}
|
3582
|
+
routerUniquenessCache.add((0, import_common12.hashString)((0, import_common12.safeStringify)(value)));
|
3583
|
+
const currentPath = runningPath ? [runningPath, key].join(".") : key;
|
3584
|
+
if (isSdkRouter(value)) {
|
3585
|
+
Object.entries(value.sdkPaths).forEach(([routePath, sdkKey]) => {
|
3586
|
+
if ("controllerSdkPaths" in value && Array.isArray(value.controllerSdkPaths) && value.controllerSdkPaths.includes(routePath)) {
|
3587
|
+
value.sdkPaths[routePath] = [currentPath, sdkKey].join(".");
|
3588
|
+
}
|
3589
|
+
});
|
3590
|
+
return [key, value.sdk];
|
3591
|
+
} else {
|
3592
|
+
return [
|
3593
|
+
key,
|
3594
|
+
mapToSdk(
|
3595
|
+
schemaValidator,
|
3596
|
+
value,
|
3597
|
+
runningPath ? [runningPath, key].join(".") : key
|
3598
|
+
)
|
3599
|
+
];
|
3600
|
+
}
|
3601
|
+
})
|
3602
|
+
);
|
3603
|
+
}
|
3604
|
+
function flattenFetchMap(schemaValidator, routerMap) {
|
3605
|
+
const _fetchMap = Object.entries(routerMap).reduce(
|
3606
|
+
(acc, [, value]) => {
|
3607
|
+
if ("_fetchMap" in value) {
|
3608
|
+
return {
|
3609
|
+
...acc,
|
3610
|
+
...value._fetchMap
|
3611
|
+
};
|
3612
|
+
} else {
|
3613
|
+
return {
|
3614
|
+
...acc,
|
3615
|
+
...flattenFetchMap(schemaValidator, value)
|
3616
|
+
};
|
3617
|
+
}
|
3618
|
+
},
|
3619
|
+
{}
|
3620
|
+
);
|
3621
|
+
return _fetchMap;
|
3622
|
+
}
|
3623
|
+
function mapToFetch(schemaValidator, routerMap) {
|
3624
|
+
const flattenedFetchMap = flattenFetchMap(
|
3625
|
+
schemaValidator,
|
3626
|
+
routerMap
|
3627
|
+
);
|
3628
|
+
return (path, ...reqInit) => {
|
3629
|
+
const method = reqInit[0]?.method;
|
3630
|
+
const version = reqInit[0] != null && "version" in reqInit[0] ? reqInit[0].version : void 0;
|
3631
|
+
return (version ? (0, import_common12.toRecord)((0, import_common12.toRecord)(flattenedFetchMap[path])[method ?? "GET"])[version] : (0, import_common12.toRecord)(flattenedFetchMap[path])[method ?? "GET"])(path, reqInit[0]);
|
3632
|
+
};
|
3633
|
+
}
|
3634
|
+
function sdkClient(schemaValidator, routerMap) {
|
3635
|
+
return {
|
3636
|
+
_finalizedSdk: true,
|
3637
|
+
sdk: mapToSdk(schemaValidator, routerMap),
|
3638
|
+
fetch: mapToFetch(schemaValidator, routerMap)
|
3639
|
+
};
|
3640
|
+
}
|
3641
|
+
|
3642
|
+
// src/http/sdk/sdkRouter.ts
|
3643
|
+
var import_common13 = require("@forklaunch/common");
|
3644
|
+
function sdkRouter(schemaValidator, controller, router) {
|
3645
|
+
const controllerSdkPaths = [];
|
3646
|
+
const mappedSdk = Object.fromEntries(
|
3647
|
+
Object.entries(controller).map(([key, value]) => {
|
3648
|
+
const sdkPath = [value._method, value._path].join(".");
|
3649
|
+
controllerSdkPaths.push(sdkPath);
|
3650
|
+
router.sdkPaths[sdkPath] = key;
|
3651
|
+
return [
|
3652
|
+
key,
|
3653
|
+
router.sdk[(0, import_common13.toPrettyCamelCase)(value.contractDetails.name)]
|
3654
|
+
];
|
3655
|
+
})
|
3656
|
+
);
|
3657
|
+
const _fetchMap = router._fetchMap;
|
3658
|
+
return {
|
3659
|
+
sdk: mappedSdk,
|
3660
|
+
fetch: router.fetch,
|
3661
|
+
_fetchMap,
|
3662
|
+
sdkPaths: router.sdkPaths,
|
3663
|
+
controllerSdkPaths
|
3664
|
+
};
|
3665
|
+
}
|
3666
|
+
|
3199
3667
|
// src/http/telemetry/evaluateTelemetryOptions.ts
|
3200
3668
|
function evaluateTelemetryOptions(telemetryOptions) {
|
3201
3669
|
return {
|
@@ -3227,6 +3695,7 @@ function metricsDefinitions(metrics2) {
|
|
3227
3695
|
ForklaunchExpressLikeApplication,
|
3228
3696
|
ForklaunchExpressLikeRouter,
|
3229
3697
|
HTTPStatuses,
|
3698
|
+
OPENAPI_DEFAULT_VERSION,
|
3230
3699
|
OpenTelemetryCollector,
|
3231
3700
|
delete_,
|
3232
3701
|
discriminateBody,
|
@@ -3234,7 +3703,7 @@ function metricsDefinitions(metrics2) {
|
|
3234
3703
|
enrichExpressLikeSend,
|
3235
3704
|
evaluateTelemetryOptions,
|
3236
3705
|
generateMcpServer,
|
3237
|
-
|
3706
|
+
generateOpenApiSpecs,
|
3238
3707
|
get,
|
3239
3708
|
getCodeForStatus,
|
3240
3709
|
head,
|
@@ -3257,6 +3726,8 @@ function metricsDefinitions(metrics2) {
|
|
3257
3726
|
post,
|
3258
3727
|
put,
|
3259
3728
|
recordMetric,
|
3729
|
+
sdkClient,
|
3730
|
+
sdkRouter,
|
3260
3731
|
trace,
|
3261
3732
|
typedAuthHandler,
|
3262
3733
|
typedHandler
|