@forklaunch/core 0.12.0 → 0.12.2
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 +3 -3
- package/lib/http/index.d.ts +3 -3
- package/lib/http/index.js +113 -75
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +87 -49
- package/lib/http/index.mjs.map +1 -1
- package/lib/services/index.d.mts +1 -6
- package/lib/services/index.d.ts +1 -6
- package/package.json +2 -2
package/lib/http/index.mjs
CHANGED
@@ -257,7 +257,8 @@ async function parseRequestAuth(req, res, next) {
|
|
257
257
|
}
|
258
258
|
|
259
259
|
// src/http/middleware/request/createContext.middleware.ts
|
260
|
-
import {
|
260
|
+
import { getEnvVar } from "@forklaunch/common";
|
261
|
+
import { context, trace } from "@opentelemetry/api";
|
261
262
|
import { v4 } from "uuid";
|
262
263
|
|
263
264
|
// src/http/telemetry/constants.ts
|
@@ -282,20 +283,24 @@ function createContext(schemaValidator) {
|
|
282
283
|
req.context = {
|
283
284
|
correlationId
|
284
285
|
};
|
285
|
-
const span = trace.
|
286
|
+
const span = trace.getSpan(context.active());
|
286
287
|
if (span != null) {
|
287
288
|
req.context.span = span;
|
288
289
|
req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);
|
290
|
+
req.context.span?.setAttribute(
|
291
|
+
ATTR_SERVICE_NAME,
|
292
|
+
getEnvVar("OTEL_SERVICE_NAME")
|
293
|
+
);
|
289
294
|
}
|
290
295
|
next?.();
|
291
296
|
};
|
292
297
|
}
|
293
298
|
|
294
299
|
// src/http/middleware/request/enrichDetails.middleware.ts
|
295
|
-
import { getEnvVar as
|
300
|
+
import { getEnvVar as getEnvVar3 } from "@forklaunch/common";
|
296
301
|
|
297
302
|
// src/http/telemetry/openTelemetryCollector.ts
|
298
|
-
import { getEnvVar } from "@forklaunch/common";
|
303
|
+
import { getEnvVar as getEnvVar2 } from "@forklaunch/common";
|
299
304
|
import { HyperExpressInstrumentation } from "@forklaunch/opentelemetry-instrumentation-hyper-express";
|
300
305
|
import {
|
301
306
|
metrics
|
@@ -321,7 +326,7 @@ function isForklaunchRequest(request) {
|
|
321
326
|
}
|
322
327
|
|
323
328
|
// src/http/telemetry/pinoLogger.ts
|
324
|
-
import { isNever as isNever2
|
329
|
+
import { isNever as isNever2 } from "@forklaunch/common";
|
325
330
|
import { trace as trace2 } from "@opentelemetry/api";
|
326
331
|
import { logs } from "@opentelemetry/api-logs";
|
327
332
|
import pino from "pino";
|
@@ -357,6 +362,21 @@ function mapSeverity(level) {
|
|
357
362
|
return 0;
|
358
363
|
}
|
359
364
|
}
|
365
|
+
function normalizeLogArgs(args) {
|
366
|
+
let message = "";
|
367
|
+
const metaObjects = [];
|
368
|
+
for (const arg of args) {
|
369
|
+
if (typeof arg === "string" && message === "") {
|
370
|
+
message = arg;
|
371
|
+
} else if (arg && typeof arg === "object" && !Array.isArray(arg)) {
|
372
|
+
metaObjects.push(arg);
|
373
|
+
} else {
|
374
|
+
message += ` ${String(arg)}`;
|
375
|
+
}
|
376
|
+
}
|
377
|
+
const metadata = Object.assign({}, ...metaObjects);
|
378
|
+
return [metadata, message.trim()];
|
379
|
+
}
|
360
380
|
var PinoLogger = class _PinoLogger {
|
361
381
|
pinoLogger;
|
362
382
|
meta;
|
@@ -387,7 +407,7 @@ var PinoLogger = class _PinoLogger {
|
|
387
407
|
return false;
|
388
408
|
}
|
389
409
|
return true;
|
390
|
-
})
|
410
|
+
});
|
391
411
|
const activeSpan = trace2.getActiveSpan();
|
392
412
|
if (activeSpan) {
|
393
413
|
const activeSpanContext = activeSpan.spanContext();
|
@@ -405,7 +425,7 @@ var PinoLogger = class _PinoLogger {
|
|
405
425
|
"correlation.id": "none",
|
406
426
|
...meta2
|
407
427
|
};
|
408
|
-
this.pinoLogger[level](...filteredArgs);
|
428
|
+
this.pinoLogger[level](...normalizeLogArgs(filteredArgs));
|
409
429
|
logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
|
410
430
|
severityText: level,
|
411
431
|
severityNumber: mapSeverity(level),
|
@@ -493,24 +513,24 @@ var OpenTelemetryCollector = class {
|
|
493
513
|
return this.metrics[metricId];
|
494
514
|
}
|
495
515
|
};
|
496
|
-
dotenv.config({ path:
|
516
|
+
dotenv.config({ path: getEnvVar2("DOTENV_FILE_PATH") });
|
497
517
|
new NodeSDK({
|
498
518
|
resource: resourceFromAttributes({
|
499
|
-
[ATTR_SERVICE_NAME2]:
|
519
|
+
[ATTR_SERVICE_NAME2]: getEnvVar2("OTEL_SERVICE_NAME")
|
500
520
|
}),
|
501
521
|
traceExporter: new OTLPTraceExporter({
|
502
|
-
url: `${
|
522
|
+
url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
|
503
523
|
}),
|
504
524
|
metricReader: new PeriodicExportingMetricReader({
|
505
525
|
exporter: new OTLPMetricExporter({
|
506
|
-
url: `${
|
526
|
+
url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
|
507
527
|
}),
|
508
528
|
exportIntervalMillis: 5e3
|
509
529
|
}),
|
510
530
|
logRecordProcessors: [
|
511
531
|
new BatchLogRecordProcessor(
|
512
532
|
new OTLPLogExporter({
|
513
|
-
url: `${
|
533
|
+
url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
|
514
534
|
})
|
515
535
|
)
|
516
536
|
],
|
@@ -518,11 +538,12 @@ new NodeSDK({
|
|
518
538
|
new HttpInstrumentation({
|
519
539
|
applyCustomAttributesOnSpan: (span, request) => {
|
520
540
|
span.setAttribute(
|
521
|
-
|
522
|
-
|
541
|
+
ATTR_SERVICE_NAME2,
|
542
|
+
getEnvVar2("OTEL_SERVICE_NAME") ?? "unknown"
|
523
543
|
);
|
524
544
|
if (isForklaunchRequest(request)) {
|
525
|
-
span.setAttribute(
|
545
|
+
span.setAttribute(ATTR_API_NAME, request.contractDetails?.name);
|
546
|
+
span.setAttribute(ATTR_CORRELATION_ID, request.context.correlationId);
|
526
547
|
}
|
527
548
|
}
|
528
549
|
}),
|
@@ -530,10 +551,10 @@ new NodeSDK({
|
|
530
551
|
new HyperExpressInstrumentation()
|
531
552
|
]
|
532
553
|
}).start();
|
533
|
-
var httpRequestsTotalCounter = metrics.getMeter(
|
554
|
+
var httpRequestsTotalCounter = metrics.getMeter(getEnvVar2("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
|
534
555
|
description: "Number of HTTP requests"
|
535
556
|
});
|
536
|
-
var httpServerDurationHistogram = metrics.getMeter(
|
557
|
+
var httpServerDurationHistogram = metrics.getMeter(getEnvVar2("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
|
537
558
|
description: "Duration of HTTP server requests",
|
538
559
|
unit: "s"
|
539
560
|
});
|
@@ -552,7 +573,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
|
|
552
573
|
const [seconds, nanoseconds] = process.hrtime(startTime);
|
553
574
|
const durationMs = seconds + nanoseconds / 1e9;
|
554
575
|
httpServerDurationHistogram.record(durationMs, {
|
555
|
-
[ATTR_SERVICE_NAME]:
|
576
|
+
[ATTR_SERVICE_NAME]: getEnvVar3("OTEL_SERVICE_NAME") || "unknown",
|
556
577
|
[ATTR_API_NAME]: req.contractDetails?.name || "unknown",
|
557
578
|
[ATTR_HTTP_REQUEST_METHOD]: req.method,
|
558
579
|
[ATTR_HTTP_ROUTE]: req.originalPath || "unknown",
|
@@ -2653,9 +2674,9 @@ var getCodeForStatus = (status) => {
|
|
2653
2674
|
var httpStatusCodes_default = HTTPStatuses;
|
2654
2675
|
|
2655
2676
|
// src/http/mcpGenerator/mcpGenerator.ts
|
2656
|
-
import { isNever as isNever3, isRecord as isRecord3, safeStringify
|
2677
|
+
import { isNever as isNever3, isRecord as isRecord3, safeStringify } from "@forklaunch/common";
|
2678
|
+
import { FastMCP } from "@forklaunch/fastmcp-fork";
|
2657
2679
|
import { string, ZodSchemaValidator } from "@forklaunch/validator/zod";
|
2658
|
-
import { FastMCP } from "fastmcp";
|
2659
2680
|
|
2660
2681
|
// src/http/guards/isVersionedInputSchema.ts
|
2661
2682
|
function isUnionable(schema) {
|
@@ -2665,8 +2686,9 @@ function isUnionable(schema) {
|
|
2665
2686
|
// src/http/router/unpackRouters.ts
|
2666
2687
|
function unpackRouters(routers, recursiveBasePath = []) {
|
2667
2688
|
return routers.reduce((acc, router) => {
|
2689
|
+
const fullPath = [...recursiveBasePath, router.basePath].join("");
|
2668
2690
|
acc.push({
|
2669
|
-
fullPath
|
2691
|
+
fullPath,
|
2670
2692
|
router
|
2671
2693
|
});
|
2672
2694
|
acc.push(
|
@@ -2704,7 +2726,7 @@ function generateInputSchema(schemaValidator, body, params, query, requestHeader
|
|
2704
2726
|
} : {}
|
2705
2727
|
});
|
2706
2728
|
}
|
2707
|
-
function generateMcpServer(schemaValidator, protocol, host, port, version,
|
2729
|
+
function generateMcpServer(schemaValidator, protocol, host, port, version, application, options2, contentTypeMap) {
|
2708
2730
|
if (!(schemaValidator instanceof ZodSchemaValidator)) {
|
2709
2731
|
throw new Error(
|
2710
2732
|
"Schema validator must be an instance of ZodSchemaValidator"
|
@@ -2715,7 +2737,15 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2715
2737
|
name: options2?.name ?? "mcp-server",
|
2716
2738
|
version
|
2717
2739
|
});
|
2718
|
-
|
2740
|
+
[
|
2741
|
+
{
|
2742
|
+
fullPath: application.basePath === "/" ? "" : application.basePath,
|
2743
|
+
router: application
|
2744
|
+
},
|
2745
|
+
...unpackRouters(application.routers, [
|
2746
|
+
application.basePath === "/" ? "" : application.basePath
|
2747
|
+
])
|
2748
|
+
].forEach(({ fullPath, router }) => {
|
2719
2749
|
router.routes.forEach((route) => {
|
2720
2750
|
const inputSchemas = [];
|
2721
2751
|
if (route.contractDetails.versions) {
|
@@ -2778,7 +2808,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2778
2808
|
if (discriminatedBody) {
|
2779
2809
|
switch (discriminatedBody.parserType) {
|
2780
2810
|
case "json": {
|
2781
|
-
parsedBody =
|
2811
|
+
parsedBody = safeStringify(body);
|
2782
2812
|
break;
|
2783
2813
|
}
|
2784
2814
|
case "text": {
|
@@ -2810,7 +2840,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2810
2840
|
parsedBody = new URLSearchParams(
|
2811
2841
|
Object.entries(body).map(([key, value]) => [
|
2812
2842
|
key,
|
2813
|
-
|
2843
|
+
safeStringify(value)
|
2814
2844
|
])
|
2815
2845
|
);
|
2816
2846
|
} else {
|
@@ -2820,7 +2850,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2820
2850
|
}
|
2821
2851
|
default: {
|
2822
2852
|
isNever3(discriminatedBody.parserType);
|
2823
|
-
parsedBody =
|
2853
|
+
parsedBody = safeStringify(body);
|
2824
2854
|
break;
|
2825
2855
|
}
|
2826
2856
|
}
|
@@ -2829,7 +2859,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2829
2859
|
const queryString = new URLSearchParams(
|
2830
2860
|
Object.entries(query).map(([key, value]) => [
|
2831
2861
|
key,
|
2832
|
-
|
2862
|
+
safeStringify(value)
|
2833
2863
|
])
|
2834
2864
|
).toString();
|
2835
2865
|
url += queryString ? `?${queryString}` : "";
|
@@ -2862,7 +2892,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
|
|
2862
2892
|
content: [
|
2863
2893
|
{
|
2864
2894
|
type: "text",
|
2865
|
-
text:
|
2895
|
+
text: safeStringify(await response.json())
|
2866
2896
|
}
|
2867
2897
|
]
|
2868
2898
|
};
|
@@ -3017,12 +3047,12 @@ import {
|
|
3017
3047
|
isNodeJsWriteableStream,
|
3018
3048
|
isRecord as isRecord4,
|
3019
3049
|
readableStreamToAsyncIterable,
|
3020
|
-
safeStringify as
|
3050
|
+
safeStringify as safeStringify2
|
3021
3051
|
} from "@forklaunch/common";
|
3022
3052
|
import { Readable, Transform } from "stream";
|
3023
3053
|
|
3024
3054
|
// src/http/telemetry/recordMetric.ts
|
3025
|
-
import { getEnvVar as
|
3055
|
+
import { getEnvVar as getEnvVar4 } from "@forklaunch/common";
|
3026
3056
|
import {
|
3027
3057
|
ATTR_HTTP_REQUEST_METHOD as ATTR_HTTP_REQUEST_METHOD3,
|
3028
3058
|
ATTR_HTTP_RESPONSE_STATUS_CODE as ATTR_HTTP_RESPONSE_STATUS_CODE3,
|
@@ -3034,7 +3064,7 @@ function recordMetric(req, res) {
|
|
3034
3064
|
return;
|
3035
3065
|
}
|
3036
3066
|
httpRequestsTotalCounter.add(1, {
|
3037
|
-
[ATTR_SERVICE_NAME3]:
|
3067
|
+
[ATTR_SERVICE_NAME3]: getEnvVar4("OTEL_SERVICE_NAME"),
|
3038
3068
|
[ATTR_API_NAME]: req.contractDetails?.name,
|
3039
3069
|
[ATTR_CORRELATION_ID]: req.context.correlationId,
|
3040
3070
|
[ATTR_HTTP_REQUEST_METHOD3]: req.method,
|
@@ -3122,7 +3152,7 @@ ${res.locals.errorMessage}`;
|
|
3122
3152
|
if (!errorSent) {
|
3123
3153
|
let data2 = "";
|
3124
3154
|
for (const [key, value] of Object.entries(chunk)) {
|
3125
|
-
data2 += `${key}: ${typeof value === "string" ? value :
|
3155
|
+
data2 += `${key}: ${typeof value === "string" ? value : safeStringify2(value)}
|
3126
3156
|
`;
|
3127
3157
|
}
|
3128
3158
|
data2 += "\n";
|
@@ -3209,7 +3239,7 @@ function transformBasePath(basePath) {
|
|
3209
3239
|
}
|
3210
3240
|
return `/${basePath}`;
|
3211
3241
|
}
|
3212
|
-
function generateOpenApiDocument(
|
3242
|
+
function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags, versionedPaths, versionedSecuritySchemes, otherServers) {
|
3213
3243
|
return {
|
3214
3244
|
[OPENAPI_DEFAULT_VERSION]: {
|
3215
3245
|
openapi: "3.1.0",
|
@@ -3222,10 +3252,10 @@ function generateOpenApiDocument(protocol, host, port, versionedTags, versionedP
|
|
3222
3252
|
},
|
3223
3253
|
tags: versionedTags[OPENAPI_DEFAULT_VERSION],
|
3224
3254
|
servers: [
|
3225
|
-
{
|
3226
|
-
url
|
3227
|
-
description:
|
3228
|
-
},
|
3255
|
+
...serverUrls.map((url, index) => ({
|
3256
|
+
url,
|
3257
|
+
description: serverDescriptions?.[index]
|
3258
|
+
})),
|
3229
3259
|
...otherServers || []
|
3230
3260
|
],
|
3231
3261
|
paths: versionedPaths[OPENAPI_DEFAULT_VERSION]
|
@@ -3244,10 +3274,11 @@ function generateOpenApiDocument(protocol, host, port, versionedTags, versionedP
|
|
3244
3274
|
},
|
3245
3275
|
tags: versionedTags[version],
|
3246
3276
|
servers: [
|
3247
|
-
{
|
3248
|
-
url
|
3249
|
-
description:
|
3250
|
-
}
|
3277
|
+
...serverUrls.map((url, index) => ({
|
3278
|
+
url,
|
3279
|
+
description: serverDescriptions?.[index]
|
3280
|
+
})),
|
3281
|
+
...otherServers || []
|
3251
3282
|
],
|
3252
3283
|
paths
|
3253
3284
|
}
|
@@ -3396,7 +3427,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
|
|
3396
3427
|
}
|
3397
3428
|
return operationObject;
|
3398
3429
|
}
|
3399
|
-
function generateOpenApiSpecs(schemaValidator,
|
3430
|
+
function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, application, otherServers) {
|
3400
3431
|
const versionedPaths = {
|
3401
3432
|
[OPENAPI_DEFAULT_VERSION]: {}
|
3402
3433
|
};
|
@@ -3406,7 +3437,15 @@ function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, ot
|
|
3406
3437
|
const versionedSecuritySchemes = {
|
3407
3438
|
[OPENAPI_DEFAULT_VERSION]: {}
|
3408
3439
|
};
|
3409
|
-
|
3440
|
+
[
|
3441
|
+
{
|
3442
|
+
fullPath: application.basePath === "/" ? "" : application.basePath,
|
3443
|
+
router: application
|
3444
|
+
},
|
3445
|
+
...unpackRouters(application.routers, [
|
3446
|
+
application.basePath === "/" ? "" : application.basePath
|
3447
|
+
])
|
3448
|
+
].forEach(({ fullPath, router }) => {
|
3410
3449
|
const controllerName = transformBasePath(fullPath);
|
3411
3450
|
router.routes.forEach((route) => {
|
3412
3451
|
const openApiPath = openApiCompliantPath(
|
@@ -3501,9 +3540,8 @@ function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, ot
|
|
3501
3540
|
});
|
3502
3541
|
});
|
3503
3542
|
return generateOpenApiDocument(
|
3504
|
-
|
3505
|
-
|
3506
|
-
port,
|
3543
|
+
serverUrls,
|
3544
|
+
serverDescriptions,
|
3507
3545
|
versionedTags,
|
3508
3546
|
versionedPaths,
|
3509
3547
|
versionedSecuritySchemes,
|
@@ -3512,7 +3550,7 @@ function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, ot
|
|
3512
3550
|
}
|
3513
3551
|
|
3514
3552
|
// src/http/sdk/sdkClient.ts
|
3515
|
-
import { hashString, safeStringify as
|
3553
|
+
import { hashString, safeStringify as safeStringify3, toRecord as toRecord2 } from "@forklaunch/common";
|
3516
3554
|
|
3517
3555
|
// src/http/guards/isSdkRouter.ts
|
3518
3556
|
function isSdkRouter(value) {
|
@@ -3524,12 +3562,12 @@ function mapToSdk(schemaValidator, routerMap, runningPath = void 0) {
|
|
3524
3562
|
const routerUniquenessCache = /* @__PURE__ */ new Set();
|
3525
3563
|
return Object.fromEntries(
|
3526
3564
|
Object.entries(routerMap).map(([key, value]) => {
|
3527
|
-
if (routerUniquenessCache.has(hashString(
|
3565
|
+
if (routerUniquenessCache.has(hashString(safeStringify3(value)))) {
|
3528
3566
|
throw new Error(
|
3529
3567
|
`SdkClient: Cannot use the same router pointer twice. Please clone the duplicate router with .clone() or only use the router once.`
|
3530
3568
|
);
|
3531
3569
|
}
|
3532
|
-
routerUniquenessCache.add(hashString(
|
3570
|
+
routerUniquenessCache.add(hashString(safeStringify3(value)));
|
3533
3571
|
const currentPath = runningPath ? [runningPath, key].join(".") : key;
|
3534
3572
|
if (isSdkRouter(value)) {
|
3535
3573
|
Object.entries(value.sdkPaths).forEach(([routePath, sdkKey]) => {
|