@@ -15,8 +15,7 @@ export type { RoleBaseAccessController, RbacAbility, RbacResource } from "@trigg
1515type RbacHelpers = { getSessionUserId : ( request : Request ) => Promise < string | null > } ;
1616
1717export type RbacCreateOptions = {
18- // When true, skip loading the enterprise plugin and use the OSS fallback directly.
19- // Useful for tests that need deterministic auth behavior without the enterprise plugin.
18+ // When true, skip loading the plugin, useful for tests
2019 forceFallback ?: boolean ;
2120} ;
2221
@@ -25,9 +24,7 @@ export type RbacCreateOptions = {
2524// a route with action: "trigger" because "write:tasks" was listed in the route's
2625// superScopes array. The new ability model matches scope-action strictly, so we
2726// restore the prior semantic here: when the underlying ability denies for action
28- // X, retry with each aliased action. The retry covers both OSS fallback
29- // (scope-based buildJwtAbility) and enterprise (DB/CASL-based) paths
30- // transparently — neither implementation needs to know about aliases.
27+ // X, retry with each aliased action.
3128const ACTION_ALIASES : Record < string , readonly string [ ] > = {
3229 trigger : [ "write" ] ,
3330 batchTrigger : [ "write" ] ,
@@ -45,7 +42,7 @@ export function withActionAliases(underlying: RbacAbility): RbacAbility {
4542 } ;
4643}
4744
48- // Loads the enterprise plugin lazily; falls back to the OSS implementation if not installed.
45+ // Loads the plugin lazily; falls back to the fallback implementation if not installed.
4946// Synchronous create() avoids top-level await (not supported in the webapp's CJS build).
5047class LazyController implements RoleBaseAccessController {
5148 private readonly _init : Promise < RoleBaseAccessController > ;
@@ -66,8 +63,34 @@ class LazyController implements RoleBaseAccessController {
6663 const moduleName = "@triggerdotdev/plugins/rbac" ;
6764 const module = await import ( moduleName ) ;
6865 const plugin : RoleBasedAccessControlPlugin = module . default ;
66+ console . log ( "RBAC: using enterprise plugin implementation" ) ;
6967 return plugin . create ( helpers ) ;
70- } catch {
68+ } catch ( err ) {
69+ // The dynamic import either succeeded (enterprise tier) or failed
70+ // for one of two distinct reasons. Distinguishing them is critical
71+ // for debugging — silently swallowing the error here is what
72+ // produced "why is the fallback being used?" mysteries before.
73+ //
74+ // 1. Module-not-found — expected for OSS deployments where the
75+ // cloud plugin isn't installed. Logged at info level only when
76+ // RBAC_LOG_FALLBACK=1 so production OSS logs stay quiet.
77+ // 2. Anything else (transitive dep missing, init error, syntax
78+ // error in the plugin's dist, etc.) — a real bug. Always
79+ // logged loudly so it surfaces in CI / production logs.
80+ const code = ( err as NodeJS . ErrnoException | undefined ) ?. code ;
81+ const isModuleNotFound = code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" ;
82+ if ( ! isModuleNotFound ) {
83+ console . error (
84+ "RBAC: enterprise plugin found but failed to load; falling back to OSS implementation" ,
85+ err
86+ ) ;
87+ } else if ( process . env . RBAC_LOG_FALLBACK === "1" ) {
88+ console . log (
89+ "RBAC: enterprise plugin not installed (ERR_MODULE_NOT_FOUND); using OSS fallback"
90+ ) ;
91+ } else {
92+ console . log ( `RBAC: using fallback implementation. ${ err } ` ) ;
93+ }
7194 return new RoleBaseAccessFallback ( prisma ) . create ( helpers ) ;
7295 }
7396 }
@@ -115,7 +138,9 @@ class LazyController implements RoleBaseAccessController {
115138 return auth ;
116139 }
117140
118- async allPermissions ( ...args : Parameters < RoleBaseAccessController [ "allPermissions" ] > ) : Promise < Permission [ ] > {
141+ async allPermissions (
142+ ...args : Parameters < RoleBaseAccessController [ "allPermissions" ] >
143+ ) : Promise < Permission [ ] > {
119144 return ( await this . c ( ) ) . allPermissions ( ...args ) ;
120145 }
121146
@@ -141,7 +166,9 @@ class LazyController implements RoleBaseAccessController {
141166 return ( await this . c ( ) ) . deleteRole ( ...args ) ;
142167 }
143168
144- async getUserRole ( ...args : Parameters < RoleBaseAccessController [ "getUserRole" ] > ) : Promise < Role | null > {
169+ async getUserRole (
170+ ...args : Parameters < RoleBaseAccessController [ "getUserRole" ] >
171+ ) : Promise < Role | null > {
145172 return ( await this . c ( ) ) . getUserRole ( ...args ) ;
146173 }
147174
@@ -157,7 +184,9 @@ class LazyController implements RoleBaseAccessController {
157184 return ( await this . c ( ) ) . removeUserRole ( ...args ) ;
158185 }
159186
160- async getTokenRole ( ...args : Parameters < RoleBaseAccessController [ "getTokenRole" ] > ) : Promise < Role | null > {
187+ async getTokenRole (
188+ ...args : Parameters < RoleBaseAccessController [ "getTokenRole" ] >
189+ ) : Promise < Role | null > {
161190 return ( await this . c ( ) ) . getTokenRole ( ...args ) ;
162191 }
163192
0 commit comments