@forklaunch/core 0.10.4 → 0.11.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 +115 -120
- package/lib/http/index.d.ts +115 -120
- package/lib/http/index.js +143 -87
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +104 -49
- package/lib/http/index.mjs.map +1 -1
- package/package.json +19 -19
package/lib/http/index.mjs
CHANGED
@@ -66,7 +66,39 @@ function isTypedHandler(maybeTypedHandler) {
|
|
66
66
|
}
|
67
67
|
|
68
68
|
// src/http/middleware/request/auth.middleware.ts
|
69
|
+
import { isNever } from "@forklaunch/common";
|
69
70
|
import { jwtVerify } from "jose";
|
71
|
+
|
72
|
+
// src/http/discriminateAuthMethod.ts
|
73
|
+
function discriminateAuthMethod(auth) {
|
74
|
+
if ("basic" in auth) {
|
75
|
+
return {
|
76
|
+
type: "basic",
|
77
|
+
auth: auth.basic
|
78
|
+
};
|
79
|
+
} else if ("jwt" in auth) {
|
80
|
+
return {
|
81
|
+
type: "jwt",
|
82
|
+
auth: auth.jwt
|
83
|
+
};
|
84
|
+
} else {
|
85
|
+
return {
|
86
|
+
type: "jwt"
|
87
|
+
};
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
// src/http/guards/hasPermissionChecks.ts
|
92
|
+
function hasPermissionChecks(maybePermissionedAuth) {
|
93
|
+
return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "mapPermissions" in maybePermissionedAuth && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
|
94
|
+
}
|
95
|
+
|
96
|
+
// src/http/guards/hasRoleChecks.ts
|
97
|
+
function hasRoleChecks(maybeRoledAuth) {
|
98
|
+
return typeof maybeRoledAuth === "object" && maybeRoledAuth !== null && "mapRoles" in maybeRoledAuth && ("allowedRoles" in maybeRoledAuth || "forbiddenRoles" in maybeRoledAuth);
|
99
|
+
}
|
100
|
+
|
101
|
+
// src/http/middleware/request/auth.middleware.ts
|
70
102
|
var invalidAuthorizationTokenFormat = [
|
71
103
|
401,
|
72
104
|
"Invalid Authorization token format."
|
@@ -97,18 +129,16 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
97
129
|
}
|
98
130
|
const [tokenPrefix, token] = authorizationToken.split(" ");
|
99
131
|
let resourceId;
|
100
|
-
|
132
|
+
const { type, auth } = discriminateAuthMethod(authorizationMethod);
|
133
|
+
switch (type) {
|
101
134
|
case "jwt": {
|
102
|
-
if (tokenPrefix !== "Bearer") {
|
135
|
+
if (tokenPrefix !== (authorizationMethod.tokenPrefix ?? "Bearer")) {
|
103
136
|
return invalidAuthorizationTokenFormat;
|
104
137
|
}
|
105
138
|
try {
|
106
139
|
const decodedJwt = await jwtVerify(
|
107
140
|
token,
|
108
|
-
new TextEncoder().encode(
|
109
|
-
// TODO: Check this at application startup if there is any route with jwt checking
|
110
|
-
process.env.JWT_SECRET
|
111
|
-
)
|
141
|
+
new TextEncoder().encode(process.env.JWT_SECRET)
|
112
142
|
);
|
113
143
|
if (!decodedJwt.payload.sub) {
|
114
144
|
return invalidAuthorizationSubject;
|
@@ -121,27 +151,24 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
121
151
|
break;
|
122
152
|
}
|
123
153
|
case "basic": {
|
124
|
-
if (authorizationToken !== "Basic") {
|
154
|
+
if (authorizationToken !== (authorizationMethod.tokenPrefix ?? "Basic")) {
|
125
155
|
return invalidAuthorizationTokenFormat;
|
126
156
|
}
|
127
157
|
const [username, password] = Buffer.from(token, "base64").toString("utf-8").split(":");
|
128
158
|
if (!username || !password) {
|
129
159
|
return invalidAuthorizationTokenFormat;
|
130
160
|
}
|
131
|
-
if (!
|
161
|
+
if (!auth.login(username, password)) {
|
132
162
|
return invalidAuthorizationLogin;
|
133
163
|
}
|
134
164
|
resourceId = username;
|
135
165
|
break;
|
136
166
|
}
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
}
|
141
|
-
resourceId = authorizationMethod.decodeResource(token);
|
142
|
-
break;
|
167
|
+
default:
|
168
|
+
isNever(type);
|
169
|
+
return [401, "Invalid Authorization method."];
|
143
170
|
}
|
144
|
-
if (authorizationMethod
|
171
|
+
if (hasPermissionChecks(authorizationMethod)) {
|
145
172
|
if (!authorizationMethod.mapPermissions) {
|
146
173
|
return [500, "No permission mapping function provided."];
|
147
174
|
}
|
@@ -149,49 +176,49 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
149
176
|
resourceId,
|
150
177
|
req
|
151
178
|
);
|
152
|
-
if (authorizationMethod.allowedPermissions) {
|
179
|
+
if ("allowedPermissions" in authorizationMethod && authorizationMethod.allowedPermissions) {
|
153
180
|
if (resourcePermissions.intersection(authorizationMethod.allowedPermissions).size === 0) {
|
154
181
|
return invalidAuthorizationTokenPermissions;
|
155
182
|
}
|
156
183
|
}
|
157
|
-
if (authorizationMethod.forbiddenPermissions) {
|
184
|
+
if ("forbiddenPermissions" in authorizationMethod && authorizationMethod.forbiddenPermissions) {
|
158
185
|
if (resourcePermissions.intersection(
|
159
186
|
authorizationMethod.forbiddenPermissions
|
160
187
|
).size !== 0) {
|
161
188
|
return invalidAuthorizationTokenPermissions;
|
162
189
|
}
|
163
190
|
}
|
164
|
-
}
|
165
|
-
if (authorizationMethod.allowedRoles || authorizationMethod.forbiddenRoles) {
|
191
|
+
} else if (hasRoleChecks(authorizationMethod)) {
|
166
192
|
if (!authorizationMethod.mapRoles) {
|
167
193
|
return [500, "No role mapping function provided."];
|
168
194
|
}
|
169
195
|
const resourceRoles = await authorizationMethod.mapRoles(resourceId, req);
|
170
|
-
if (authorizationMethod.allowedRoles) {
|
196
|
+
if ("allowedRoles" in authorizationMethod && authorizationMethod.allowedRoles) {
|
171
197
|
if (resourceRoles.intersection(authorizationMethod.allowedRoles).size === 0) {
|
172
198
|
return invalidAuthorizationTokenRoles;
|
173
199
|
}
|
174
200
|
}
|
175
|
-
if (authorizationMethod.forbiddenRoles) {
|
201
|
+
if ("forbiddenRoles" in authorizationMethod && authorizationMethod.forbiddenRoles) {
|
176
202
|
if (resourceRoles.intersection(authorizationMethod.forbiddenRoles).size !== 0) {
|
177
203
|
return invalidAuthorizationTokenRoles;
|
178
204
|
}
|
179
205
|
}
|
206
|
+
} else {
|
207
|
+
return [401, "Invalid Authorization method."];
|
180
208
|
}
|
181
|
-
return [401, "Invalid Authorization method."];
|
182
209
|
}
|
183
210
|
async function parseRequestAuth(req, res, next) {
|
184
211
|
const auth = req.contractDetails.auth;
|
185
212
|
if (auth) {
|
186
213
|
const [error, message] = await checkAuthorizationToken(
|
187
214
|
auth,
|
188
|
-
req.headers[
|
215
|
+
req.headers[auth.headerName ?? "Authorization"] || req.headers[auth.headerName ?? "authorization"],
|
189
216
|
req
|
190
217
|
) ?? [];
|
191
218
|
if (error != null) {
|
192
219
|
res.type("text/plain");
|
193
220
|
res.status(error).send(message);
|
194
|
-
|
221
|
+
return;
|
195
222
|
}
|
196
223
|
}
|
197
224
|
next?.();
|
@@ -262,7 +289,7 @@ function isForklaunchRequest(request) {
|
|
262
289
|
}
|
263
290
|
|
264
291
|
// src/http/telemetry/pinoLogger.ts
|
265
|
-
import { isNever, safeStringify } from "@forklaunch/common";
|
292
|
+
import { isNever as isNever2, safeStringify } from "@forklaunch/common";
|
266
293
|
import { trace as trace2 } from "@opentelemetry/api";
|
267
294
|
import { logs } from "@opentelemetry/api-logs";
|
268
295
|
import pino from "pino";
|
@@ -294,7 +321,7 @@ function mapSeverity(level) {
|
|
294
321
|
case "fatal":
|
295
322
|
return 21;
|
296
323
|
default:
|
297
|
-
|
324
|
+
isNever2(level);
|
298
325
|
return 0;
|
299
326
|
}
|
300
327
|
}
|
@@ -1457,6 +1484,11 @@ var trace3 = (_schemaValidator, path, contractDetails, ...handlers) => {
|
|
1457
1484
|
return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
|
1458
1485
|
};
|
1459
1486
|
|
1487
|
+
// src/http/handlers/typedAuthHandler.ts
|
1488
|
+
function typedAuthHandler(_schemaValidator, _contractDetails, authHandler) {
|
1489
|
+
return authHandler;
|
1490
|
+
}
|
1491
|
+
|
1460
1492
|
// src/http/httpStatusCodes.ts
|
1461
1493
|
var HTTPStatuses = {
|
1462
1494
|
/**
|
@@ -2441,8 +2473,8 @@ var getCodeForStatus = (status) => {
|
|
2441
2473
|
var httpStatusCodes_default = HTTPStatuses;
|
2442
2474
|
|
2443
2475
|
// src/http/mcpGenerator/mcpGenerator.ts
|
2444
|
-
import { isNever as
|
2445
|
-
import { ZodSchemaValidator } from "@forklaunch/validator/zod";
|
2476
|
+
import { isNever as isNever3, isRecord, safeStringify as safeStringify2 } from "@forklaunch/common";
|
2477
|
+
import { string, ZodSchemaValidator } from "@forklaunch/validator/zod";
|
2446
2478
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
2447
2479
|
|
2448
2480
|
// src/http/router/unpackRouters.ts
|
@@ -2499,14 +2531,15 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2499
2531
|
...route.contractDetails.params ? { params: schemaValidator.schemify(route.contractDetails.params) } : {},
|
2500
2532
|
...route.contractDetails.query ? { query: schemaValidator.schemify(route.contractDetails.query) } : {},
|
2501
2533
|
...route.contractDetails.requestHeaders ? {
|
2502
|
-
headers: schemaValidator.schemify(
|
2503
|
-
route.contractDetails.requestHeaders
|
2504
|
-
|
2534
|
+
headers: schemaValidator.schemify({
|
2535
|
+
...route.contractDetails.requestHeaders,
|
2536
|
+
...route.contractDetails.auth ? {
|
2537
|
+
[route.contractDetails.auth.headerName ?? "authorization"]: string.startsWith(
|
2538
|
+
route.contractDetails.auth.tokenPrefix ?? ("basic" in route.contractDetails.auth ? "Basic " : "Bearer ")
|
2539
|
+
)
|
2540
|
+
} : {}
|
2541
|
+
})
|
2505
2542
|
} : {}
|
2506
|
-
// TODO: support auth
|
2507
|
-
// ...(route.contractDetails.auth
|
2508
|
-
// ? { auth: route.contractDetails.auth }
|
2509
|
-
// : {})
|
2510
2543
|
};
|
2511
2544
|
mcpServer.tool(
|
2512
2545
|
route.contractDetails.name,
|
@@ -2568,7 +2601,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2568
2601
|
break;
|
2569
2602
|
}
|
2570
2603
|
default: {
|
2571
|
-
|
2604
|
+
isNever3(discriminatedBody.parserType);
|
2572
2605
|
parsedBody = safeStringify2(body);
|
2573
2606
|
break;
|
2574
2607
|
}
|
@@ -2718,7 +2751,7 @@ ${parseErrors.join("\n\n")}`
|
|
2718
2751
|
// src/http/middleware/response/enrichExpressLikeSend.middleware.ts
|
2719
2752
|
import {
|
2720
2753
|
isAsyncGenerator,
|
2721
|
-
isNever as
|
2754
|
+
isNever as isNever4,
|
2722
2755
|
isNodeJsWriteableStream,
|
2723
2756
|
isRecord as isRecord2,
|
2724
2757
|
readableStreamToAsyncIterable,
|
@@ -2855,7 +2888,7 @@ ${res.locals.errorMessage}`;
|
|
2855
2888
|
res.bodyData = data;
|
2856
2889
|
break;
|
2857
2890
|
default:
|
2858
|
-
|
2891
|
+
isNever4(parserType);
|
2859
2892
|
res.bodyData = data;
|
2860
2893
|
break;
|
2861
2894
|
}
|
@@ -2901,7 +2934,7 @@ function transformBasePath(basePath) {
|
|
2901
2934
|
}
|
2902
2935
|
return `/${basePath}`;
|
2903
2936
|
}
|
2904
|
-
function generateOpenApiDocument(protocol, host, port, tags, paths, otherServers) {
|
2937
|
+
function generateOpenApiDocument(protocol, host, port, tags, paths, securitySchemes, otherServers) {
|
2905
2938
|
return {
|
2906
2939
|
openapi: "3.1.0",
|
2907
2940
|
info: {
|
@@ -2909,13 +2942,7 @@ function generateOpenApiDocument(protocol, host, port, tags, paths, otherServers
|
|
2909
2942
|
version: process.env.VERSION || "1.0.0"
|
2910
2943
|
},
|
2911
2944
|
components: {
|
2912
|
-
securitySchemes
|
2913
|
-
bearer: {
|
2914
|
-
type: "http",
|
2915
|
-
scheme: "bearer",
|
2916
|
-
bearerFormat: "JWT"
|
2917
|
-
}
|
2918
|
-
}
|
2945
|
+
securitySchemes
|
2919
2946
|
},
|
2920
2947
|
tags,
|
2921
2948
|
servers: [
|
@@ -2947,6 +2974,7 @@ function contentResolver(schemaValidator, body, contentType) {
|
|
2947
2974
|
function generateSwaggerDocument(schemaValidator, protocol, host, port, routers, otherServers) {
|
2948
2975
|
const tags = [];
|
2949
2976
|
const paths = {};
|
2977
|
+
const securitySchemes = {};
|
2950
2978
|
unpackRouters(routers).forEach(({ fullPath, router, sdkPath }) => {
|
2951
2979
|
const controllerName = transformBasePath(fullPath);
|
2952
2980
|
tags.push({
|
@@ -3043,14 +3071,39 @@ function generateSwaggerDocument(schemaValidator, protocol, host, port, routers,
|
|
3043
3071
|
description: httpStatusCodes_default[403],
|
3044
3072
|
content: contentResolver(schemaValidator, schemaValidator.string)
|
3045
3073
|
};
|
3046
|
-
if (route.contractDetails.auth
|
3074
|
+
if ("basic" in route.contractDetails.auth) {
|
3047
3075
|
operationObject.security = [
|
3048
3076
|
{
|
3049
|
-
|
3050
|
-
route.contractDetails.auth.allowedPermissions?.values() || []
|
3077
|
+
basic: Array.from(
|
3078
|
+
"allowedPermissions" in route.contractDetails.auth ? route.contractDetails.auth.allowedPermissions?.values() || [] : []
|
3051
3079
|
)
|
3052
3080
|
}
|
3053
3081
|
];
|
3082
|
+
securitySchemes["basic"] = {
|
3083
|
+
type: "http",
|
3084
|
+
scheme: "basic"
|
3085
|
+
};
|
3086
|
+
} else if (route.contractDetails.auth) {
|
3087
|
+
operationObject.security = [
|
3088
|
+
{
|
3089
|
+
[route.contractDetails.auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
|
3090
|
+
"allowedPermissions" in route.contractDetails.auth ? route.contractDetails.auth.allowedPermissions?.values() || [] : []
|
3091
|
+
)
|
3092
|
+
}
|
3093
|
+
];
|
3094
|
+
if (route.contractDetails.auth.headerName && route.contractDetails.auth.headerName !== "Authorization") {
|
3095
|
+
securitySchemes[route.contractDetails.auth.headerName] = {
|
3096
|
+
type: "apiKey",
|
3097
|
+
in: "header",
|
3098
|
+
name: route.contractDetails.auth.headerName
|
3099
|
+
};
|
3100
|
+
} else {
|
3101
|
+
securitySchemes["Authorization"] = {
|
3102
|
+
type: "http",
|
3103
|
+
scheme: "bearer",
|
3104
|
+
bearerFormat: "JWT"
|
3105
|
+
};
|
3106
|
+
}
|
3054
3107
|
}
|
3055
3108
|
}
|
3056
3109
|
if (route.method !== "middleware") {
|
@@ -3064,6 +3117,7 @@ function generateSwaggerDocument(schemaValidator, protocol, host, port, routers,
|
|
3064
3117
|
port,
|
3065
3118
|
tags,
|
3066
3119
|
paths,
|
3120
|
+
securitySchemes,
|
3067
3121
|
otherServers
|
3068
3122
|
);
|
3069
3123
|
}
|
@@ -3129,6 +3183,7 @@ export {
|
|
3129
3183
|
put,
|
3130
3184
|
recordMetric,
|
3131
3185
|
trace3 as trace,
|
3186
|
+
typedAuthHandler,
|
3132
3187
|
typedHandler
|
3133
3188
|
};
|
3134
3189
|
//# sourceMappingURL=index.mjs.map
|