@forklaunch/core 0.16.1 → 0.17.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.
@@ -22,18 +22,12 @@ function cors(corsOptions) {
22
22
  // src/http/router/expressLikeRouter.ts
23
23
  import {
24
24
  hashString,
25
- isRecord as isRecord2,
26
- safeStringify as safeStringify2,
25
+ safeStringify as safeStringify3,
27
26
  sanitizePathSlashes,
28
27
  toPrettyCamelCase,
29
28
  toRecord
30
29
  } from "@forklaunch/common";
31
30
 
32
- // src/http/guards/hasVersionedSchema.ts
33
- function hasVersionedSchema(contractDetails) {
34
- return typeof contractDetails === "object" && contractDetails !== null && "versions" in contractDetails && contractDetails.versions !== null;
35
- }
36
-
37
31
  // src/http/guards/isForklaunchRouter.ts
38
32
  function isForklaunchRouter(maybeForklaunchRouter) {
39
33
  return maybeForklaunchRouter != null && typeof maybeForklaunchRouter === "object" && "basePath" in maybeForklaunchRouter && "routes" in maybeForklaunchRouter && Array.isArray(maybeForklaunchRouter.routes);
@@ -62,6 +56,197 @@ function isForklaunchExpressLikeRouter(maybeForklaunchExpressLikeRouter) {
62
56
  ) && "basePath" in maybeForklaunchExpressLikeRouter && "internal" in maybeForklaunchExpressLikeRouter;
63
57
  }
64
58
 
59
+ // src/http/guards/isSdkHandler.ts
60
+ function isSdkHandler(handler) {
61
+ return typeof handler === "object" && handler !== null && "_path" in handler && "_method" in handler && "contractDetails" in handler;
62
+ }
63
+
64
+ // src/http/guards/isTypedHandler.ts
65
+ function isTypedHandler(maybeTypedHandler) {
66
+ return maybeTypedHandler != null && typeof maybeTypedHandler === "object" && "_typedHandler" in maybeTypedHandler && maybeTypedHandler._typedHandler === true;
67
+ }
68
+
69
+ // src/http/middleware/request/createContext.middleware.ts
70
+ import { getEnvVar } from "@forklaunch/common";
71
+ import { context, trace } from "@opentelemetry/api";
72
+ import { v4 } from "uuid";
73
+
74
+ // src/http/telemetry/constants.ts
75
+ import {
76
+ ATTR_HTTP_REQUEST_METHOD,
77
+ ATTR_HTTP_RESPONSE_STATUS_CODE,
78
+ ATTR_HTTP_ROUTE,
79
+ ATTR_SERVICE_NAME
80
+ } from "@opentelemetry/semantic-conventions";
81
+ var ATTR_API_NAME = "api.name";
82
+ var ATTR_CORRELATION_ID = "correlation.id";
83
+ var ATTR_APPLICATION_ID = "application_id";
84
+
85
+ // src/http/middleware/request/createContext.middleware.ts
86
+ function createContext(schemaValidator) {
87
+ return function setContext(req, res, next) {
88
+ req.schemaValidator = schemaValidator;
89
+ let correlationId = v4();
90
+ if (req.headers["x-correlation-id"]) {
91
+ correlationId = req.headers["x-correlation-id"];
92
+ }
93
+ res.setHeader("x-correlation-id", correlationId);
94
+ req.context = {
95
+ correlationId
96
+ };
97
+ const span = trace.getSpan(context.active());
98
+ if (span != null) {
99
+ req.context.span = span;
100
+ req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);
101
+ req.context.span?.setAttribute(
102
+ ATTR_SERVICE_NAME,
103
+ getEnvVar("OTEL_SERVICE_NAME")
104
+ );
105
+ }
106
+ next?.();
107
+ };
108
+ }
109
+
110
+ // src/http/router/discriminateBody.ts
111
+ function discriminateBody(schemaValidator, body) {
112
+ if (body == null) {
113
+ return void 0;
114
+ }
115
+ const maybeTypedBody = body;
116
+ if ("text" in maybeTypedBody && maybeTypedBody.text != null) {
117
+ return {
118
+ contentType: maybeTypedBody.contentType ?? "text/plain",
119
+ parserType: "text",
120
+ schema: maybeTypedBody.text
121
+ };
122
+ } else if ("json" in maybeTypedBody && maybeTypedBody.json != null) {
123
+ return {
124
+ contentType: maybeTypedBody.contentType ?? "application/json",
125
+ parserType: "json",
126
+ schema: maybeTypedBody.json
127
+ };
128
+ } else if ("file" in maybeTypedBody && maybeTypedBody.file != null) {
129
+ return {
130
+ contentType: maybeTypedBody.contentType ?? "application/octet-stream",
131
+ parserType: "file",
132
+ schema: maybeTypedBody.file
133
+ };
134
+ } else if ("multipartForm" in maybeTypedBody && maybeTypedBody.multipartForm != null) {
135
+ return {
136
+ contentType: maybeTypedBody.contentType ?? "multipart/form-data",
137
+ parserType: "multipart",
138
+ schema: maybeTypedBody.multipartForm
139
+ };
140
+ } else if ("urlEncodedForm" in maybeTypedBody && maybeTypedBody.urlEncodedForm != null) {
141
+ return {
142
+ contentType: maybeTypedBody.contentType ?? "application/x-www-form-urlencoded",
143
+ parserType: "urlEncoded",
144
+ schema: maybeTypedBody.urlEncodedForm
145
+ };
146
+ } else if ("schema" in maybeTypedBody && maybeTypedBody.schema != null) {
147
+ return {
148
+ contentType: maybeTypedBody.contentType ?? "application/json",
149
+ parserType: "text",
150
+ schema: maybeTypedBody.schema
151
+ };
152
+ } else if (schemaValidator.isInstanceOf(
153
+ maybeTypedBody,
154
+ schemaValidator.string
155
+ )) {
156
+ return {
157
+ contentType: "text/plain",
158
+ parserType: "text",
159
+ schema: maybeTypedBody
160
+ };
161
+ } else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
162
+ return {
163
+ contentType: "application/octet-stream",
164
+ parserType: "file",
165
+ schema: maybeTypedBody
166
+ };
167
+ } else {
168
+ return {
169
+ contentType: "application/json",
170
+ parserType: "json",
171
+ schema: maybeTypedBody
172
+ };
173
+ }
174
+ }
175
+ function discriminateResponseBodies(schemaValidator, responses) {
176
+ const discriminatedResponses = {};
177
+ for (const [statusCode, response] of Object.entries(responses)) {
178
+ if (response != null && typeof response === "object") {
179
+ if ("json" in response && response.json != null) {
180
+ discriminatedResponses[Number(statusCode)] = {
181
+ contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/json") ?? "application/json",
182
+ parserType: "json",
183
+ schema: response.json
184
+ };
185
+ } else if ("schema" in response && response.schema != null) {
186
+ discriminatedResponses[Number(statusCode)] = {
187
+ contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/json") ?? "application/json",
188
+ parserType: "text",
189
+ schema: response.schema
190
+ };
191
+ } else if ("text" in response && response.text != null) {
192
+ discriminatedResponses[Number(statusCode)] = {
193
+ contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "text/plain") ?? "text/plain",
194
+ parserType: "text",
195
+ schema: response.text
196
+ };
197
+ } else if ("file" in response && response.file != null) {
198
+ discriminatedResponses[Number(statusCode)] = {
199
+ contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/octet-stream") ?? "application/octet-stream",
200
+ parserType: "file",
201
+ schema: response.file
202
+ };
203
+ } else if ("event" in response && response.event != null) {
204
+ discriminatedResponses[Number(statusCode)] = {
205
+ contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "text/event-stream") ?? "text/event-stream",
206
+ parserType: "serverSentEvent",
207
+ schema: response.event
208
+ };
209
+ } else if (schemaValidator.isInstanceOf(
210
+ response,
211
+ schemaValidator.string
212
+ )) {
213
+ discriminatedResponses[Number(statusCode)] = {
214
+ contentType: "text/plain",
215
+ parserType: "text",
216
+ schema: response
217
+ };
218
+ } else if (schemaValidator.openapi(response).format === "binary") {
219
+ discriminatedResponses[Number(statusCode)] = {
220
+ contentType: "application/octet-stream",
221
+ parserType: "file",
222
+ schema: response
223
+ };
224
+ } else {
225
+ discriminatedResponses[Number(statusCode)] = {
226
+ contentType: "application/json",
227
+ parserType: "json",
228
+ schema: response
229
+ };
230
+ }
231
+ } else {
232
+ discriminatedResponses[Number(statusCode)] = {
233
+ contentType: "application/json",
234
+ parserType: "json",
235
+ schema: response
236
+ };
237
+ }
238
+ }
239
+ return discriminatedResponses;
240
+ }
241
+
242
+ // src/http/router/routerSharedLogic.ts
243
+ import { isRecord as isRecord2 } from "@forklaunch/common";
244
+
245
+ // src/http/guards/hasVersionedSchema.ts
246
+ function hasVersionedSchema(contractDetails) {
247
+ return typeof contractDetails === "object" && contractDetails !== null && "versions" in contractDetails && contractDetails.versions !== null;
248
+ }
249
+
65
250
  // src/http/guards/isPathParamContractDetails.ts
66
251
  function isPathParamHttpContractDetails(maybePathParamHttpContractDetails) {
67
252
  return maybePathParamHttpContractDetails != null && typeof maybePathParamHttpContractDetails === "object" && "name" in maybePathParamHttpContractDetails && "summary" in maybePathParamHttpContractDetails && maybePathParamHttpContractDetails.name != null && maybePathParamHttpContractDetails.summary != null && ("responses" in maybePathParamHttpContractDetails && maybePathParamHttpContractDetails.responses != null || "versions" in maybePathParamHttpContractDetails && typeof maybePathParamHttpContractDetails.versions === "object" && maybePathParamHttpContractDetails.versions != null && Object.values(maybePathParamHttpContractDetails.versions).every(
@@ -76,23 +261,14 @@ function isHttpContractDetails(maybeContractDetails) {
76
261
  ));
77
262
  }
78
263
 
79
- // src/http/guards/isSdkHandler.ts
80
- function isSdkHandler(handler) {
81
- return typeof handler === "object" && handler !== null && "_path" in handler && "_method" in handler && "contractDetails" in handler;
82
- }
83
-
84
- // src/http/guards/isTypedHandler.ts
85
- function isTypedHandler(maybeTypedHandler) {
86
- return maybeTypedHandler != null && typeof maybeTypedHandler === "object" && "_typedHandler" in maybeTypedHandler && maybeTypedHandler._typedHandler === true;
87
- }
88
-
89
264
  // src/http/middleware/request/auth.middleware.ts
90
- import { isNever } from "@forklaunch/common";
265
+ import { isNever as isNever2 } from "@forklaunch/common";
91
266
  import {
92
267
  prettyPrintParseErrors
93
268
  } from "@forklaunch/validator";
94
269
 
95
270
  // src/http/discriminateAuthMethod.ts
271
+ import { safeStringify as safeStringify2 } from "@forklaunch/common";
96
272
  import { jwtVerify } from "jose";
97
273
 
98
274
  // src/http/createHmacToken.ts
@@ -154,7 +330,7 @@ async function getCachedJwks(jwksPublicKeyUrl) {
154
330
  return jwks;
155
331
  }
156
332
  }
157
- async function discriminateAuthMethod(auth) {
333
+ async function discriminateAuthMethod(auth, openTelemetryCollector) {
158
334
  let authMethod;
159
335
  if (isBasicAuthMethod(auth)) {
160
336
  authMethod = {
@@ -217,37 +393,219 @@ async function discriminateAuthMethod(auth) {
217
393
  signature,
218
394
  secretKey
219
395
  }) => {
220
- return createHmacToken({
396
+ const computedSignature = createHmacToken({
221
397
  method,
222
398
  path,
223
399
  body,
224
400
  timestamp,
225
401
  nonce,
226
402
  secretKey
227
- }) === signature;
403
+ });
404
+ const isValid = computedSignature === signature;
405
+ if (!isValid) {
406
+ const errorInfo = {
407
+ method,
408
+ path,
409
+ timestamp: timestamp.toISOString(),
410
+ nonce,
411
+ receivedSignature: signature,
412
+ computedSignature
413
+ };
414
+ openTelemetryCollector?.debug("[HMAC Verification Failed]", {
415
+ ...errorInfo,
416
+ bodyType: body ? typeof body : "undefined",
417
+ body: body ? safeStringify2(body) : "undefined"
418
+ });
419
+ }
420
+ return isValid;
421
+ }
422
+ }
423
+ };
424
+ }
425
+ if (authMethod == null) {
426
+ throw new Error("Invalid auth method");
427
+ }
428
+ return authMethod;
429
+ }
430
+
431
+ // src/http/guards/hasPermissionChecks.ts
432
+ function hasPermissionChecks(maybePermissionedAuth) {
433
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
434
+ }
435
+
436
+ // src/http/guards/hasRoleChecks.ts
437
+ function hasRoleChecks(maybeRoledAuth) {
438
+ return typeof maybeRoledAuth === "object" && maybeRoledAuth !== null && ("allowedRoles" in maybeRoledAuth || "forbiddenRoles" in maybeRoledAuth);
439
+ }
440
+
441
+ // src/http/guards/hasScopeChecks.ts
442
+ function hasScopeChecks(maybePermissionedAuth) {
443
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
444
+ }
445
+
446
+ // src/http/telemetry/pinoLogger.ts
447
+ import { isNever } from "@forklaunch/common";
448
+ import { trace as trace2 } from "@opentelemetry/api";
449
+ import { logs } from "@opentelemetry/api-logs";
450
+ import pino from "pino";
451
+
452
+ // src/http/guards/isLoggerMeta.ts
453
+ function isLoggerMeta(arg) {
454
+ return typeof arg === "object" && arg !== null && "_meta" in arg;
455
+ }
456
+
457
+ // src/http/telemetry/pinoLogger.ts
458
+ function meta(meta2) {
459
+ return meta2;
460
+ }
461
+ function mapSeverity(level) {
462
+ switch (level) {
463
+ case "silent":
464
+ return 0;
465
+ case "trace":
466
+ return 1;
467
+ case "debug":
468
+ return 5;
469
+ case "info":
470
+ return 9;
471
+ case "warn":
472
+ return 13;
473
+ case "error":
474
+ return 17;
475
+ case "fatal":
476
+ return 21;
477
+ default:
478
+ isNever(level);
479
+ return 0;
480
+ }
481
+ }
482
+ function normalizeLogArgs(args) {
483
+ let message = "";
484
+ const metaObjects = [];
485
+ for (const arg of args) {
486
+ if (typeof arg === "string" && message === "") {
487
+ message = arg;
488
+ } else if (arg instanceof Error) {
489
+ metaObjects.push({
490
+ err: {
491
+ message: arg.message,
492
+ name: arg.name,
493
+ stack: arg.stack,
494
+ ...Object.keys(arg).length > 0 ? arg : {}
228
495
  }
496
+ });
497
+ } else if (arg && typeof arg === "object" && !Array.isArray(arg)) {
498
+ metaObjects.push(arg);
499
+ } else {
500
+ message += ` ${String(arg)}`;
501
+ }
502
+ }
503
+ const metadata = Object.assign({}, ...metaObjects);
504
+ return [metadata, message.trim()];
505
+ }
506
+ function safePrettyFormat(level, args, timestamp) {
507
+ try {
508
+ const [metadata, message] = normalizeLogArgs(args);
509
+ const formattedTimestamp = timestamp || (/* @__PURE__ */ new Date()).toISOString();
510
+ return `[${formattedTimestamp}] ${level.toUpperCase()}: ${message}${Object.keys(metadata).length > 0 ? `
511
+ ${JSON.stringify(metadata, null, 2)}` : ""}`;
512
+ } catch (error) {
513
+ const fallbackMessage = args.map((arg) => {
514
+ try {
515
+ if (typeof arg === "string") return arg;
516
+ if (arg === null) return "null";
517
+ if (arg === void 0) return "undefined";
518
+ return JSON.stringify(arg);
519
+ } catch {
520
+ return "[Circular/Non-serializable Object]";
521
+ }
522
+ }).join(" ");
523
+ return `[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}: ${fallbackMessage} [Pretty Print Error: ${error instanceof Error ? error.message : "Unknown error"}]`;
524
+ }
525
+ }
526
+ var PinoLogger = class _PinoLogger {
527
+ pinoLogger;
528
+ meta;
529
+ constructor(level, meta2 = {}) {
530
+ this.pinoLogger = pino({
531
+ level: level || "info",
532
+ formatters: {
533
+ level(label) {
534
+ return { level: label };
535
+ }
536
+ },
537
+ timestamp: pino.stdTimeFunctions.isoTime,
538
+ transport: {
539
+ target: "pino-pretty",
540
+ options: {
541
+ colorize: true,
542
+ errorLikeObjectKeys: ["err", "error"],
543
+ ignore: "pid,hostname",
544
+ translateTime: "SYS:standard"
545
+ }
546
+ }
547
+ });
548
+ this.meta = meta2;
549
+ }
550
+ log(level, ...args) {
551
+ let meta2 = {};
552
+ const filteredArgs = args.filter((arg) => {
553
+ if (isLoggerMeta(arg)) {
554
+ Object.assign(meta2, arg);
555
+ return false;
229
556
  }
557
+ return true;
558
+ });
559
+ const [errorMetadata] = normalizeLogArgs(filteredArgs);
560
+ Object.assign(meta2, errorMetadata);
561
+ const activeSpan = trace2.getActiveSpan();
562
+ if (activeSpan) {
563
+ const activeSpanContext = activeSpan.spanContext();
564
+ meta2.trace_id = activeSpanContext.traceId;
565
+ meta2.span_id = activeSpanContext.spanId;
566
+ meta2.trace_flags = activeSpanContext.traceFlags;
567
+ meta2 = {
568
+ // @ts-expect-error accessing private property
569
+ ...activeSpan.attributes,
570
+ ...meta2
571
+ };
572
+ }
573
+ meta2 = {
574
+ "api.name": "none",
575
+ "correlation.id": "none",
576
+ ...meta2
230
577
  };
578
+ this.pinoLogger[level](...normalizeLogArgs(filteredArgs));
579
+ const formattedBody = safePrettyFormat(level, filteredArgs);
580
+ try {
581
+ logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
582
+ severityText: level,
583
+ severityNumber: mapSeverity(level),
584
+ body: formattedBody,
585
+ attributes: { ...this.meta, ...meta2 }
586
+ });
587
+ } catch (error) {
588
+ console.error("Failed to emit OpenTelemetry log:", error);
589
+ console.log(
590
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}:`,
591
+ ...filteredArgs
592
+ );
593
+ }
231
594
  }
232
- if (authMethod == null) {
233
- throw new Error("Invalid auth method");
595
+ error = (msg, ...args) => this.log("error", msg, ...args);
596
+ info = (msg, ...args) => this.log("info", msg, ...args);
597
+ debug = (msg, ...args) => this.log("debug", msg, ...args);
598
+ warn = (msg, ...args) => this.log("warn", msg, ...args);
599
+ trace = (msg, ...args) => this.log("trace", msg, ...args);
600
+ child(meta2 = {}) {
601
+ return new _PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta2 });
234
602
  }
235
- return authMethod;
236
- }
237
-
238
- // src/http/guards/hasPermissionChecks.ts
239
- function hasPermissionChecks(maybePermissionedAuth) {
240
- return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
241
- }
242
-
243
- // src/http/guards/hasRoleChecks.ts
244
- function hasRoleChecks(maybeRoledAuth) {
245
- return typeof maybeRoledAuth === "object" && maybeRoledAuth !== null && ("allowedRoles" in maybeRoledAuth || "forbiddenRoles" in maybeRoledAuth);
246
- }
247
-
248
- // src/http/guards/hasScopeChecks.ts
249
- function hasScopeChecks(maybePermissionedAuth) {
250
- return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
603
+ getBaseLogger() {
604
+ return this.pinoLogger;
605
+ }
606
+ };
607
+ function logger(level, meta2 = {}) {
608
+ return new PinoLogger(level, meta2);
251
609
  }
252
610
 
253
611
  // src/http/middleware/request/auth.middleware.ts
@@ -311,7 +669,8 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
311
669
  }
312
670
  let sessionPayload;
313
671
  const { type, auth } = await discriminateAuthMethod(
314
- collapsedAuthorizationMethod
672
+ collapsedAuthorizationMethod,
673
+ req.openTelemetryCollector
315
674
  );
316
675
  switch (type) {
317
676
  case "hmac": {
@@ -332,7 +691,7 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
332
691
  const verificationResult = await auth.verificationFunction({
333
692
  method: req?.method ?? "",
334
693
  path: req?.path ?? "",
335
- body: req?.body,
694
+ body: req?._rawBody ?? req?.body,
336
695
  timestamp: new Date(parsedTimestamp),
337
696
  nonce: parsedNonce,
338
697
  signature: parsedSignature,
@@ -356,7 +715,15 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
356
715
  }
357
716
  sessionPayload = decodedJwt;
358
717
  } catch (error) {
359
- req?.openTelemetryCollector.error(error);
718
+ req?.openTelemetryCollector?.error(
719
+ "JWT Verification Failed",
720
+ meta({
721
+ error,
722
+ method: req.method,
723
+ path: req.path,
724
+ token
725
+ })
726
+ );
360
727
  return invalidAuthorizationToken;
361
728
  }
362
729
  break;
@@ -383,7 +750,7 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
383
750
  break;
384
751
  }
385
752
  default:
386
- isNever(type);
753
+ isNever2(type);
387
754
  return [401, "Invalid Authorization method."];
388
755
  }
389
756
  if (isHmacMethod(collapsedAuthorizationMethod) && sessionPayload == null) {
@@ -468,14 +835,18 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
468
835
  }
469
836
  async function parseRequestAuth(req, res, next) {
470
837
  const auth = req.contractDetails.auth;
471
- const [error, message] = await checkAuthorizationToken(
472
- req,
473
- auth,
474
- req.headers[auth?.headerName ?? "Authorization"] || req.headers[auth?.headerName ?? "authorization"],
475
- req._globalOptions?.()?.auth
476
- ) ?? [];
838
+ const token = req.headers[auth?.headerName ?? "Authorization"] || req.headers[auth?.headerName ?? "authorization"];
839
+ const [error, message] = await checkAuthorizationToken(req, auth, token, req._globalOptions?.()?.auth) ?? [];
477
840
  if (error != null) {
478
- req.openTelemetryCollector.error(error, message);
841
+ req.openTelemetryCollector?.error(
842
+ message || "Authorization Failed",
843
+ meta({
844
+ statusCode: error,
845
+ method: req.method,
846
+ path: req.path,
847
+ token
848
+ })
849
+ );
479
850
  res.type("text/plain");
480
851
  res.status(error).send(message);
481
852
  return;
@@ -483,46 +854,6 @@ async function parseRequestAuth(req, res, next) {
483
854
  next?.();
484
855
  }
485
856
 
486
- // src/http/middleware/request/createContext.middleware.ts
487
- import { getEnvVar } from "@forklaunch/common";
488
- import { context, trace } from "@opentelemetry/api";
489
- import { v4 } from "uuid";
490
-
491
- // src/http/telemetry/constants.ts
492
- import {
493
- ATTR_HTTP_REQUEST_METHOD,
494
- ATTR_HTTP_RESPONSE_STATUS_CODE,
495
- ATTR_HTTP_ROUTE,
496
- ATTR_SERVICE_NAME
497
- } from "@opentelemetry/semantic-conventions";
498
- var ATTR_API_NAME = "api.name";
499
- var ATTR_CORRELATION_ID = "correlation.id";
500
-
501
- // src/http/middleware/request/createContext.middleware.ts
502
- function createContext(schemaValidator) {
503
- return function setContext(req, res, next) {
504
- req.schemaValidator = schemaValidator;
505
- let correlationId = v4();
506
- if (req.headers["x-correlation-id"]) {
507
- correlationId = req.headers["x-correlation-id"];
508
- }
509
- res.setHeader("x-correlation-id", correlationId);
510
- req.context = {
511
- correlationId
512
- };
513
- const span = trace.getSpan(context.active());
514
- if (span != null) {
515
- req.context.span = span;
516
- req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);
517
- req.context.span?.setAttribute(
518
- ATTR_SERVICE_NAME,
519
- getEnvVar("OTEL_SERVICE_NAME")
520
- );
521
- }
522
- next?.();
523
- };
524
- }
525
-
526
857
  // src/http/middleware/request/enrichDetails.middleware.ts
527
858
  import { getEnvVar as getEnvVar3 } from "@forklaunch/common";
528
859
 
@@ -551,171 +882,6 @@ function isForklaunchRequest(request) {
551
882
  return request != null && typeof request === "object" && "contractDetails" in request;
552
883
  }
553
884
 
554
- // src/http/telemetry/pinoLogger.ts
555
- import { isNever as isNever2 } from "@forklaunch/common";
556
- import { trace as trace2 } from "@opentelemetry/api";
557
- import { logs } from "@opentelemetry/api-logs";
558
- import pino from "pino";
559
-
560
- // src/http/guards/isLoggerMeta.ts
561
- function isLoggerMeta(arg) {
562
- return typeof arg === "object" && arg !== null && "_meta" in arg;
563
- }
564
-
565
- // src/http/telemetry/pinoLogger.ts
566
- function meta(meta2) {
567
- return meta2;
568
- }
569
- function mapSeverity(level) {
570
- switch (level) {
571
- case "silent":
572
- return 0;
573
- case "trace":
574
- return 1;
575
- case "debug":
576
- return 5;
577
- case "info":
578
- return 9;
579
- case "warn":
580
- return 13;
581
- case "error":
582
- return 17;
583
- case "fatal":
584
- return 21;
585
- default:
586
- isNever2(level);
587
- return 0;
588
- }
589
- }
590
- function normalizeLogArgs(args) {
591
- let message = "";
592
- const metaObjects = [];
593
- for (const arg of args) {
594
- if (typeof arg === "string" && message === "") {
595
- message = arg;
596
- } else if (arg instanceof Error) {
597
- metaObjects.push({
598
- err: {
599
- message: arg.message,
600
- name: arg.name,
601
- stack: arg.stack,
602
- ...Object.keys(arg).length > 0 ? arg : {}
603
- }
604
- });
605
- } else if (arg && typeof arg === "object" && !Array.isArray(arg)) {
606
- metaObjects.push(arg);
607
- } else {
608
- message += ` ${String(arg)}`;
609
- }
610
- }
611
- const metadata = Object.assign({}, ...metaObjects);
612
- return [metadata, message.trim()];
613
- }
614
- function safePrettyFormat(level, args, timestamp) {
615
- try {
616
- const [metadata, message] = normalizeLogArgs(args);
617
- const formattedTimestamp = timestamp || (/* @__PURE__ */ new Date()).toISOString();
618
- return `[${formattedTimestamp}] ${level.toUpperCase()}: ${message}${Object.keys(metadata).length > 0 ? `
619
- ${JSON.stringify(metadata, null, 2)}` : ""}`;
620
- } catch (error) {
621
- const fallbackMessage = args.map((arg) => {
622
- try {
623
- if (typeof arg === "string") return arg;
624
- if (arg === null) return "null";
625
- if (arg === void 0) return "undefined";
626
- return JSON.stringify(arg);
627
- } catch {
628
- return "[Circular/Non-serializable Object]";
629
- }
630
- }).join(" ");
631
- return `[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}: ${fallbackMessage} [Pretty Print Error: ${error instanceof Error ? error.message : "Unknown error"}]`;
632
- }
633
- }
634
- var PinoLogger = class _PinoLogger {
635
- pinoLogger;
636
- meta;
637
- constructor(level, meta2 = {}) {
638
- this.pinoLogger = pino({
639
- level: level || "info",
640
- formatters: {
641
- level(label) {
642
- return { level: label };
643
- }
644
- },
645
- timestamp: pino.stdTimeFunctions.isoTime,
646
- transport: {
647
- target: "pino-pretty",
648
- options: {
649
- colorize: true,
650
- errorLikeObjectKeys: ["err", "error"],
651
- ignore: "pid,hostname",
652
- translateTime: "SYS:standard"
653
- }
654
- }
655
- });
656
- this.meta = meta2;
657
- }
658
- log(level, ...args) {
659
- let meta2 = {};
660
- const filteredArgs = args.filter((arg) => {
661
- if (isLoggerMeta(arg)) {
662
- Object.assign(meta2, arg);
663
- return false;
664
- }
665
- return true;
666
- });
667
- const [errorMetadata] = normalizeLogArgs(filteredArgs);
668
- Object.assign(meta2, errorMetadata);
669
- const activeSpan = trace2.getActiveSpan();
670
- if (activeSpan) {
671
- const activeSpanContext = activeSpan.spanContext();
672
- meta2.trace_id = activeSpanContext.traceId;
673
- meta2.span_id = activeSpanContext.spanId;
674
- meta2.trace_flags = activeSpanContext.traceFlags;
675
- meta2 = {
676
- // @ts-expect-error accessing private property
677
- ...activeSpan.attributes,
678
- ...meta2
679
- };
680
- }
681
- meta2 = {
682
- "api.name": "none",
683
- "correlation.id": "none",
684
- ...meta2
685
- };
686
- this.pinoLogger[level](...normalizeLogArgs(filteredArgs));
687
- const formattedBody = safePrettyFormat(level, filteredArgs);
688
- try {
689
- logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
690
- severityText: level,
691
- severityNumber: mapSeverity(level),
692
- body: formattedBody,
693
- attributes: { ...this.meta, ...meta2 }
694
- });
695
- } catch (error) {
696
- console.error("Failed to emit OpenTelemetry log:", error);
697
- console.log(
698
- `[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}:`,
699
- ...filteredArgs
700
- );
701
- }
702
- }
703
- error = (msg, ...args) => this.log("error", msg, ...args);
704
- info = (msg, ...args) => this.log("info", msg, ...args);
705
- debug = (msg, ...args) => this.log("debug", msg, ...args);
706
- warn = (msg, ...args) => this.log("warn", msg, ...args);
707
- trace = (msg, ...args) => this.log("trace", msg, ...args);
708
- child(meta2 = {}) {
709
- return new _PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta2 });
710
- }
711
- getBaseLogger() {
712
- return this.pinoLogger;
713
- }
714
- };
715
- function logger(level, meta2 = {}) {
716
- return new PinoLogger(level, meta2);
717
- }
718
-
719
885
  // src/http/telemetry/openTelemetryCollector.ts
720
886
  var OpenTelemetryCollector = class {
721
887
  #logger;
@@ -781,23 +947,39 @@ var OpenTelemetryCollector = class {
781
947
  }
782
948
  };
783
949
  dotenv.config({ path: getEnvVar2("DOTENV_FILE_PATH") });
950
+ function parseOtelHeaders() {
951
+ const headersEnv = getEnvVar2("OTEL_EXPORTER_OTLP_HEADERS");
952
+ if (!headersEnv) return void 0;
953
+ const headers = {};
954
+ for (const pair of headersEnv.split(",")) {
955
+ const [key, ...valueParts] = pair.split("=");
956
+ if (key && valueParts.length > 0) {
957
+ headers[key.trim()] = valueParts.join("=").trim();
958
+ }
959
+ }
960
+ return Object.keys(headers).length > 0 ? headers : void 0;
961
+ }
962
+ var otelHeaders = parseOtelHeaders();
784
963
  new NodeSDK({
785
964
  resource: resourceFromAttributes({
786
965
  [ATTR_SERVICE_NAME2]: getEnvVar2("OTEL_SERVICE_NAME")
787
966
  }),
788
967
  traceExporter: new OTLPTraceExporter({
789
- url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
968
+ url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`,
969
+ headers: otelHeaders
790
970
  }),
791
971
  metricReader: new PeriodicExportingMetricReader({
792
972
  exporter: new OTLPMetricExporter({
793
- url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
973
+ url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`,
974
+ headers: otelHeaders
794
975
  }),
795
976
  exportIntervalMillis: 5e3
796
977
  }),
797
978
  logRecordProcessors: [
798
979
  new BatchLogRecordProcessor(
799
980
  new OTLPLogExporter({
800
- url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
981
+ url: `${getEnvVar2("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`,
982
+ headers: otelHeaders
801
983
  })
802
984
  )
803
985
  ],
@@ -834,7 +1016,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
834
1016
  req.requestSchema = requestSchema;
835
1017
  res.responseSchemas = responseSchemas;
836
1018
  req.openTelemetryCollector = openTelemetryCollector;
837
- req._globalOptions = globalOptions;
1019
+ req._globalOptions = globalOptions ?? (() => void 0);
838
1020
  req.context?.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
839
1021
  const startTime = process.hrtime();
840
1022
  res.on("finish", () => {
@@ -842,6 +1024,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
842
1024
  const durationMs = seconds + nanoseconds / 1e9;
843
1025
  httpServerDurationHistogram.record(durationMs, {
844
1026
  [ATTR_SERVICE_NAME]: getEnvVar3("OTEL_SERVICE_NAME") || "unknown",
1027
+ [ATTR_APPLICATION_ID]: getEnvVar3("OTEL_APPLICATION_ID"),
845
1028
  [ATTR_API_NAME]: req.contractDetails?.name || "unknown",
846
1029
  [ATTR_HTTP_REQUEST_METHOD]: req.method,
847
1030
  [ATTR_HTTP_ROUTE]: req.originalPath || "unknown",
@@ -872,6 +1055,7 @@ function isRequestShape(maybeResponseShape) {
872
1055
  function parse(req, res, next) {
873
1056
  const globalOptions = req._globalOptions?.();
874
1057
  const collapsedOptions = req.contractDetails.options?.requestValidation ?? (globalOptions?.validation === false ? "none" : globalOptions?.validation?.request);
1058
+ req._rawBody = req.body;
875
1059
  const request = {
876
1060
  params: req.params,
877
1061
  query: req.query,
@@ -961,7 +1145,7 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
961
1145
  }
962
1146
  return;
963
1147
  case "warning":
964
- req.openTelemetryCollector.warn(
1148
+ req.openTelemetryCollector?.warn(
965
1149
  collectedParseErrors ?? prettyPrintParseErrors2(parsedRequest.errors, "Request")
966
1150
  );
967
1151
  break;
@@ -969,143 +1153,221 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
969
1153
  break;
970
1154
  }
971
1155
  }
972
- req._parsedVersions = matchedVersions;
973
- next?.();
1156
+ req._parsedVersions = matchedVersions;
1157
+ next?.();
1158
+ }
1159
+
1160
+ // src/http/router/routerSharedLogic.ts
1161
+ function resolveContractDetailsAndHandlers(contractDetailsOrMiddlewareOrTypedHandler, middlewareOrMiddlewareAndTypedHandler) {
1162
+ let contractDetails;
1163
+ let handlers = [];
1164
+ if (isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
1165
+ contractDetails = contractDetailsOrMiddlewareOrTypedHandler.contractDetails;
1166
+ handlers = contractDetailsOrMiddlewareOrTypedHandler.handlers;
1167
+ } else {
1168
+ const maybeTypedHandler = middlewareOrMiddlewareAndTypedHandler[middlewareOrMiddlewareAndTypedHandler.length - 1];
1169
+ if (isTypedHandler(maybeTypedHandler)) {
1170
+ contractDetails = maybeTypedHandler.contractDetails;
1171
+ const typedHandlerHandlers = maybeTypedHandler.handlers;
1172
+ const finalHandlers = [];
1173
+ if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
1174
+ finalHandlers.push(contractDetailsOrMiddlewareOrTypedHandler);
1175
+ }
1176
+ finalHandlers.push(...middlewareOrMiddlewareAndTypedHandler.slice(0, -1));
1177
+ finalHandlers.push(
1178
+ ...typedHandlerHandlers
1179
+ );
1180
+ handlers = finalHandlers.filter(
1181
+ (handler) => isExpressLikeSchemaHandler(handler)
1182
+ );
1183
+ } else {
1184
+ if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler) || isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
1185
+ throw new Error("Contract details are not defined");
1186
+ }
1187
+ contractDetails = contractDetailsOrMiddlewareOrTypedHandler;
1188
+ handlers = middlewareOrMiddlewareAndTypedHandler.filter(
1189
+ (handler) => isExpressLikeSchemaHandler(handler)
1190
+ );
1191
+ }
1192
+ }
1193
+ return { contractDetails, handlers };
974
1194
  }
975
-
976
- // src/http/router/discriminateBody.ts
977
- function discriminateBody(schemaValidator, body) {
978
- if (body == null) {
979
- return void 0;
1195
+ function validateContractDetails(contractDetails, schemaValidator) {
1196
+ if (!isHttpContractDetails(contractDetails) && !isPathParamHttpContractDetails(contractDetails)) {
1197
+ throw new Error("Contract details are malformed for route definition");
980
1198
  }
981
- const maybeTypedBody = body;
982
- if ("text" in maybeTypedBody && maybeTypedBody.text != null) {
983
- return {
984
- contentType: maybeTypedBody.contentType ?? "text/plain",
985
- parserType: "text",
986
- schema: maybeTypedBody.text
987
- };
988
- } else if ("json" in maybeTypedBody && maybeTypedBody.json != null) {
989
- return {
990
- contentType: maybeTypedBody.contentType ?? "application/json",
991
- parserType: "json",
992
- schema: maybeTypedBody.json
993
- };
994
- } else if ("file" in maybeTypedBody && maybeTypedBody.file != null) {
995
- return {
996
- contentType: maybeTypedBody.contentType ?? "application/octet-stream",
997
- parserType: "file",
998
- schema: maybeTypedBody.file
999
- };
1000
- } else if ("multipartForm" in maybeTypedBody && maybeTypedBody.multipartForm != null) {
1001
- return {
1002
- contentType: maybeTypedBody.contentType ?? "multipart/form-data",
1003
- parserType: "multipart",
1004
- schema: maybeTypedBody.multipartForm
1005
- };
1006
- } else if ("urlEncodedForm" in maybeTypedBody && maybeTypedBody.urlEncodedForm != null) {
1007
- return {
1008
- contentType: maybeTypedBody.contentType ?? "application/x-www-form-urlencoded",
1009
- parserType: "urlEncoded",
1010
- schema: maybeTypedBody.urlEncodedForm
1011
- };
1012
- } else if ("schema" in maybeTypedBody && maybeTypedBody.schema != null) {
1013
- return {
1014
- contentType: maybeTypedBody.contentType ?? "application/json",
1015
- parserType: "text",
1016
- schema: maybeTypedBody.schema
1017
- };
1018
- } else if (schemaValidator.isInstanceOf(
1019
- maybeTypedBody,
1020
- schemaValidator.string
1021
- )) {
1022
- return {
1023
- contentType: "text/plain",
1024
- parserType: "text",
1025
- schema: maybeTypedBody
1026
- };
1027
- } else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
1028
- return {
1029
- contentType: "application/octet-stream",
1030
- parserType: "file",
1031
- schema: maybeTypedBody
1032
- };
1033
- } else {
1034
- return {
1035
- contentType: "application/json",
1036
- parserType: "json",
1037
- schema: maybeTypedBody
1038
- };
1199
+ if (contractDetails.versions) {
1200
+ const parserTypes = Object.values(contractDetails.versions).map(
1201
+ (version) => discriminateBody(schemaValidator, version.body)?.parserType
1202
+ );
1203
+ const allParserTypesSame = parserTypes.length === 0 || parserTypes.every((pt) => pt === parserTypes[0]);
1204
+ if (!allParserTypesSame) {
1205
+ throw new Error(
1206
+ "All versioned contractDetails must have the same parsing type for body."
1207
+ );
1208
+ }
1039
1209
  }
1040
1210
  }
1041
- function discriminateResponseBodies(schemaValidator, responses) {
1042
- const discriminatedResponses = {};
1043
- for (const [statusCode, response] of Object.entries(responses)) {
1044
- if (response != null && typeof response === "object") {
1045
- if ("json" in response && response.json != null) {
1046
- discriminatedResponses[Number(statusCode)] = {
1047
- contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/json") ?? "application/json",
1048
- parserType: "json",
1049
- schema: response.json
1050
- };
1051
- } else if ("schema" in response && response.schema != null) {
1052
- discriminatedResponses[Number(statusCode)] = {
1053
- contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/json") ?? "application/json",
1054
- parserType: "text",
1055
- schema: response.schema
1056
- };
1057
- } else if ("text" in response && response.text != null) {
1058
- discriminatedResponses[Number(statusCode)] = {
1059
- contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "text/plain") ?? "text/plain",
1060
- parserType: "text",
1061
- schema: response.text
1062
- };
1063
- } else if ("file" in response && response.file != null) {
1064
- discriminatedResponses[Number(statusCode)] = {
1065
- contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/octet-stream") ?? "application/octet-stream",
1066
- parserType: "file",
1067
- schema: response.file
1068
- };
1069
- } else if ("event" in response && response.event != null) {
1070
- discriminatedResponses[Number(statusCode)] = {
1071
- contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "text/event-stream") ?? "text/event-stream",
1072
- parserType: "serverSentEvent",
1073
- schema: response.event
1074
- };
1075
- } else if (schemaValidator.isInstanceOf(
1076
- response,
1077
- schemaValidator.string
1078
- )) {
1079
- discriminatedResponses[Number(statusCode)] = {
1080
- contentType: "text/plain",
1081
- parserType: "text",
1082
- schema: response
1083
- };
1084
- } else if (schemaValidator.openapi(response).format === "binary") {
1085
- discriminatedResponses[Number(statusCode)] = {
1086
- contentType: "application/octet-stream",
1087
- parserType: "file",
1088
- schema: response
1089
- };
1090
- } else {
1091
- discriminatedResponses[Number(statusCode)] = {
1092
- contentType: "application/json",
1093
- parserType: "json",
1094
- schema: response
1095
- };
1096
- }
1097
- } else {
1098
- discriminatedResponses[Number(statusCode)] = {
1099
- contentType: "application/json",
1100
- parserType: "json",
1101
- schema: response
1102
- };
1211
+ function processContractDetailsIO(schemaValidator, contractDetailsIO, routeParams) {
1212
+ const responseSchemas = {
1213
+ 400: schemaValidator.string,
1214
+ 401: schemaValidator.string,
1215
+ 403: schemaValidator.string,
1216
+ 404: schemaValidator.string,
1217
+ 500: schemaValidator.string,
1218
+ ...Object.fromEntries(
1219
+ Object.entries(
1220
+ discriminateResponseBodies(schemaValidator, contractDetailsIO.responses)
1221
+ ).map(([key, value]) => [Number(key), value.schema])
1222
+ )
1223
+ };
1224
+ return {
1225
+ requestSchema: schemaValidator.compile(
1226
+ schemaValidator.schemify({
1227
+ ...routeParams != null ? { params: routeParams } : { params: schemaValidator.unknown },
1228
+ ...contractDetailsIO.requestHeaders != null ? { headers: contractDetailsIO.requestHeaders } : { headers: schemaValidator.unknown },
1229
+ ...contractDetailsIO.query != null ? { query: contractDetailsIO.query } : { query: schemaValidator.unknown },
1230
+ ...contractDetailsIO.body != null ? {
1231
+ body: discriminateBody(schemaValidator, contractDetailsIO.body)?.schema
1232
+ } : { body: schemaValidator.unknown }
1233
+ })
1234
+ ),
1235
+ responseSchemas: {
1236
+ ...contractDetailsIO.responseHeaders != null ? {
1237
+ headers: schemaValidator.compile(
1238
+ schemaValidator.schemify(contractDetailsIO.responseHeaders)
1239
+ )
1240
+ } : { headers: schemaValidator.unknown },
1241
+ responses: Object.fromEntries(
1242
+ Object.entries(responseSchemas).map(([key, value]) => [
1243
+ key,
1244
+ schemaValidator.compile(schemaValidator.schemify(value))
1245
+ ])
1246
+ )
1103
1247
  }
1248
+ };
1249
+ }
1250
+ function compileRouteSchemas(contractDetails, schemaValidator) {
1251
+ const validator = schemaValidator;
1252
+ let requestSchema;
1253
+ let responseSchemas;
1254
+ if (hasVersionedSchema(contractDetails)) {
1255
+ requestSchema = {};
1256
+ responseSchemas = {};
1257
+ Object.entries(contractDetails.versions ?? {}).forEach(
1258
+ ([version, versionedContractDetails]) => {
1259
+ const {
1260
+ requestSchema: versionedRequestSchema,
1261
+ responseSchemas: versionedResponseSchemas
1262
+ } = processContractDetailsIO(
1263
+ validator,
1264
+ versionedContractDetails,
1265
+ contractDetails.params
1266
+ );
1267
+ if (isRecord2(requestSchema)) {
1268
+ requestSchema = {
1269
+ ...requestSchema,
1270
+ [version]: versionedRequestSchema
1271
+ };
1272
+ }
1273
+ if (isRecord2(responseSchemas)) {
1274
+ responseSchemas = {
1275
+ ...responseSchemas,
1276
+ [version]: versionedResponseSchemas
1277
+ };
1278
+ }
1279
+ }
1280
+ );
1281
+ } else {
1282
+ const {
1283
+ requestSchema: unversionedRequestSchema,
1284
+ responseSchemas: unversionedResponseSchemas
1285
+ } = processContractDetailsIO(
1286
+ validator,
1287
+ {
1288
+ ..."params" in contractDetails && contractDetails.params != null ? { params: contractDetails.params } : { params: validator.unknown },
1289
+ ..."requestHeaders" in contractDetails && contractDetails.requestHeaders != null ? { requestHeaders: contractDetails.requestHeaders } : {
1290
+ requestHeaders: validator.unknown
1291
+ },
1292
+ ..."responseHeaders" in contractDetails && contractDetails.responseHeaders != null ? { responseHeaders: contractDetails.responseHeaders } : {
1293
+ responseHeaders: validator.unknown
1294
+ },
1295
+ ..."query" in contractDetails && contractDetails.query != null ? { query: contractDetails.query } : {
1296
+ query: validator.unknown
1297
+ },
1298
+ ..."body" in contractDetails && contractDetails.body != null ? { body: contractDetails.body } : {
1299
+ body: validator.unknown
1300
+ },
1301
+ responses: "responses" in contractDetails && contractDetails.responses != null ? contractDetails.responses : validator.unknown
1302
+ },
1303
+ contractDetails.params
1304
+ );
1305
+ requestSchema = unversionedRequestSchema;
1306
+ responseSchemas = unversionedResponseSchemas;
1104
1307
  }
1105
- return discriminatedResponses;
1308
+ return { requestSchema, responseSchemas };
1309
+ }
1310
+ function resolveRouteMiddlewares(params) {
1311
+ const handlersCopy = [...params.handlers];
1312
+ const controllerHandler = handlersCopy.pop();
1313
+ if (typeof controllerHandler !== "function") {
1314
+ throw new Error(
1315
+ `Last argument must be a handler, received: ${controllerHandler}`
1316
+ );
1317
+ }
1318
+ const middlewares = [
1319
+ ...params.includeCreateContext !== false ? [createContext] : [],
1320
+ enrichDetails(
1321
+ `${params.basePath}${params.path}`,
1322
+ params.contractDetails,
1323
+ params.requestSchema,
1324
+ params.responseSchemas,
1325
+ params.openTelemetryCollector,
1326
+ () => params.routerOptions
1327
+ ),
1328
+ ...params.postEnrichMiddleware,
1329
+ parse,
1330
+ parseRequestAuth,
1331
+ ...handlersCopy
1332
+ ];
1333
+ return {
1334
+ middlewares,
1335
+ controllerHandler
1336
+ };
1106
1337
  }
1107
1338
 
1108
1339
  // src/http/router/expressLikeRouter.ts
1340
+ function extractRouteHandlers(params) {
1341
+ const schemaValidator = params.schemaValidator;
1342
+ const { contractDetails, handlers } = resolveContractDetailsAndHandlers(
1343
+ params.contractDetailsOrMiddlewareOrTypedHandler,
1344
+ params.middlewareOrMiddlewareAndTypedHandler
1345
+ );
1346
+ validateContractDetails(contractDetails, schemaValidator);
1347
+ const { requestSchema, responseSchemas } = compileRouteSchemas(
1348
+ contractDetails,
1349
+ schemaValidator
1350
+ );
1351
+ const { middlewares, controllerHandler } = resolveRouteMiddlewares({
1352
+ basePath: params.basePath,
1353
+ path: params.path,
1354
+ contractDetails,
1355
+ requestSchema,
1356
+ responseSchemas,
1357
+ openTelemetryCollector: params.openTelemetryCollector,
1358
+ routerOptions: params.routerOptions,
1359
+ postEnrichMiddleware: params.postEnrichMiddleware ?? [],
1360
+ handlers,
1361
+ includeCreateContext: false
1362
+ });
1363
+ return {
1364
+ middlewares: [
1365
+ createContext(schemaValidator),
1366
+ ...middlewares
1367
+ ],
1368
+ controllerHandler
1369
+ };
1370
+ }
1109
1371
  var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1110
1372
  constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector, routerOptions) {
1111
1373
  this.basePath = basePath;
@@ -1145,21 +1407,6 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1145
1407
  * @param {PathParamHttpContractDetails<SV> | HttpContractDetails<SV>} contractDetails - The contract details.
1146
1408
  * @returns {MiddlewareHandler<SV>[]} - The resolved middlewares.
1147
1409
  */
1148
- #resolveMiddlewares(path, contractDetails, requestSchema, responseSchemas) {
1149
- return [
1150
- enrichDetails(
1151
- `${this.basePath}${path}`,
1152
- contractDetails,
1153
- requestSchema,
1154
- responseSchemas,
1155
- this.openTelemetryCollector,
1156
- () => this.routerOptions
1157
- ),
1158
- ...this.postEnrichMiddleware,
1159
- parse,
1160
- parseRequestAuth
1161
- ];
1162
- }
1163
1410
  /**
1164
1411
  * Parses and runs the controller handler with error handling.
1165
1412
  *
@@ -1200,126 +1447,6 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1200
1447
  * @returns {MiddlewareHandler<SV, P, ResBodyMap, ReqBody, ReqQuery, LocalsObj>} - The extracted controller handler.
1201
1448
  * @throws {Error} - Throws an error if the last argument is not a handler.
1202
1449
  */
1203
- #extractControllerHandler(handlers) {
1204
- const controllerHandler = handlers.pop();
1205
- if (typeof controllerHandler !== "function") {
1206
- throw new Error(
1207
- `Last argument must be a handler, received: ${controllerHandler}`
1208
- );
1209
- }
1210
- return controllerHandler;
1211
- }
1212
- #processContractDetailsIO(contractDetailsIO, params) {
1213
- const schemaValidator = this.schemaValidator;
1214
- const responseSchemas = {
1215
- 400: schemaValidator.string,
1216
- 401: schemaValidator.string,
1217
- 403: schemaValidator.string,
1218
- 404: schemaValidator.string,
1219
- 500: schemaValidator.string,
1220
- ...Object.fromEntries(
1221
- Object.entries(
1222
- discriminateResponseBodies(
1223
- this.schemaValidator,
1224
- contractDetailsIO.responses
1225
- )
1226
- ).map(([key, value]) => {
1227
- return [Number(key), value.schema];
1228
- })
1229
- )
1230
- };
1231
- return {
1232
- requestSchema: schemaValidator.compile(
1233
- schemaValidator.schemify({
1234
- ...params != null ? { params } : { params: schemaValidator.unknown },
1235
- ...contractDetailsIO.requestHeaders != null ? { headers: contractDetailsIO.requestHeaders } : { headers: schemaValidator.unknown },
1236
- ...contractDetailsIO.query != null ? { query: contractDetailsIO.query } : { query: schemaValidator.unknown },
1237
- ...contractDetailsIO.body != null ? {
1238
- body: discriminateBody(
1239
- this.schemaValidator,
1240
- contractDetailsIO.body
1241
- )?.schema
1242
- } : { body: schemaValidator.unknown }
1243
- })
1244
- ),
1245
- responseSchemas: {
1246
- ...contractDetailsIO.responseHeaders != null ? {
1247
- headers: schemaValidator.compile(
1248
- schemaValidator.schemify(contractDetailsIO.responseHeaders)
1249
- )
1250
- } : { headers: schemaValidator.unknown },
1251
- responses: Object.fromEntries(
1252
- Object.entries(responseSchemas).map(([key, value]) => {
1253
- return [
1254
- key,
1255
- schemaValidator.compile(schemaValidator.schemify(value))
1256
- ];
1257
- })
1258
- )
1259
- }
1260
- };
1261
- }
1262
- #compile(contractDetails) {
1263
- const schemaValidator = this.schemaValidator;
1264
- let requestSchema;
1265
- let responseSchemas;
1266
- if (hasVersionedSchema(contractDetails)) {
1267
- requestSchema = {};
1268
- responseSchemas = {};
1269
- Object.entries(contractDetails.versions ?? {}).forEach(
1270
- ([version, versionedContractDetails]) => {
1271
- const {
1272
- requestSchema: versionedRequestSchema,
1273
- responseSchemas: versionedResponseSchemas
1274
- } = this.#processContractDetailsIO(
1275
- versionedContractDetails,
1276
- contractDetails.params
1277
- );
1278
- if (isRecord2(requestSchema)) {
1279
- requestSchema = {
1280
- ...requestSchema,
1281
- [version]: versionedRequestSchema
1282
- };
1283
- }
1284
- if (isRecord2(responseSchemas)) {
1285
- responseSchemas = {
1286
- ...responseSchemas,
1287
- [version]: versionedResponseSchemas
1288
- };
1289
- }
1290
- }
1291
- );
1292
- } else {
1293
- const {
1294
- requestSchema: unversionedRequestSchema,
1295
- responseSchemas: unversionedResponseSchemas
1296
- } = this.#processContractDetailsIO(
1297
- {
1298
- ..."params" in contractDetails && contractDetails.params != null ? { params: contractDetails.params } : { params: schemaValidator.unknown },
1299
- ..."requestHeaders" in contractDetails && contractDetails.requestHeaders != null ? { requestHeaders: contractDetails.requestHeaders } : {
1300
- requestHeaders: schemaValidator.unknown
1301
- },
1302
- ..."responseHeaders" in contractDetails && contractDetails.responseHeaders != null ? { responseHeaders: contractDetails.responseHeaders } : {
1303
- responseHeaders: schemaValidator.unknown
1304
- },
1305
- ..."query" in contractDetails && contractDetails.query != null ? { query: contractDetails.query } : {
1306
- query: schemaValidator.unknown
1307
- },
1308
- ..."body" in contractDetails && contractDetails.body != null ? { body: contractDetails.body } : {
1309
- body: schemaValidator.unknown
1310
- },
1311
- responses: "responses" in contractDetails && contractDetails.responses != null ? contractDetails.responses : schemaValidator.unknown
1312
- },
1313
- contractDetails.params
1314
- );
1315
- requestSchema = unversionedRequestSchema;
1316
- responseSchemas = unversionedResponseSchemas;
1317
- }
1318
- return {
1319
- requestSchema,
1320
- responseSchemas
1321
- };
1322
- }
1323
1450
  /**
1324
1451
  * Fetches a route from the route map and executes it with the given parameters.
1325
1452
  *
@@ -1409,92 +1536,71 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1409
1536
  };
1410
1537
  }
1411
1538
  registerRoute(method, path, registrationMethod, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareAndTypedHandler) {
1412
- if (isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
1413
- const { contractDetails, handlers } = contractDetailsOrMiddlewareOrTypedHandler;
1414
- const router = this.registerRoute(method, path, registrationMethod, contractDetails, ...handlers);
1415
- return router;
1416
- } else {
1417
- const maybeTypedHandler = middlewareOrMiddlewareAndTypedHandler[middlewareOrMiddlewareAndTypedHandler.length - 1];
1418
- if (isTypedHandler(maybeTypedHandler)) {
1419
- const { contractDetails, handlers } = maybeTypedHandler;
1420
- const finalHandlers = [];
1421
- if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
1422
- finalHandlers.push(
1423
- contractDetailsOrMiddlewareOrTypedHandler
1424
- );
1425
- }
1426
- finalHandlers.push(...middlewareOrMiddlewareAndTypedHandler);
1427
- finalHandlers.push(...handlers);
1428
- const router = this.registerRoute(method, path, registrationMethod, contractDetails, ...finalHandlers);
1429
- return router;
1430
- } else {
1431
- if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler) || isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
1432
- throw new Error("Contract details are not defined");
1433
- }
1434
- const contractDetails = contractDetailsOrMiddlewareOrTypedHandler;
1435
- const handlers = middlewareOrMiddlewareAndTypedHandler.filter(
1436
- (handler) => isExpressLikeSchemaHandler(handler)
1437
- );
1438
- if (!isHttpContractDetails(contractDetails) && !isPathParamHttpContractDetails(contractDetails)) {
1439
- throw new Error(
1440
- "Contract details are malformed for route definition"
1441
- );
1442
- }
1443
- if (contractDetails.versions) {
1444
- const parserTypes = Object.values(contractDetails.versions).map(
1445
- (version) => discriminateBody(this.schemaValidator, version.body)?.parserType
1446
- );
1447
- const allParserTypesSame = parserTypes.length === 0 || parserTypes.every((pt) => pt === parserTypes[0]);
1448
- if (!allParserTypesSame) {
1449
- throw new Error(
1450
- "All versioned contractDetails must have the same parsing type for body."
1451
- );
1452
- }
1453
- }
1454
- this.routes.push({
1455
- basePath: this.basePath,
1456
- path,
1457
- method,
1458
- contractDetails
1459
- });
1460
- const { requestSchema, responseSchemas } = this.#compile(contractDetails);
1461
- const controllerHandler = this.#extractControllerHandler(handlers);
1462
- const resolvedMiddlewares = this.#resolveMiddlewares(
1463
- path,
1464
- contractDetails,
1465
- requestSchema,
1466
- responseSchemas
1467
- ).concat(handlers);
1468
- registrationMethod.bind(this.internal)(
1469
- path,
1470
- ...resolvedMiddlewares,
1471
- this.#parseAndRunControllerHandler(controllerHandler)
1472
- );
1473
- toRecord(this._fetchMap)[sanitizePathSlashes(`${this.basePath}${path}`)] = {
1474
- ...this._fetchMap[sanitizePathSlashes(`${this.basePath}${path}`)] ?? {},
1475
- [method.toUpperCase()]: contractDetails.versions ? Object.fromEntries(
1476
- Object.keys(contractDetails.versions).map((version) => [
1477
- version,
1478
- this.#localParamRequest(handlers, controllerHandler, version)
1479
- ])
1480
- ) : this.#localParamRequest(handlers, controllerHandler)
1481
- };
1482
- toRecord(this.sdk)[toPrettyCamelCase(contractDetails.name)] = contractDetails.versions ? Object.fromEntries(
1483
- Object.keys(contractDetails.versions).map((version) => [
1484
- version,
1485
- (req) => this.#localParamRequest(
1486
- handlers,
1487
- controllerHandler,
1488
- version
1489
- )(`${this.basePath}${path}`, req)
1490
- ])
1491
- ) : (req) => this.#localParamRequest(handlers, controllerHandler)(
1492
- `${this.basePath}${path}`,
1493
- req
1494
- );
1495
- return this;
1496
- }
1539
+ const { contractDetails, handlers } = resolveContractDetailsAndHandlers(
1540
+ contractDetailsOrMiddlewareOrTypedHandler,
1541
+ middlewareOrMiddlewareAndTypedHandler
1542
+ );
1543
+ validateContractDetails(contractDetails, this.schemaValidator);
1544
+ this.routes.push({
1545
+ basePath: this.basePath,
1546
+ path,
1547
+ method,
1548
+ contractDetails
1549
+ });
1550
+ const { requestSchema, responseSchemas } = compileRouteSchemas(
1551
+ contractDetails,
1552
+ this.schemaValidator
1553
+ );
1554
+ const { middlewares, controllerHandler } = resolveRouteMiddlewares({
1555
+ basePath: this.basePath,
1556
+ path,
1557
+ contractDetails,
1558
+ requestSchema,
1559
+ responseSchemas,
1560
+ openTelemetryCollector: this.openTelemetryCollector,
1561
+ routerOptions: this.routerOptions,
1562
+ postEnrichMiddleware: this.postEnrichMiddleware,
1563
+ includeCreateContext: false,
1564
+ handlers
1565
+ });
1566
+ registrationMethod.bind(this.internal)(
1567
+ path,
1568
+ ...middlewares,
1569
+ this.#parseAndRunControllerHandler(controllerHandler)
1570
+ );
1571
+ toRecord(this._fetchMap)[sanitizePathSlashes(`${this.basePath}${path}`)] = {
1572
+ ...this._fetchMap[sanitizePathSlashes(`${this.basePath}${path}`)] ?? {},
1573
+ [method.toUpperCase()]: contractDetails.versions ? Object.fromEntries(
1574
+ Object.keys(contractDetails.versions).map((version) => [
1575
+ version,
1576
+ this.#localParamRequest(
1577
+ middlewares,
1578
+ controllerHandler,
1579
+ version
1580
+ )
1581
+ ])
1582
+ ) : this.#localParamRequest(
1583
+ middlewares,
1584
+ controllerHandler
1585
+ )
1586
+ };
1587
+ const contractDetailsName = contractDetails.name;
1588
+ if (contractDetailsName) {
1589
+ toRecord(this.sdk)[toPrettyCamelCase(contractDetailsName)] = contractDetails.versions ? Object.fromEntries(
1590
+ Object.keys(contractDetails.versions).map((version) => [
1591
+ version,
1592
+ (req) => this.#localParamRequest(
1593
+ middlewares,
1594
+ controllerHandler,
1595
+ version
1596
+ )(`${this.basePath}${path}`, req)
1597
+ ])
1598
+ ) : (req) => this.#localParamRequest(
1599
+ middlewares,
1600
+ controllerHandler
1601
+ )(`${this.basePath}${path}`, req);
1497
1602
  }
1603
+ return this;
1498
1604
  }
1499
1605
  #extractHandlers(handlers, processMiddleware) {
1500
1606
  const last = handlers.pop();
@@ -1873,7 +1979,7 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1873
1979
  unpackSdks(sdks, path, routerUniquenessCache) {
1874
1980
  Object.entries(sdks).forEach(([key, maybeHandler]) => {
1875
1981
  if (isSdkHandler(maybeHandler)) {
1876
- const cacheKey = hashString(safeStringify2(maybeHandler));
1982
+ const cacheKey = hashString(safeStringify3(maybeHandler));
1877
1983
  if (routerUniquenessCache.has(cacheKey)) {
1878
1984
  throw new Error(`SDK handler ${key} is already registered`);
1879
1985
  }
@@ -1967,6 +2073,30 @@ function isPortBound(port, host = "localhost") {
1967
2073
  });
1968
2074
  }
1969
2075
 
2076
+ // src/http/generateHmacAuthHeaders.ts
2077
+ import { randomUUID } from "crypto";
2078
+ function generateHmacAuthHeaders({
2079
+ secretKey,
2080
+ method,
2081
+ path,
2082
+ body,
2083
+ keyId = "default"
2084
+ }) {
2085
+ const timestamp = /* @__PURE__ */ new Date();
2086
+ const nonce = randomUUID();
2087
+ const signature = createHmacToken({
2088
+ secretKey,
2089
+ method,
2090
+ path,
2091
+ timestamp,
2092
+ nonce,
2093
+ body
2094
+ });
2095
+ return {
2096
+ authorization: `HMAC keyId=${keyId} ts=${timestamp.toISOString()} nonce=${nonce} signature=${signature}`
2097
+ };
2098
+ }
2099
+
1970
2100
  // src/http/guards/isPath.ts
1971
2101
  function isPath(path) {
1972
2102
  return path.startsWith("/");
@@ -3037,9 +3167,9 @@ var getCodeForStatus = (status) => {
3037
3167
  var httpStatusCodes_default = HTTPStatuses;
3038
3168
 
3039
3169
  // src/http/mcpGenerator/mcpGenerator.ts
3040
- import { isNever as isNever3, isRecord as isRecord3, safeStringify as safeStringify3 } from "@forklaunch/common";
3041
- import { FastMCP } from "@forklaunch/fastmcp-fork";
3170
+ import { isNever as isNever3, isRecord as isRecord3, safeStringify as safeStringify4 } from "@forklaunch/common";
3042
3171
  import { string, ZodSchemaValidator } from "@forklaunch/validator/zod";
3172
+ import { FastMCP } from "fastmcp";
3043
3173
 
3044
3174
  // src/http/router/unpackRouters.ts
3045
3175
  function unpackRouters(routers, recursiveBasePath = []) {
@@ -3172,7 +3302,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3172
3302
  if (discriminatedBody) {
3173
3303
  switch (discriminatedBody.parserType) {
3174
3304
  case "json": {
3175
- parsedBody = safeStringify3(body);
3305
+ parsedBody = safeStringify4(body);
3176
3306
  break;
3177
3307
  }
3178
3308
  case "text": {
@@ -3180,7 +3310,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3180
3310
  break;
3181
3311
  }
3182
3312
  case "file": {
3183
- parsedBody = Buffer.from(safeStringify3(body));
3313
+ parsedBody = Buffer.from(safeStringify4(body));
3184
3314
  break;
3185
3315
  }
3186
3316
  case "multipart": {
@@ -3214,7 +3344,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3214
3344
  parsedBody = new URLSearchParams(
3215
3345
  Object.entries(body).map(([key, value]) => [
3216
3346
  key,
3217
- safeStringify3(value)
3347
+ safeStringify4(value)
3218
3348
  ])
3219
3349
  );
3220
3350
  } else {
@@ -3224,7 +3354,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3224
3354
  }
3225
3355
  default: {
3226
3356
  isNever3(discriminatedBody.parserType);
3227
- parsedBody = safeStringify3(body);
3357
+ parsedBody = safeStringify4(body);
3228
3358
  break;
3229
3359
  }
3230
3360
  }
@@ -3233,7 +3363,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3233
3363
  const queryString = new URLSearchParams(
3234
3364
  Object.entries(query).map(([key, value]) => [
3235
3365
  key,
3236
- safeStringify3(value)
3366
+ safeStringify4(value)
3237
3367
  ])
3238
3368
  ).toString();
3239
3369
  url += queryString ? `?${queryString}` : "";
@@ -3266,7 +3396,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3266
3396
  content: [
3267
3397
  {
3268
3398
  type: "text",
3269
- text: safeStringify3(await response.json())
3399
+ text: safeStringify4(await response.json())
3270
3400
  }
3271
3401
  ]
3272
3402
  };
@@ -3410,7 +3540,7 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
3410
3540
  }
3411
3541
  return;
3412
3542
  case "warning":
3413
- req.openTelemetryCollector.warn(
3543
+ req.openTelemetryCollector?.warn(
3414
3544
  `Invalid response:
3415
3545
  ${parseErrors.join("\n\n")}`
3416
3546
  );
@@ -3429,7 +3559,7 @@ import {
3429
3559
  isNodeJsWriteableStream,
3430
3560
  isRecord as isRecord4,
3431
3561
  readableStreamToAsyncIterable,
3432
- safeStringify as safeStringify4
3562
+ safeStringify as safeStringify5
3433
3563
  } from "@forklaunch/common";
3434
3564
  import { Readable, Transform } from "stream";
3435
3565
 
@@ -3447,6 +3577,7 @@ function recordMetric(req, res) {
3447
3577
  }
3448
3578
  httpRequestsTotalCounter.add(1, {
3449
3579
  [ATTR_SERVICE_NAME3]: getEnvVar4("OTEL_SERVICE_NAME"),
3580
+ [ATTR_APPLICATION_ID]: getEnvVar4("OTEL_APPLICATION_ID"),
3450
3581
  [ATTR_API_NAME]: req.contractDetails?.name,
3451
3582
  [ATTR_HTTP_REQUEST_METHOD3]: req.method,
3452
3583
  [ATTR_HTTP_ROUTE3]: req.originalPath,
@@ -3465,7 +3596,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
3465
3596
  if (res.statusCode === 404) {
3466
3597
  res.type("text/plain");
3467
3598
  res.status(404);
3468
- req.openTelemetryCollector.error("Not Found");
3599
+ req.openTelemetryCollector?.error("Not Found");
3469
3600
  originalSend.call(instance, "Not Found");
3470
3601
  errorSent = true;
3471
3602
  }
@@ -3520,7 +3651,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
3520
3651
  ------------------
3521
3652
  ${res.locals.errorMessage}`;
3522
3653
  }
3523
- req.openTelemetryCollector.error(errorString);
3654
+ req.openTelemetryCollector?.error(errorString);
3524
3655
  res.type("text/plain");
3525
3656
  res.status(500);
3526
3657
  originalSend.call(instance, errorString);
@@ -3529,7 +3660,7 @@ ${res.locals.errorMessage}`;
3529
3660
  } else {
3530
3661
  let data2 = "";
3531
3662
  for (const [key, value] of Object.entries(chunk)) {
3532
- data2 += `${key}: ${typeof value === "string" ? value : safeStringify4(value)}
3663
+ data2 += `${key}: ${typeof value === "string" ? value : safeStringify5(value)}
3533
3664
  `;
3534
3665
  }
3535
3666
  data2 += "\n";
@@ -3540,7 +3671,7 @@ ${res.locals.errorMessage}`;
3540
3671
  } else if (!errorSent) {
3541
3672
  let data2 = "";
3542
3673
  for (const [key, value] of Object.entries(chunk)) {
3543
- data2 += `${key}: ${typeof value === "string" ? value : safeStringify4(value)}
3674
+ data2 += `${key}: ${typeof value === "string" ? value : safeStringify5(value)}
3544
3675
  `;
3545
3676
  }
3546
3677
  data2 += "\n";
@@ -3593,7 +3724,7 @@ ${res.locals.errorMessage}`;
3593
3724
  ------------------
3594
3725
  ${res.locals.errorMessage}`;
3595
3726
  }
3596
- req.openTelemetryCollector.error(errorString);
3727
+ req.openTelemetryCollector?.error(errorString);
3597
3728
  res.type("text/plain");
3598
3729
  res.status(500);
3599
3730
  originalSend.call(instance, errorString);
@@ -3616,7 +3747,7 @@ ${res.locals.errorMessage}`;
3616
3747
 
3617
3748
  // src/http/openApiV3Generator/openApiV3Generator.ts
3618
3749
  import { openApiCompliantPath } from "@forklaunch/common";
3619
- var OPENAPI_DEFAULT_VERSION = Symbol("default");
3750
+ var OPENAPI_DEFAULT_VERSION = /* @__PURE__ */ Symbol("default");
3620
3751
  function toUpperCase(str) {
3621
3752
  return str.charAt(0).toUpperCase() + str.slice(1);
3622
3753
  }
@@ -3964,6 +4095,7 @@ function metricsDefinitions(metrics2) {
3964
4095
  }
3965
4096
  export {
3966
4097
  ATTR_API_NAME,
4098
+ ATTR_APPLICATION_ID,
3967
4099
  ATTR_CORRELATION_ID,
3968
4100
  ATTR_HTTP_REQUEST_METHOD,
3969
4101
  ATTR_HTTP_RESPONSE_STATUS_CODE,
@@ -3975,6 +4107,7 @@ export {
3975
4107
  OPENAPI_DEFAULT_VERSION,
3976
4108
  OpenTelemetryCollector,
3977
4109
  PinoLogger,
4110
+ createContext,
3978
4111
  createHmacToken,
3979
4112
  delete_,
3980
4113
  discriminateAuthMethod,
@@ -3982,6 +4115,8 @@ export {
3982
4115
  discriminateResponseBodies,
3983
4116
  enrichExpressLikeSend,
3984
4117
  evaluateTelemetryOptions,
4118
+ extractRouteHandlers,
4119
+ generateHmacAuthHeaders,
3985
4120
  generateMcpServer,
3986
4121
  generateOpenApiSpecs,
3987
4122
  get,