@forklaunch/core 0.9.22 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/http/index.d.mts +296 -102
- package/lib/http/index.d.ts +296 -102
- package/lib/http/index.js +421 -160
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +420 -156
- package/lib/http/index.mjs.map +1 -1
- package/package.json +13 -12
package/lib/http/index.js
CHANGED
@@ -45,6 +45,7 @@ __export(http_exports, {
|
|
45
45
|
discriminateResponseBodies: () => discriminateResponseBodies,
|
46
46
|
enrichExpressLikeSend: () => enrichExpressLikeSend,
|
47
47
|
evaluateTelemetryOptions: () => evaluateTelemetryOptions,
|
48
|
+
generateMcpServer: () => generateMcpServer,
|
48
49
|
generateSwaggerDocument: () => generateSwaggerDocument,
|
49
50
|
get: () => get,
|
50
51
|
getCodeForStatus: () => getCodeForStatus,
|
@@ -90,6 +91,9 @@ function cors(corsOptions) {
|
|
90
91
|
};
|
91
92
|
}
|
92
93
|
|
94
|
+
// src/http/router/expressLikeRouter.ts
|
95
|
+
var import_common5 = require("@forklaunch/common");
|
96
|
+
|
93
97
|
// src/http/guards/isForklaunchRouter.ts
|
94
98
|
function isForklaunchRouter(maybeForklaunchRouter) {
|
95
99
|
return maybeForklaunchRouter != null && typeof maybeForklaunchRouter === "object" && "basePath" in maybeForklaunchRouter && "routes" in maybeForklaunchRouter && Array.isArray(maybeForklaunchRouter.routes);
|
@@ -108,7 +112,7 @@ function isExpressLikeSchemaHandler(middleware2) {
|
|
108
112
|
(argumentName) => argumentName.toLowerCase()
|
109
113
|
)
|
110
114
|
);
|
111
|
-
return extractedArgumentNames && extractedArgumentNames.size <=
|
115
|
+
return extractedArgumentNames && extractedArgumentNames.size <= 4;
|
112
116
|
}
|
113
117
|
|
114
118
|
// src/http/guards/isForklaunchExpressLikeRouter.ts
|
@@ -681,6 +685,12 @@ function discriminateBody(schemaValidator, body) {
|
|
681
685
|
parserType: "text",
|
682
686
|
schema: maybeTypedBody
|
683
687
|
};
|
688
|
+
} else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
|
689
|
+
return {
|
690
|
+
contentType: "application/octet-stream",
|
691
|
+
parserType: "file",
|
692
|
+
schema: maybeTypedBody
|
693
|
+
};
|
684
694
|
} else {
|
685
695
|
return {
|
686
696
|
contentType: "application/json",
|
@@ -732,6 +742,12 @@ function discriminateResponseBodies(schemaValidator, responses) {
|
|
732
742
|
parserType: "text",
|
733
743
|
schema: response
|
734
744
|
};
|
745
|
+
} else if (schemaValidator.openapi(response).format === "binary") {
|
746
|
+
discriminatedResponses[Number(statusCode)] = {
|
747
|
+
contentType: "application/octet-stream",
|
748
|
+
parserType: "file",
|
749
|
+
schema: response
|
750
|
+
};
|
735
751
|
} else {
|
736
752
|
discriminatedResponses[Number(statusCode)] = {
|
737
753
|
contentType: "application/json",
|
@@ -751,37 +767,38 @@ function discriminateResponseBodies(schemaValidator, responses) {
|
|
751
767
|
}
|
752
768
|
|
753
769
|
// src/http/router/expressLikeRouter.ts
|
754
|
-
var ForklaunchExpressLikeRouter = class {
|
755
|
-
constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
|
770
|
+
var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
771
|
+
constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector, sdkName) {
|
772
|
+
this.basePath = basePath;
|
756
773
|
this.schemaValidator = schemaValidator;
|
757
774
|
this.internal = internal;
|
758
775
|
this.postEnrichMiddleware = postEnrichMiddleware;
|
759
776
|
this.openTelemetryCollector = openTelemetryCollector;
|
760
|
-
this.
|
761
|
-
process.
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
);
|
770
|
-
process.exit(
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
});
|
777
|
+
this.sdkName = sdkName;
|
778
|
+
if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
|
779
|
+
process.on("uncaughtException", (err) => {
|
780
|
+
this.openTelemetryCollector.error(`Uncaught exception: ${err}`);
|
781
|
+
process.exit(1);
|
782
|
+
});
|
783
|
+
process.on("unhandledRejection", (reason) => {
|
784
|
+
this.openTelemetryCollector.error(`Unhandled rejection: ${reason}`);
|
785
|
+
process.exit(1);
|
786
|
+
});
|
787
|
+
process.on("exit", () => {
|
788
|
+
this.openTelemetryCollector.info("Shutting down application");
|
789
|
+
});
|
790
|
+
process.on("SIGINT", () => {
|
791
|
+
this.openTelemetryCollector.info("Shutting down application");
|
792
|
+
process.exit(0);
|
793
|
+
});
|
794
|
+
}
|
779
795
|
this.internal.use(createContext(this.schemaValidator));
|
780
796
|
}
|
781
797
|
requestHandler;
|
782
798
|
routers = [];
|
783
799
|
routes = [];
|
784
|
-
|
800
|
+
fetchMap = {};
|
801
|
+
sdk = {};
|
785
802
|
/**
|
786
803
|
* Resolves middlewares based on the contract details.
|
787
804
|
*
|
@@ -900,6 +917,20 @@ var ForklaunchExpressLikeRouter = class {
|
|
900
917
|
responseSchemas
|
901
918
|
};
|
902
919
|
}
|
920
|
+
/**
|
921
|
+
* Fetches a route from the route map and executes it with the given parameters.
|
922
|
+
*
|
923
|
+
* @template Path - The path type that extends keyof fetchMap and string.
|
924
|
+
* @param {Path} path - The route path
|
925
|
+
* @param {Parameters<fetchMap[Path]>[1]} [requestInit] - Optional request initialization parameters.
|
926
|
+
* @returns {Promise<ReturnType<fetchMap[Path]>>} - The result of executing the route handler.
|
927
|
+
*/
|
928
|
+
fetch = async (path, ...reqInit) => {
|
929
|
+
return this.fetchMap[path](
|
930
|
+
path,
|
931
|
+
reqInit[0]
|
932
|
+
);
|
933
|
+
};
|
903
934
|
/**
|
904
935
|
* Executes request locally, applying parameters
|
905
936
|
*
|
@@ -919,24 +950,6 @@ var ForklaunchExpressLikeRouter = class {
|
|
919
950
|
body: discriminateBody(this.schemaValidator, request?.body)?.schema ?? {},
|
920
951
|
path: route
|
921
952
|
};
|
922
|
-
function remapFileBody(body) {
|
923
|
-
if (body instanceof File) {
|
924
|
-
return (name, contentType) => {
|
925
|
-
return new File([body], name, { type: contentType });
|
926
|
-
};
|
927
|
-
}
|
928
|
-
Object.entries(body).forEach(([key, value]) => {
|
929
|
-
if (value instanceof File) {
|
930
|
-
body[key] = (name, contentType) => {
|
931
|
-
return new File([value], name, { type: contentType });
|
932
|
-
};
|
933
|
-
} else if (typeof value === "object") {
|
934
|
-
body[key] = remapFileBody(value);
|
935
|
-
}
|
936
|
-
});
|
937
|
-
return body;
|
938
|
-
}
|
939
|
-
req.body = remapFileBody(req.body);
|
940
953
|
const res = {
|
941
954
|
status: (code) => {
|
942
955
|
statusCode = code;
|
@@ -990,24 +1003,20 @@ var ForklaunchExpressLikeRouter = class {
|
|
990
1003
|
registerRoute(method, path, registrationMethod, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareAndTypedHandler) {
|
991
1004
|
if (isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
|
992
1005
|
const { contractDetails, handlers } = contractDetailsOrMiddlewareOrTypedHandler;
|
993
|
-
|
994
|
-
|
995
|
-
path,
|
996
|
-
registrationMethod,
|
997
|
-
contractDetails,
|
998
|
-
...handlers
|
999
|
-
);
|
1006
|
+
this.registerRoute(method, path, registrationMethod, contractDetails, ...handlers);
|
1007
|
+
return this;
|
1000
1008
|
} else {
|
1001
1009
|
const maybeTypedHandler = middlewareOrMiddlewareAndTypedHandler[middlewareOrMiddlewareAndTypedHandler.length - 1];
|
1002
1010
|
if (isTypedHandler(maybeTypedHandler)) {
|
1003
1011
|
const { contractDetails, handlers } = maybeTypedHandler;
|
1004
|
-
|
1012
|
+
this.registerRoute(
|
1005
1013
|
method,
|
1006
1014
|
path,
|
1007
1015
|
registrationMethod,
|
1008
1016
|
contractDetails,
|
1009
1017
|
...middlewareOrMiddlewareAndTypedHandler.concat(handlers)
|
1010
1018
|
);
|
1019
|
+
return this;
|
1011
1020
|
} else {
|
1012
1021
|
if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler) || isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
|
1013
1022
|
throw new Error("Contract details are not defined");
|
@@ -1039,10 +1048,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1039
1048
|
).concat(handlers),
|
1040
1049
|
this.#parseAndRunControllerHandler(controllerHandler)
|
1041
1050
|
);
|
1042
|
-
|
1051
|
+
const localParamRequest = this.#localParamRequest(
|
1043
1052
|
handlers,
|
1044
1053
|
controllerHandler
|
1045
1054
|
);
|
1055
|
+
(0, import_common5.toRecord)(this.fetchMap)[(0, import_common5.sanitizePathSlashes)(`${this.basePath}${path}`)] = localParamRequest;
|
1056
|
+
(0, import_common5.toRecord)(this.sdk)[(0, import_common5.toPrettyCamelCase)(contractDetails.name ?? this.basePath)] = (req) => localParamRequest(`${this.basePath}${path}`, req);
|
1057
|
+
return this;
|
1046
1058
|
}
|
1047
1059
|
}
|
1048
1060
|
}
|
@@ -1066,10 +1078,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1066
1078
|
return this.#extractHandlers(handlers);
|
1067
1079
|
}
|
1068
1080
|
#extractNestableMiddlewareFromEnrichedTypedHandlerArray(handlers) {
|
1069
|
-
return this.#extractHandlers(
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1081
|
+
return this.#extractHandlers(handlers, (handler) => {
|
1082
|
+
if (isForklaunchExpressLikeRouter(handler)) {
|
1083
|
+
this.addRouterToSdk(handler);
|
1084
|
+
return handler.internal;
|
1085
|
+
}
|
1086
|
+
return handler;
|
1087
|
+
});
|
1073
1088
|
}
|
1074
1089
|
#processTypedHandlerOrMiddleware(handler, middleware2) {
|
1075
1090
|
if (isTypedHandler(handler)) {
|
@@ -1093,6 +1108,7 @@ var ForklaunchExpressLikeRouter = class {
|
|
1093
1108
|
middleware2
|
1094
1109
|
);
|
1095
1110
|
if (isForklaunchExpressLikeRouter(contractDetailsOrMiddlewareOrTypedHandler)) {
|
1111
|
+
this.addRouterToSdk(contractDetailsOrMiddlewareOrTypedHandler);
|
1096
1112
|
middleware2.push(contractDetailsOrMiddlewareOrTypedHandler.internal);
|
1097
1113
|
}
|
1098
1114
|
middleware2.push(
|
@@ -1122,6 +1138,16 @@ var ForklaunchExpressLikeRouter = class {
|
|
1122
1138
|
}
|
1123
1139
|
return this;
|
1124
1140
|
}
|
1141
|
+
addRouterToSdk(router) {
|
1142
|
+
Object.entries(router.fetchMap).map(
|
1143
|
+
([key, value]) => (0, import_common5.toRecord)(this.fetchMap)[(0, import_common5.sanitizePathSlashes)(`${this.basePath}${key}`)] = value
|
1144
|
+
);
|
1145
|
+
const existingSdk = this.sdk[router.sdkName ?? (0, import_common5.toPrettyCamelCase)(router.basePath)];
|
1146
|
+
(0, import_common5.toRecord)(this.sdk)[router.sdkName ?? (0, import_common5.toPrettyCamelCase)(router.basePath)] = {
|
1147
|
+
...typeof existingSdk === "object" ? existingSdk : {},
|
1148
|
+
...router.sdk
|
1149
|
+
};
|
1150
|
+
}
|
1125
1151
|
registerNestableMiddlewareHandler(registrationMethod, pathOrContractDetailsOrMiddlewareOrTypedHandler, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) {
|
1126
1152
|
const middleware2 = [];
|
1127
1153
|
let path;
|
@@ -1137,7 +1163,9 @@ var ForklaunchExpressLikeRouter = class {
|
|
1137
1163
|
if (isConstrainedForklaunchRouter(
|
1138
1164
|
pathOrContractDetailsOrMiddlewareOrTypedHandler
|
1139
1165
|
)) {
|
1140
|
-
|
1166
|
+
const router = pathOrContractDetailsOrMiddlewareOrTypedHandler;
|
1167
|
+
this.addRouterToSdk(router);
|
1168
|
+
path = router.basePath;
|
1141
1169
|
}
|
1142
1170
|
middleware2.push(
|
1143
1171
|
...this.#extractNestableMiddlewareAsRouterHandlers(
|
@@ -1148,6 +1176,11 @@ var ForklaunchExpressLikeRouter = class {
|
|
1148
1176
|
)
|
1149
1177
|
);
|
1150
1178
|
}
|
1179
|
+
if (!path) {
|
1180
|
+
path = middleware2.filter(
|
1181
|
+
(m) => isForklaunchExpressLikeRouter(m)
|
1182
|
+
)[0]?.basePath;
|
1183
|
+
}
|
1151
1184
|
if (path) {
|
1152
1185
|
registrationMethod.bind(this.internal)(path, ...middleware2);
|
1153
1186
|
} else {
|
@@ -1202,15 +1235,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1202
1235
|
* @returns {ExpressRouter} - The Express router.
|
1203
1236
|
*/
|
1204
1237
|
get = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1205
|
-
return
|
1206
|
-
get
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
)
|
1213
|
-
};
|
1238
|
+
return this.registerRoute(
|
1239
|
+
"get",
|
1240
|
+
path,
|
1241
|
+
this.internal.get,
|
1242
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1243
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1244
|
+
);
|
1214
1245
|
};
|
1215
1246
|
/**
|
1216
1247
|
* Registers a POST route with the specified contract details and handler handlers.
|
@@ -1226,15 +1257,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1226
1257
|
* @returns {ExpressRouter} - The Expxwress router.
|
1227
1258
|
*/
|
1228
1259
|
post = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1229
|
-
return
|
1230
|
-
post
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
)
|
1237
|
-
};
|
1260
|
+
return this.registerRoute(
|
1261
|
+
"post",
|
1262
|
+
path,
|
1263
|
+
this.internal.post,
|
1264
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1265
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1266
|
+
);
|
1238
1267
|
};
|
1239
1268
|
/**
|
1240
1269
|
* Registers a PUT route with the specified contract details and handler handlers.
|
@@ -1250,15 +1279,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1250
1279
|
* @returns {ExpressRouter} - The Express router.
|
1251
1280
|
*/
|
1252
1281
|
put = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1253
|
-
return
|
1254
|
-
put
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
)
|
1261
|
-
};
|
1282
|
+
return this.registerRoute(
|
1283
|
+
"put",
|
1284
|
+
path,
|
1285
|
+
this.internal.put,
|
1286
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1287
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1288
|
+
);
|
1262
1289
|
};
|
1263
1290
|
/**
|
1264
1291
|
* Registers a PATCH route with the specified contract details and handler handlers.
|
@@ -1274,15 +1301,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1274
1301
|
* @returns {ExpressRouter} - The Express router.
|
1275
1302
|
*/
|
1276
1303
|
patch = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1277
|
-
return
|
1278
|
-
patch
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
)
|
1285
|
-
};
|
1304
|
+
return this.registerRoute(
|
1305
|
+
"patch",
|
1306
|
+
path,
|
1307
|
+
this.internal.patch,
|
1308
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1309
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1310
|
+
);
|
1286
1311
|
};
|
1287
1312
|
/**
|
1288
1313
|
* Registers a DELETE route with the specified contract details and handler handlers.
|
@@ -1298,15 +1323,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1298
1323
|
* @returns {ExpressRouter} - The Express router.
|
1299
1324
|
*/
|
1300
1325
|
delete = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1301
|
-
return
|
1302
|
-
delete
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
)
|
1309
|
-
};
|
1326
|
+
return this.registerRoute(
|
1327
|
+
"delete",
|
1328
|
+
path,
|
1329
|
+
this.internal.delete,
|
1330
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1331
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1332
|
+
);
|
1310
1333
|
};
|
1311
1334
|
/**
|
1312
1335
|
* Registers a OPTIONS route with the specified contract details and handler handlers.
|
@@ -1322,15 +1345,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1322
1345
|
* @returns {ExpressRouter} - The Express router.
|
1323
1346
|
*/
|
1324
1347
|
options = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1325
|
-
return
|
1326
|
-
options
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
)
|
1333
|
-
};
|
1348
|
+
return this.registerRoute(
|
1349
|
+
"options",
|
1350
|
+
path,
|
1351
|
+
this.internal.options,
|
1352
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1353
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1354
|
+
);
|
1334
1355
|
};
|
1335
1356
|
/**
|
1336
1357
|
* Registers a HEAD route with the specified contract details and handler handlers.
|
@@ -1346,15 +1367,13 @@ var ForklaunchExpressLikeRouter = class {
|
|
1346
1367
|
* @returns {ExpressRouter} - The Express router.
|
1347
1368
|
*/
|
1348
1369
|
head = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1349
|
-
return
|
1350
|
-
head
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
)
|
1357
|
-
};
|
1370
|
+
return this.registerRoute(
|
1371
|
+
"head",
|
1372
|
+
path,
|
1373
|
+
this.internal.head,
|
1374
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1375
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1376
|
+
);
|
1358
1377
|
};
|
1359
1378
|
/**
|
1360
1379
|
* Registers a TRACE route with the specified contract details and handler handlers.
|
@@ -1370,16 +1389,32 @@ var ForklaunchExpressLikeRouter = class {
|
|
1370
1389
|
* @returns {ExpressRouter} - The Express router.
|
1371
1390
|
*/
|
1372
1391
|
trace = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
|
1373
|
-
return
|
1374
|
-
trace
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
)
|
1381
|
-
};
|
1392
|
+
return this.registerRoute(
|
1393
|
+
"trace",
|
1394
|
+
path,
|
1395
|
+
this.internal.trace,
|
1396
|
+
contractDetailsOrMiddlewareOrTypedHandler,
|
1397
|
+
...middlewareOrMiddlewareWithTypedHandler
|
1398
|
+
);
|
1382
1399
|
};
|
1400
|
+
cloneInternals(clone) {
|
1401
|
+
clone.routers = [...this.routers];
|
1402
|
+
clone.routes = [...this.routes];
|
1403
|
+
clone.fetchMap = { ...this.fetchMap };
|
1404
|
+
clone.sdk = { ...this.sdk };
|
1405
|
+
}
|
1406
|
+
clone() {
|
1407
|
+
const clone = new _ForklaunchExpressLikeRouter(
|
1408
|
+
this.basePath,
|
1409
|
+
this.schemaValidator,
|
1410
|
+
this.internal,
|
1411
|
+
this.postEnrichMiddleware,
|
1412
|
+
this.openTelemetryCollector,
|
1413
|
+
this.sdkName
|
1414
|
+
);
|
1415
|
+
this.cloneInternals(clone);
|
1416
|
+
return clone;
|
1417
|
+
}
|
1383
1418
|
};
|
1384
1419
|
|
1385
1420
|
// src/http/application/expressLikeApplication.ts
|
@@ -2466,6 +2501,218 @@ var getCodeForStatus = (status) => {
|
|
2466
2501
|
};
|
2467
2502
|
var httpStatusCodes_default = HTTPStatuses;
|
2468
2503
|
|
2504
|
+
// src/http/mcpGenerator/mcpGenerator.ts
|
2505
|
+
var import_common7 = require("@forklaunch/common");
|
2506
|
+
var import_zod = require("@forklaunch/validator/zod");
|
2507
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
2508
|
+
|
2509
|
+
// src/http/router/unpackRouters.ts
|
2510
|
+
var import_common6 = require("@forklaunch/common");
|
2511
|
+
function unpackRouters(routers, recursiveBasePath = [], recursiveSdkPath = []) {
|
2512
|
+
return routers.reduce((acc, router) => {
|
2513
|
+
acc.push({
|
2514
|
+
fullPath: [...recursiveBasePath, router.basePath].join(""),
|
2515
|
+
sdkPath: [
|
2516
|
+
...recursiveSdkPath,
|
2517
|
+
(0, import_common6.toPrettyCamelCase)(router.sdkName ?? router.basePath)
|
2518
|
+
].join("."),
|
2519
|
+
router
|
2520
|
+
});
|
2521
|
+
acc.push(
|
2522
|
+
...unpackRouters(
|
2523
|
+
router.routers,
|
2524
|
+
[...recursiveBasePath, router.basePath],
|
2525
|
+
[
|
2526
|
+
...recursiveSdkPath,
|
2527
|
+
(0, import_common6.toPrettyCamelCase)(router.sdkName ?? router.basePath)
|
2528
|
+
]
|
2529
|
+
)
|
2530
|
+
);
|
2531
|
+
return acc;
|
2532
|
+
}, []);
|
2533
|
+
}
|
2534
|
+
|
2535
|
+
// src/http/mcpGenerator/mcpGenerator.ts
|
2536
|
+
function generateMcpServer(schemaValidator, protocol, host, port, version, routers, contentTypeMap) {
|
2537
|
+
if (!(schemaValidator instanceof import_zod.ZodSchemaValidator)) {
|
2538
|
+
throw new Error(
|
2539
|
+
"Schema validator must be an instance of ZodSchemaValidator"
|
2540
|
+
);
|
2541
|
+
}
|
2542
|
+
const mcpServer = new import_mcp.McpServer({
|
2543
|
+
name: "example-server",
|
2544
|
+
version
|
2545
|
+
});
|
2546
|
+
unpackRouters(routers).forEach(({ fullPath, router }) => {
|
2547
|
+
router.routes.forEach((route) => {
|
2548
|
+
let discriminatedBody;
|
2549
|
+
if ("body" in route.contractDetails) {
|
2550
|
+
discriminatedBody = discriminateBody(
|
2551
|
+
schemaValidator,
|
2552
|
+
route.contractDetails.body
|
2553
|
+
);
|
2554
|
+
}
|
2555
|
+
const inputSchema = {
|
2556
|
+
...discriminatedBody && "body" in route.contractDetails ? {
|
2557
|
+
..."contentType" in route.contractDetails.body ? { contentType: route.contractDetails.body.contentType } : {},
|
2558
|
+
body: schemaValidator.schemify(discriminatedBody.schema)
|
2559
|
+
} : {},
|
2560
|
+
...route.contractDetails.params ? { params: schemaValidator.schemify(route.contractDetails.params) } : {},
|
2561
|
+
...route.contractDetails.query ? { query: schemaValidator.schemify(route.contractDetails.query) } : {},
|
2562
|
+
...route.contractDetails.requestHeaders ? {
|
2563
|
+
headers: schemaValidator.schemify(
|
2564
|
+
route.contractDetails.requestHeaders
|
2565
|
+
)
|
2566
|
+
} : {}
|
2567
|
+
// TODO: support auth
|
2568
|
+
// ...(route.contractDetails.auth
|
2569
|
+
// ? { auth: route.contractDetails.auth }
|
2570
|
+
// : {})
|
2571
|
+
};
|
2572
|
+
mcpServer.tool(
|
2573
|
+
route.contractDetails.name,
|
2574
|
+
route.contractDetails.summary,
|
2575
|
+
inputSchema,
|
2576
|
+
async (args) => {
|
2577
|
+
const { contentType, body, params, query, headers } = args;
|
2578
|
+
let url = `${protocol}://${host}:${port}${fullPath}${route.path}`;
|
2579
|
+
if (params) {
|
2580
|
+
for (const key in params) {
|
2581
|
+
url = url.replace(
|
2582
|
+
`:${key}`,
|
2583
|
+
encodeURIComponent(params[key])
|
2584
|
+
);
|
2585
|
+
}
|
2586
|
+
}
|
2587
|
+
let parsedBody;
|
2588
|
+
if (discriminatedBody) {
|
2589
|
+
switch (discriminatedBody.parserType) {
|
2590
|
+
case "json": {
|
2591
|
+
parsedBody = (0, import_common7.safeStringify)(body);
|
2592
|
+
break;
|
2593
|
+
}
|
2594
|
+
case "text": {
|
2595
|
+
parsedBody = body;
|
2596
|
+
break;
|
2597
|
+
}
|
2598
|
+
case "file": {
|
2599
|
+
parsedBody = body;
|
2600
|
+
break;
|
2601
|
+
}
|
2602
|
+
case "multipart": {
|
2603
|
+
const formData = new FormData();
|
2604
|
+
if ((0, import_common7.isRecord)(body)) {
|
2605
|
+
for (const key in body) {
|
2606
|
+
if (typeof body[key] === "string" || body[key] instanceof Blob) {
|
2607
|
+
formData.append(key, body[key]);
|
2608
|
+
} else {
|
2609
|
+
throw new Error("Body is not a valid multipart object");
|
2610
|
+
}
|
2611
|
+
}
|
2612
|
+
} else {
|
2613
|
+
throw new Error("Body is not a valid multipart object");
|
2614
|
+
}
|
2615
|
+
parsedBody = formData;
|
2616
|
+
break;
|
2617
|
+
}
|
2618
|
+
case "urlEncoded": {
|
2619
|
+
if ((0, import_common7.isRecord)(body)) {
|
2620
|
+
parsedBody = new URLSearchParams(
|
2621
|
+
Object.entries(body).map(([key, value]) => [
|
2622
|
+
key,
|
2623
|
+
(0, import_common7.safeStringify)(value)
|
2624
|
+
])
|
2625
|
+
);
|
2626
|
+
} else {
|
2627
|
+
throw new Error("Body is not a valid url encoded object");
|
2628
|
+
}
|
2629
|
+
break;
|
2630
|
+
}
|
2631
|
+
default: {
|
2632
|
+
(0, import_common7.isNever)(discriminatedBody.parserType);
|
2633
|
+
parsedBody = (0, import_common7.safeStringify)(body);
|
2634
|
+
break;
|
2635
|
+
}
|
2636
|
+
}
|
2637
|
+
}
|
2638
|
+
if (query) {
|
2639
|
+
const queryString = new URLSearchParams(
|
2640
|
+
Object.entries(query).map(([key, value]) => [
|
2641
|
+
key,
|
2642
|
+
(0, import_common7.safeStringify)(value)
|
2643
|
+
])
|
2644
|
+
).toString();
|
2645
|
+
url += queryString ? `?${queryString}` : "";
|
2646
|
+
}
|
2647
|
+
const response = await fetch(encodeURI(url), {
|
2648
|
+
method: route.method.toUpperCase(),
|
2649
|
+
headers: {
|
2650
|
+
...headers,
|
2651
|
+
...discriminatedBody?.contentType != "multipart/form-data" ? {
|
2652
|
+
"Content-Type": contentType ?? discriminatedBody?.contentType
|
2653
|
+
} : {}
|
2654
|
+
},
|
2655
|
+
body: parsedBody
|
2656
|
+
});
|
2657
|
+
if (response.status >= 300) {
|
2658
|
+
throw new Error(
|
2659
|
+
`Error received while proxying request to ${url}: ${await response.text()}`
|
2660
|
+
);
|
2661
|
+
}
|
2662
|
+
const contractContentType = discriminateResponseBodies(
|
2663
|
+
schemaValidator,
|
2664
|
+
route.contractDetails.responses
|
2665
|
+
)[response.status].contentType;
|
2666
|
+
switch (contentTypeMap && contentTypeMap[contractContentType] ? contentTypeMap[contractContentType] : contractContentType) {
|
2667
|
+
case "application/json":
|
2668
|
+
return {
|
2669
|
+
content: [
|
2670
|
+
{
|
2671
|
+
type: "text",
|
2672
|
+
text: (0, import_common7.safeStringify)(await response.json())
|
2673
|
+
}
|
2674
|
+
]
|
2675
|
+
};
|
2676
|
+
case "text/plain":
|
2677
|
+
return {
|
2678
|
+
content: [
|
2679
|
+
{ type: "text", text: await response.text() }
|
2680
|
+
]
|
2681
|
+
};
|
2682
|
+
case "application/octet-stream":
|
2683
|
+
return {
|
2684
|
+
content: [
|
2685
|
+
{
|
2686
|
+
type: "resource",
|
2687
|
+
resource: {
|
2688
|
+
uri: response.url,
|
2689
|
+
blob: Buffer.from(
|
2690
|
+
await (await response.blob()).arrayBuffer()
|
2691
|
+
).toString("base64")
|
2692
|
+
}
|
2693
|
+
}
|
2694
|
+
]
|
2695
|
+
};
|
2696
|
+
case "text/event-stream":
|
2697
|
+
return {
|
2698
|
+
content: [
|
2699
|
+
{ type: "text", text: await response.text() }
|
2700
|
+
]
|
2701
|
+
};
|
2702
|
+
default:
|
2703
|
+
return {
|
2704
|
+
content: [
|
2705
|
+
{ type: "text", text: await response.text() }
|
2706
|
+
]
|
2707
|
+
};
|
2708
|
+
}
|
2709
|
+
}
|
2710
|
+
);
|
2711
|
+
});
|
2712
|
+
});
|
2713
|
+
return mcpServer;
|
2714
|
+
}
|
2715
|
+
|
2469
2716
|
// src/http/middleware/response/parse.middleware.ts
|
2470
2717
|
var import_validator2 = require("@forklaunch/validator");
|
2471
2718
|
function parse2(req, res, next) {
|
@@ -2528,18 +2775,18 @@ ${parseErrors.join("\n\n")}`
|
|
2528
2775
|
}
|
2529
2776
|
|
2530
2777
|
// src/http/middleware/response/enrichExpressLikeSend.middleware.ts
|
2531
|
-
var
|
2778
|
+
var import_common9 = require("@forklaunch/common");
|
2532
2779
|
var import_stream = require("stream");
|
2533
2780
|
|
2534
2781
|
// src/http/telemetry/recordMetric.ts
|
2535
|
-
var
|
2782
|
+
var import_common8 = require("@forklaunch/common");
|
2536
2783
|
var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
|
2537
2784
|
function recordMetric(req, res) {
|
2538
2785
|
if (res.metricRecorded) {
|
2539
2786
|
return;
|
2540
2787
|
}
|
2541
2788
|
httpRequestsTotalCounter.add(1, {
|
2542
|
-
[import_semantic_conventions3.ATTR_SERVICE_NAME]: (0,
|
2789
|
+
[import_semantic_conventions3.ATTR_SERVICE_NAME]: (0, import_common8.getEnvVar)("OTEL_SERVICE_NAME"),
|
2543
2790
|
[ATTR_API_NAME]: req.contractDetails?.name,
|
2544
2791
|
[ATTR_CORRELATION_ID]: req.context.correlationId,
|
2545
2792
|
[import_semantic_conventions3.ATTR_HTTP_REQUEST_METHOD]: req.method,
|
@@ -2559,7 +2806,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
|
|
2559
2806
|
if (res.statusCode === 404) {
|
2560
2807
|
res.type("text/plain");
|
2561
2808
|
res.status(404);
|
2562
|
-
|
2809
|
+
req.openTelemetryCollector.error("Not Found");
|
2563
2810
|
originalSend.call(instance, "Not Found");
|
2564
2811
|
errorSent = true;
|
2565
2812
|
}
|
@@ -2577,8 +2824,8 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
|
|
2577
2824
|
`attachment; filename="${data.name}"`
|
2578
2825
|
);
|
2579
2826
|
}
|
2580
|
-
if ((0,
|
2581
|
-
import_stream.Readable.from((0,
|
2827
|
+
if ((0, import_common9.isNodeJsWriteableStream)(res)) {
|
2828
|
+
import_stream.Readable.from((0, import_common9.readableStreamToAsyncIterable)(data.stream())).pipe(
|
2582
2829
|
res
|
2583
2830
|
);
|
2584
2831
|
} else {
|
@@ -2587,7 +2834,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
|
|
2587
2834
|
originalSend.call(instance, "Not a NodeJS WritableStream");
|
2588
2835
|
errorSent = true;
|
2589
2836
|
}
|
2590
|
-
} else if ((0,
|
2837
|
+
} else if ((0, import_common9.isAsyncGenerator)(data)) {
|
2591
2838
|
let firstPass = true;
|
2592
2839
|
const transformer = new import_stream.Transform({
|
2593
2840
|
objectMode: true,
|
@@ -2602,7 +2849,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
|
|
2602
2849
|
------------------
|
2603
2850
|
${res.locals.errorMessage}`;
|
2604
2851
|
}
|
2605
|
-
|
2852
|
+
req.openTelemetryCollector.error(errorString);
|
2606
2853
|
res.type("text/plain");
|
2607
2854
|
res.status(500);
|
2608
2855
|
originalSend.call(instance, errorString);
|
@@ -2615,7 +2862,7 @@ ${res.locals.errorMessage}`;
|
|
2615
2862
|
if (!errorSent) {
|
2616
2863
|
let data2 = "";
|
2617
2864
|
for (const [key, value] of Object.entries(chunk)) {
|
2618
|
-
data2 += `${key}: ${typeof value === "string" ? value : (0,
|
2865
|
+
data2 += `${key}: ${typeof value === "string" ? value : (0, import_common9.safeStringify)(value)}
|
2619
2866
|
`;
|
2620
2867
|
}
|
2621
2868
|
data2 += "\n";
|
@@ -2623,7 +2870,7 @@ ${res.locals.errorMessage}`;
|
|
2623
2870
|
}
|
2624
2871
|
}
|
2625
2872
|
});
|
2626
|
-
if ((0,
|
2873
|
+
if ((0, import_common9.isNodeJsWriteableStream)(res)) {
|
2627
2874
|
import_stream.Readable.from(data).pipe(transformer).pipe(res);
|
2628
2875
|
} else {
|
2629
2876
|
res.type("text/plain");
|
@@ -2634,7 +2881,7 @@ ${res.locals.errorMessage}`;
|
|
2634
2881
|
} else {
|
2635
2882
|
const parserType = responseBodies?.[Number(res.statusCode)]?.parserType;
|
2636
2883
|
res.bodyData = data;
|
2637
|
-
if ((0,
|
2884
|
+
if ((0, import_common9.isRecord)(data)) {
|
2638
2885
|
switch (parserType) {
|
2639
2886
|
case "json":
|
2640
2887
|
res.bodyData = "json" in data ? data.json : data;
|
@@ -2655,7 +2902,7 @@ ${res.locals.errorMessage}`;
|
|
2655
2902
|
res.bodyData = data;
|
2656
2903
|
break;
|
2657
2904
|
default:
|
2658
|
-
(0,
|
2905
|
+
(0, import_common9.isNever)(parserType);
|
2659
2906
|
res.bodyData = data;
|
2660
2907
|
break;
|
2661
2908
|
}
|
@@ -2668,7 +2915,7 @@ ${res.locals.errorMessage}`;
|
|
2668
2915
|
------------------
|
2669
2916
|
${res.locals.errorMessage}`;
|
2670
2917
|
}
|
2671
|
-
|
2918
|
+
req.openTelemetryCollector.error(errorString);
|
2672
2919
|
res.type("text/plain");
|
2673
2920
|
res.status(500);
|
2674
2921
|
originalSend.call(instance, errorString);
|
@@ -2691,16 +2938,17 @@ ${res.locals.errorMessage}`;
|
|
2691
2938
|
}
|
2692
2939
|
|
2693
2940
|
// src/http/openApiV3Generator/openApiV3Generator.ts
|
2941
|
+
var import_common10 = require("@forklaunch/common");
|
2694
2942
|
function toUpperCase(str) {
|
2695
2943
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
2696
2944
|
}
|
2697
2945
|
function transformBasePath(basePath) {
|
2698
2946
|
if (basePath.startsWith("/")) {
|
2699
|
-
return
|
2947
|
+
return basePath.slice(1);
|
2700
2948
|
}
|
2701
2949
|
return `/${basePath}`;
|
2702
2950
|
}
|
2703
|
-
function generateOpenApiDocument(port, tags, paths) {
|
2951
|
+
function generateOpenApiDocument(protocol, host, port, tags, paths, otherServers) {
|
2704
2952
|
return {
|
2705
2953
|
openapi: "3.1.0",
|
2706
2954
|
info: {
|
@@ -2719,8 +2967,10 @@ function generateOpenApiDocument(port, tags, paths) {
|
|
2719
2967
|
tags,
|
2720
2968
|
servers: [
|
2721
2969
|
{
|
2722
|
-
url:
|
2723
|
-
|
2970
|
+
url: `${protocol}://${host}:${port}`,
|
2971
|
+
description: "Main Server"
|
2972
|
+
},
|
2973
|
+
...otherServers || []
|
2724
2974
|
],
|
2725
2975
|
paths
|
2726
2976
|
};
|
@@ -2741,19 +2991,21 @@ function contentResolver(schemaValidator, body, contentType) {
|
|
2741
2991
|
}
|
2742
2992
|
};
|
2743
2993
|
}
|
2744
|
-
function generateSwaggerDocument(schemaValidator, port, routers) {
|
2994
|
+
function generateSwaggerDocument(schemaValidator, protocol, host, port, routers, otherServers) {
|
2745
2995
|
const tags = [];
|
2746
2996
|
const paths = {};
|
2747
|
-
routers
|
2748
|
-
const controllerName = transformBasePath(
|
2997
|
+
unpackRouters(routers).forEach(({ fullPath, router, sdkPath }) => {
|
2998
|
+
const controllerName = transformBasePath(fullPath);
|
2749
2999
|
tags.push({
|
2750
3000
|
name: controllerName,
|
2751
|
-
description: `${controllerName} Operations`
|
3001
|
+
description: `${toUpperCase(controllerName)} Operations`
|
2752
3002
|
});
|
2753
3003
|
router.routes.forEach((route) => {
|
2754
|
-
const
|
2755
|
-
|
2756
|
-
|
3004
|
+
const openApiPath = (0, import_common10.openApiCompliantPath)(
|
3005
|
+
`${fullPath}${route.path === "/" ? "" : route.path}`
|
3006
|
+
);
|
3007
|
+
if (!paths[openApiPath]) {
|
3008
|
+
paths[openApiPath] = {};
|
2757
3009
|
}
|
2758
3010
|
const { name, summary, query, requestHeaders } = route.contractDetails;
|
2759
3011
|
const responses = {};
|
@@ -2780,15 +3032,16 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2780
3032
|
};
|
2781
3033
|
}
|
2782
3034
|
}
|
2783
|
-
const
|
3035
|
+
const operationObject = {
|
2784
3036
|
tags: [controllerName],
|
2785
3037
|
summary: `${name}: ${summary}`,
|
2786
3038
|
parameters: [],
|
2787
|
-
responses
|
3039
|
+
responses,
|
3040
|
+
operationId: `${sdkPath}.${(0, import_common10.toPrettyCamelCase)(name)}`
|
2788
3041
|
};
|
2789
3042
|
if (route.contractDetails.params) {
|
2790
3043
|
for (const key in route.contractDetails.params) {
|
2791
|
-
|
3044
|
+
operationObject.parameters?.push({
|
2792
3045
|
name: key,
|
2793
3046
|
in: "path",
|
2794
3047
|
schema: schemaValidator.openapi(
|
@@ -2799,7 +3052,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2799
3052
|
}
|
2800
3053
|
const discriminatedBodyResult = "body" in route.contractDetails ? discriminateBody(schemaValidator, route.contractDetails.body) : null;
|
2801
3054
|
if (discriminatedBodyResult) {
|
2802
|
-
|
3055
|
+
operationObject.requestBody = {
|
2803
3056
|
required: true,
|
2804
3057
|
content: contentResolver(
|
2805
3058
|
schemaValidator,
|
@@ -2810,7 +3063,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2810
3063
|
}
|
2811
3064
|
if (requestHeaders) {
|
2812
3065
|
for (const key in requestHeaders) {
|
2813
|
-
|
3066
|
+
operationObject.parameters?.push({
|
2814
3067
|
name: key,
|
2815
3068
|
in: "header",
|
2816
3069
|
schema: schemaValidator.openapi(
|
@@ -2821,7 +3074,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2821
3074
|
}
|
2822
3075
|
if (query) {
|
2823
3076
|
for (const key in query) {
|
2824
|
-
|
3077
|
+
operationObject.parameters?.push({
|
2825
3078
|
name: key,
|
2826
3079
|
in: "query",
|
2827
3080
|
schema: schemaValidator.openapi(query[key])
|
@@ -2838,7 +3091,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2838
3091
|
content: contentResolver(schemaValidator, schemaValidator.string)
|
2839
3092
|
};
|
2840
3093
|
if (route.contractDetails.auth.method === "jwt") {
|
2841
|
-
|
3094
|
+
operationObject.security = [
|
2842
3095
|
{
|
2843
3096
|
bearer: Array.from(
|
2844
3097
|
route.contractDetails.auth.allowedPermissions?.values() || []
|
@@ -2848,11 +3101,18 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2848
3101
|
}
|
2849
3102
|
}
|
2850
3103
|
if (route.method !== "middleware") {
|
2851
|
-
paths[
|
3104
|
+
paths[openApiPath][route.method] = operationObject;
|
2852
3105
|
}
|
2853
3106
|
});
|
2854
3107
|
});
|
2855
|
-
return generateOpenApiDocument(
|
3108
|
+
return generateOpenApiDocument(
|
3109
|
+
protocol,
|
3110
|
+
host,
|
3111
|
+
port,
|
3112
|
+
tags,
|
3113
|
+
paths,
|
3114
|
+
otherServers
|
3115
|
+
);
|
2856
3116
|
}
|
2857
3117
|
|
2858
3118
|
// src/http/telemetry/evaluateTelemetryOptions.ts
|
@@ -2892,6 +3152,7 @@ function metricsDefinitions(metrics2) {
|
|
2892
3152
|
discriminateResponseBodies,
|
2893
3153
|
enrichExpressLikeSend,
|
2894
3154
|
evaluateTelemetryOptions,
|
3155
|
+
generateMcpServer,
|
2895
3156
|
generateSwaggerDocument,
|
2896
3157
|
get,
|
2897
3158
|
getCodeForStatus,
|