@forklaunch/core 0.11.7 → 0.12.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.
@@ -1,6 +1,6 @@
1
1
  import { ParsedQs } from 'qs';
2
2
  export { ParsedQs } from 'qs';
3
- import { UnionToIntersection, StringWithoutSlash, Prettify, SanitizePathSlashes, MakePropertyOptionalIfChildrenOptional, PrettyCamelCase, TypeSafeFunction, EmptyObject } from '@forklaunch/common';
3
+ import { UnionToIntersection, TypeSafeFunction, StringWithoutSlash, Prettify, SanitizePathSlashes, MakePropertyOptionalIfChildrenOptional, PrettyCamelCase, EmptyObject } from '@forklaunch/common';
4
4
  import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
5
5
  import { CorsOptions } from 'cors';
6
6
  import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
@@ -224,7 +224,7 @@ type TypedRequestBody<SV extends AnySchemaValidator> = {
224
224
  [K in keyof (ExclusiveSchemaCatchall<SV> & ExclusiveRequestBodyBase<SV>)]?: K extends keyof UnknownBody<SV> ? UnknownBody<SV>[K] : undefined;
225
225
  };
226
226
  type TypedBody<SV extends AnySchemaValidator> = JsonBody<SV> | TextBody<SV> | FileBody<SV> | MultipartForm<SV> | UrlEncodedForm<SV> | UnknownBody<SV>;
227
- type Body<SV extends AnySchemaValidator> = TypedRequestBody<SV> | (ExclusiveRequestBodyBase<SV> & SV['_ValidSchemaObject']) | (ExclusiveRequestBodyBase<SV> & UnboxedObjectSchema<SV>) | (ExclusiveRequestBodyBase<SV> & SV['string']) | (ExclusiveRequestBodyBase<SV> & SV['number']) | (ExclusiveRequestBodyBase<SV> & SV['boolean']) | (ExclusiveRequestBodyBase<SV> & SV['date']) | (ExclusiveRequestBodyBase<SV> & SV['array']) | (ExclusiveRequestBodyBase<SV> & SV['file']);
227
+ type Body<SV extends AnySchemaValidator> = TypedRequestBody<SV> | (ExclusiveRequestBodyBase<SV> & SV['_ValidSchemaObject']) | (ExclusiveRequestBodyBase<SV> & UnboxedObjectSchema<SV>) | (ExclusiveRequestBodyBase<SV> & SV['string']) | (ExclusiveRequestBodyBase<SV> & SV['number']) | (ExclusiveRequestBodyBase<SV> & SV['boolean']) | (ExclusiveRequestBodyBase<SV> & SV['date']) | (ExclusiveRequestBodyBase<SV> & SV['array']) | (ExclusiveRequestBodyBase<SV> & SV['file']) | (ExclusiveRequestBodyBase<SV> & SV['any']) | (ExclusiveRequestBodyBase<SV> & SV['unknown']) | (ExclusiveRequestBodyBase<SV> & SV['binary']) | (ExclusiveRequestBodyBase<SV> & (SV['type'] extends TypeSafeFunction ? ReturnType<SV['type']> : never));
228
228
  type BasicAuthMethods = {
229
229
  readonly basic: {
230
230
  readonly login: (username: string, password: string) => boolean;
@@ -1661,7 +1661,7 @@ declare const getCodeForStatus: (status: string) => null | StatusCode;
1661
1661
  * @param {ForklaunchRouter<SV>[]} routers - The routers to include in the server.
1662
1662
  * @returns {McpServer} - The generated ModelContextProtocol server.
1663
1663
  */
1664
- declare function generateMcpServer<T extends Record<string, unknown> | undefined = Record<string, unknown> | undefined>(schemaValidator: ZodSchemaValidator, protocol: 'http' | 'https', host: string, port: number, version: `${number}.${number}.${number}`, routers: ForklaunchRouter<ZodSchemaValidator>[], options?: ConstructorParameters<typeof FastMCP<T>>[0], contentTypeMap?: Record<string, string>): FastMCP<T>;
1664
+ declare function generateMcpServer<T extends Record<string, unknown> | undefined = Record<string, unknown> | undefined>(schemaValidator: ZodSchemaValidator, protocol: 'http' | 'https', host: string, port: number, version: `${number}.${number}.${number}`, application: ForklaunchRouter<ZodSchemaValidator>, options?: ConstructorParameters<typeof FastMCP<T>>[0], contentTypeMap?: Record<string, string>): FastMCP<T>;
1665
1665
 
1666
1666
  /**
1667
1667
  * Enhances the Express-like `send` method with additional logic for response handling and validation.
@@ -1701,7 +1701,7 @@ declare const OPENAPI_DEFAULT_VERSION: unique symbol;
1701
1701
  * @param {ForklaunchRouter<SV>[]} routers - The routers to include in the Swagger document.
1702
1702
  * @returns {OpenAPIObject} - The generated Swagger document.
1703
1703
  */
1704
- declare function generateOpenApiSpecs<SV extends AnySchemaValidator>(schemaValidator: SV, protocol: 'http' | 'https', host: string, port: string | number, routers: ForklaunchRouter<SV>[], otherServers?: {
1704
+ declare function generateOpenApiSpecs<SV extends AnySchemaValidator>(schemaValidator: SV, serverUrls: string[], serverDescriptions: string[], application: ForklaunchRouter<SV>, otherServers?: {
1705
1705
  url: string;
1706
1706
  description: string;
1707
1707
  }[]): Record<string | symbol, OpenAPIObject>;
@@ -1,6 +1,6 @@
1
1
  import { ParsedQs } from 'qs';
2
2
  export { ParsedQs } from 'qs';
3
- import { UnionToIntersection, StringWithoutSlash, Prettify, SanitizePathSlashes, MakePropertyOptionalIfChildrenOptional, PrettyCamelCase, TypeSafeFunction, EmptyObject } from '@forklaunch/common';
3
+ import { UnionToIntersection, TypeSafeFunction, StringWithoutSlash, Prettify, SanitizePathSlashes, MakePropertyOptionalIfChildrenOptional, PrettyCamelCase, EmptyObject } from '@forklaunch/common';
4
4
  import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
5
5
  import { CorsOptions } from 'cors';
6
6
  import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
@@ -224,7 +224,7 @@ type TypedRequestBody<SV extends AnySchemaValidator> = {
224
224
  [K in keyof (ExclusiveSchemaCatchall<SV> & ExclusiveRequestBodyBase<SV>)]?: K extends keyof UnknownBody<SV> ? UnknownBody<SV>[K] : undefined;
225
225
  };
226
226
  type TypedBody<SV extends AnySchemaValidator> = JsonBody<SV> | TextBody<SV> | FileBody<SV> | MultipartForm<SV> | UrlEncodedForm<SV> | UnknownBody<SV>;
227
- type Body<SV extends AnySchemaValidator> = TypedRequestBody<SV> | (ExclusiveRequestBodyBase<SV> & SV['_ValidSchemaObject']) | (ExclusiveRequestBodyBase<SV> & UnboxedObjectSchema<SV>) | (ExclusiveRequestBodyBase<SV> & SV['string']) | (ExclusiveRequestBodyBase<SV> & SV['number']) | (ExclusiveRequestBodyBase<SV> & SV['boolean']) | (ExclusiveRequestBodyBase<SV> & SV['date']) | (ExclusiveRequestBodyBase<SV> & SV['array']) | (ExclusiveRequestBodyBase<SV> & SV['file']);
227
+ type Body<SV extends AnySchemaValidator> = TypedRequestBody<SV> | (ExclusiveRequestBodyBase<SV> & SV['_ValidSchemaObject']) | (ExclusiveRequestBodyBase<SV> & UnboxedObjectSchema<SV>) | (ExclusiveRequestBodyBase<SV> & SV['string']) | (ExclusiveRequestBodyBase<SV> & SV['number']) | (ExclusiveRequestBodyBase<SV> & SV['boolean']) | (ExclusiveRequestBodyBase<SV> & SV['date']) | (ExclusiveRequestBodyBase<SV> & SV['array']) | (ExclusiveRequestBodyBase<SV> & SV['file']) | (ExclusiveRequestBodyBase<SV> & SV['any']) | (ExclusiveRequestBodyBase<SV> & SV['unknown']) | (ExclusiveRequestBodyBase<SV> & SV['binary']) | (ExclusiveRequestBodyBase<SV> & (SV['type'] extends TypeSafeFunction ? ReturnType<SV['type']> : never));
228
228
  type BasicAuthMethods = {
229
229
  readonly basic: {
230
230
  readonly login: (username: string, password: string) => boolean;
@@ -1661,7 +1661,7 @@ declare const getCodeForStatus: (status: string) => null | StatusCode;
1661
1661
  * @param {ForklaunchRouter<SV>[]} routers - The routers to include in the server.
1662
1662
  * @returns {McpServer} - The generated ModelContextProtocol server.
1663
1663
  */
1664
- declare function generateMcpServer<T extends Record<string, unknown> | undefined = Record<string, unknown> | undefined>(schemaValidator: ZodSchemaValidator, protocol: 'http' | 'https', host: string, port: number, version: `${number}.${number}.${number}`, routers: ForklaunchRouter<ZodSchemaValidator>[], options?: ConstructorParameters<typeof FastMCP<T>>[0], contentTypeMap?: Record<string, string>): FastMCP<T>;
1664
+ declare function generateMcpServer<T extends Record<string, unknown> | undefined = Record<string, unknown> | undefined>(schemaValidator: ZodSchemaValidator, protocol: 'http' | 'https', host: string, port: number, version: `${number}.${number}.${number}`, application: ForklaunchRouter<ZodSchemaValidator>, options?: ConstructorParameters<typeof FastMCP<T>>[0], contentTypeMap?: Record<string, string>): FastMCP<T>;
1665
1665
 
1666
1666
  /**
1667
1667
  * Enhances the Express-like `send` method with additional logic for response handling and validation.
@@ -1701,7 +1701,7 @@ declare const OPENAPI_DEFAULT_VERSION: unique symbol;
1701
1701
  * @param {ForklaunchRouter<SV>[]} routers - The routers to include in the Swagger document.
1702
1702
  * @returns {OpenAPIObject} - The generated Swagger document.
1703
1703
  */
1704
- declare function generateOpenApiSpecs<SV extends AnySchemaValidator>(schemaValidator: SV, protocol: 'http' | 'https', host: string, port: string | number, routers: ForklaunchRouter<SV>[], otherServers?: {
1704
+ declare function generateOpenApiSpecs<SV extends AnySchemaValidator>(schemaValidator: SV, serverUrls: string[], serverDescriptions: string[], application: ForklaunchRouter<SV>, otherServers?: {
1705
1705
  url: string;
1706
1706
  description: string;
1707
1707
  }[]): Record<string | symbol, OpenAPIObject>;
package/lib/http/index.js CHANGED
@@ -100,7 +100,7 @@ function cors(corsOptions) {
100
100
  }
101
101
 
102
102
  // src/http/router/expressLikeRouter.ts
103
- var import_common7 = require("@forklaunch/common");
103
+ var import_common8 = require("@forklaunch/common");
104
104
 
105
105
  // src/http/guards/hasVersionedSchema.ts
106
106
  function hasVersionedSchema(contractDetails) {
@@ -332,6 +332,7 @@ async function parseRequestAuth(req, res, next) {
332
332
  }
333
333
 
334
334
  // src/http/middleware/request/createContext.middleware.ts
335
+ var import_common3 = require("@forklaunch/common");
335
336
  var import_api = require("@opentelemetry/api");
336
337
  var import_uuid = require("uuid");
337
338
 
@@ -352,20 +353,24 @@ function createContext(schemaValidator) {
352
353
  req.context = {
353
354
  correlationId
354
355
  };
355
- const span = import_api.trace.getActiveSpan();
356
+ const span = import_api.trace.getSpan(import_api.context.active());
356
357
  if (span != null) {
357
358
  req.context.span = span;
358
359
  req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);
360
+ req.context.span?.setAttribute(
361
+ import_semantic_conventions.ATTR_SERVICE_NAME,
362
+ (0, import_common3.getEnvVar)("OTEL_SERVICE_NAME")
363
+ );
359
364
  }
360
365
  next?.();
361
366
  };
362
367
  }
363
368
 
364
369
  // src/http/middleware/request/enrichDetails.middleware.ts
365
- var import_common5 = require("@forklaunch/common");
370
+ var import_common6 = require("@forklaunch/common");
366
371
 
367
372
  // src/http/telemetry/openTelemetryCollector.ts
368
- var import_common4 = require("@forklaunch/common");
373
+ var import_common5 = require("@forklaunch/common");
369
374
  var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
370
375
  var import_api3 = require("@opentelemetry/api");
371
376
  var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
@@ -387,7 +392,7 @@ function isForklaunchRequest(request) {
387
392
  }
388
393
 
389
394
  // src/http/telemetry/pinoLogger.ts
390
- var import_common3 = require("@forklaunch/common");
395
+ var import_common4 = require("@forklaunch/common");
391
396
  var import_api2 = require("@opentelemetry/api");
392
397
  var import_api_logs = require("@opentelemetry/api-logs");
393
398
  var import_pino = __toESM(require("pino"));
@@ -419,10 +424,25 @@ function mapSeverity(level) {
419
424
  case "fatal":
420
425
  return 21;
421
426
  default:
422
- (0, import_common3.isNever)(level);
427
+ (0, import_common4.isNever)(level);
423
428
  return 0;
424
429
  }
425
430
  }
431
+ function normalizeLogArgs(args) {
432
+ let message = "";
433
+ const metaObjects = [];
434
+ for (const arg of args) {
435
+ if (typeof arg === "string" && message === "") {
436
+ message = arg;
437
+ } else if (arg && typeof arg === "object" && !Array.isArray(arg)) {
438
+ metaObjects.push(arg);
439
+ } else {
440
+ message += ` ${String(arg)}`;
441
+ }
442
+ }
443
+ const metadata = Object.assign({}, ...metaObjects);
444
+ return [metadata, message.trim()];
445
+ }
426
446
  var PinoLogger = class _PinoLogger {
427
447
  pinoLogger;
428
448
  meta;
@@ -453,7 +473,7 @@ var PinoLogger = class _PinoLogger {
453
473
  return false;
454
474
  }
455
475
  return true;
456
- }).map(import_common3.safeStringify);
476
+ });
457
477
  const activeSpan = import_api2.trace.getActiveSpan();
458
478
  if (activeSpan) {
459
479
  const activeSpanContext = activeSpan.spanContext();
@@ -471,7 +491,7 @@ var PinoLogger = class _PinoLogger {
471
491
  "correlation.id": "none",
472
492
  ...meta2
473
493
  };
474
- this.pinoLogger[level](...filteredArgs);
494
+ this.pinoLogger[level](...normalizeLogArgs(filteredArgs));
475
495
  import_api_logs.logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
476
496
  severityText: level,
477
497
  severityNumber: mapSeverity(level),
@@ -559,24 +579,24 @@ var OpenTelemetryCollector = class {
559
579
  return this.metrics[metricId];
560
580
  }
561
581
  };
562
- import_dotenv.default.config({ path: (0, import_common4.getEnvVar)("DOTENV_FILE_PATH") });
582
+ import_dotenv.default.config({ path: (0, import_common5.getEnvVar)("DOTENV_FILE_PATH") });
563
583
  new import_sdk_node.NodeSDK({
564
584
  resource: (0, import_resources.resourceFromAttributes)({
565
- [import_semantic_conventions2.ATTR_SERVICE_NAME]: (0, import_common4.getEnvVar)("OTEL_SERVICE_NAME")
585
+ [import_semantic_conventions2.ATTR_SERVICE_NAME]: (0, import_common5.getEnvVar)("OTEL_SERVICE_NAME")
566
586
  }),
567
587
  traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
568
- url: `${(0, import_common4.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
588
+ url: `${(0, import_common5.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
569
589
  }),
570
590
  metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
571
591
  exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
572
- url: `${(0, import_common4.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
592
+ url: `${(0, import_common5.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
573
593
  }),
574
594
  exportIntervalMillis: 5e3
575
595
  }),
576
596
  logRecordProcessors: [
577
597
  new import_sdk_logs.BatchLogRecordProcessor(
578
598
  new import_exporter_logs_otlp_http.OTLPLogExporter({
579
- url: `${(0, import_common4.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
599
+ url: `${(0, import_common5.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
580
600
  })
581
601
  )
582
602
  ],
@@ -584,11 +604,12 @@ new import_sdk_node.NodeSDK({
584
604
  new import_instrumentation_http.HttpInstrumentation({
585
605
  applyCustomAttributesOnSpan: (span, request) => {
586
606
  span.setAttribute(
587
- "service.name",
588
- (0, import_common4.getEnvVar)("OTEL_SERVICE_NAME") ?? "unknown"
607
+ import_semantic_conventions2.ATTR_SERVICE_NAME,
608
+ (0, import_common5.getEnvVar)("OTEL_SERVICE_NAME") ?? "unknown"
589
609
  );
590
610
  if (isForklaunchRequest(request)) {
591
- span.setAttribute("api.name", request.contractDetails?.name);
611
+ span.setAttribute(ATTR_API_NAME, request.contractDetails?.name);
612
+ span.setAttribute(ATTR_CORRELATION_ID, request.context.correlationId);
592
613
  }
593
614
  }
594
615
  }),
@@ -596,10 +617,10 @@ new import_sdk_node.NodeSDK({
596
617
  new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
597
618
  ]
598
619
  }).start();
599
- var httpRequestsTotalCounter = import_api3.metrics.getMeter((0, import_common4.getEnvVar)("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
620
+ var httpRequestsTotalCounter = import_api3.metrics.getMeter((0, import_common5.getEnvVar)("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
600
621
  description: "Number of HTTP requests"
601
622
  });
602
- var httpServerDurationHistogram = import_api3.metrics.getMeter((0, import_common4.getEnvVar)("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
623
+ var httpServerDurationHistogram = import_api3.metrics.getMeter((0, import_common5.getEnvVar)("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
603
624
  description: "Duration of HTTP server requests",
604
625
  unit: "s"
605
626
  });
@@ -618,7 +639,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
618
639
  const [seconds, nanoseconds] = process.hrtime(startTime);
619
640
  const durationMs = seconds + nanoseconds / 1e9;
620
641
  httpServerDurationHistogram.record(durationMs, {
621
- [import_semantic_conventions.ATTR_SERVICE_NAME]: (0, import_common5.getEnvVar)("OTEL_SERVICE_NAME") || "unknown",
642
+ [import_semantic_conventions.ATTR_SERVICE_NAME]: (0, import_common6.getEnvVar)("OTEL_SERVICE_NAME") || "unknown",
622
643
  [ATTR_API_NAME]: req.contractDetails?.name || "unknown",
623
644
  [import_semantic_conventions.ATTR_HTTP_REQUEST_METHOD]: req.method,
624
645
  [import_semantic_conventions.ATTR_HTTP_ROUTE]: req.originalPath || "unknown",
@@ -630,7 +651,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
630
651
  }
631
652
 
632
653
  // src/http/middleware/request/parse.middleware.ts
633
- var import_common6 = require("@forklaunch/common");
654
+ var import_common7 = require("@forklaunch/common");
634
655
  var import_validator = require("@forklaunch/validator");
635
656
 
636
657
  // src/http/guards/hasSend.ts
@@ -656,7 +677,7 @@ function parse(req, res, next) {
656
677
  let parsedRequest;
657
678
  let collectedParseErrors;
658
679
  if (req.contractDetails.versions) {
659
- if ((0, import_common6.isRecord)(req.requestSchema)) {
680
+ if ((0, import_common7.isRecord)(req.requestSchema)) {
660
681
  let runningParseErrors = "";
661
682
  matchedVersions = [];
662
683
  Object.entries(req.requestSchema).forEach(([version, schema]) => {
@@ -1045,13 +1066,13 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1045
1066
  versionedContractDetails,
1046
1067
  contractDetails.params
1047
1068
  );
1048
- if ((0, import_common7.isRecord)(requestSchema)) {
1069
+ if ((0, import_common8.isRecord)(requestSchema)) {
1049
1070
  requestSchema = {
1050
1071
  ...requestSchema,
1051
1072
  [version]: versionedRequestSchema
1052
1073
  };
1053
1074
  }
1054
- if ((0, import_common7.isRecord)(responseSchemas)) {
1075
+ if ((0, import_common8.isRecord)(responseSchemas)) {
1055
1076
  responseSchemas = {
1056
1077
  ...responseSchemas,
1057
1078
  [version]: versionedResponseSchemas
@@ -1238,8 +1259,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1238
1259
  ...resolvedMiddlewares,
1239
1260
  this.#parseAndRunControllerHandler(controllerHandler)
1240
1261
  );
1241
- (0, import_common7.toRecord)(this._fetchMap)[(0, import_common7.sanitizePathSlashes)(`${this.basePath}${path}`)] = {
1242
- ...this._fetchMap[(0, import_common7.sanitizePathSlashes)(`${this.basePath}${path}`)] ?? {},
1262
+ (0, import_common8.toRecord)(this._fetchMap)[(0, import_common8.sanitizePathSlashes)(`${this.basePath}${path}`)] = {
1263
+ ...this._fetchMap[(0, import_common8.sanitizePathSlashes)(`${this.basePath}${path}`)] ?? {},
1243
1264
  [method.toUpperCase()]: contractDetails.versions ? Object.fromEntries(
1244
1265
  Object.keys(contractDetails.versions).map((version) => [
1245
1266
  version,
@@ -1247,7 +1268,7 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1247
1268
  ])
1248
1269
  ) : this.#localParamRequest(handlers, controllerHandler)
1249
1270
  };
1250
- (0, import_common7.toRecord)(this.sdk)[(0, import_common7.toPrettyCamelCase)(contractDetails.name)] = contractDetails.versions ? Object.fromEntries(
1271
+ (0, import_common8.toRecord)(this.sdk)[(0, import_common8.toPrettyCamelCase)(contractDetails.name)] = contractDetails.versions ? Object.fromEntries(
1251
1272
  Object.keys(contractDetails.versions).map((version) => [
1252
1273
  version,
1253
1274
  (req) => this.#localParamRequest(
@@ -1346,10 +1367,10 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1346
1367
  }
1347
1368
  addRouterToSdk(router) {
1348
1369
  Object.entries(router._fetchMap).map(
1349
- ([key, value]) => (0, import_common7.toRecord)(this._fetchMap)[(0, import_common7.sanitizePathSlashes)(`${this.basePath}${key}`)] = value
1370
+ ([key, value]) => (0, import_common8.toRecord)(this._fetchMap)[(0, import_common8.sanitizePathSlashes)(`${this.basePath}${key}`)] = value
1350
1371
  );
1351
- const existingSdk = this.sdk[router.sdkName ?? (0, import_common7.toPrettyCamelCase)(router.basePath)];
1352
- (0, import_common7.toRecord)(this.sdk)[router.sdkName ?? (0, import_common7.toPrettyCamelCase)(router.basePath)] = {
1372
+ const existingSdk = this.sdk[router.sdkName ?? (0, import_common8.toPrettyCamelCase)(router.basePath)];
1373
+ (0, import_common8.toRecord)(this.sdk)[router.sdkName ?? (0, import_common8.toPrettyCamelCase)(router.basePath)] = {
1353
1374
  ...typeof existingSdk === "object" ? existingSdk : {},
1354
1375
  ...router.sdk
1355
1376
  };
@@ -2717,7 +2738,7 @@ var getCodeForStatus = (status) => {
2717
2738
  var httpStatusCodes_default = HTTPStatuses;
2718
2739
 
2719
2740
  // src/http/mcpGenerator/mcpGenerator.ts
2720
- var import_common8 = require("@forklaunch/common");
2741
+ var import_common9 = require("@forklaunch/common");
2721
2742
  var import_zod = require("@forklaunch/validator/zod");
2722
2743
  var import_fastmcp = require("fastmcp");
2723
2744
 
@@ -2729,8 +2750,9 @@ function isUnionable(schema) {
2729
2750
  // src/http/router/unpackRouters.ts
2730
2751
  function unpackRouters(routers, recursiveBasePath = []) {
2731
2752
  return routers.reduce((acc, router) => {
2753
+ const fullPath = [...recursiveBasePath, router.basePath].join("");
2732
2754
  acc.push({
2733
- fullPath: [...recursiveBasePath, router.basePath].join(""),
2755
+ fullPath,
2734
2756
  router
2735
2757
  });
2736
2758
  acc.push(
@@ -2768,7 +2790,7 @@ function generateInputSchema(schemaValidator, body, params, query, requestHeader
2768
2790
  } : {}
2769
2791
  });
2770
2792
  }
2771
- function generateMcpServer(schemaValidator, protocol, host, port, version, routers, options2, contentTypeMap) {
2793
+ function generateMcpServer(schemaValidator, protocol, host, port, version, application, options2, contentTypeMap) {
2772
2794
  if (!(schemaValidator instanceof import_zod.ZodSchemaValidator)) {
2773
2795
  throw new Error(
2774
2796
  "Schema validator must be an instance of ZodSchemaValidator"
@@ -2779,7 +2801,15 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
2779
2801
  name: options2?.name ?? "mcp-server",
2780
2802
  version
2781
2803
  });
2782
- unpackRouters(routers).forEach(({ fullPath, router }) => {
2804
+ [
2805
+ {
2806
+ fullPath: application.basePath === "/" ? "" : application.basePath,
2807
+ router: application
2808
+ },
2809
+ ...unpackRouters(application.routers, [
2810
+ application.basePath === "/" ? "" : application.basePath
2811
+ ])
2812
+ ].forEach(({ fullPath, router }) => {
2783
2813
  router.routes.forEach((route) => {
2784
2814
  const inputSchemas = [];
2785
2815
  if (route.contractDetails.versions) {
@@ -2842,7 +2872,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
2842
2872
  if (discriminatedBody) {
2843
2873
  switch (discriminatedBody.parserType) {
2844
2874
  case "json": {
2845
- parsedBody = (0, import_common8.safeStringify)(body);
2875
+ parsedBody = (0, import_common9.safeStringify)(body);
2846
2876
  break;
2847
2877
  }
2848
2878
  case "text": {
@@ -2855,7 +2885,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
2855
2885
  }
2856
2886
  case "multipart": {
2857
2887
  const formData = new FormData();
2858
- if ((0, import_common8.isRecord)(body)) {
2888
+ if ((0, import_common9.isRecord)(body)) {
2859
2889
  for (const key in body) {
2860
2890
  if (typeof body[key] === "string" || body[key] instanceof Blob) {
2861
2891
  formData.append(key, body[key]);
@@ -2870,11 +2900,11 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
2870
2900
  break;
2871
2901
  }
2872
2902
  case "urlEncoded": {
2873
- if ((0, import_common8.isRecord)(body)) {
2903
+ if ((0, import_common9.isRecord)(body)) {
2874
2904
  parsedBody = new URLSearchParams(
2875
2905
  Object.entries(body).map(([key, value]) => [
2876
2906
  key,
2877
- (0, import_common8.safeStringify)(value)
2907
+ (0, import_common9.safeStringify)(value)
2878
2908
  ])
2879
2909
  );
2880
2910
  } else {
@@ -2883,8 +2913,8 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
2883
2913
  break;
2884
2914
  }
2885
2915
  default: {
2886
- (0, import_common8.isNever)(discriminatedBody.parserType);
2887
- parsedBody = (0, import_common8.safeStringify)(body);
2916
+ (0, import_common9.isNever)(discriminatedBody.parserType);
2917
+ parsedBody = (0, import_common9.safeStringify)(body);
2888
2918
  break;
2889
2919
  }
2890
2920
  }
@@ -2893,7 +2923,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
2893
2923
  const queryString = new URLSearchParams(
2894
2924
  Object.entries(query).map(([key, value]) => [
2895
2925
  key,
2896
- (0, import_common8.safeStringify)(value)
2926
+ (0, import_common9.safeStringify)(value)
2897
2927
  ])
2898
2928
  ).toString();
2899
2929
  url += queryString ? `?${queryString}` : "";
@@ -2926,7 +2956,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, route
2926
2956
  content: [
2927
2957
  {
2928
2958
  type: "text",
2929
- text: (0, import_common8.safeStringify)(await response.json())
2959
+ text: (0, import_common9.safeStringify)(await response.json())
2930
2960
  }
2931
2961
  ]
2932
2962
  };
@@ -3017,7 +3047,7 @@ function parse2(req, res, next) {
3017
3047
  }
3018
3048
  const statusCode = Number(res.statusCode);
3019
3049
  const parsedResponse = schemaValidator.parse(
3020
- responses?.[statusCode],
3050
+ [400, 401, 404, 403, 500].includes(statusCode) ? schemaValidator.union([schemaValidator.string, responses?.[statusCode]]) : responses?.[statusCode],
3021
3051
  res.bodyData
3022
3052
  );
3023
3053
  const parsedHeaders = schemaValidator.parse(
@@ -3073,18 +3103,18 @@ ${parseErrors.join("\n\n")}`
3073
3103
  }
3074
3104
 
3075
3105
  // src/http/middleware/response/enrichExpressLikeSend.middleware.ts
3076
- var import_common10 = require("@forklaunch/common");
3106
+ var import_common11 = require("@forklaunch/common");
3077
3107
  var import_stream = require("stream");
3078
3108
 
3079
3109
  // src/http/telemetry/recordMetric.ts
3080
- var import_common9 = require("@forklaunch/common");
3110
+ var import_common10 = require("@forklaunch/common");
3081
3111
  var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
3082
3112
  function recordMetric(req, res) {
3083
3113
  if (res.metricRecorded) {
3084
3114
  return;
3085
3115
  }
3086
3116
  httpRequestsTotalCounter.add(1, {
3087
- [import_semantic_conventions3.ATTR_SERVICE_NAME]: (0, import_common9.getEnvVar)("OTEL_SERVICE_NAME"),
3117
+ [import_semantic_conventions3.ATTR_SERVICE_NAME]: (0, import_common10.getEnvVar)("OTEL_SERVICE_NAME"),
3088
3118
  [ATTR_API_NAME]: req.contractDetails?.name,
3089
3119
  [ATTR_CORRELATION_ID]: req.context.correlationId,
3090
3120
  [import_semantic_conventions3.ATTR_HTTP_REQUEST_METHOD]: req.method,
@@ -3134,8 +3164,8 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
3134
3164
  `attachment; filename="${data.name}"`
3135
3165
  );
3136
3166
  }
3137
- if ((0, import_common10.isNodeJsWriteableStream)(res)) {
3138
- import_stream.Readable.from((0, import_common10.readableStreamToAsyncIterable)(data.stream())).pipe(
3167
+ if ((0, import_common11.isNodeJsWriteableStream)(res)) {
3168
+ import_stream.Readable.from((0, import_common11.readableStreamToAsyncIterable)(data.stream())).pipe(
3139
3169
  res
3140
3170
  );
3141
3171
  } else {
@@ -3144,7 +3174,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
3144
3174
  originalSend.call(instance, "Not a NodeJS WritableStream");
3145
3175
  errorSent = true;
3146
3176
  }
3147
- } else if ((0, import_common10.isAsyncGenerator)(data)) {
3177
+ } else if ((0, import_common11.isAsyncGenerator)(data)) {
3148
3178
  let firstPass = true;
3149
3179
  const transformer = new import_stream.Transform({
3150
3180
  objectMode: true,
@@ -3172,7 +3202,7 @@ ${res.locals.errorMessage}`;
3172
3202
  if (!errorSent) {
3173
3203
  let data2 = "";
3174
3204
  for (const [key, value] of Object.entries(chunk)) {
3175
- data2 += `${key}: ${typeof value === "string" ? value : (0, import_common10.safeStringify)(value)}
3205
+ data2 += `${key}: ${typeof value === "string" ? value : (0, import_common11.safeStringify)(value)}
3176
3206
  `;
3177
3207
  }
3178
3208
  data2 += "\n";
@@ -3180,7 +3210,7 @@ ${res.locals.errorMessage}`;
3180
3210
  }
3181
3211
  }
3182
3212
  });
3183
- if ((0, import_common10.isNodeJsWriteableStream)(res)) {
3213
+ if ((0, import_common11.isNodeJsWriteableStream)(res)) {
3184
3214
  import_stream.Readable.from(data).pipe(transformer).pipe(res);
3185
3215
  } else {
3186
3216
  res.type("text/plain");
@@ -3191,7 +3221,7 @@ ${res.locals.errorMessage}`;
3191
3221
  } else {
3192
3222
  const parserType = responseBodies?.[Number(res.statusCode)]?.parserType;
3193
3223
  res.bodyData = data;
3194
- if ((0, import_common10.isRecord)(data)) {
3224
+ if ((0, import_common11.isRecord)(data)) {
3195
3225
  switch (parserType) {
3196
3226
  case "json":
3197
3227
  res.bodyData = "json" in data ? data.json : data;
@@ -3212,7 +3242,7 @@ ${res.locals.errorMessage}`;
3212
3242
  res.bodyData = data;
3213
3243
  break;
3214
3244
  default:
3215
- (0, import_common10.isNever)(parserType);
3245
+ (0, import_common11.isNever)(parserType);
3216
3246
  res.bodyData = data;
3217
3247
  break;
3218
3248
  }
@@ -3248,7 +3278,7 @@ ${res.locals.errorMessage}`;
3248
3278
  }
3249
3279
 
3250
3280
  // src/http/openApiV3Generator/openApiV3Generator.ts
3251
- var import_common11 = require("@forklaunch/common");
3281
+ var import_common12 = require("@forklaunch/common");
3252
3282
  var OPENAPI_DEFAULT_VERSION = Symbol("default");
3253
3283
  function toUpperCase(str) {
3254
3284
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -3259,7 +3289,7 @@ function transformBasePath(basePath) {
3259
3289
  }
3260
3290
  return `/${basePath}`;
3261
3291
  }
3262
- function generateOpenApiDocument(protocol, host, port, versionedTags, versionedPaths, versionedSecuritySchemes, otherServers) {
3292
+ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags, versionedPaths, versionedSecuritySchemes, otherServers) {
3263
3293
  return {
3264
3294
  [OPENAPI_DEFAULT_VERSION]: {
3265
3295
  openapi: "3.1.0",
@@ -3272,10 +3302,10 @@ function generateOpenApiDocument(protocol, host, port, versionedTags, versionedP
3272
3302
  },
3273
3303
  tags: versionedTags[OPENAPI_DEFAULT_VERSION],
3274
3304
  servers: [
3275
- {
3276
- url: `${protocol}://${host}:${port}`,
3277
- description: "Main Server"
3278
- },
3305
+ ...serverUrls.map((url, index) => ({
3306
+ url,
3307
+ description: serverDescriptions?.[index]
3308
+ })),
3279
3309
  ...otherServers || []
3280
3310
  ],
3281
3311
  paths: versionedPaths[OPENAPI_DEFAULT_VERSION]
@@ -3294,10 +3324,11 @@ function generateOpenApiDocument(protocol, host, port, versionedTags, versionedP
3294
3324
  },
3295
3325
  tags: versionedTags[version],
3296
3326
  servers: [
3297
- {
3298
- url: `${protocol}://${host}:${port}`,
3299
- description: "Main Server"
3300
- }
3327
+ ...serverUrls.map((url, index) => ({
3328
+ url,
3329
+ description: serverDescriptions?.[index]
3330
+ })),
3331
+ ...otherServers || []
3301
3332
  ],
3302
3333
  paths
3303
3334
  }
@@ -3446,7 +3477,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
3446
3477
  }
3447
3478
  return operationObject;
3448
3479
  }
3449
- function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, otherServers) {
3480
+ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, application, otherServers) {
3450
3481
  const versionedPaths = {
3451
3482
  [OPENAPI_DEFAULT_VERSION]: {}
3452
3483
  };
@@ -3456,10 +3487,18 @@ function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, ot
3456
3487
  const versionedSecuritySchemes = {
3457
3488
  [OPENAPI_DEFAULT_VERSION]: {}
3458
3489
  };
3459
- unpackRouters(routers).forEach(({ fullPath, router }) => {
3490
+ [
3491
+ {
3492
+ fullPath: application.basePath === "/" ? "" : application.basePath,
3493
+ router: application
3494
+ },
3495
+ ...unpackRouters(application.routers, [
3496
+ application.basePath === "/" ? "" : application.basePath
3497
+ ])
3498
+ ].forEach(({ fullPath, router }) => {
3460
3499
  const controllerName = transformBasePath(fullPath);
3461
3500
  router.routes.forEach((route) => {
3462
- const openApiPath = (0, import_common11.openApiCompliantPath)(
3501
+ const openApiPath = (0, import_common12.openApiCompliantPath)(
3463
3502
  `${fullPath}${route.path === "/" ? "" : route.path}`
3464
3503
  );
3465
3504
  const { name, summary, params, versions, auth } = route.contractDetails;
@@ -3551,9 +3590,8 @@ function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, ot
3551
3590
  });
3552
3591
  });
3553
3592
  return generateOpenApiDocument(
3554
- protocol,
3555
- host,
3556
- port,
3593
+ serverUrls,
3594
+ serverDescriptions,
3557
3595
  versionedTags,
3558
3596
  versionedPaths,
3559
3597
  versionedSecuritySchemes,
@@ -3562,7 +3600,7 @@ function generateOpenApiSpecs(schemaValidator, protocol, host, port, routers, ot
3562
3600
  }
3563
3601
 
3564
3602
  // src/http/sdk/sdkClient.ts
3565
- var import_common12 = require("@forklaunch/common");
3603
+ var import_common13 = require("@forklaunch/common");
3566
3604
 
3567
3605
  // src/http/guards/isSdkRouter.ts
3568
3606
  function isSdkRouter(value) {
@@ -3574,12 +3612,12 @@ function mapToSdk(schemaValidator, routerMap, runningPath = void 0) {
3574
3612
  const routerUniquenessCache = /* @__PURE__ */ new Set();
3575
3613
  return Object.fromEntries(
3576
3614
  Object.entries(routerMap).map(([key, value]) => {
3577
- if (routerUniquenessCache.has((0, import_common12.hashString)((0, import_common12.safeStringify)(value)))) {
3615
+ if (routerUniquenessCache.has((0, import_common13.hashString)((0, import_common13.safeStringify)(value)))) {
3578
3616
  throw new Error(
3579
3617
  `SdkClient: Cannot use the same router pointer twice. Please clone the duplicate router with .clone() or only use the router once.`
3580
3618
  );
3581
3619
  }
3582
- routerUniquenessCache.add((0, import_common12.hashString)((0, import_common12.safeStringify)(value)));
3620
+ routerUniquenessCache.add((0, import_common13.hashString)((0, import_common13.safeStringify)(value)));
3583
3621
  const currentPath = runningPath ? [runningPath, key].join(".") : key;
3584
3622
  if (isSdkRouter(value)) {
3585
3623
  Object.entries(value.sdkPaths).forEach(([routePath, sdkKey]) => {
@@ -3628,7 +3666,7 @@ function mapToFetch(schemaValidator, routerMap) {
3628
3666
  return (path, ...reqInit) => {
3629
3667
  const method = reqInit[0]?.method;
3630
3668
  const version = reqInit[0] != null && "version" in reqInit[0] ? reqInit[0].version : void 0;
3631
- return (version ? (0, import_common12.toRecord)((0, import_common12.toRecord)(flattenedFetchMap[path])[method ?? "GET"])[version] : (0, import_common12.toRecord)(flattenedFetchMap[path])[method ?? "GET"])(path, reqInit[0]);
3669
+ 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]);
3632
3670
  };
3633
3671
  }
3634
3672
  function sdkClient(schemaValidator, routerMap) {
@@ -3640,7 +3678,7 @@ function sdkClient(schemaValidator, routerMap) {
3640
3678
  }
3641
3679
 
3642
3680
  // src/http/sdk/sdkRouter.ts
3643
- var import_common13 = require("@forklaunch/common");
3681
+ var import_common14 = require("@forklaunch/common");
3644
3682
  function sdkRouter(schemaValidator, controller, router) {
3645
3683
  const controllerSdkPaths = [];
3646
3684
  const mappedSdk = Object.fromEntries(
@@ -3650,7 +3688,7 @@ function sdkRouter(schemaValidator, controller, router) {
3650
3688
  router.sdkPaths[sdkPath] = key;
3651
3689
  return [
3652
3690
  key,
3653
- router.sdk[(0, import_common13.toPrettyCamelCase)(value.contractDetails.name)]
3691
+ router.sdk[(0, import_common14.toPrettyCamelCase)(value.contractDetails.name)]
3654
3692
  ];
3655
3693
  })
3656
3694
  );