@forklaunch/core 0.12.3 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -81,10 +81,10 @@ function isTypedHandler(maybeTypedHandler) {
81
81
 
82
82
  // src/http/middleware/request/auth.middleware.ts
83
83
  import { isNever } from "@forklaunch/common";
84
- import { jwtVerify } from "jose";
85
84
 
86
85
  // src/http/discriminateAuthMethod.ts
87
- function discriminateAuthMethod(auth) {
86
+ import { jwtVerify } from "jose";
87
+ async function discriminateAuthMethod(auth) {
88
88
  if ("basic" in auth) {
89
89
  return {
90
90
  type: "basic",
@@ -93,18 +93,59 @@ function discriminateAuthMethod(auth) {
93
93
  login: auth.basic.login
94
94
  }
95
95
  };
96
- } else if ("jwt" in auth) {
96
+ } else if ("jwt" in auth && auth.jwt != null) {
97
+ const jwt = auth.jwt;
98
+ let verificationFunction;
99
+ if ("privateKey" in jwt) {
100
+ verificationFunction = async (token) => {
101
+ const { payload } = await jwtVerify(token, Buffer.from(jwt.privateKey));
102
+ return payload;
103
+ };
104
+ } else {
105
+ let jwks;
106
+ if ("jwksPublicKeyUrl" in jwt) {
107
+ const jwksResponse = await fetch(jwt.jwksPublicKeyUrl);
108
+ jwks = (await jwksResponse.json()).keys;
109
+ } else {
110
+ jwks = [jwt.jwksPublicKey];
111
+ }
112
+ verificationFunction = async (token) => {
113
+ for (const key of jwks) {
114
+ try {
115
+ const { payload } = await jwtVerify(token, key);
116
+ return payload;
117
+ } catch {
118
+ continue;
119
+ }
120
+ }
121
+ };
122
+ }
97
123
  return {
98
124
  type: "jwt",
99
125
  auth: {
100
- decodeResource: auth.decodeResource
126
+ decodeResource: auth.decodeResource,
127
+ verificationFunction
128
+ }
129
+ };
130
+ } else if ("secretKey" in auth) {
131
+ return {
132
+ type: "system",
133
+ auth: {
134
+ secretKey: auth.secretKey
101
135
  }
102
136
  };
103
137
  } else {
104
138
  return {
105
139
  type: "jwt",
106
140
  auth: {
107
- decodeResource: auth.decodeResource
141
+ decodeResource: auth.decodeResource,
142
+ verificationFunction: async (token) => {
143
+ const { payload } = await jwtVerify(
144
+ token,
145
+ Buffer.from(process.env.JWT_SECRET_KEY)
146
+ );
147
+ return payload;
148
+ }
108
149
  }
109
150
  };
110
151
  }
@@ -112,12 +153,22 @@ function discriminateAuthMethod(auth) {
112
153
 
113
154
  // src/http/guards/hasPermissionChecks.ts
114
155
  function hasPermissionChecks(maybePermissionedAuth) {
115
- return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "mapPermissions" in maybePermissionedAuth && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
156
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
116
157
  }
117
158
 
118
159
  // src/http/guards/hasRoleChecks.ts
119
160
  function hasRoleChecks(maybeRoledAuth) {
120
- return typeof maybeRoledAuth === "object" && maybeRoledAuth !== null && "mapRoles" in maybeRoledAuth && ("allowedRoles" in maybeRoledAuth || "forbiddenRoles" in maybeRoledAuth);
161
+ return typeof maybeRoledAuth === "object" && maybeRoledAuth !== null && ("allowedRoles" in maybeRoledAuth || "forbiddenRoles" in maybeRoledAuth);
162
+ }
163
+
164
+ // src/http/guards/hasScopeChecks.ts
165
+ function hasScopeChecks(maybePermissionedAuth) {
166
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
167
+ }
168
+
169
+ // src/http/guards/isSystemAuthMethod.ts
170
+ function isSystemAuthMethod(maybeSystemAuthMethod) {
171
+ return typeof maybeSystemAuthMethod === "object" && maybeSystemAuthMethod !== null && "secretKey" in maybeSystemAuthMethod;
121
172
  }
122
173
 
123
174
  // src/http/middleware/request/auth.middleware.ts
@@ -145,23 +196,45 @@ var invalidAuthorizationLogin = [
145
196
  403,
146
197
  "Invalid Authorization login."
147
198
  ];
148
- async function checkAuthorizationToken(authorizationMethod, authorizationToken, req) {
199
+ var invalidScope = [403, "Invalid scope for operation."];
200
+ var invalidAuthorizationMethod = [
201
+ 401,
202
+ "Invalid Authorization method."
203
+ ];
204
+ var authorizationTokenRequired = [
205
+ 401,
206
+ "Authorization token required."
207
+ ];
208
+ async function checkAuthorizationToken(authorizationMethod, globalOptions, authorizationToken, req) {
209
+ if (authorizationMethod == null) {
210
+ return void 0;
211
+ }
212
+ const collapsedAuthorizationMethod = {
213
+ ...globalOptions,
214
+ ...authorizationMethod
215
+ };
149
216
  if (authorizationToken == null) {
150
- return [401, "No Authorization token provided."];
217
+ return authorizationTokenRequired;
151
218
  }
152
219
  const [tokenPrefix, token] = authorizationToken.split(" ");
153
220
  let resourceId;
154
- const { type, auth } = discriminateAuthMethod(authorizationMethod);
221
+ const { type, auth } = await discriminateAuthMethod(
222
+ collapsedAuthorizationMethod
223
+ );
155
224
  switch (type) {
225
+ case "system": {
226
+ if (token !== auth.secretKey || tokenPrefix !== collapsedAuthorizationMethod.tokenPrefix) {
227
+ return invalidAuthorizationToken;
228
+ }
229
+ resourceId = null;
230
+ break;
231
+ }
156
232
  case "jwt": {
157
- if (tokenPrefix !== (authorizationMethod.tokenPrefix ?? "Bearer")) {
233
+ if (tokenPrefix !== (collapsedAuthorizationMethod.tokenPrefix ?? "Bearer")) {
158
234
  return invalidAuthorizationTokenFormat;
159
235
  }
160
236
  try {
161
- const decodedJwt = await auth?.decodeResource?.(token) ?? (await jwtVerify(
162
- token,
163
- new TextEncoder().encode(process.env.JWT_SECRET)
164
- )).payload;
237
+ const decodedJwt = "decodeResource" in auth && auth.decodeResource ? await auth.decodeResource(token) : "verificationFunction" in auth && auth.verificationFunction ? await auth.verificationFunction(token) : void 0;
165
238
  if (!decodedJwt) {
166
239
  return invalidAuthorizationSubject;
167
240
  }
@@ -173,7 +246,7 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
173
246
  break;
174
247
  }
175
248
  case "basic": {
176
- if (tokenPrefix !== (authorizationMethod.tokenPrefix ?? "Basic")) {
249
+ if (tokenPrefix !== (collapsedAuthorizationMethod.tokenPrefix ?? "Basic")) {
177
250
  return invalidAuthorizationTokenFormat;
178
251
  }
179
252
  if (auth.decodeResource) {
@@ -196,62 +269,82 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
196
269
  isNever(type);
197
270
  return [401, "Invalid Authorization method."];
198
271
  }
199
- if (hasPermissionChecks(authorizationMethod)) {
200
- if (!authorizationMethod.mapPermissions) {
201
- return [500, "No permission mapping function provided."];
272
+ if (isSystemAuthMethod(collapsedAuthorizationMethod) || resourceId == null) {
273
+ return;
274
+ }
275
+ if (hasScopeChecks(collapsedAuthorizationMethod)) {
276
+ if (collapsedAuthorizationMethod.surfaceScopes) {
277
+ const resourceScopes = await collapsedAuthorizationMethod.surfaceScopes(
278
+ resourceId,
279
+ req
280
+ );
281
+ if (collapsedAuthorizationMethod.scopeHeirarchy) {
282
+ if (collapsedAuthorizationMethod.requiredScope) {
283
+ if (!resourceScopes.has(collapsedAuthorizationMethod.requiredScope) || Array.from(resourceScopes).every(
284
+ (scope) => collapsedAuthorizationMethod.scopeHeirarchy?.indexOf(scope) ?? -1 > -1
285
+ )) {
286
+ return invalidScope;
287
+ }
288
+ }
289
+ }
202
290
  }
203
- const resourcePermissions = await authorizationMethod.mapPermissions(
291
+ }
292
+ if (hasPermissionChecks(collapsedAuthorizationMethod)) {
293
+ if (!collapsedAuthorizationMethod.surfacePermissions) {
294
+ return [500, "No permission surfacing function provided."];
295
+ }
296
+ const resourcePermissions = await collapsedAuthorizationMethod.surfacePermissions(
204
297
  resourceId,
205
298
  req
206
299
  );
207
- if ("allowedPermissions" in authorizationMethod && authorizationMethod.allowedPermissions) {
208
- if (resourcePermissions.intersection(authorizationMethod.allowedPermissions).size === 0) {
300
+ if ("allowedPermissions" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedPermissions) {
301
+ if (resourcePermissions.intersection(
302
+ collapsedAuthorizationMethod.allowedPermissions
303
+ ).size === 0) {
209
304
  return invalidAuthorizationTokenPermissions;
210
305
  }
211
306
  }
212
- if ("forbiddenPermissions" in authorizationMethod && authorizationMethod.forbiddenPermissions) {
307
+ if ("forbiddenPermissions" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.forbiddenPermissions) {
213
308
  if (resourcePermissions.intersection(
214
- authorizationMethod.forbiddenPermissions
309
+ collapsedAuthorizationMethod.forbiddenPermissions
215
310
  ).size !== 0) {
216
311
  return invalidAuthorizationTokenPermissions;
217
312
  }
218
313
  }
219
- } else if (hasRoleChecks(authorizationMethod)) {
220
- if (!authorizationMethod.mapRoles) {
221
- return [500, "No role mapping function provided."];
314
+ } else if (hasRoleChecks(collapsedAuthorizationMethod)) {
315
+ if (!collapsedAuthorizationMethod.surfaceRoles) {
316
+ return [500, "No role surfacing function provided."];
222
317
  }
223
- const resourceRoles = await authorizationMethod.mapRoles(
318
+ const resourceRoles = await collapsedAuthorizationMethod.surfaceRoles(
224
319
  resourceId,
225
320
  req
226
321
  );
227
- if ("allowedRoles" in authorizationMethod && authorizationMethod.allowedRoles) {
228
- if (resourceRoles.intersection(authorizationMethod.allowedRoles).size === 0) {
322
+ if ("allowedRoles" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedRoles) {
323
+ if (resourceRoles.intersection(collapsedAuthorizationMethod.allowedRoles).size === 0) {
229
324
  return invalidAuthorizationTokenRoles;
230
325
  }
231
326
  }
232
- if ("forbiddenRoles" in authorizationMethod && authorizationMethod.forbiddenRoles) {
233
- if (resourceRoles.intersection(authorizationMethod.forbiddenRoles).size !== 0) {
327
+ if ("forbiddenRoles" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.forbiddenRoles) {
328
+ if (resourceRoles.intersection(collapsedAuthorizationMethod.forbiddenRoles).size !== 0) {
234
329
  return invalidAuthorizationTokenRoles;
235
330
  }
236
331
  }
237
332
  } else {
238
- return [401, "Invalid Authorization method."];
333
+ return invalidAuthorizationMethod;
239
334
  }
240
335
  }
241
336
  async function parseRequestAuth(req, res, next) {
242
337
  const auth = req.contractDetails.auth;
243
- if (auth) {
244
- const [error, message] = await checkAuthorizationToken(
245
- auth,
246
- req.headers[auth.headerName ?? "Authorization"] || req.headers[auth.headerName ?? "authorization"],
247
- // we can safely cast here because we know that the user will supply resolution for the request
248
- req
249
- ) ?? [];
250
- if (error != null) {
251
- res.type("text/plain");
252
- res.status(error).send(message);
253
- return;
254
- }
338
+ const [error, message] = await checkAuthorizationToken(
339
+ auth,
340
+ req._globalOptions?.auth,
341
+ req.headers[auth?.headerName ?? "Authorization"] || req.headers[auth?.headerName ?? "authorization"],
342
+ req
343
+ ) ?? [];
344
+ if (error != null) {
345
+ res.type("text/plain");
346
+ res.status(error).send(message);
347
+ return;
255
348
  }
256
349
  next?.();
257
350
  }
@@ -330,7 +423,7 @@ import { isNever as isNever2 } from "@forklaunch/common";
330
423
  import { trace as trace2 } from "@opentelemetry/api";
331
424
  import { logs } from "@opentelemetry/api-logs";
332
425
  import pino from "pino";
333
- import PinoPretty from "pino-pretty";
426
+ import * as PinoPretty from "pino-pretty";
334
427
 
335
428
  // src/http/guards/isLoggerMeta.ts
336
429
  function isLoggerMeta(arg) {
@@ -377,11 +470,35 @@ function normalizeLogArgs(args) {
377
470
  const metadata = Object.assign({}, ...metaObjects);
378
471
  return [metadata, message.trim()];
379
472
  }
473
+ function safePrettyFormat(level, args, timestamp) {
474
+ try {
475
+ const [metadata, message] = normalizeLogArgs(args);
476
+ const formattedTimestamp = timestamp || (/* @__PURE__ */ new Date()).toISOString();
477
+ return `[${formattedTimestamp}] ${level.toUpperCase()}: ${message}${Object.keys(metadata).length > 0 ? `
478
+ ${JSON.stringify(metadata, null, 2)}` : ""}`;
479
+ } catch (error) {
480
+ const fallbackMessage = args.map((arg) => {
481
+ try {
482
+ if (typeof arg === "string") return arg;
483
+ if (arg === null) return "null";
484
+ if (arg === void 0) return "undefined";
485
+ return JSON.stringify(arg);
486
+ } catch {
487
+ return "[Circular/Non-serializable Object]";
488
+ }
489
+ }).join(" ");
490
+ return `[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}: ${fallbackMessage} [Pretty Print Error: ${error instanceof Error ? error.message : "Unknown error"}]`;
491
+ }
492
+ }
380
493
  var PinoLogger = class _PinoLogger {
381
494
  pinoLogger;
382
495
  meta;
383
496
  prettyPrinter = PinoPretty.prettyFactory({
384
- colorize: true
497
+ colorize: true,
498
+ // Add error handling options
499
+ errorLikeObjectKeys: ["err", "error"],
500
+ ignore: "pid,hostname",
501
+ translateTime: "SYS:standard"
385
502
  });
386
503
  constructor(level, meta2 = {}) {
387
504
  this.pinoLogger = pino({
@@ -394,7 +511,12 @@ var PinoLogger = class _PinoLogger {
394
511
  timestamp: pino.stdTimeFunctions.isoTime,
395
512
  transport: {
396
513
  target: "pino-pretty",
397
- options: { colorize: true }
514
+ options: {
515
+ colorize: true,
516
+ errorLikeObjectKeys: ["err", "error"],
517
+ ignore: "pid,hostname",
518
+ translateTime: "SYS:standard"
519
+ }
398
520
  }
399
521
  });
400
522
  this.meta = meta2;
@@ -426,12 +548,21 @@ var PinoLogger = class _PinoLogger {
426
548
  ...meta2
427
549
  };
428
550
  this.pinoLogger[level](...normalizeLogArgs(filteredArgs));
429
- logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
430
- severityText: level,
431
- severityNumber: mapSeverity(level),
432
- body: this.prettyPrinter(filteredArgs),
433
- attributes: { ...this.meta, ...meta2 }
434
- });
551
+ const formattedBody = safePrettyFormat(level, filteredArgs);
552
+ try {
553
+ logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
554
+ severityText: level,
555
+ severityNumber: mapSeverity(level),
556
+ body: formattedBody,
557
+ attributes: { ...this.meta, ...meta2 }
558
+ });
559
+ } catch (error) {
560
+ console.error("Failed to emit OpenTelemetry log:", error);
561
+ console.log(
562
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] ${level.toUpperCase()}:`,
563
+ ...filteredArgs
564
+ );
565
+ }
435
566
  }
436
567
  error = (msg, ...args) => this.log("error", msg, ...args);
437
568
  info = (msg, ...args) => this.log("info", msg, ...args);
@@ -560,13 +691,14 @@ var httpServerDurationHistogram = metrics.getMeter(getEnvVar2("OTEL_SERVICE_NAME
560
691
  });
561
692
 
562
693
  // src/http/middleware/request/enrichDetails.middleware.ts
563
- function enrichDetails(path, contractDetails, requestSchema, responseSchemas, openTelemetryCollector) {
694
+ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, openTelemetryCollector, globalOptions) {
564
695
  return (req, res, next) => {
565
696
  req.originalPath = path;
566
697
  req.contractDetails = contractDetails;
567
698
  req.requestSchema = requestSchema;
568
699
  res.responseSchemas = responseSchemas;
569
700
  req.openTelemetryCollector = openTelemetryCollector;
701
+ req._globalOptions = globalOptions;
570
702
  req.context?.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
571
703
  const startTime = process.hrtime();
572
704
  res.on("finish", () => {
@@ -602,6 +734,7 @@ function isRequestShape(maybeResponseShape) {
602
734
 
603
735
  // src/http/middleware/request/parse.middleware.ts
604
736
  function parse(req, res, next) {
737
+ const collapsedOptions = req.contractDetails.options?.requestValidation ?? (req._globalOptions?.validation === false ? "none" : req._globalOptions?.validation?.request);
605
738
  const request = {
606
739
  params: req.params,
607
740
  query: req.query,
@@ -674,8 +807,9 @@ function parse(req, res, next) {
674
807
  );
675
808
  }
676
809
  if (!parsedRequest.ok) {
677
- switch (req.contractDetails.options?.requestValidation) {
810
+ switch (collapsedOptions) {
678
811
  default:
812
+ case void 0:
679
813
  case "error":
680
814
  res.type("application/json");
681
815
  res.status(400);
@@ -836,12 +970,13 @@ function discriminateResponseBodies(schemaValidator, responses) {
836
970
 
837
971
  // src/http/router/expressLikeRouter.ts
838
972
  var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
839
- constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
973
+ constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector, routerOptions) {
840
974
  this.basePath = basePath;
841
975
  this.schemaValidator = schemaValidator;
842
976
  this.internal = internal;
843
977
  this.postEnrichMiddleware = postEnrichMiddleware;
844
978
  this.openTelemetryCollector = openTelemetryCollector;
979
+ this.routerOptions = routerOptions;
845
980
  if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
846
981
  process.on("uncaughtException", (err) => {
847
982
  this.openTelemetryCollector.error(`Uncaught exception: ${err}`);
@@ -880,7 +1015,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
880
1015
  contractDetails,
881
1016
  requestSchema,
882
1017
  responseSchemas,
883
- this.openTelemetryCollector
1018
+ this.openTelemetryCollector,
1019
+ this.routerOptions
884
1020
  ),
885
1021
  ...this.postEnrichMiddleware,
886
1022
  parse,
@@ -1573,7 +1709,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1573
1709
  this.schemaValidator,
1574
1710
  this.internal,
1575
1711
  this.postEnrichMiddleware,
1576
- this.openTelemetryCollector
1712
+ this.openTelemetryCollector,
1713
+ this.routerOptions
1577
1714
  );
1578
1715
  this.cloneInternals(clone);
1579
1716
  return clone;
@@ -1593,7 +1730,12 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
1593
1730
  schemaValidator,
1594
1731
  internal,
1595
1732
  postEnrichMiddleware,
1596
- openTelemetryCollector
1733
+ openTelemetryCollector,
1734
+ {
1735
+ ...appOptions,
1736
+ openapi: appOptions?.openapi !== false,
1737
+ mcp: appOptions?.mcp !== false
1738
+ }
1597
1739
  );
1598
1740
  this.schemaValidator = schemaValidator;
1599
1741
  this.internal = internal;
@@ -1604,6 +1746,27 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
1604
1746
  }
1605
1747
  };
1606
1748
 
1749
+ // src/http/cluster/isPortBound.ts
1750
+ import * as net from "net";
1751
+ function isPortBound(port, host = "localhost") {
1752
+ return new Promise((resolve) => {
1753
+ const socket = new net.Socket();
1754
+ socket.setTimeout(1e3);
1755
+ socket.on("connect", () => {
1756
+ socket.destroy();
1757
+ resolve(true);
1758
+ });
1759
+ socket.on("timeout", () => {
1760
+ socket.destroy();
1761
+ resolve(false);
1762
+ });
1763
+ socket.on("error", () => {
1764
+ resolve(false);
1765
+ });
1766
+ socket.connect(port, host);
1767
+ });
1768
+ }
1769
+
1607
1770
  // src/http/guards/isPath.ts
1608
1771
  function isPath(path) {
1609
1772
  return path.startsWith("/");
@@ -2726,7 +2889,7 @@ function generateInputSchema(schemaValidator, body, params, query, requestHeader
2726
2889
  } : {}
2727
2890
  });
2728
2891
  }
2729
- function generateMcpServer(schemaValidator, protocol, host, port, version, application, options2, contentTypeMap) {
2892
+ function generateMcpServer(schemaValidator, protocol, host, port, version, application, appOptions, options2, contentTypeMap) {
2730
2893
  if (!(schemaValidator instanceof ZodSchemaValidator)) {
2731
2894
  throw new Error(
2732
2895
  "Schema validator must be an instance of ZodSchemaValidator"
@@ -2747,6 +2910,9 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
2747
2910
  ])
2748
2911
  ].forEach(({ fullPath, router }) => {
2749
2912
  router.routes.forEach((route) => {
2913
+ if (!(route.contractDetails.options?.mcp ?? router.routerOptions?.mcp ?? appOptions !== false)) {
2914
+ return;
2915
+ }
2750
2916
  const inputSchemas = [];
2751
2917
  if (route.contractDetails.versions) {
2752
2918
  Object.values(route.contractDetails.versions).forEach((version2) => {
@@ -2950,6 +3116,11 @@ function isResponseCompiledSchema(schema) {
2950
3116
  function parse2(req, res, next) {
2951
3117
  let headers;
2952
3118
  let responses;
3119
+ const collapsedOptions = req.contractDetails.options?.responseValidation ?? (req._globalOptions?.validation === false ? "none" : req._globalOptions?.validation?.response);
3120
+ if (collapsedOptions === "none") {
3121
+ next?.();
3122
+ return;
3123
+ }
2953
3124
  const responseSchemas = res.responseSchemas;
2954
3125
  const schemaValidator = req.schemaValidator;
2955
3126
  if (!isResponseCompiledSchema(responseSchemas)) {
@@ -3011,6 +3182,7 @@ function parse2(req, res, next) {
3011
3182
  if (parseErrors.length > 0) {
3012
3183
  switch (req.contractDetails.options?.responseValidation) {
3013
3184
  default:
3185
+ case void 0:
3014
3186
  case "error":
3015
3187
  res.type("text/plain");
3016
3188
  res.status(500);
@@ -3239,13 +3411,16 @@ function transformBasePath(basePath) {
3239
3411
  }
3240
3412
  return `/${basePath}`;
3241
3413
  }
3242
- function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags, versionedPaths, versionedSecuritySchemes, otherServers) {
3414
+ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags, versionedPaths, versionedSecuritySchemes, appOptions) {
3415
+ const { title, description, contact } = appOptions !== false ? appOptions ?? {} : {};
3243
3416
  return {
3244
3417
  [OPENAPI_DEFAULT_VERSION]: {
3245
3418
  openapi: "3.1.0",
3246
3419
  info: {
3247
- title: process.env.API_TITLE || "API Definition",
3248
- version: process.env.VERSION || "latest"
3420
+ title: title || process.env.API_TITLE || "API Definition",
3421
+ version: "latest",
3422
+ description,
3423
+ contact
3249
3424
  },
3250
3425
  components: {
3251
3426
  securitySchemes: versionedSecuritySchemes[OPENAPI_DEFAULT_VERSION]
@@ -3255,8 +3430,7 @@ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags,
3255
3430
  ...serverUrls.map((url, index) => ({
3256
3431
  url,
3257
3432
  description: serverDescriptions?.[index]
3258
- })),
3259
- ...otherServers || []
3433
+ }))
3260
3434
  ],
3261
3435
  paths: versionedPaths[OPENAPI_DEFAULT_VERSION]
3262
3436
  },
@@ -3266,8 +3440,10 @@ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags,
3266
3440
  {
3267
3441
  openapi: "3.1.0",
3268
3442
  info: {
3269
- title: process.env.API_TITLE || "API Definition",
3270
- version
3443
+ title: title || process.env.API_TITLE || "API Definition",
3444
+ version,
3445
+ description,
3446
+ contact
3271
3447
  },
3272
3448
  components: {
3273
3449
  securitySchemes: versionedSecuritySchemes[version]
@@ -3277,8 +3453,7 @@ function generateOpenApiDocument(serverUrls, serverDescriptions, versionedTags,
3277
3453
  ...serverUrls.map((url, index) => ({
3278
3454
  url,
3279
3455
  description: serverDescriptions?.[index]
3280
- })),
3281
- ...otherServers || []
3456
+ }))
3282
3457
  ],
3283
3458
  paths
3284
3459
  }
@@ -3382,11 +3557,11 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
3382
3557
  }
3383
3558
  }
3384
3559
  if (auth) {
3385
- responses[401] = {
3560
+ coercedResponses[401] = {
3386
3561
  description: httpStatusCodes_default[401],
3387
3562
  content: contentResolver(schemaValidator, schemaValidator.string)
3388
3563
  };
3389
- responses[403] = {
3564
+ coercedResponses[403] = {
3390
3565
  description: httpStatusCodes_default[403],
3391
3566
  content: contentResolver(schemaValidator, schemaValidator.string)
3392
3567
  };
@@ -3427,7 +3602,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
3427
3602
  }
3428
3603
  return operationObject;
3429
3604
  }
3430
- function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, application, otherServers) {
3605
+ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, application, appOptions) {
3431
3606
  const versionedPaths = {
3432
3607
  [OPENAPI_DEFAULT_VERSION]: {}
3433
3608
  };
@@ -3451,7 +3626,10 @@ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, a
3451
3626
  const openApiPath = openApiCompliantPath(
3452
3627
  `${fullPath}${route.path === "/" ? "" : route.path}`
3453
3628
  );
3454
- const { name, summary, params, versions, auth } = route.contractDetails;
3629
+ const { name, summary, params, versions, auth, options: options2 } = route.contractDetails;
3630
+ if (!(options2?.openapi ?? router.routerOptions?.openapi ?? appOptions !== false)) {
3631
+ return;
3632
+ }
3455
3633
  if (versions) {
3456
3634
  for (const version of Object.keys(versions)) {
3457
3635
  if (!versionedPaths[version]) {
@@ -3545,7 +3723,7 @@ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, a
3545
3723
  versionedTags,
3546
3724
  versionedPaths,
3547
3725
  versionedSecuritySchemes,
3548
- otherServers
3726
+ appOptions
3549
3727
  );
3550
3728
  }
3551
3729
 
@@ -3613,11 +3791,11 @@ function mapToFetch(schemaValidator, routerMap) {
3613
3791
  schemaValidator,
3614
3792
  routerMap
3615
3793
  );
3616
- return (path, ...reqInit) => {
3794
+ return ((path, ...reqInit) => {
3617
3795
  const method = reqInit[0]?.method;
3618
3796
  const version = reqInit[0] != null && "version" in reqInit[0] ? reqInit[0].version : void 0;
3619
3797
  return (version ? toRecord2(toRecord2(flattenedFetchMap[path])[method ?? "GET"])[version] : toRecord2(flattenedFetchMap[path])[method ?? "GET"])(path, reqInit[0]);
3620
- };
3798
+ });
3621
3799
  }
3622
3800
  function sdkClient(schemaValidator, routerMap) {
3623
3801
  return {
@@ -3700,6 +3878,7 @@ export {
3700
3878
  isForklaunchRequest,
3701
3879
  isForklaunchRouter,
3702
3880
  isInformational,
3881
+ isPortBound,
3703
3882
  isRedirection,
3704
3883
  isServerError,
3705
3884
  isSuccessful,