@krutai/rbac 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,440 @@
1
+ 'use strict';
2
+
3
+ var krutai = require('krutai');
4
+
5
+ // src/errors.ts
6
+ var RBACError = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "RBACError";
10
+ Object.setPrototypeOf(this, new.target.prototype);
11
+ }
12
+ };
13
+ var PermissionDeniedError = class extends RBACError {
14
+ /** The permission that was required but not held */
15
+ permission;
16
+ /** The roles that were evaluated */
17
+ roles;
18
+ constructor(permission, roles) {
19
+ super(
20
+ `Permission denied: "${permission}" is required. User has roles: [${roles.join(", ") || "none"}]`
21
+ );
22
+ this.name = "PermissionDeniedError";
23
+ this.permission = permission;
24
+ this.roles = roles;
25
+ Object.setPrototypeOf(this, new.target.prototype);
26
+ }
27
+ };
28
+ var RoleNotFoundError = class extends RBACError {
29
+ /** The name of the role that was not found */
30
+ roleName;
31
+ constructor(roleName) {
32
+ super(`Role not found: "${roleName}"`);
33
+ this.name = "RoleNotFoundError";
34
+ this.roleName = roleName;
35
+ Object.setPrototypeOf(this, new.target.prototype);
36
+ }
37
+ };
38
+ var CircularInheritanceError = class extends RBACError {
39
+ /** The chain of roles that form the cycle */
40
+ chain;
41
+ constructor(chain) {
42
+ super(`Circular role inheritance detected: ${chain.join(" \u2192 ")}`);
43
+ this.name = "CircularInheritanceError";
44
+ this.chain = chain;
45
+ Object.setPrototypeOf(this, new.target.prototype);
46
+ }
47
+ };
48
+
49
+ // src/rbac.ts
50
+ var RBACManager = class {
51
+ roles = /* @__PURE__ */ new Map();
52
+ defaultRole;
53
+ /** Cache of resolved permissions per role to avoid repeated traversal */
54
+ permissionCache = /* @__PURE__ */ new Map();
55
+ constructor(config) {
56
+ this.defaultRole = config.defaultRole;
57
+ for (const role of config.roles) {
58
+ this.roles.set(role.name, role);
59
+ }
60
+ }
61
+ // -------------------------------------------------------------------------
62
+ // Role Management
63
+ // -------------------------------------------------------------------------
64
+ /**
65
+ * Register a new role. Clears the permission cache.
66
+ */
67
+ addRole(role) {
68
+ this.roles.set(role.name, role);
69
+ this.permissionCache.clear();
70
+ }
71
+ /**
72
+ * Remove a role by name. Clears the permission cache.
73
+ * @throws {RoleNotFoundError} if the role does not exist
74
+ */
75
+ removeRole(name) {
76
+ if (!this.roles.has(name)) {
77
+ throw new RoleNotFoundError(name);
78
+ }
79
+ this.roles.delete(name);
80
+ this.permissionCache.clear();
81
+ }
82
+ /**
83
+ * Retrieve a role definition by name.
84
+ */
85
+ getRole(name) {
86
+ return this.roles.get(name);
87
+ }
88
+ /**
89
+ * Returns all registered roles as an array.
90
+ */
91
+ getAllRoles() {
92
+ return Array.from(this.roles.values());
93
+ }
94
+ // -------------------------------------------------------------------------
95
+ // Permission Resolution
96
+ // -------------------------------------------------------------------------
97
+ /**
98
+ * Resolves the full set of permissions for a single role,
99
+ * including all inherited permissions (with cycle detection).
100
+ *
101
+ * @throws {RoleNotFoundError} if the role does not exist
102
+ * @throws {CircularInheritanceError} if a circular inheritance chain is detected
103
+ */
104
+ getPermissionsForRole(roleName) {
105
+ if (!this.roles.has(roleName)) {
106
+ throw new RoleNotFoundError(roleName);
107
+ }
108
+ if (this.permissionCache.has(roleName)) {
109
+ return this.permissionCache.get(roleName);
110
+ }
111
+ const resolved = this.resolvePermissions(roleName, []);
112
+ this.permissionCache.set(roleName, resolved);
113
+ return resolved;
114
+ }
115
+ /**
116
+ * Resolves the union of all permissions across multiple roles.
117
+ */
118
+ getPermissionsForRoles(roleNames) {
119
+ const combined = /* @__PURE__ */ new Set();
120
+ for (const name of roleNames) {
121
+ for (const perm of this.getPermissionsForRole(name)) {
122
+ combined.add(perm);
123
+ }
124
+ }
125
+ return combined;
126
+ }
127
+ // -------------------------------------------------------------------------
128
+ // Permission Checks
129
+ // -------------------------------------------------------------------------
130
+ /**
131
+ * Check whether the context has a specific permission.
132
+ * Supports wildcard permissions (e.g. "*:*" or "posts:*").
133
+ */
134
+ hasPermission(context, permission, _opts) {
135
+ const roles = this.resolveContextRoles(context);
136
+ const userPerms = this.getPermissionsForRoles(roles);
137
+ return this.matchPermission(userPerms, permission);
138
+ }
139
+ /**
140
+ * Check whether the context has AT LEAST ONE of the given permissions.
141
+ */
142
+ hasAnyPermission(context, permissions) {
143
+ return permissions.some((p) => this.hasPermission(context, p));
144
+ }
145
+ /**
146
+ * Check whether the context has ALL of the given permissions.
147
+ */
148
+ hasAllPermissions(context, permissions) {
149
+ return permissions.every((p) => this.hasPermission(context, p));
150
+ }
151
+ /**
152
+ * Check whether the context has a specific role assigned.
153
+ */
154
+ hasRole(context, roleName) {
155
+ const roles = this.resolveContextRoles(context);
156
+ return roles.includes(roleName);
157
+ }
158
+ /**
159
+ * Check whether the context has AT LEAST ONE of the given roles.
160
+ */
161
+ hasAnyRole(context, roleNames) {
162
+ return roleNames.some((r) => this.hasRole(context, r));
163
+ }
164
+ /**
165
+ * Alias for `hasPermission`. Reads naturally in conditional expressions.
166
+ *
167
+ * @example
168
+ * if (rbac.can(ctx, 'posts:delete')) { ... }
169
+ */
170
+ can(context, permission) {
171
+ return this.hasPermission(context, permission);
172
+ }
173
+ /**
174
+ * Inverse of `can`. Reads naturally in guard expressions.
175
+ *
176
+ * @example
177
+ * if (rbac.cannot(ctx, 'posts:delete')) throw new PermissionDeniedError(...);
178
+ */
179
+ cannot(context, permission) {
180
+ return !this.hasPermission(context, permission);
181
+ }
182
+ /**
183
+ * Detailed permission check that returns a result object with context.
184
+ */
185
+ check(context, permissions, opts = {}) {
186
+ const { requireAll = false } = opts;
187
+ const roles = this.resolveContextRoles(context);
188
+ const userPerms = this.getPermissionsForRoles(roles);
189
+ const missing = permissions.filter(
190
+ (p) => !this.matchPermission(userPerms, p)
191
+ );
192
+ const granted = requireAll ? missing.length === 0 : missing.length < permissions.length;
193
+ return {
194
+ granted,
195
+ permissions,
196
+ roles,
197
+ missing: missing.length > 0 ? missing : void 0
198
+ };
199
+ }
200
+ // -------------------------------------------------------------------------
201
+ // Private Helpers
202
+ // -------------------------------------------------------------------------
203
+ resolveContextRoles(context) {
204
+ if (context.roles.length === 0 && this.defaultRole) {
205
+ return [this.defaultRole];
206
+ }
207
+ return context.roles;
208
+ }
209
+ resolvePermissions(roleName, visited) {
210
+ if (visited.includes(roleName)) {
211
+ throw new CircularInheritanceError([...visited, roleName]);
212
+ }
213
+ const role = this.roles.get(roleName);
214
+ if (!role) {
215
+ throw new RoleNotFoundError(roleName);
216
+ }
217
+ const perms = new Set(role.permissions);
218
+ const nextVisited = [...visited, roleName];
219
+ for (const parentName of role.inherits ?? []) {
220
+ for (const perm of this.resolvePermissions(parentName, nextVisited)) {
221
+ perms.add(perm);
222
+ }
223
+ }
224
+ return perms;
225
+ }
226
+ /**
227
+ * Matches a required permission against the user's permission set,
228
+ * supporting wildcard segments ("*").
229
+ *
230
+ * Wildcard rules:
231
+ * - "*:*" matches everything
232
+ * - "posts:*" matches any action on "posts"
233
+ * - "*:read" matches "read" on any resource
234
+ */
235
+ matchPermission(userPerms, required) {
236
+ if (userPerms.has(required)) return true;
237
+ if (userPerms.has("*:*") || userPerms.has("*")) return true;
238
+ const [reqResource, reqAction] = required.split(":");
239
+ for (const perm of userPerms) {
240
+ const [permResource, permAction] = perm.split(":");
241
+ const resourceMatch = permResource === "*" || permResource === reqResource;
242
+ const actionMatch = permAction === "*" || permAction === reqAction;
243
+ if (resourceMatch && actionMatch) return true;
244
+ }
245
+ return false;
246
+ }
247
+ };
248
+
249
+ // src/role.ts
250
+ function defineRole(config) {
251
+ return config;
252
+ }
253
+ function definePermission(resource, action) {
254
+ return `${resource}:${action}`;
255
+ }
256
+ function crudPermissions(resource) {
257
+ return [
258
+ `${resource}:create`,
259
+ `${resource}:read`,
260
+ `${resource}:update`,
261
+ `${resource}:delete`
262
+ ];
263
+ }
264
+ function wildcardPermission(resource) {
265
+ return `${resource}:*`;
266
+ }
267
+ var GUEST_ROLE = defineRole({
268
+ name: "guest",
269
+ description: "Unauthenticated or anonymous user with read-only public access",
270
+ permissions: ["public:read"]
271
+ });
272
+ var USER_ROLE = defineRole({
273
+ name: "user",
274
+ description: "Standard authenticated user",
275
+ inherits: ["guest"],
276
+ permissions: [
277
+ "profile:read",
278
+ "profile:update",
279
+ "posts:read",
280
+ "posts:create",
281
+ "comments:read",
282
+ "comments:create"
283
+ ]
284
+ });
285
+ var MODERATOR_ROLE = defineRole({
286
+ name: "moderator",
287
+ description: "Can review and moderate user-generated content",
288
+ inherits: ["user"],
289
+ permissions: [
290
+ "posts:update",
291
+ "posts:delete",
292
+ "comments:update",
293
+ "comments:delete",
294
+ "users:read"
295
+ ]
296
+ });
297
+ var ADMIN_ROLE = defineRole({
298
+ name: "admin",
299
+ description: "Administrator with full control over application resources",
300
+ inherits: ["moderator"],
301
+ permissions: [
302
+ "users:create",
303
+ "users:update",
304
+ "users:delete",
305
+ "roles:read",
306
+ "roles:assign",
307
+ "settings:read",
308
+ "settings:update"
309
+ ]
310
+ });
311
+ var SUPER_ADMIN_ROLE = defineRole({
312
+ name: "super_admin",
313
+ description: "Unrestricted access to all resources and actions",
314
+ inherits: ["admin"],
315
+ permissions: [
316
+ wildcardPermission("*"),
317
+ // grants all permissions
318
+ "roles:create",
319
+ "roles:delete",
320
+ "settings:delete",
321
+ "system:manage"
322
+ ]
323
+ });
324
+ var DEFAULT_ROLES = [
325
+ GUEST_ROLE,
326
+ USER_ROLE,
327
+ MODERATOR_ROLE,
328
+ ADMIN_ROLE,
329
+ SUPER_ADMIN_ROLE
330
+ ];
331
+
332
+ // src/guards.ts
333
+ function createPermissionGuard(rbac, permission) {
334
+ return (context) => rbac.can(context, permission);
335
+ }
336
+ function createRoleGuard(rbac, roleName) {
337
+ return (context) => rbac.hasRole(context, roleName);
338
+ }
339
+ function createAllPermissionsGuard(rbac, permissions) {
340
+ return (context) => rbac.hasAllPermissions(context, permissions);
341
+ }
342
+ function createAnyPermissionGuard(rbac, permissions) {
343
+ return (context) => rbac.hasAnyPermission(context, permissions);
344
+ }
345
+ function withPermission(rbac, permission, handler, onDenied) {
346
+ return async (context) => {
347
+ if (rbac.cannot(context.rbac, permission)) {
348
+ if (onDenied) {
349
+ return onDenied(context, permission);
350
+ }
351
+ throw new PermissionDeniedError(permission, context.rbac.roles);
352
+ }
353
+ return handler(context);
354
+ };
355
+ }
356
+ function requirePermission(rbac, permission) {
357
+ return function middleware(req, res, next) {
358
+ const context = req.rbacContext;
359
+ if (!context) {
360
+ res.status(401).json({
361
+ error: "Unauthorized",
362
+ message: "No RBAC context found on request. Ensure auth middleware runs first."
363
+ });
364
+ return;
365
+ }
366
+ if (rbac.cannot(context, permission)) {
367
+ res.status(403).json({
368
+ error: "Forbidden",
369
+ message: `Permission denied: "${permission}" is required.`,
370
+ roles: context.roles
371
+ });
372
+ return;
373
+ }
374
+ next();
375
+ };
376
+ }
377
+ function requireRole(rbac, roleName) {
378
+ return function middleware(req, res, next) {
379
+ const context = req.rbacContext;
380
+ if (!context) {
381
+ res.status(401).json({
382
+ error: "Unauthorized",
383
+ message: "No RBAC context found on request."
384
+ });
385
+ return;
386
+ }
387
+ if (!rbac.hasRole(context, roleName)) {
388
+ res.status(403).json({
389
+ error: "Forbidden",
390
+ message: `Role "${roleName}" is required.`,
391
+ roles: context.roles
392
+ });
393
+ return;
394
+ }
395
+ next();
396
+ };
397
+ }
398
+ var VERSION = "0.1.0";
399
+
400
+ Object.defineProperty(exports, "ApiKeyValidationError", {
401
+ enumerable: true,
402
+ get: function () { return krutai.ApiKeyValidationError; }
403
+ });
404
+ Object.defineProperty(exports, "createApiKeyChecker", {
405
+ enumerable: true,
406
+ get: function () { return krutai.createApiKeyChecker; }
407
+ });
408
+ Object.defineProperty(exports, "validateApiKeyFormat", {
409
+ enumerable: true,
410
+ get: function () { return krutai.validateApiKeyFormat; }
411
+ });
412
+ Object.defineProperty(exports, "validateApiKeyWithService", {
413
+ enumerable: true,
414
+ get: function () { return krutai.validateApiKeyWithService; }
415
+ });
416
+ exports.ADMIN_ROLE = ADMIN_ROLE;
417
+ exports.CircularInheritanceError = CircularInheritanceError;
418
+ exports.DEFAULT_ROLES = DEFAULT_ROLES;
419
+ exports.GUEST_ROLE = GUEST_ROLE;
420
+ exports.MODERATOR_ROLE = MODERATOR_ROLE;
421
+ exports.PermissionDeniedError = PermissionDeniedError;
422
+ exports.RBACError = RBACError;
423
+ exports.RBACManager = RBACManager;
424
+ exports.RoleNotFoundError = RoleNotFoundError;
425
+ exports.SUPER_ADMIN_ROLE = SUPER_ADMIN_ROLE;
426
+ exports.USER_ROLE = USER_ROLE;
427
+ exports.VERSION = VERSION;
428
+ exports.createAllPermissionsGuard = createAllPermissionsGuard;
429
+ exports.createAnyPermissionGuard = createAnyPermissionGuard;
430
+ exports.createPermissionGuard = createPermissionGuard;
431
+ exports.createRoleGuard = createRoleGuard;
432
+ exports.crudPermissions = crudPermissions;
433
+ exports.definePermission = definePermission;
434
+ exports.defineRole = defineRole;
435
+ exports.requirePermission = requirePermission;
436
+ exports.requireRole = requireRole;
437
+ exports.wildcardPermission = wildcardPermission;
438
+ exports.withPermission = withPermission;
439
+ //# sourceMappingURL=index.js.map
440
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/rbac.ts","../src/role.ts","../src/guards.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Custom error classes for @krutai/rbac\n */\n\n/**\n * Base error class for all RBAC-related errors\n */\nexport class RBACError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RBACError';\n // Maintain proper prototype chain in transpiled environments\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a user lacks the required permission to perform an action\n */\nexport class PermissionDeniedError extends RBACError {\n /** The permission that was required but not held */\n public readonly permission: string;\n /** The roles that were evaluated */\n public readonly roles: string[];\n\n constructor(permission: string, roles: string[]) {\n super(\n `Permission denied: \"${permission}\" is required. ` +\n `User has roles: [${roles.join(', ') || 'none'}]`\n );\n this.name = 'PermissionDeniedError';\n this.permission = permission;\n this.roles = roles;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a referenced role does not exist in the registry\n */\nexport class RoleNotFoundError extends RBACError {\n /** The name of the role that was not found */\n public readonly roleName: string;\n\n constructor(roleName: string) {\n super(`Role not found: \"${roleName}\"`);\n this.name = 'RoleNotFoundError';\n this.roleName = roleName;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a circular inheritance chain is detected in role definitions\n */\nexport class CircularInheritanceError extends RBACError {\n /** The chain of roles that form the cycle */\n public readonly chain: string[];\n\n constructor(chain: string[]) {\n super(`Circular role inheritance detected: ${chain.join(' → ')}`);\n this.name = 'CircularInheritanceError';\n this.chain = chain;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","/**\n * RBACManager — core class for role-based access control\n */\n\nimport type {\n Role,\n Permission,\n RBACConfig,\n RBACContext,\n CheckOptions,\n PermissionCheckResult,\n} from './types.js';\nimport { RoleNotFoundError, CircularInheritanceError } from './errors.js';\n\n/**\n * The main RBAC engine. Manages roles, resolves inheritance chains,\n * and evaluates permission checks against a user context.\n *\n * @example\n * const rbac = new RBACManager({\n * roles: [\n * { name: 'user', permissions: ['posts:read'] },\n * { name: 'admin', permissions: ['posts:delete'], inherits: ['user'] },\n * ],\n * });\n *\n * const ctx = { roles: ['admin'] };\n * rbac.can(ctx, 'posts:read'); // true (inherited from user)\n * rbac.can(ctx, 'posts:delete'); // true\n * rbac.can(ctx, 'users:manage'); // false\n */\nexport class RBACManager {\n private readonly roles: Map<string, Role> = new Map();\n private readonly defaultRole?: string;\n /** Cache of resolved permissions per role to avoid repeated traversal */\n private readonly permissionCache: Map<string, Set<Permission>> = new Map();\n\n constructor(config: RBACConfig) {\n this.defaultRole = config.defaultRole;\n for (const role of config.roles) {\n this.roles.set(role.name, role);\n }\n }\n\n // -------------------------------------------------------------------------\n // Role Management\n // -------------------------------------------------------------------------\n\n /**\n * Register a new role. Clears the permission cache.\n */\n addRole(role: Role): void {\n this.roles.set(role.name, role);\n this.permissionCache.clear();\n }\n\n /**\n * Remove a role by name. Clears the permission cache.\n * @throws {RoleNotFoundError} if the role does not exist\n */\n removeRole(name: string): void {\n if (!this.roles.has(name)) {\n throw new RoleNotFoundError(name);\n }\n this.roles.delete(name);\n this.permissionCache.clear();\n }\n\n /**\n * Retrieve a role definition by name.\n */\n getRole(name: string): Role | undefined {\n return this.roles.get(name);\n }\n\n /**\n * Returns all registered roles as an array.\n */\n getAllRoles(): Role[] {\n return Array.from(this.roles.values());\n }\n\n // -------------------------------------------------------------------------\n // Permission Resolution\n // -------------------------------------------------------------------------\n\n /**\n * Resolves the full set of permissions for a single role,\n * including all inherited permissions (with cycle detection).\n *\n * @throws {RoleNotFoundError} if the role does not exist\n * @throws {CircularInheritanceError} if a circular inheritance chain is detected\n */\n getPermissionsForRole(roleName: string): Set<Permission> {\n if (!this.roles.has(roleName)) {\n throw new RoleNotFoundError(roleName);\n }\n if (this.permissionCache.has(roleName)) {\n return this.permissionCache.get(roleName)!;\n }\n const resolved = this.resolvePermissions(roleName, []);\n this.permissionCache.set(roleName, resolved);\n return resolved;\n }\n\n /**\n * Resolves the union of all permissions across multiple roles.\n */\n getPermissionsForRoles(roleNames: string[]): Set<Permission> {\n const combined = new Set<Permission>();\n for (const name of roleNames) {\n for (const perm of this.getPermissionsForRole(name)) {\n combined.add(perm);\n }\n }\n return combined;\n }\n\n // -------------------------------------------------------------------------\n // Permission Checks\n // -------------------------------------------------------------------------\n\n /**\n * Check whether the context has a specific permission.\n * Supports wildcard permissions (e.g. \"*:*\" or \"posts:*\").\n */\n hasPermission(\n context: RBACContext,\n permission: Permission,\n _opts?: CheckOptions\n ): boolean {\n const roles = this.resolveContextRoles(context);\n const userPerms = this.getPermissionsForRoles(roles);\n return this.matchPermission(userPerms, permission);\n }\n\n /**\n * Check whether the context has AT LEAST ONE of the given permissions.\n */\n hasAnyPermission(context: RBACContext, permissions: Permission[]): boolean {\n return permissions.some((p) => this.hasPermission(context, p));\n }\n\n /**\n * Check whether the context has ALL of the given permissions.\n */\n hasAllPermissions(context: RBACContext, permissions: Permission[]): boolean {\n return permissions.every((p) => this.hasPermission(context, p));\n }\n\n /**\n * Check whether the context has a specific role assigned.\n */\n hasRole(context: RBACContext, roleName: string): boolean {\n const roles = this.resolveContextRoles(context);\n return roles.includes(roleName);\n }\n\n /**\n * Check whether the context has AT LEAST ONE of the given roles.\n */\n hasAnyRole(context: RBACContext, roleNames: string[]): boolean {\n return roleNames.some((r) => this.hasRole(context, r));\n }\n\n /**\n * Alias for `hasPermission`. Reads naturally in conditional expressions.\n *\n * @example\n * if (rbac.can(ctx, 'posts:delete')) { ... }\n */\n can(context: RBACContext, permission: Permission): boolean {\n return this.hasPermission(context, permission);\n }\n\n /**\n * Inverse of `can`. Reads naturally in guard expressions.\n *\n * @example\n * if (rbac.cannot(ctx, 'posts:delete')) throw new PermissionDeniedError(...);\n */\n cannot(context: RBACContext, permission: Permission): boolean {\n return !this.hasPermission(context, permission);\n }\n\n /**\n * Detailed permission check that returns a result object with context.\n */\n check(\n context: RBACContext,\n permissions: Permission[],\n opts: CheckOptions = {}\n ): PermissionCheckResult {\n const { requireAll = false } = opts;\n const roles = this.resolveContextRoles(context);\n const userPerms = this.getPermissionsForRoles(roles);\n\n const missing = permissions.filter(\n (p) => !this.matchPermission(userPerms, p)\n );\n\n const granted = requireAll\n ? missing.length === 0\n : missing.length < permissions.length;\n\n return {\n granted,\n permissions,\n roles,\n missing: missing.length > 0 ? missing : undefined,\n };\n }\n\n // -------------------------------------------------------------------------\n // Private Helpers\n // -------------------------------------------------------------------------\n\n private resolveContextRoles(context: RBACContext): string[] {\n if (context.roles.length === 0 && this.defaultRole) {\n return [this.defaultRole];\n }\n return context.roles;\n }\n\n private resolvePermissions(\n roleName: string,\n visited: string[]\n ): Set<Permission> {\n if (visited.includes(roleName)) {\n throw new CircularInheritanceError([...visited, roleName]);\n }\n\n const role = this.roles.get(roleName);\n if (!role) {\n throw new RoleNotFoundError(roleName);\n }\n\n const perms = new Set<Permission>(role.permissions);\n const nextVisited = [...visited, roleName];\n\n for (const parentName of role.inherits ?? []) {\n for (const perm of this.resolvePermissions(parentName, nextVisited)) {\n perms.add(perm);\n }\n }\n\n return perms;\n }\n\n /**\n * Matches a required permission against the user's permission set,\n * supporting wildcard segments (\"*\").\n *\n * Wildcard rules:\n * - \"*:*\" matches everything\n * - \"posts:*\" matches any action on \"posts\"\n * - \"*:read\" matches \"read\" on any resource\n */\n private matchPermission(\n userPerms: Set<Permission>,\n required: Permission\n ): boolean {\n // Exact match\n if (userPerms.has(required)) return true;\n\n // Global wildcard\n if (userPerms.has('*:*') || userPerms.has('*')) return true;\n\n const [reqResource, reqAction] = required.split(':');\n\n for (const perm of userPerms) {\n const [permResource, permAction] = perm.split(':');\n\n const resourceMatch =\n permResource === '*' || permResource === reqResource;\n const actionMatch =\n permAction === '*' || permAction === reqAction;\n\n if (resourceMatch && actionMatch) return true;\n }\n\n return false;\n }\n}\n","/**\n * Role and permission definition helpers for @krutai/rbac\n */\n\nimport type { Role, Permission } from './types.js';\n\n/**\n * Type-safe helper to define a role.\n * Useful for getting autocomplete and validation when building role configs.\n *\n * @example\n * const editorRole = defineRole({\n * name: 'editor',\n * permissions: ['posts:read', 'posts:write'],\n * inherits: ['user'],\n * });\n */\nexport function defineRole(config: Role): Role {\n return config;\n}\n\n/**\n * Creates a typed permission string in the format \"resource:action\".\n *\n * @example\n * const canReadPosts = definePermission('posts', 'read'); // \"posts:read\"\n * const canDeleteUsers = definePermission('users', 'delete'); // \"users:delete\"\n */\nexport function definePermission(resource: string, action: string): Permission {\n return `${resource}:${action}`;\n}\n\n/**\n * Creates a set of CRUD permissions for a given resource.\n *\n * @example\n * const postPermissions = crudPermissions('posts');\n * // [\"posts:create\", \"posts:read\", \"posts:update\", \"posts:delete\"]\n */\nexport function crudPermissions(resource: string): Permission[] {\n return [\n `${resource}:create`,\n `${resource}:read`,\n `${resource}:update`,\n `${resource}:delete`,\n ];\n}\n\n/**\n * Creates a wildcard permission for a resource (grants all actions).\n *\n * @example\n * wildcardPermission('posts') // \"posts:*\"\n */\nexport function wildcardPermission(resource: string): Permission {\n return `${resource}:*`;\n}\n\n// ---------------------------------------------------------------------------\n// Pre-built common roles\n// ---------------------------------------------------------------------------\n\n/**\n * Guest role — read-only access to public resources\n */\nexport const GUEST_ROLE: Role = defineRole({\n name: 'guest',\n description: 'Unauthenticated or anonymous user with read-only public access',\n permissions: ['public:read'],\n});\n\n/**\n * User role — standard authenticated user\n */\nexport const USER_ROLE: Role = defineRole({\n name: 'user',\n description: 'Standard authenticated user',\n inherits: ['guest'],\n permissions: [\n 'profile:read',\n 'profile:update',\n 'posts:read',\n 'posts:create',\n 'comments:read',\n 'comments:create',\n ],\n});\n\n/**\n * Moderator role — can manage user-generated content\n */\nexport const MODERATOR_ROLE: Role = defineRole({\n name: 'moderator',\n description: 'Can review and moderate user-generated content',\n inherits: ['user'],\n permissions: [\n 'posts:update',\n 'posts:delete',\n 'comments:update',\n 'comments:delete',\n 'users:read',\n ],\n});\n\n/**\n * Admin role — full control over application resources\n */\nexport const ADMIN_ROLE: Role = defineRole({\n name: 'admin',\n description: 'Administrator with full control over application resources',\n inherits: ['moderator'],\n permissions: [\n 'users:create',\n 'users:update',\n 'users:delete',\n 'roles:read',\n 'roles:assign',\n 'settings:read',\n 'settings:update',\n ],\n});\n\n/**\n * Super Admin role — unrestricted access to everything\n */\nexport const SUPER_ADMIN_ROLE: Role = defineRole({\n name: 'super_admin',\n description: 'Unrestricted access to all resources and actions',\n inherits: ['admin'],\n permissions: [\n wildcardPermission('*'), // grants all permissions\n 'roles:create',\n 'roles:delete',\n 'settings:delete',\n 'system:manage',\n ],\n});\n\n/**\n * All pre-built roles in hierarchy order (least → most privileged)\n */\nexport const DEFAULT_ROLES: Role[] = [\n GUEST_ROLE,\n USER_ROLE,\n MODERATOR_ROLE,\n ADMIN_ROLE,\n SUPER_ADMIN_ROLE,\n];\n","/**\n * Guard and middleware helpers for @krutai/rbac\n *\n * These utilities make it easy to integrate RBAC checks into\n * request handlers, middleware chains, and framework-agnostic guards.\n */\n\nimport type {\n Permission,\n RBACContext,\n GuardFn,\n HandlerFn,\n DenyHandlerFn,\n} from './types.js';\nimport { RBACManager } from './rbac.js';\nimport { PermissionDeniedError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Guard factories\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a guard function that checks a single permission.\n *\n * @example\n * const canDeletePosts = createPermissionGuard(rbac, 'posts:delete');\n * if (!canDeletePosts(ctx)) throw new PermissionDeniedError('posts:delete', ctx.roles);\n */\nexport function createPermissionGuard(\n rbac: RBACManager,\n permission: Permission\n): GuardFn {\n return (context: RBACContext) => rbac.can(context, permission);\n}\n\n/**\n * Creates a guard function that checks whether the context has a specific role.\n *\n * @example\n * const isAdmin = createRoleGuard(rbac, 'admin');\n * if (!isAdmin(ctx)) throw new PermissionDeniedError('admin role', ctx.roles);\n */\nexport function createRoleGuard(\n rbac: RBACManager,\n roleName: string\n): GuardFn {\n return (context: RBACContext) => rbac.hasRole(context, roleName);\n}\n\n/**\n * Creates a guard that requires ALL of the given permissions.\n *\n * @example\n * const canManagePosts = createAllPermissionsGuard(rbac, ['posts:read', 'posts:delete']);\n */\nexport function createAllPermissionsGuard(\n rbac: RBACManager,\n permissions: Permission[]\n): GuardFn {\n return (context: RBACContext) => rbac.hasAllPermissions(context, permissions);\n}\n\n/**\n * Creates a guard that requires AT LEAST ONE of the given permissions.\n *\n * @example\n * const canViewContent = createAnyPermissionGuard(rbac, ['posts:read', 'drafts:read']);\n */\nexport function createAnyPermissionGuard(\n rbac: RBACManager,\n permissions: Permission[]\n): GuardFn {\n return (context: RBACContext) => rbac.hasAnyPermission(context, permissions);\n}\n\n// ---------------------------------------------------------------------------\n// Handler wrappers\n// ---------------------------------------------------------------------------\n\n/**\n * Wraps a handler function with a permission check.\n * Calls `onDenied` (or throws `PermissionDeniedError`) if the check fails.\n *\n * @example\n * const deletePost = withPermission(\n * rbac,\n * 'posts:delete',\n * async (ctx) => { await db.posts.delete(ctx.postId); },\n * (ctx, perm) => { throw new Error(`Forbidden: ${perm}`); }\n * );\n */\nexport function withPermission<TContext extends { rbac: RBACContext }, TResult>(\n rbac: RBACManager,\n permission: Permission,\n handler: HandlerFn<TContext, TResult>,\n onDenied?: DenyHandlerFn<TContext, TResult>\n): HandlerFn<TContext, TResult> {\n return async (context: TContext): Promise<TResult> => {\n if (rbac.cannot(context.rbac, permission)) {\n if (onDenied) {\n return onDenied(context, permission);\n }\n throw new PermissionDeniedError(permission, context.rbac.roles);\n }\n return handler(context);\n };\n}\n\n// ---------------------------------------------------------------------------\n// Express / Next.js-style middleware factory\n// ---------------------------------------------------------------------------\n\n/**\n * Represents a minimal Express/Next.js-style request with an `rbacContext` property.\n * Attach an `RBACContext` to your request object before using this middleware.\n */\nexport interface RBACRequest {\n rbacContext?: RBACContext;\n}\n\n/**\n * Express/Next.js-compatible middleware factory.\n * Expects `req.rbacContext` to be populated by a preceding auth middleware.\n *\n * @example\n * // Express\n * app.delete('/posts/:id', requirePermission(rbac, 'posts:delete'), deletePostHandler);\n *\n * @example\n * // Next.js API route (pages router)\n * export default requirePermission(rbac, 'posts:delete')(handler);\n */\nexport function requirePermission(\n rbac: RBACManager,\n permission: Permission\n) {\n return function middleware(\n req: RBACRequest,\n res: { status: (code: number) => { json: (body: unknown) => void } },\n next: () => void\n ): void {\n const context = req.rbacContext;\n\n if (!context) {\n res.status(401).json({\n error: 'Unauthorized',\n message: 'No RBAC context found on request. Ensure auth middleware runs first.',\n });\n return;\n }\n\n if (rbac.cannot(context, permission)) {\n res.status(403).json({\n error: 'Forbidden',\n message: `Permission denied: \"${permission}\" is required.`,\n roles: context.roles,\n });\n return;\n }\n\n next();\n };\n}\n\n/**\n * Express/Next.js-compatible middleware factory for role checks.\n *\n * @example\n * app.get('/admin', requireRole(rbac, 'admin'), adminHandler);\n */\nexport function requireRole(rbac: RBACManager, roleName: string) {\n return function middleware(\n req: RBACRequest,\n res: { status: (code: number) => { json: (body: unknown) => void } },\n next: () => void\n ): void {\n const context = req.rbacContext;\n\n if (!context) {\n res.status(401).json({\n error: 'Unauthorized',\n message: 'No RBAC context found on request.',\n });\n return;\n }\n\n if (!rbac.hasRole(context, roleName)) {\n res.status(403).json({\n error: 'Forbidden',\n message: `Role \"${roleName}\" is required.`,\n roles: context.roles,\n });\n return;\n }\n\n next();\n };\n}\n","/**\n * @krutai/rbac — Role-Based Access Control for KrutAI\n *\n * A flexible, type-safe RBAC library with role inheritance,\n * wildcard permissions, and framework-agnostic guard helpers.\n *\n * @packageDocumentation\n */\n\n// Core manager\nexport { RBACManager } from './rbac.js';\n\n// Types\nexport type {\n Permission,\n Role,\n RBACConfig,\n RBACContext,\n CheckOptions,\n PermissionCheckResult,\n GuardFn,\n HandlerFn,\n DenyHandlerFn,\n} from './types.js';\n\n// Role & permission helpers\nexport {\n defineRole,\n definePermission,\n crudPermissions,\n wildcardPermission,\n // Pre-built roles\n GUEST_ROLE,\n USER_ROLE,\n MODERATOR_ROLE,\n ADMIN_ROLE,\n SUPER_ADMIN_ROLE,\n DEFAULT_ROLES,\n} from './role.js';\n\n// Guard & middleware helpers\nexport {\n createPermissionGuard,\n createRoleGuard,\n createAllPermissionsGuard,\n createAnyPermissionGuard,\n withPermission,\n requirePermission,\n requireRole,\n} from './guards.js';\nexport type { RBACRequest } from './guards.js';\n\n// Errors\nexport {\n RBACError,\n PermissionDeniedError,\n RoleNotFoundError,\n CircularInheritanceError,\n} from './errors.js';\n\n// Re-export validator utilities from krutai peer dependency\nexport {\n validateApiKeyFormat,\n validateApiKeyWithService,\n createApiKeyChecker,\n ApiKeyValidationError,\n} from 'krutai';\n\n// Package metadata\nexport const VERSION = '0.1.0';\n"]}