@forklaunch/core 1.3.6 → 1.3.8

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.
@@ -189,25 +189,16 @@ type RoleSet = {
189
189
  } | {
190
190
  readonly forbiddenRoles: Set<string>;
191
191
  };
192
- /** Auth base for authenticated routes JWT/Basic, no RBAC. */
193
- type AuthenticatedAuthBase = TokenOptions & {
194
- readonly decodeResource?: DecodeResource;
192
+ /** Constraints that forbid RBAC fields on authenticated routes. */
193
+ type NoRbacConstraint = {
195
194
  readonly allowedPermissions?: never;
196
195
  readonly forbiddenPermissions?: never;
197
196
  readonly allowedRoles?: never;
198
197
  readonly forbiddenRoles?: never;
199
- } & (BasicAuthMethods | JwtAuthMethods);
200
- /** Auth base for protected routes — JWT/Basic with required RBAC. */
201
- type ProtectedAuthBase = TokenOptions & {
202
- readonly decodeResource?: DecodeResource;
203
- } & (PermissionSet | RoleSet) & (BasicAuthMethods | JwtAuthMethods);
204
- /**
205
- * @deprecated Use AccessAuth discriminated union instead.
206
- * Kept for backwards compatibility with AuthMethods.
207
- */
198
+ };
208
199
  type AuthMethodsBase = TokenOptions & (HmacMethods | ({
209
200
  readonly decodeResource?: DecodeResource;
210
- } & ((PermissionSet & (BasicAuthMethods | JwtAuthMethods)) | (RoleSet & (BasicAuthMethods | JwtAuthMethods)) | (BasicAuthMethods | JwtAuthMethods))));
201
+ } & (BasicAuthMethods | JwtAuthMethods)));
211
202
  /**
212
203
  * Route access level — determines authentication and authorization requirements.
213
204
  *
@@ -222,6 +213,7 @@ type AccessLevel = 'public' | 'authenticated' | 'protected' | 'internal';
222
213
  * - `public`: auth not allowed
223
214
  * - `authenticated`: JWT/Basic auth required, RBAC fields disallowed
224
215
  * - `protected`: auth required, must include at least one RBAC declaration
216
+ * (surfacing functions are optional here — they can be provided at router/app level)
225
217
  * - `internal`: auth required, must be HMAC
226
218
  */
227
219
  type AccessAuth<Auth> = {
@@ -229,10 +221,10 @@ type AccessAuth<Auth> = {
229
221
  readonly auth?: never;
230
222
  } | {
231
223
  readonly access: 'authenticated';
232
- readonly auth?: Auth & AuthenticatedAuthBase;
224
+ readonly auth?: Auth & NoRbacConstraint;
233
225
  } | {
234
226
  readonly access: 'protected';
235
- readonly auth: Auth & ProtectedAuthBase & ({
227
+ readonly auth: Auth & ({
236
228
  readonly allowedPermissions: Set<string>;
237
229
  } | {
238
230
  readonly forbiddenPermissions: Set<string>;
@@ -255,25 +247,25 @@ type SchemaAuthMethods<SV extends AnySchemaValidator, ParamsSchema extends Param
255
247
  readonly requiredScope?: string;
256
248
  readonly scopeHeirarchy?: string[];
257
249
  readonly surfaceScopes?: ExpressLikeSchemaAuthMapper<SV, ParamsSchema, ReqBody, QuerySchema, ReqHeaders, VersionedApi, SessionObject<SV>, BaseRequest>;
258
- readonly requiredFeatures?: string[];
259
- readonly requireActiveSubscription?: boolean;
260
- } & ({
261
250
  readonly surfacePermissions?: ExpressLikeSchemaAuthMapper<SV, ParamsSchema, ReqBody, QuerySchema, ReqHeaders, VersionedApi, SessionObject<SV>, BaseRequest>;
262
- } | {
263
251
  readonly surfaceRoles?: ExpressLikeSchemaAuthMapper<SV, ParamsSchema, ReqBody, QuerySchema, ReqHeaders, VersionedApi, SessionObject<SV>, BaseRequest>;
264
- });
252
+ readonly requiredFeatures?: string[];
253
+ readonly requireActiveSubscription?: boolean;
254
+ };
265
255
  type AuthMethods<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends Record<string, unknown>, ReqHeaders extends Record<string, string>, VersionedReqs extends VersionedRequests, BaseRequest> = AuthMethodsBase & {
266
256
  readonly sessionSchema?: SessionObject<SV>;
267
257
  readonly requiredScope?: string;
268
258
  readonly scopeHeirarchy?: string[];
269
259
  readonly surfaceScopes?: ExpressLikeAuthMapper<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedReqs, SessionObject<SV>, BaseRequest>;
270
- readonly requiredFeatures?: string[];
271
- readonly requireActiveSubscription?: boolean;
272
- } & (({
273
260
  readonly surfacePermissions?: ExpressLikeAuthMapper<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedReqs, SessionObject<SV>, BaseRequest>;
274
- } & PermissionSet) | ({
275
261
  readonly surfaceRoles?: ExpressLikeAuthMapper<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedReqs, SessionObject<SV>, BaseRequest>;
276
- } & RoleSet));
262
+ readonly allowedPermissions?: Set<string>;
263
+ readonly forbiddenPermissions?: Set<string>;
264
+ readonly allowedRoles?: Set<string>;
265
+ readonly forbiddenRoles?: Set<string>;
266
+ readonly requiredFeatures?: string[];
267
+ readonly requireActiveSubscription?: boolean;
268
+ };
277
269
  /**
278
270
  * Type representing a mapped schema.
279
271
  *s ParamsDictionary,
@@ -189,25 +189,16 @@ type RoleSet = {
189
189
  } | {
190
190
  readonly forbiddenRoles: Set<string>;
191
191
  };
192
- /** Auth base for authenticated routes JWT/Basic, no RBAC. */
193
- type AuthenticatedAuthBase = TokenOptions & {
194
- readonly decodeResource?: DecodeResource;
192
+ /** Constraints that forbid RBAC fields on authenticated routes. */
193
+ type NoRbacConstraint = {
195
194
  readonly allowedPermissions?: never;
196
195
  readonly forbiddenPermissions?: never;
197
196
  readonly allowedRoles?: never;
198
197
  readonly forbiddenRoles?: never;
199
- } & (BasicAuthMethods | JwtAuthMethods);
200
- /** Auth base for protected routes — JWT/Basic with required RBAC. */
201
- type ProtectedAuthBase = TokenOptions & {
202
- readonly decodeResource?: DecodeResource;
203
- } & (PermissionSet | RoleSet) & (BasicAuthMethods | JwtAuthMethods);
204
- /**
205
- * @deprecated Use AccessAuth discriminated union instead.
206
- * Kept for backwards compatibility with AuthMethods.
207
- */
198
+ };
208
199
  type AuthMethodsBase = TokenOptions & (HmacMethods | ({
209
200
  readonly decodeResource?: DecodeResource;
210
- } & ((PermissionSet & (BasicAuthMethods | JwtAuthMethods)) | (RoleSet & (BasicAuthMethods | JwtAuthMethods)) | (BasicAuthMethods | JwtAuthMethods))));
201
+ } & (BasicAuthMethods | JwtAuthMethods)));
211
202
  /**
212
203
  * Route access level — determines authentication and authorization requirements.
213
204
  *
@@ -222,6 +213,7 @@ type AccessLevel = 'public' | 'authenticated' | 'protected' | 'internal';
222
213
  * - `public`: auth not allowed
223
214
  * - `authenticated`: JWT/Basic auth required, RBAC fields disallowed
224
215
  * - `protected`: auth required, must include at least one RBAC declaration
216
+ * (surfacing functions are optional here — they can be provided at router/app level)
225
217
  * - `internal`: auth required, must be HMAC
226
218
  */
227
219
  type AccessAuth<Auth> = {
@@ -229,10 +221,10 @@ type AccessAuth<Auth> = {
229
221
  readonly auth?: never;
230
222
  } | {
231
223
  readonly access: 'authenticated';
232
- readonly auth?: Auth & AuthenticatedAuthBase;
224
+ readonly auth?: Auth & NoRbacConstraint;
233
225
  } | {
234
226
  readonly access: 'protected';
235
- readonly auth: Auth & ProtectedAuthBase & ({
227
+ readonly auth: Auth & ({
236
228
  readonly allowedPermissions: Set<string>;
237
229
  } | {
238
230
  readonly forbiddenPermissions: Set<string>;
@@ -255,25 +247,25 @@ type SchemaAuthMethods<SV extends AnySchemaValidator, ParamsSchema extends Param
255
247
  readonly requiredScope?: string;
256
248
  readonly scopeHeirarchy?: string[];
257
249
  readonly surfaceScopes?: ExpressLikeSchemaAuthMapper<SV, ParamsSchema, ReqBody, QuerySchema, ReqHeaders, VersionedApi, SessionObject<SV>, BaseRequest>;
258
- readonly requiredFeatures?: string[];
259
- readonly requireActiveSubscription?: boolean;
260
- } & ({
261
250
  readonly surfacePermissions?: ExpressLikeSchemaAuthMapper<SV, ParamsSchema, ReqBody, QuerySchema, ReqHeaders, VersionedApi, SessionObject<SV>, BaseRequest>;
262
- } | {
263
251
  readonly surfaceRoles?: ExpressLikeSchemaAuthMapper<SV, ParamsSchema, ReqBody, QuerySchema, ReqHeaders, VersionedApi, SessionObject<SV>, BaseRequest>;
264
- });
252
+ readonly requiredFeatures?: string[];
253
+ readonly requireActiveSubscription?: boolean;
254
+ };
265
255
  type AuthMethods<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends Record<string, unknown>, ReqHeaders extends Record<string, string>, VersionedReqs extends VersionedRequests, BaseRequest> = AuthMethodsBase & {
266
256
  readonly sessionSchema?: SessionObject<SV>;
267
257
  readonly requiredScope?: string;
268
258
  readonly scopeHeirarchy?: string[];
269
259
  readonly surfaceScopes?: ExpressLikeAuthMapper<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedReqs, SessionObject<SV>, BaseRequest>;
270
- readonly requiredFeatures?: string[];
271
- readonly requireActiveSubscription?: boolean;
272
- } & (({
273
260
  readonly surfacePermissions?: ExpressLikeAuthMapper<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedReqs, SessionObject<SV>, BaseRequest>;
274
- } & PermissionSet) | ({
275
261
  readonly surfaceRoles?: ExpressLikeAuthMapper<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedReqs, SessionObject<SV>, BaseRequest>;
276
- } & RoleSet));
262
+ readonly allowedPermissions?: Set<string>;
263
+ readonly forbiddenPermissions?: Set<string>;
264
+ readonly allowedRoles?: Set<string>;
265
+ readonly forbiddenRoles?: Set<string>;
266
+ readonly requiredFeatures?: string[];
267
+ readonly requireActiveSubscription?: boolean;
268
+ };
277
269
  /**
278
270
  * Type representing a mapped schema.
279
271
  *s ParamsDictionary,
@@ -5,8 +5,8 @@ import { AnySchemaValidator } from '@forklaunch/validator';
5
5
  import { ServerOptions, IncomingMessage, ServerResponse } from 'node:http';
6
6
  import { O as OpenTelemetryCollector, M as MetricsDefinition, T as TelemetryOptions } from '../openTelemetryCollector-DorlR4pn.mjs';
7
7
  export { L as LogFn, a as LoggerMeta, b as MetricType, P as PinoLogger, h as httpRequestsTotalCounter, c as httpServerDurationHistogram, l as logger, m as meta } from '../openTelemetryCollector-DorlR4pn.mjs';
8
- import { M as Method, P as PathParamHttpContractDetails, H as HttpContractDetails, E as ExpressLikeRouterOptions, a as SessionObject, b as ParamsObject, R as ResponsesObject, B as Body, Q as QueryObject, c as HeadersObject, V as VersionSchema, d as SchemaAuthMethods, e as ExpressLikeSchemaHandler, f as ResolvedSessionObject, C as ContractDetails, g as PathMatch, L as LiveTypeFunction, h as LiveSdkFunction, A as AuthMethodsBase, i as ExpressLikeApplicationOptions, j as ParamsDictionary, k as VersionedRequests, l as AuthMethods, D as DecodeResource, m as BasicAuthMethods, F as ForklaunchRequest, n as MiddlewareContractDetails, o as ExpressLikeSchemaAuthMapper, p as ForklaunchNextFunction, q as ForklaunchResponse, r as ForklaunchResHeaders, s as ForklaunchStatusResponse, t as ForklaunchSendableData } from '../apiDefinition.types-CN-qa49j.mjs';
9
- export { u as AccessLevel, v as BodyObject, w as DefaultSubscriptionData, x as DocsConfiguration, y as ErrorContainer, z as ExpressLikeAuthMapper, G as ExpressLikeGlobalAuthOptions, I as ExpressLikeHandler, J as ExpressLikeSchemaGlobalAuthOptions, K as ExtractBody, N as ExtractContentType, O as ExtractResponseBody, T as ExtractedParamsObject, U as FileBody, W as ForklaunchBaseRequest, X as ForklaunchResErrors, Y as HmacMethods, Z as HttpMethod, _ as JsonBody, $ as JwtAuthMethods, a0 as LiveTypeFunctionRequestInit, a1 as MapParamsSchema, a2 as MapReqBodySchema, a3 as MapReqHeadersSchema, a4 as MapReqQuerySchema, a5 as MapResBodyMapSchema, a6 as MapResHeadersSchema, a7 as MapSchema, a8 as MapSessionSchema, a9 as MapVersionedReqsSchema, aa as MapVersionedRespsSchema, ab as MultipartForm, ac as NumberOnlyObject, ad as PathParamMethod, ae as PermissionSet, af as RawTypedResponseBody, ag as RequestContext, ah as ResolvedForklaunchAuthRequest, ai as ResolvedForklaunchRequest, aj as ResolvedForklaunchResponse, ak as ResponseBody, al as ResponseCompiledSchema, am as ResponseShape, an as RoleSet, ao as ServerSentEventBody, S as StringOnlyObject, ap as TextBody, aq as TypedBody, ar as TypedRequestBody, as as TypedResponseBody, at as UnknownBody, au as UnknownResponseBody, av as UrlEncodedForm, aw as VersionedResponses } from '../apiDefinition.types-CN-qa49j.mjs';
8
+ import { M as Method, P as PathParamHttpContractDetails, H as HttpContractDetails, E as ExpressLikeRouterOptions, a as SessionObject, b as ParamsObject, R as ResponsesObject, B as Body, Q as QueryObject, c as HeadersObject, V as VersionSchema, d as SchemaAuthMethods, e as ExpressLikeSchemaHandler, f as ResolvedSessionObject, C as ContractDetails, g as PathMatch, L as LiveTypeFunction, h as LiveSdkFunction, A as AuthMethodsBase, i as ExpressLikeApplicationOptions, j as ParamsDictionary, k as VersionedRequests, l as AuthMethods, D as DecodeResource, m as BasicAuthMethods, F as ForklaunchRequest, n as MiddlewareContractDetails, o as ExpressLikeSchemaAuthMapper, p as ForklaunchNextFunction, q as ForklaunchResponse, r as ForklaunchResHeaders, s as ForklaunchStatusResponse, t as ForklaunchSendableData } from '../apiDefinition.types-CtTiIWvC.mjs';
9
+ export { u as AccessLevel, v as BodyObject, w as DefaultSubscriptionData, x as DocsConfiguration, y as ErrorContainer, z as ExpressLikeAuthMapper, G as ExpressLikeGlobalAuthOptions, I as ExpressLikeHandler, J as ExpressLikeSchemaGlobalAuthOptions, K as ExtractBody, N as ExtractContentType, O as ExtractResponseBody, T as ExtractedParamsObject, U as FileBody, W as ForklaunchBaseRequest, X as ForklaunchResErrors, Y as HmacMethods, Z as HttpMethod, _ as JsonBody, $ as JwtAuthMethods, a0 as LiveTypeFunctionRequestInit, a1 as MapParamsSchema, a2 as MapReqBodySchema, a3 as MapReqHeadersSchema, a4 as MapReqQuerySchema, a5 as MapResBodyMapSchema, a6 as MapResHeadersSchema, a7 as MapSchema, a8 as MapSessionSchema, a9 as MapVersionedReqsSchema, aa as MapVersionedRespsSchema, ab as MultipartForm, ac as NumberOnlyObject, ad as PathParamMethod, ae as PermissionSet, af as RawTypedResponseBody, ag as RequestContext, ah as ResolvedForklaunchAuthRequest, ai as ResolvedForklaunchRequest, aj as ResolvedForklaunchResponse, ak as ResponseBody, al as ResponseCompiledSchema, am as ResponseShape, an as RoleSet, ao as ServerSentEventBody, S as StringOnlyObject, ap as TextBody, aq as TypedBody, ar as TypedRequestBody, as as TypedResponseBody, at as UnknownBody, au as UnknownResponseBody, av as UrlEncodedForm, aw as VersionedResponses } from '../apiDefinition.types-CtTiIWvC.mjs';
10
10
  import { JWTPayload, JWK } from 'jose';
11
11
  import { ZodSchemaValidator } from '@forklaunch/validator/zod';
12
12
  import { FastMCP } from 'fastmcp';
@@ -581,6 +581,12 @@ declare class ForklaunchExpressLikeRouter<SV extends AnySchemaValidator, BasePat
581
581
  private addRouterToSdk;
582
582
  registerNestableMiddlewareHandler<Name extends string, Path extends `/${string}`, P extends ParamsObject<SV>, ResBodyMap extends ResponsesObject<SV>, ReqBody extends Body<SV>, ReqQuery extends QueryObject<SV>, ReqHeaders extends HeadersObject<SV>, ResHeaders extends HeadersObject<SV>, LocalsObj extends Record<string, unknown>, VersionedApi extends VersionSchema<SV, 'middleware'>, const SessionSchema extends SessionObject<SV>, Auth extends SchemaAuthMethods<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedApi, BaseRequest>>(registrationMethod: NestableRouterBasedHandler<RouterHandler, Internal>, pathOrContractDetailsOrMiddlewareOrTypedHandler: Path | ContractDetailsOrMiddlewareOrTypedHandler<SV, Name, 'middleware', Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, VersionedApi, BaseRequest, BaseResponse, NextFunction, RouterSession, Auth> | ConstrainedForklaunchRouter<SV, RouterHandler>, contractDetailsOrMiddlewareOrTypedHandler?: ContractDetailsOrMiddlewareOrTypedHandler<SV, Name, 'middleware', Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, VersionedApi, BaseRequest, BaseResponse, NextFunction, RouterSession, Auth> | ConstrainedForklaunchRouter<SV, RouterHandler>, ...middlewareOrMiddlewareWithTypedHandler: (MiddlewareOrMiddlewareWithTypedHandler<SV, Name, 'middleware', Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, VersionedApi, BaseRequest, BaseResponse, NextFunction, RouterSession, Auth> | ConstrainedForklaunchRouter<SV, RouterHandler>)[]): this;
583
583
  private addRouterOptions;
584
+ /**
585
+ * Validates that all protected routes have the required surfacing functions
586
+ * available (from the route, router, or application level).
587
+ * Call this at listen() time when the full option chain is assembled.
588
+ */
589
+ validateSurfacingFunctions(router?: ForklaunchRouter<SV>, parentAuth?: Record<string, unknown>): void;
584
590
  use: TypedNestableMiddlewareDefinition<this, RouterHandler, Internal, SV, RouterSession, BaseRequest, BaseResponse, NextFunction>;
585
591
  all: TypedMiddlewareDefinition<this, SV, RouterSession, BaseRequest, BaseResponse, NextFunction, RouterHandler>;
586
592
  connect: TypedMiddlewareDefinition<this, SV, RouterSession, BaseRequest, BaseResponse, NextFunction, RouterHandler>;
@@ -726,6 +732,11 @@ declare abstract class ForklaunchExpressLikeApplication<SV extends AnySchemaVali
726
732
  * @param {SV} schemaValidator - The schema validator.
727
733
  */
728
734
  constructor(schemaValidator: SV, internal: Server, postEnrichMiddleware: RouterHandler[], openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, appOptions?: ExpressLikeApplicationOptions<SV, Session> | undefined);
735
+ /**
736
+ * Validates all registered routes across the app and all mounted routers.
737
+ * Call this at the start of listen() to fail fast at startup.
738
+ */
739
+ protected validateAllRoutes(): void;
729
740
  abstract listen(...args: unknown[]): void;
730
741
  }
731
742
 
@@ -5,8 +5,8 @@ import { AnySchemaValidator } from '@forklaunch/validator';
5
5
  import { ServerOptions, IncomingMessage, ServerResponse } from 'node:http';
6
6
  import { O as OpenTelemetryCollector, M as MetricsDefinition, T as TelemetryOptions } from '../openTelemetryCollector-DorlR4pn.js';
7
7
  export { L as LogFn, a as LoggerMeta, b as MetricType, P as PinoLogger, h as httpRequestsTotalCounter, c as httpServerDurationHistogram, l as logger, m as meta } from '../openTelemetryCollector-DorlR4pn.js';
8
- import { M as Method, P as PathParamHttpContractDetails, H as HttpContractDetails, E as ExpressLikeRouterOptions, a as SessionObject, b as ParamsObject, R as ResponsesObject, B as Body, Q as QueryObject, c as HeadersObject, V as VersionSchema, d as SchemaAuthMethods, e as ExpressLikeSchemaHandler, f as ResolvedSessionObject, C as ContractDetails, g as PathMatch, L as LiveTypeFunction, h as LiveSdkFunction, A as AuthMethodsBase, i as ExpressLikeApplicationOptions, j as ParamsDictionary, k as VersionedRequests, l as AuthMethods, D as DecodeResource, m as BasicAuthMethods, F as ForklaunchRequest, n as MiddlewareContractDetails, o as ExpressLikeSchemaAuthMapper, p as ForklaunchNextFunction, q as ForklaunchResponse, r as ForklaunchResHeaders, s as ForklaunchStatusResponse, t as ForklaunchSendableData } from '../apiDefinition.types-CAOGkjXe.js';
9
- export { u as AccessLevel, v as BodyObject, w as DefaultSubscriptionData, x as DocsConfiguration, y as ErrorContainer, z as ExpressLikeAuthMapper, G as ExpressLikeGlobalAuthOptions, I as ExpressLikeHandler, J as ExpressLikeSchemaGlobalAuthOptions, K as ExtractBody, N as ExtractContentType, O as ExtractResponseBody, T as ExtractedParamsObject, U as FileBody, W as ForklaunchBaseRequest, X as ForklaunchResErrors, Y as HmacMethods, Z as HttpMethod, _ as JsonBody, $ as JwtAuthMethods, a0 as LiveTypeFunctionRequestInit, a1 as MapParamsSchema, a2 as MapReqBodySchema, a3 as MapReqHeadersSchema, a4 as MapReqQuerySchema, a5 as MapResBodyMapSchema, a6 as MapResHeadersSchema, a7 as MapSchema, a8 as MapSessionSchema, a9 as MapVersionedReqsSchema, aa as MapVersionedRespsSchema, ab as MultipartForm, ac as NumberOnlyObject, ad as PathParamMethod, ae as PermissionSet, af as RawTypedResponseBody, ag as RequestContext, ah as ResolvedForklaunchAuthRequest, ai as ResolvedForklaunchRequest, aj as ResolvedForklaunchResponse, ak as ResponseBody, al as ResponseCompiledSchema, am as ResponseShape, an as RoleSet, ao as ServerSentEventBody, S as StringOnlyObject, ap as TextBody, aq as TypedBody, ar as TypedRequestBody, as as TypedResponseBody, at as UnknownBody, au as UnknownResponseBody, av as UrlEncodedForm, aw as VersionedResponses } from '../apiDefinition.types-CAOGkjXe.js';
8
+ import { M as Method, P as PathParamHttpContractDetails, H as HttpContractDetails, E as ExpressLikeRouterOptions, a as SessionObject, b as ParamsObject, R as ResponsesObject, B as Body, Q as QueryObject, c as HeadersObject, V as VersionSchema, d as SchemaAuthMethods, e as ExpressLikeSchemaHandler, f as ResolvedSessionObject, C as ContractDetails, g as PathMatch, L as LiveTypeFunction, h as LiveSdkFunction, A as AuthMethodsBase, i as ExpressLikeApplicationOptions, j as ParamsDictionary, k as VersionedRequests, l as AuthMethods, D as DecodeResource, m as BasicAuthMethods, F as ForklaunchRequest, n as MiddlewareContractDetails, o as ExpressLikeSchemaAuthMapper, p as ForklaunchNextFunction, q as ForklaunchResponse, r as ForklaunchResHeaders, s as ForklaunchStatusResponse, t as ForklaunchSendableData } from '../apiDefinition.types-fBjxhhaM.js';
9
+ export { u as AccessLevel, v as BodyObject, w as DefaultSubscriptionData, x as DocsConfiguration, y as ErrorContainer, z as ExpressLikeAuthMapper, G as ExpressLikeGlobalAuthOptions, I as ExpressLikeHandler, J as ExpressLikeSchemaGlobalAuthOptions, K as ExtractBody, N as ExtractContentType, O as ExtractResponseBody, T as ExtractedParamsObject, U as FileBody, W as ForklaunchBaseRequest, X as ForklaunchResErrors, Y as HmacMethods, Z as HttpMethod, _ as JsonBody, $ as JwtAuthMethods, a0 as LiveTypeFunctionRequestInit, a1 as MapParamsSchema, a2 as MapReqBodySchema, a3 as MapReqHeadersSchema, a4 as MapReqQuerySchema, a5 as MapResBodyMapSchema, a6 as MapResHeadersSchema, a7 as MapSchema, a8 as MapSessionSchema, a9 as MapVersionedReqsSchema, aa as MapVersionedRespsSchema, ab as MultipartForm, ac as NumberOnlyObject, ad as PathParamMethod, ae as PermissionSet, af as RawTypedResponseBody, ag as RequestContext, ah as ResolvedForklaunchAuthRequest, ai as ResolvedForklaunchRequest, aj as ResolvedForklaunchResponse, ak as ResponseBody, al as ResponseCompiledSchema, am as ResponseShape, an as RoleSet, ao as ServerSentEventBody, S as StringOnlyObject, ap as TextBody, aq as TypedBody, ar as TypedRequestBody, as as TypedResponseBody, at as UnknownBody, au as UnknownResponseBody, av as UrlEncodedForm, aw as VersionedResponses } from '../apiDefinition.types-fBjxhhaM.js';
10
10
  import { JWTPayload, JWK } from 'jose';
11
11
  import { ZodSchemaValidator } from '@forklaunch/validator/zod';
12
12
  import { FastMCP } from 'fastmcp';
@@ -581,6 +581,12 @@ declare class ForklaunchExpressLikeRouter<SV extends AnySchemaValidator, BasePat
581
581
  private addRouterToSdk;
582
582
  registerNestableMiddlewareHandler<Name extends string, Path extends `/${string}`, P extends ParamsObject<SV>, ResBodyMap extends ResponsesObject<SV>, ReqBody extends Body<SV>, ReqQuery extends QueryObject<SV>, ReqHeaders extends HeadersObject<SV>, ResHeaders extends HeadersObject<SV>, LocalsObj extends Record<string, unknown>, VersionedApi extends VersionSchema<SV, 'middleware'>, const SessionSchema extends SessionObject<SV>, Auth extends SchemaAuthMethods<SV, P, ReqBody, ReqQuery, ReqHeaders, VersionedApi, BaseRequest>>(registrationMethod: NestableRouterBasedHandler<RouterHandler, Internal>, pathOrContractDetailsOrMiddlewareOrTypedHandler: Path | ContractDetailsOrMiddlewareOrTypedHandler<SV, Name, 'middleware', Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, VersionedApi, BaseRequest, BaseResponse, NextFunction, RouterSession, Auth> | ConstrainedForklaunchRouter<SV, RouterHandler>, contractDetailsOrMiddlewareOrTypedHandler?: ContractDetailsOrMiddlewareOrTypedHandler<SV, Name, 'middleware', Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, VersionedApi, BaseRequest, BaseResponse, NextFunction, RouterSession, Auth> | ConstrainedForklaunchRouter<SV, RouterHandler>, ...middlewareOrMiddlewareWithTypedHandler: (MiddlewareOrMiddlewareWithTypedHandler<SV, Name, 'middleware', Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, VersionedApi, BaseRequest, BaseResponse, NextFunction, RouterSession, Auth> | ConstrainedForklaunchRouter<SV, RouterHandler>)[]): this;
583
583
  private addRouterOptions;
584
+ /**
585
+ * Validates that all protected routes have the required surfacing functions
586
+ * available (from the route, router, or application level).
587
+ * Call this at listen() time when the full option chain is assembled.
588
+ */
589
+ validateSurfacingFunctions(router?: ForklaunchRouter<SV>, parentAuth?: Record<string, unknown>): void;
584
590
  use: TypedNestableMiddlewareDefinition<this, RouterHandler, Internal, SV, RouterSession, BaseRequest, BaseResponse, NextFunction>;
585
591
  all: TypedMiddlewareDefinition<this, SV, RouterSession, BaseRequest, BaseResponse, NextFunction, RouterHandler>;
586
592
  connect: TypedMiddlewareDefinition<this, SV, RouterSession, BaseRequest, BaseResponse, NextFunction, RouterHandler>;
@@ -726,6 +732,11 @@ declare abstract class ForklaunchExpressLikeApplication<SV extends AnySchemaVali
726
732
  * @param {SV} schemaValidator - The schema validator.
727
733
  */
728
734
  constructor(schemaValidator: SV, internal: Server, postEnrichMiddleware: RouterHandler[], openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, appOptions?: ExpressLikeApplicationOptions<SV, Session> | undefined);
735
+ /**
736
+ * Validates all registered routes across the app and all mounted routers.
737
+ * Call this at the start of listen() to fail fast at startup.
738
+ */
739
+ protected validateAllRoutes(): void;
729
740
  abstract listen(...args: unknown[]): void;
730
741
  }
731
742
 
package/lib/http/index.js CHANGED
@@ -111,6 +111,31 @@ function cors(corsOptions) {
111
111
  // src/http/router/expressLikeRouter.ts
112
112
  var import_common11 = require("@forklaunch/common");
113
113
 
114
+ // src/http/guards/hasPermissionChecks.ts
115
+ function hasPermissionChecks(maybePermissionedAuth) {
116
+ if (typeof maybePermissionedAuth !== "object" || maybePermissionedAuth === null) {
117
+ return false;
118
+ }
119
+ const hasAllowedPermissions = "allowedPermissions" in maybePermissionedAuth && maybePermissionedAuth.allowedPermissions instanceof Set && maybePermissionedAuth.allowedPermissions.size > 0;
120
+ const hasForbiddenPermissions = "forbiddenPermissions" in maybePermissionedAuth && maybePermissionedAuth.forbiddenPermissions instanceof Set && maybePermissionedAuth.forbiddenPermissions.size > 0;
121
+ return hasAllowedPermissions || hasForbiddenPermissions;
122
+ }
123
+
124
+ // src/http/guards/hasRoleChecks.ts
125
+ function hasRoleChecks(maybeRoledAuth) {
126
+ if (typeof maybeRoledAuth !== "object" || maybeRoledAuth === null) {
127
+ return false;
128
+ }
129
+ const hasAllowedRoles = "allowedRoles" in maybeRoledAuth && maybeRoledAuth.allowedRoles instanceof Set && maybeRoledAuth.allowedRoles.size > 0;
130
+ const hasForbiddenRoles = "forbiddenRoles" in maybeRoledAuth && maybeRoledAuth.forbiddenRoles instanceof Set && maybeRoledAuth.forbiddenRoles.size > 0;
131
+ return hasAllowedRoles || hasForbiddenRoles;
132
+ }
133
+
134
+ // src/http/guards/hasScopeChecks.ts
135
+ function hasScopeChecks(maybePermissionedAuth) {
136
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
137
+ }
138
+
114
139
  // src/http/guards/isForklaunchRouter.ts
115
140
  function isForklaunchRouter(maybeForklaunchRouter) {
116
141
  return maybeForklaunchRouter != null && typeof maybeForklaunchRouter === "object" && "basePath" in maybeForklaunchRouter && "routes" in maybeForklaunchRouter && Array.isArray(maybeForklaunchRouter.routes);
@@ -320,31 +345,6 @@ function discriminateResponseBodies(schemaValidator, responses) {
320
345
  // src/http/router/routerSharedLogic.ts
321
346
  var import_common10 = require("@forklaunch/common");
322
347
 
323
- // src/http/guards/hasPermissionChecks.ts
324
- function hasPermissionChecks(maybePermissionedAuth) {
325
- if (typeof maybePermissionedAuth !== "object" || maybePermissionedAuth === null) {
326
- return false;
327
- }
328
- const hasAllowedPermissions = "allowedPermissions" in maybePermissionedAuth && maybePermissionedAuth.allowedPermissions instanceof Set && maybePermissionedAuth.allowedPermissions.size > 0;
329
- const hasForbiddenPermissions = "forbiddenPermissions" in maybePermissionedAuth && maybePermissionedAuth.forbiddenPermissions instanceof Set && maybePermissionedAuth.forbiddenPermissions.size > 0;
330
- return hasAllowedPermissions || hasForbiddenPermissions;
331
- }
332
-
333
- // src/http/guards/hasRoleChecks.ts
334
- function hasRoleChecks(maybeRoledAuth) {
335
- if (typeof maybeRoledAuth !== "object" || maybeRoledAuth === null) {
336
- return false;
337
- }
338
- const hasAllowedRoles = "allowedRoles" in maybeRoledAuth && maybeRoledAuth.allowedRoles instanceof Set && maybeRoledAuth.allowedRoles.size > 0;
339
- const hasForbiddenRoles = "forbiddenRoles" in maybeRoledAuth && maybeRoledAuth.forbiddenRoles instanceof Set && maybeRoledAuth.forbiddenRoles.size > 0;
340
- return hasAllowedRoles || hasForbiddenRoles;
341
- }
342
-
343
- // src/http/guards/hasScopeChecks.ts
344
- function hasScopeChecks(maybePermissionedAuth) {
345
- return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
346
- }
347
-
348
348
  // src/http/guards/hasVersionedSchema.ts
349
349
  function hasVersionedSchema(contractDetails) {
350
350
  return typeof contractDetails === "object" && contractDetails !== null && "versions" in contractDetails && contractDetails.versions !== null;
@@ -1960,6 +1960,46 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1960
1960
  );
1961
1961
  });
1962
1962
  }
1963
+ /**
1964
+ * Validates that all protected routes have the required surfacing functions
1965
+ * available (from the route, router, or application level).
1966
+ * Call this at listen() time when the full option chain is assembled.
1967
+ */
1968
+ validateSurfacingFunctions(router = this, parentAuth) {
1969
+ const routerAuth = router.routerOptions?.auth && typeof router.routerOptions.auth === "object" ? router.routerOptions.auth : void 0;
1970
+ const globalAuth = routerAuth ?? parentAuth;
1971
+ for (const route of router.routes) {
1972
+ const auth = route.contractDetails.auth;
1973
+ const access = route.contractDetails.access;
1974
+ if (!auth || access !== "protected") continue;
1975
+ const routeAuth = auth;
1976
+ const routeName = route.contractDetails.name;
1977
+ if (hasPermissionChecks(auth)) {
1978
+ if (!routeAuth["surfacePermissions"] && !globalAuth?.surfacePermissions) {
1979
+ throw new Error(
1980
+ `Route '${routeName}': declares allowedPermissions or forbiddenPermissions but no surfacePermissions function was provided on the route, router, or application`
1981
+ );
1982
+ }
1983
+ }
1984
+ if (hasRoleChecks(auth)) {
1985
+ if (!routeAuth["surfaceRoles"] && !globalAuth?.surfaceRoles) {
1986
+ throw new Error(
1987
+ `Route '${routeName}': declares allowedRoles or forbiddenRoles but no surfaceRoles function was provided on the route, router, or application`
1988
+ );
1989
+ }
1990
+ }
1991
+ if (hasScopeChecks(auth)) {
1992
+ if (!routeAuth["surfaceScopes"] && !globalAuth?.surfaceScopes) {
1993
+ throw new Error(
1994
+ `Route '${routeName}': declares requiredScope but no surfaceScopes function was provided on the route, router, or application`
1995
+ );
1996
+ }
1997
+ }
1998
+ }
1999
+ for (const subRouter of router.routers) {
2000
+ this.validateSurfacingFunctions(subRouter, globalAuth);
2001
+ }
2002
+ }
1963
2003
  use = (pathOrContractDetailsOrMiddlewareOrTypedHandler, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1964
2004
  [
1965
2005
  pathOrContractDetailsOrMiddlewareOrTypedHandler,
@@ -2267,6 +2307,13 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
2267
2307
  this.appOptions = appOptions;
2268
2308
  this.internal.use(cors(this.appOptions?.cors ?? {}));
2269
2309
  }
2310
+ /**
2311
+ * Validates all registered routes across the app and all mounted routers.
2312
+ * Call this at the start of listen() to fail fast at startup.
2313
+ */
2314
+ validateAllRoutes() {
2315
+ this.validateSurfacingFunctions();
2316
+ }
2270
2317
  };
2271
2318
 
2272
2319
  // src/http/cluster/isPortBound.ts
@@ -4111,7 +4158,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
4111
4158
  operationObject.security = [
4112
4159
  {
4113
4160
  basic: Array.from(
4114
- "allowedPermissions" in auth ? auth.allowedPermissions?.values() || [] : []
4161
+ "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
4115
4162
  )
4116
4163
  }
4117
4164
  ];
@@ -4123,7 +4170,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
4123
4170
  operationObject.security = [
4124
4171
  {
4125
4172
  [auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
4126
- "allowedPermissions" in auth ? auth.allowedPermissions?.values() || [] : []
4173
+ "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
4127
4174
  )
4128
4175
  }
4129
4176
  ];