@forklaunch/core 0.12.3 → 0.13.1
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 +347 -118
- package/lib/http/index.d.ts +347 -118
- package/lib/http/index.js +258 -78
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +256 -77
- package/lib/http/index.mjs.map +1 -1
- package/package.json +12 -11
package/lib/http/index.js
CHANGED
@@ -57,6 +57,7 @@ __export(http_exports, {
|
|
57
57
|
isForklaunchRequest: () => isForklaunchRequest,
|
58
58
|
isForklaunchRouter: () => isForklaunchRouter,
|
59
59
|
isInformational: () => isInformational,
|
60
|
+
isPortBound: () => isPortBound,
|
60
61
|
isRedirection: () => isRedirection,
|
61
62
|
isServerError: () => isServerError,
|
62
63
|
isSuccessful: () => isSuccessful,
|
@@ -156,10 +157,10 @@ function isTypedHandler(maybeTypedHandler) {
|
|
156
157
|
|
157
158
|
// src/http/middleware/request/auth.middleware.ts
|
158
159
|
var import_common2 = require("@forklaunch/common");
|
159
|
-
var import_jose = require("jose");
|
160
160
|
|
161
161
|
// src/http/discriminateAuthMethod.ts
|
162
|
-
|
162
|
+
var import_jose = require("jose");
|
163
|
+
async function discriminateAuthMethod(auth) {
|
163
164
|
if ("basic" in auth) {
|
164
165
|
return {
|
165
166
|
type: "basic",
|
@@ -168,18 +169,59 @@ function discriminateAuthMethod(auth) {
|
|
168
169
|
login: auth.basic.login
|
169
170
|
}
|
170
171
|
};
|
171
|
-
} else if ("jwt" in auth) {
|
172
|
+
} else if ("jwt" in auth && auth.jwt != null) {
|
173
|
+
const jwt = auth.jwt;
|
174
|
+
let verificationFunction;
|
175
|
+
if ("privateKey" in jwt) {
|
176
|
+
verificationFunction = async (token) => {
|
177
|
+
const { payload } = await (0, import_jose.jwtVerify)(token, Buffer.from(jwt.privateKey));
|
178
|
+
return payload;
|
179
|
+
};
|
180
|
+
} else {
|
181
|
+
let jwks;
|
182
|
+
if ("jwksPublicKeyUrl" in jwt) {
|
183
|
+
const jwksResponse = await fetch(jwt.jwksPublicKeyUrl);
|
184
|
+
jwks = (await jwksResponse.json()).keys;
|
185
|
+
} else {
|
186
|
+
jwks = [jwt.jwksPublicKey];
|
187
|
+
}
|
188
|
+
verificationFunction = async (token) => {
|
189
|
+
for (const key of jwks) {
|
190
|
+
try {
|
191
|
+
const { payload } = await (0, import_jose.jwtVerify)(token, key);
|
192
|
+
return payload;
|
193
|
+
} catch {
|
194
|
+
continue;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
};
|
198
|
+
}
|
172
199
|
return {
|
173
200
|
type: "jwt",
|
174
201
|
auth: {
|
175
|
-
decodeResource: auth.decodeResource
|
202
|
+
decodeResource: auth.decodeResource,
|
203
|
+
verificationFunction
|
204
|
+
}
|
205
|
+
};
|
206
|
+
} else if ("secretKey" in auth) {
|
207
|
+
return {
|
208
|
+
type: "system",
|
209
|
+
auth: {
|
210
|
+
secretKey: auth.secretKey
|
176
211
|
}
|
177
212
|
};
|
178
213
|
} else {
|
179
214
|
return {
|
180
215
|
type: "jwt",
|
181
216
|
auth: {
|
182
|
-
decodeResource: auth.decodeResource
|
217
|
+
decodeResource: auth.decodeResource,
|
218
|
+
verificationFunction: async (token) => {
|
219
|
+
const { payload } = await (0, import_jose.jwtVerify)(
|
220
|
+
token,
|
221
|
+
Buffer.from(process.env.JWT_SECRET_KEY)
|
222
|
+
);
|
223
|
+
return payload;
|
224
|
+
}
|
183
225
|
}
|
184
226
|
};
|
185
227
|
}
|
@@ -187,12 +229,22 @@ function discriminateAuthMethod(auth) {
|
|
187
229
|
|
188
230
|
// src/http/guards/hasPermissionChecks.ts
|
189
231
|
function hasPermissionChecks(maybePermissionedAuth) {
|
190
|
-
return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null &&
|
232
|
+
return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
|
191
233
|
}
|
192
234
|
|
193
235
|
// src/http/guards/hasRoleChecks.ts
|
194
236
|
function hasRoleChecks(maybeRoledAuth) {
|
195
|
-
return typeof maybeRoledAuth === "object" && maybeRoledAuth !== null &&
|
237
|
+
return typeof maybeRoledAuth === "object" && maybeRoledAuth !== null && ("allowedRoles" in maybeRoledAuth || "forbiddenRoles" in maybeRoledAuth);
|
238
|
+
}
|
239
|
+
|
240
|
+
// src/http/guards/hasScopeChecks.ts
|
241
|
+
function hasScopeChecks(maybePermissionedAuth) {
|
242
|
+
return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
|
243
|
+
}
|
244
|
+
|
245
|
+
// src/http/guards/isSystemAuthMethod.ts
|
246
|
+
function isSystemAuthMethod(maybeSystemAuthMethod) {
|
247
|
+
return typeof maybeSystemAuthMethod === "object" && maybeSystemAuthMethod !== null && "secretKey" in maybeSystemAuthMethod;
|
196
248
|
}
|
197
249
|
|
198
250
|
// src/http/middleware/request/auth.middleware.ts
|
@@ -220,23 +272,45 @@ var invalidAuthorizationLogin = [
|
|
220
272
|
403,
|
221
273
|
"Invalid Authorization login."
|
222
274
|
];
|
223
|
-
|
275
|
+
var invalidScope = [403, "Invalid scope for operation."];
|
276
|
+
var invalidAuthorizationMethod = [
|
277
|
+
401,
|
278
|
+
"Invalid Authorization method."
|
279
|
+
];
|
280
|
+
var authorizationTokenRequired = [
|
281
|
+
401,
|
282
|
+
"Authorization token required."
|
283
|
+
];
|
284
|
+
async function checkAuthorizationToken(authorizationMethod, globalOptions, authorizationToken, req) {
|
285
|
+
if (authorizationMethod == null) {
|
286
|
+
return void 0;
|
287
|
+
}
|
288
|
+
const collapsedAuthorizationMethod = {
|
289
|
+
...globalOptions,
|
290
|
+
...authorizationMethod
|
291
|
+
};
|
224
292
|
if (authorizationToken == null) {
|
225
|
-
return
|
293
|
+
return authorizationTokenRequired;
|
226
294
|
}
|
227
295
|
const [tokenPrefix, token] = authorizationToken.split(" ");
|
228
296
|
let resourceId;
|
229
|
-
const { type, auth } = discriminateAuthMethod(
|
297
|
+
const { type, auth } = await discriminateAuthMethod(
|
298
|
+
collapsedAuthorizationMethod
|
299
|
+
);
|
230
300
|
switch (type) {
|
301
|
+
case "system": {
|
302
|
+
if (token !== auth.secretKey || tokenPrefix !== collapsedAuthorizationMethod.tokenPrefix) {
|
303
|
+
return invalidAuthorizationToken;
|
304
|
+
}
|
305
|
+
resourceId = null;
|
306
|
+
break;
|
307
|
+
}
|
231
308
|
case "jwt": {
|
232
|
-
if (tokenPrefix !== (
|
309
|
+
if (tokenPrefix !== (collapsedAuthorizationMethod.tokenPrefix ?? "Bearer")) {
|
233
310
|
return invalidAuthorizationTokenFormat;
|
234
311
|
}
|
235
312
|
try {
|
236
|
-
const decodedJwt = await auth
|
237
|
-
token,
|
238
|
-
new TextEncoder().encode(process.env.JWT_SECRET)
|
239
|
-
)).payload;
|
313
|
+
const decodedJwt = "decodeResource" in auth && auth.decodeResource ? await auth.decodeResource(token) : "verificationFunction" in auth && auth.verificationFunction ? await auth.verificationFunction(token) : void 0;
|
240
314
|
if (!decodedJwt) {
|
241
315
|
return invalidAuthorizationSubject;
|
242
316
|
}
|
@@ -248,7 +322,7 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
248
322
|
break;
|
249
323
|
}
|
250
324
|
case "basic": {
|
251
|
-
if (tokenPrefix !== (
|
325
|
+
if (tokenPrefix !== (collapsedAuthorizationMethod.tokenPrefix ?? "Basic")) {
|
252
326
|
return invalidAuthorizationTokenFormat;
|
253
327
|
}
|
254
328
|
if (auth.decodeResource) {
|
@@ -271,62 +345,82 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
|
|
271
345
|
(0, import_common2.isNever)(type);
|
272
346
|
return [401, "Invalid Authorization method."];
|
273
347
|
}
|
274
|
-
if (
|
275
|
-
|
276
|
-
|
348
|
+
if (isSystemAuthMethod(collapsedAuthorizationMethod) || resourceId == null) {
|
349
|
+
return;
|
350
|
+
}
|
351
|
+
if (hasScopeChecks(collapsedAuthorizationMethod)) {
|
352
|
+
if (collapsedAuthorizationMethod.surfaceScopes) {
|
353
|
+
const resourceScopes = await collapsedAuthorizationMethod.surfaceScopes(
|
354
|
+
resourceId,
|
355
|
+
req
|
356
|
+
);
|
357
|
+
if (collapsedAuthorizationMethod.scopeHeirarchy) {
|
358
|
+
if (collapsedAuthorizationMethod.requiredScope) {
|
359
|
+
if (!resourceScopes.has(collapsedAuthorizationMethod.requiredScope) || Array.from(resourceScopes).every(
|
360
|
+
(scope) => collapsedAuthorizationMethod.scopeHeirarchy?.indexOf(scope) ?? -1 > -1
|
361
|
+
)) {
|
362
|
+
return invalidScope;
|
363
|
+
}
|
364
|
+
}
|
365
|
+
}
|
277
366
|
}
|
278
|
-
|
367
|
+
}
|
368
|
+
if (hasPermissionChecks(collapsedAuthorizationMethod)) {
|
369
|
+
if (!collapsedAuthorizationMethod.surfacePermissions) {
|
370
|
+
return [500, "No permission surfacing function provided."];
|
371
|
+
}
|
372
|
+
const resourcePermissions = await collapsedAuthorizationMethod.surfacePermissions(
|
279
373
|
resourceId,
|
280
374
|
req
|
281
375
|
);
|
282
|
-
if ("allowedPermissions" in
|
283
|
-
if (resourcePermissions.intersection(
|
376
|
+
if ("allowedPermissions" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedPermissions) {
|
377
|
+
if (resourcePermissions.intersection(
|
378
|
+
collapsedAuthorizationMethod.allowedPermissions
|
379
|
+
).size === 0) {
|
284
380
|
return invalidAuthorizationTokenPermissions;
|
285
381
|
}
|
286
382
|
}
|
287
|
-
if ("forbiddenPermissions" in
|
383
|
+
if ("forbiddenPermissions" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.forbiddenPermissions) {
|
288
384
|
if (resourcePermissions.intersection(
|
289
|
-
|
385
|
+
collapsedAuthorizationMethod.forbiddenPermissions
|
290
386
|
).size !== 0) {
|
291
387
|
return invalidAuthorizationTokenPermissions;
|
292
388
|
}
|
293
389
|
}
|
294
|
-
} else if (hasRoleChecks(
|
295
|
-
if (!
|
296
|
-
return [500, "No role
|
390
|
+
} else if (hasRoleChecks(collapsedAuthorizationMethod)) {
|
391
|
+
if (!collapsedAuthorizationMethod.surfaceRoles) {
|
392
|
+
return [500, "No role surfacing function provided."];
|
297
393
|
}
|
298
|
-
const resourceRoles = await
|
394
|
+
const resourceRoles = await collapsedAuthorizationMethod.surfaceRoles(
|
299
395
|
resourceId,
|
300
396
|
req
|
301
397
|
);
|
302
|
-
if ("allowedRoles" in
|
303
|
-
if (resourceRoles.intersection(
|
398
|
+
if ("allowedRoles" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedRoles) {
|
399
|
+
if (resourceRoles.intersection(collapsedAuthorizationMethod.allowedRoles).size === 0) {
|
304
400
|
return invalidAuthorizationTokenRoles;
|
305
401
|
}
|
306
402
|
}
|
307
|
-
if ("forbiddenRoles" in
|
308
|
-
if (resourceRoles.intersection(
|
403
|
+
if ("forbiddenRoles" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.forbiddenRoles) {
|
404
|
+
if (resourceRoles.intersection(collapsedAuthorizationMethod.forbiddenRoles).size !== 0) {
|
309
405
|
return invalidAuthorizationTokenRoles;
|
310
406
|
}
|
311
407
|
}
|
312
408
|
} else {
|
313
|
-
return
|
409
|
+
return invalidAuthorizationMethod;
|
314
410
|
}
|
315
411
|
}
|
316
412
|
async function parseRequestAuth(req, res, next) {
|
317
413
|
const auth = req.contractDetails.auth;
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
return;
|
329
|
-
}
|
414
|
+
const [error, message] = await checkAuthorizationToken(
|
415
|
+
auth,
|
416
|
+
req._globalOptions?.auth,
|
417
|
+
req.headers[auth?.headerName ?? "Authorization"] || req.headers[auth?.headerName ?? "authorization"],
|
418
|
+
req
|
419
|
+
) ?? [];
|
420
|
+
if (error != null) {
|
421
|
+
res.type("text/plain");
|
422
|
+
res.status(error).send(message);
|
423
|
+
return;
|
330
424
|
}
|
331
425
|
next?.();
|
332
426
|
}
|
@@ -396,7 +490,7 @@ var import_common4 = require("@forklaunch/common");
|
|
396
490
|
var import_api2 = require("@opentelemetry/api");
|
397
491
|
var import_api_logs = require("@opentelemetry/api-logs");
|
398
492
|
var import_pino = __toESM(require("pino"));
|
399
|
-
var
|
493
|
+
var PinoPretty = __toESM(require("pino-pretty"));
|
400
494
|
|
401
495
|
// src/http/guards/isLoggerMeta.ts
|
402
496
|
function isLoggerMeta(arg) {
|
@@ -443,11 +537,35 @@ function normalizeLogArgs(args) {
|
|
443
537
|
const metadata = Object.assign({}, ...metaObjects);
|
444
538
|
return [metadata, message.trim()];
|
445
539
|
}
|
540
|
+
function safePrettyFormat(level, args, timestamp) {
|
541
|
+
try {
|
542
|
+
const [metadata, message] = normalizeLogArgs(args);
|
543
|
+
const formattedTimestamp = timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
544
|
+
return `[${formattedTimestamp}] ${level.toUpperCase()}: ${message}${Object.keys(metadata).length > 0 ? `
|
545
|
+
${JSON.stringify(metadata, null, 2)}` : ""}`;
|
546
|
+
} catch (error) {
|
547
|
+
const fallbackMessage = args.map((arg) => {
|
548
|
+
try {
|
549
|
+
if (typeof arg === "string") return arg;
|
550
|
+
if (arg === null) return "null";
|
551
|
+
if (arg === void 0) return "undefined";
|
552
|
+
return JSON.stringify(arg);
|
553
|
+
} catch {
|
554
|
+
return "[Circular/Non-serializable Object]";
|
555
|
+
}
|
556
|
+
}).join(" ");
|
557
|
+
return `[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}: ${fallbackMessage} [Pretty Print Error: ${error instanceof Error ? error.message : "Unknown error"}]`;
|
558
|
+
}
|
559
|
+
}
|
446
560
|
var PinoLogger = class _PinoLogger {
|
447
561
|
pinoLogger;
|
448
562
|
meta;
|
449
|
-
prettyPrinter =
|
450
|
-
colorize: true
|
563
|
+
prettyPrinter = PinoPretty.prettyFactory({
|
564
|
+
colorize: true,
|
565
|
+
// Add error handling options
|
566
|
+
errorLikeObjectKeys: ["err", "error"],
|
567
|
+
ignore: "pid,hostname",
|
568
|
+
translateTime: "SYS:standard"
|
451
569
|
});
|
452
570
|
constructor(level, meta2 = {}) {
|
453
571
|
this.pinoLogger = (0, import_pino.default)({
|
@@ -460,7 +578,12 @@ var PinoLogger = class _PinoLogger {
|
|
460
578
|
timestamp: import_pino.default.stdTimeFunctions.isoTime,
|
461
579
|
transport: {
|
462
580
|
target: "pino-pretty",
|
463
|
-
options: {
|
581
|
+
options: {
|
582
|
+
colorize: true,
|
583
|
+
errorLikeObjectKeys: ["err", "error"],
|
584
|
+
ignore: "pid,hostname",
|
585
|
+
translateTime: "SYS:standard"
|
586
|
+
}
|
464
587
|
}
|
465
588
|
});
|
466
589
|
this.meta = meta2;
|
@@ -492,12 +615,21 @@ var PinoLogger = class _PinoLogger {
|
|
492
615
|
...meta2
|
493
616
|
};
|
494
617
|
this.pinoLogger[level](...normalizeLogArgs(filteredArgs));
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
618
|
+
const formattedBody = safePrettyFormat(level, filteredArgs);
|
619
|
+
try {
|
620
|
+
import_api_logs.logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
|
621
|
+
severityText: level,
|
622
|
+
severityNumber: mapSeverity(level),
|
623
|
+
body: formattedBody,
|
624
|
+
attributes: { ...this.meta, ...meta2 }
|
625
|
+
});
|
626
|
+
} catch (error) {
|
627
|
+
console.error("Failed to emit OpenTelemetry log:", error);
|
628
|
+
console.log(
|
629
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}:`,
|
630
|
+
...filteredArgs
|
631
|
+
);
|
632
|
+
}
|
501
633
|
}
|
502
634
|
error = (msg, ...args) => this.log("error", msg, ...args);
|
503
635
|
info = (msg, ...args) => this.log("info", msg, ...args);
|
@@ -626,13 +758,14 @@ var httpServerDurationHistogram = import_api3.metrics.getMeter((0, import_common
|
|
626
758
|
});
|
627
759
|
|
628
760
|
// src/http/middleware/request/enrichDetails.middleware.ts
|
629
|
-
function enrichDetails(path, contractDetails, requestSchema, responseSchemas, openTelemetryCollector) {
|
761
|
+
function enrichDetails(path, contractDetails, requestSchema, responseSchemas, openTelemetryCollector, globalOptions) {
|
630
762
|
return (req, res, next) => {
|
631
763
|
req.originalPath = path;
|
632
764
|
req.contractDetails = contractDetails;
|
633
765
|
req.requestSchema = requestSchema;
|
634
766
|
res.responseSchemas = responseSchemas;
|
635
767
|
req.openTelemetryCollector = openTelemetryCollector;
|
768
|
+
req._globalOptions = globalOptions;
|
636
769
|
req.context?.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
|
637
770
|
const startTime = process.hrtime();
|
638
771
|
res.on("finish", () => {
|
@@ -666,6 +799,7 @@ function isRequestShape(maybeResponseShape) {
|
|
666
799
|
|
667
800
|
// src/http/middleware/request/parse.middleware.ts
|
668
801
|
function parse(req, res, next) {
|
802
|
+
const collapsedOptions = req.contractDetails.options?.requestValidation ?? (req._globalOptions?.validation === false ? "none" : req._globalOptions?.validation?.request);
|
669
803
|
const request = {
|
670
804
|
params: req.params,
|
671
805
|
query: req.query,
|
@@ -738,8 +872,9 @@ function parse(req, res, next) {
|
|
738
872
|
);
|
739
873
|
}
|
740
874
|
if (!parsedRequest.ok) {
|
741
|
-
switch (
|
875
|
+
switch (collapsedOptions) {
|
742
876
|
default:
|
877
|
+
case void 0:
|
743
878
|
case "error":
|
744
879
|
res.type("application/json");
|
745
880
|
res.status(400);
|
@@ -900,12 +1035,13 @@ function discriminateResponseBodies(schemaValidator, responses) {
|
|
900
1035
|
|
901
1036
|
// src/http/router/expressLikeRouter.ts
|
902
1037
|
var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
903
|
-
constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
|
1038
|
+
constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector, routerOptions) {
|
904
1039
|
this.basePath = basePath;
|
905
1040
|
this.schemaValidator = schemaValidator;
|
906
1041
|
this.internal = internal;
|
907
1042
|
this.postEnrichMiddleware = postEnrichMiddleware;
|
908
1043
|
this.openTelemetryCollector = openTelemetryCollector;
|
1044
|
+
this.routerOptions = routerOptions;
|
909
1045
|
if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
|
910
1046
|
process.on("uncaughtException", (err) => {
|
911
1047
|
this.openTelemetryCollector.error(`Uncaught exception: ${err}`);
|
@@ -944,7 +1080,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
944
1080
|
contractDetails,
|
945
1081
|
requestSchema,
|
946
1082
|
responseSchemas,
|
947
|
-
this.openTelemetryCollector
|
1083
|
+
this.openTelemetryCollector,
|
1084
|
+
this.routerOptions
|
948
1085
|
),
|
949
1086
|
...this.postEnrichMiddleware,
|
950
1087
|
parse,
|
@@ -1637,7 +1774,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1637
1774
|
this.schemaValidator,
|
1638
1775
|
this.internal,
|
1639
1776
|
this.postEnrichMiddleware,
|
1640
|
-
this.openTelemetryCollector
|
1777
|
+
this.openTelemetryCollector,
|
1778
|
+
this.routerOptions
|
1641
1779
|
);
|
1642
1780
|
this.cloneInternals(clone);
|
1643
1781
|
return clone;
|
@@ -1657,7 +1795,12 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
|
|
1657
1795
|
schemaValidator,
|
1658
1796
|
internal,
|
1659
1797
|
postEnrichMiddleware,
|
1660
|
-
openTelemetryCollector
|
1798
|
+
openTelemetryCollector,
|
1799
|
+
{
|
1800
|
+
...appOptions,
|
1801
|
+
openapi: appOptions?.openapi !== false,
|
1802
|
+
mcp: appOptions?.mcp !== false
|
1803
|
+
}
|
1661
1804
|
);
|
1662
1805
|
this.schemaValidator = schemaValidator;
|
1663
1806
|
this.internal = internal;
|
@@ -1668,6 +1811,27 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
|
|
1668
1811
|
}
|
1669
1812
|
};
|
1670
1813
|
|
1814
|
+
// src/http/cluster/isPortBound.ts
|
1815
|
+
var net = __toESM(require("net"));
|
1816
|
+
function isPortBound(port, host = "localhost") {
|
1817
|
+
return new Promise((resolve) => {
|
1818
|
+
const socket = new net.Socket();
|
1819
|
+
socket.setTimeout(1e3);
|
1820
|
+
socket.on("connect", () => {
|
1821
|
+
socket.destroy();
|
1822
|
+
resolve(true);
|
1823
|
+
});
|
1824
|
+
socket.on("timeout", () => {
|
1825
|
+
socket.destroy();
|
1826
|
+
resolve(false);
|
1827
|
+
});
|
1828
|
+
socket.on("error", () => {
|
1829
|
+
resolve(false);
|
1830
|
+
});
|
1831
|
+
socket.connect(port, host);
|
1832
|
+
});
|
1833
|
+
}
|
1834
|
+
|
1671
1835
|
// src/http/guards/isPath.ts
|
1672
1836
|
function isPath(path) {
|
1673
1837
|
return path.startsWith("/");
|
@@ -2790,7 +2954,7 @@ function generateInputSchema(schemaValidator, body, params, query, requestHeader
|
|
2790
2954
|
} : {}
|
2791
2955
|
});
|
2792
2956
|
}
|
2793
|
-
function generateMcpServer(schemaValidator, protocol, host, port, version, application, options2, contentTypeMap) {
|
2957
|
+
function generateMcpServer(schemaValidator, protocol, host, port, version, application, appOptions, options2, contentTypeMap) {
|
2794
2958
|
if (!(schemaValidator instanceof import_zod.ZodSchemaValidator)) {
|
2795
2959
|
throw new Error(
|
2796
2960
|
"Schema validator must be an instance of ZodSchemaValidator"
|
@@ -2811,6 +2975,9 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
|
|
2811
2975
|
])
|
2812
2976
|
].forEach(({ fullPath, router }) => {
|
2813
2977
|
router.routes.forEach((route) => {
|
2978
|
+
if (!(route.contractDetails.options?.mcp ?? router.routerOptions?.mcp ?? appOptions !== false)) {
|
2979
|
+
return;
|
2980
|
+
}
|
2814
2981
|
const inputSchemas = [];
|
2815
2982
|
if (route.contractDetails.versions) {
|
2816
2983
|
Object.values(route.contractDetails.versions).forEach((version2) => {
|
@@ -3012,6 +3179,11 @@ function isResponseCompiledSchema(schema) {
|
|
3012
3179
|
function parse2(req, res, next) {
|
3013
3180
|
let headers;
|
3014
3181
|
let responses;
|
3182
|
+
const collapsedOptions = req.contractDetails.options?.responseValidation ?? (req._globalOptions?.validation === false ? "none" : req._globalOptions?.validation?.response);
|
3183
|
+
if (collapsedOptions === "none") {
|
3184
|
+
next?.();
|
3185
|
+
return;
|
3186
|
+
}
|
3015
3187
|
const responseSchemas = res.responseSchemas;
|
3016
3188
|
const schemaValidator = req.schemaValidator;
|
3017
3189
|
if (!isResponseCompiledSchema(responseSchemas)) {
|
@@ -3073,6 +3245,7 @@ function parse2(req, res, next) {
|
|
3073
3245
|
if (parseErrors.length > 0) {
|
3074
3246
|
switch (req.contractDetails.options?.responseValidation) {
|
3075
3247
|
default:
|
3248
|
+
case void 0:
|
3076
3249
|
case "error":
|
3077
3250
|
res.type("text/plain");
|
3078
3251
|
res.status(500);
|
@@ -3289,13 +3462,16 @@ function transformBasePath(basePath) {
|
|
3289
3462
|
}
|
3290
3463
|
return `/${basePath}`;
|
3291
3464
|
}
|
3292
|
-
function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags, versionedPaths, versionedSecuritySchemes,
|
3465
|
+
function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags, versionedPaths, versionedSecuritySchemes, appOptions) {
|
3466
|
+
const { title, description, contact } = appOptions !== false ? appOptions ?? {} : {};
|
3293
3467
|
return {
|
3294
3468
|
[OPENAPI_DEFAULT_VERSION]: {
|
3295
3469
|
openapi: "3.1.0",
|
3296
3470
|
info: {
|
3297
|
-
title: process.env.API_TITLE || "API Definition",
|
3298
|
-
version:
|
3471
|
+
title: title || process.env.API_TITLE || "API Definition",
|
3472
|
+
version: "latest",
|
3473
|
+
description,
|
3474
|
+
contact
|
3299
3475
|
},
|
3300
3476
|
components: {
|
3301
3477
|
securitySchemes: versionedSecuritySchemes[OPENAPI_DEFAULT_VERSION]
|
@@ -3305,8 +3481,7 @@ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags,
|
|
3305
3481
|
...serverUrls.map((url, index) => ({
|
3306
3482
|
url,
|
3307
3483
|
description: serverDescriptions?.[index]
|
3308
|
-
}))
|
3309
|
-
...otherServers || []
|
3484
|
+
}))
|
3310
3485
|
],
|
3311
3486
|
paths: versionedPaths[OPENAPI_DEFAULT_VERSION]
|
3312
3487
|
},
|
@@ -3316,8 +3491,10 @@ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags,
|
|
3316
3491
|
{
|
3317
3492
|
openapi: "3.1.0",
|
3318
3493
|
info: {
|
3319
|
-
title: process.env.API_TITLE || "API Definition",
|
3320
|
-
version
|
3494
|
+
title: title || process.env.API_TITLE || "API Definition",
|
3495
|
+
version,
|
3496
|
+
description,
|
3497
|
+
contact
|
3321
3498
|
},
|
3322
3499
|
components: {
|
3323
3500
|
securitySchemes: versionedSecuritySchemes[version]
|
@@ -3327,8 +3504,7 @@ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags,
|
|
3327
3504
|
...serverUrls.map((url, index) => ({
|
3328
3505
|
url,
|
3329
3506
|
description: serverDescriptions?.[index]
|
3330
|
-
}))
|
3331
|
-
...otherServers || []
|
3507
|
+
}))
|
3332
3508
|
],
|
3333
3509
|
paths
|
3334
3510
|
}
|
@@ -3432,11 +3608,11 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
|
|
3432
3608
|
}
|
3433
3609
|
}
|
3434
3610
|
if (auth) {
|
3435
|
-
|
3611
|
+
coercedResponses[401] = {
|
3436
3612
|
description: httpStatusCodes_default[401],
|
3437
3613
|
content: contentResolver(schemaValidator, schemaValidator.string)
|
3438
3614
|
};
|
3439
|
-
|
3615
|
+
coercedResponses[403] = {
|
3440
3616
|
description: httpStatusCodes_default[403],
|
3441
3617
|
content: contentResolver(schemaValidator, schemaValidator.string)
|
3442
3618
|
};
|
@@ -3477,7 +3653,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
|
|
3477
3653
|
}
|
3478
3654
|
return operationObject;
|
3479
3655
|
}
|
3480
|
-
function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, application,
|
3656
|
+
function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, application, appOptions) {
|
3481
3657
|
const versionedPaths = {
|
3482
3658
|
[OPENAPI_DEFAULT_VERSION]: {}
|
3483
3659
|
};
|
@@ -3501,7 +3677,10 @@ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, a
|
|
3501
3677
|
const openApiPath = (0, import_common12.openApiCompliantPath)(
|
3502
3678
|
`${fullPath}${route.path === "/" ? "" : route.path}`
|
3503
3679
|
);
|
3504
|
-
const { name, summary, params, versions, auth } = route.contractDetails;
|
3680
|
+
const { name, summary, params, versions, auth, options: options2 } = route.contractDetails;
|
3681
|
+
if (!(options2?.openapi ?? router.routerOptions?.openapi ?? appOptions !== false)) {
|
3682
|
+
return;
|
3683
|
+
}
|
3505
3684
|
if (versions) {
|
3506
3685
|
for (const version of Object.keys(versions)) {
|
3507
3686
|
if (!versionedPaths[version]) {
|
@@ -3595,7 +3774,7 @@ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, a
|
|
3595
3774
|
versionedTags,
|
3596
3775
|
versionedPaths,
|
3597
3776
|
versionedSecuritySchemes,
|
3598
|
-
|
3777
|
+
appOptions
|
3599
3778
|
);
|
3600
3779
|
}
|
3601
3780
|
|
@@ -3663,11 +3842,11 @@ function mapToFetch(schemaValidator, routerMap) {
|
|
3663
3842
|
schemaValidator,
|
3664
3843
|
routerMap
|
3665
3844
|
);
|
3666
|
-
return (path, ...reqInit) => {
|
3845
|
+
return ((path, ...reqInit) => {
|
3667
3846
|
const method = reqInit[0]?.method;
|
3668
3847
|
const version = reqInit[0] != null && "version" in reqInit[0] ? reqInit[0].version : void 0;
|
3669
3848
|
return (version ? (0, import_common13.toRecord)((0, import_common13.toRecord)(flattenedFetchMap[path])[method ?? "GET"])[version] : (0, import_common13.toRecord)(flattenedFetchMap[path])[method ?? "GET"])(path, reqInit[0]);
|
3670
|
-
};
|
3849
|
+
});
|
3671
3850
|
}
|
3672
3851
|
function sdkClient(schemaValidator, routerMap) {
|
3673
3852
|
return {
|
@@ -3751,6 +3930,7 @@ function metricsDefinitions(metrics2) {
|
|
3751
3930
|
isForklaunchRequest,
|
3752
3931
|
isForklaunchRouter,
|
3753
3932
|
isInformational,
|
3933
|
+
isPortBound,
|
3754
3934
|
isRedirection,
|
3755
3935
|
isServerError,
|
3756
3936
|
isSuccessful,
|