@jmruthers/pace-core 0.2.4 → 0.2.5

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.
Files changed (88) hide show
  1. package/dist/{api-GZHIDA4X.js → api-T6CBS7IO.js} +3 -2
  2. package/dist/cache-I72HKDOA.js +12 -0
  3. package/dist/cache-I72HKDOA.js.map +1 -0
  4. package/dist/{chunk-6ZQVSHKL.js → chunk-ANE4PDC2.js} +9 -158
  5. package/dist/chunk-ANE4PDC2.js.map +1 -0
  6. package/dist/chunk-MRRFJ6SA.js +161 -0
  7. package/dist/chunk-MRRFJ6SA.js.map +1 -0
  8. package/dist/{chunk-OKXMUYIB.js → chunk-O4T53L7X.js} +3 -3
  9. package/dist/{chunk-7JL3T7BO.js → chunk-UY7AM4QG.js} +3 -3
  10. package/dist/components.js +3 -2
  11. package/dist/components.js.map +1 -1
  12. package/dist/hooks.js +1 -1
  13. package/dist/index.js +4 -3
  14. package/dist/index.js.map +1 -1
  15. package/dist/rbac/index.d.ts +2 -0
  16. package/dist/rbac/index.js +18 -11
  17. package/dist/rbac/index.js.map +1 -1
  18. package/docs/api/classes/ErrorBoundary.md +1 -1
  19. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  20. package/docs/api/interfaces/AggregateConfig.md +1 -1
  21. package/docs/api/interfaces/ButtonProps.md +1 -1
  22. package/docs/api/interfaces/CardProps.md +1 -1
  23. package/docs/api/interfaces/ColorPalette.md +1 -1
  24. package/docs/api/interfaces/ColorShade.md +1 -1
  25. package/docs/api/interfaces/DataTableAction.md +1 -1
  26. package/docs/api/interfaces/DataTableColumn.md +1 -1
  27. package/docs/api/interfaces/DataTableProps.md +1 -1
  28. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  29. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  30. package/docs/api/interfaces/EventContextType.md +1 -1
  31. package/docs/api/interfaces/EventLogoProps.md +1 -1
  32. package/docs/api/interfaces/EventProviderProps.md +1 -1
  33. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  34. package/docs/api/interfaces/FileUploadProps.md +1 -1
  35. package/docs/api/interfaces/FooterProps.md +1 -1
  36. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  37. package/docs/api/interfaces/InputProps.md +1 -1
  38. package/docs/api/interfaces/LabelProps.md +1 -1
  39. package/docs/api/interfaces/LoginFormProps.md +1 -1
  40. package/docs/api/interfaces/NavigationItem.md +1 -1
  41. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  42. package/docs/api/interfaces/Organisation.md +1 -1
  43. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  44. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  45. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  46. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  47. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  48. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  49. package/docs/api/interfaces/PaletteData.md +1 -1
  50. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  51. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  52. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  53. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  54. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  55. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  56. package/docs/api/interfaces/StorageConfig.md +1 -1
  57. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  58. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  59. package/docs/api/interfaces/StorageListOptions.md +1 -1
  60. package/docs/api/interfaces/StorageListResult.md +1 -1
  61. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  62. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  63. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  64. package/docs/api/interfaces/StyleImport.md +1 -1
  65. package/docs/api/interfaces/ToastActionElement.md +1 -1
  66. package/docs/api/interfaces/ToastProps.md +1 -1
  67. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  68. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  69. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  70. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  71. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  72. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  73. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  74. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  75. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  76. package/docs/api/interfaces/UserEventAccess.md +1 -1
  77. package/docs/api/interfaces/UserMenuProps.md +1 -1
  78. package/docs/api/interfaces/UserProfile.md +1 -1
  79. package/docs/api/modules.md +2 -2
  80. package/package.json +1 -1
  81. package/src/rbac/api.ts +2 -0
  82. package/src/rbac/cache.ts +2 -0
  83. package/src/rbac/hooks.ts +7 -0
  84. package/src/rbac/types.ts +2 -0
  85. package/dist/chunk-6ZQVSHKL.js.map +0 -1
  86. /package/dist/{api-GZHIDA4X.js.map → api-T6CBS7IO.js.map} +0 -0
  87. /package/dist/{chunk-OKXMUYIB.js.map → chunk-O4T53L7X.js.map} +0 -0
  88. /package/dist/{chunk-7JL3T7BO.js.map → chunk-UY7AM4QG.js.map} +0 -0
@@ -16,7 +16,8 @@ import {
16
16
  isPermittedCached,
17
17
  isSuperAdmin,
18
18
  setupRBAC
19
- } from "./chunk-6ZQVSHKL.js";
19
+ } from "./chunk-ANE4PDC2.js";
20
+ import "./chunk-MRRFJ6SA.js";
20
21
  import "./chunk-7BNPOCLL.js";
21
22
  import "./chunk-PLDDJCW6.js";
22
23
  export {
@@ -38,4 +39,4 @@ export {
38
39
  isSuperAdmin,
39
40
  setupRBAC
40
41
  };
41
- //# sourceMappingURL=api-GZHIDA4X.js.map
42
+ //# sourceMappingURL=api-T6CBS7IO.js.map
@@ -0,0 +1,12 @@
1
+ import {
2
+ CACHE_PATTERNS,
3
+ RBACCache,
4
+ rbacCache
5
+ } from "./chunk-MRRFJ6SA.js";
6
+ import "./chunk-PLDDJCW6.js";
7
+ export {
8
+ CACHE_PATTERNS,
9
+ RBACCache,
10
+ rbacCache
11
+ };
12
+ //# sourceMappingURL=cache-I72HKDOA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,3 +1,8 @@
1
+ import {
2
+ CACHE_PATTERNS,
3
+ RBACCache,
4
+ rbacCache
5
+ } from "./chunk-MRRFJ6SA.js";
1
6
  import {
2
7
  createAuditManager,
3
8
  emitAuditEvent,
@@ -32,159 +37,6 @@ var RBACNotInitializedError = class extends RBACError {
32
37
  }
33
38
  };
34
39
 
35
- // src/rbac/cache.ts
36
- var RBACCache = class {
37
- constructor() {
38
- this.cache = /* @__PURE__ */ new Map();
39
- this.TTL = 60 * 1e3;
40
- // 60 seconds
41
- this.invalidationCallbacks = /* @__PURE__ */ new Set();
42
- }
43
- /**
44
- * Get a value from the cache
45
- *
46
- * @param key - Cache key
47
- * @returns Cached value or null if not found/expired
48
- */
49
- get(key) {
50
- const entry = this.cache.get(key);
51
- if (!entry) {
52
- return null;
53
- }
54
- if (Date.now() > entry.expires) {
55
- this.cache.delete(key);
56
- return null;
57
- }
58
- return entry.data;
59
- }
60
- /**
61
- * Set a value in the cache
62
- *
63
- * @param key - Cache key
64
- * @param data - Data to cache
65
- * @param ttl - Time to live in milliseconds (defaults to 60s)
66
- */
67
- set(key, data, ttl = this.TTL) {
68
- const expires = ttl <= 0 ? Date.now() - 1 : Date.now() + ttl;
69
- this.cache.set(key, {
70
- data,
71
- expires
72
- });
73
- }
74
- /**
75
- * Delete a specific key from the cache
76
- *
77
- * @param key - Cache key to delete
78
- */
79
- delete(key) {
80
- this.cache.delete(key);
81
- }
82
- /**
83
- * Invalidate cache entries matching a pattern
84
- *
85
- * @param pattern - Pattern to match against cache keys
86
- */
87
- invalidate(pattern) {
88
- const keysToDelete = [];
89
- for (const key of this.cache.keys()) {
90
- if (key.includes(pattern)) {
91
- keysToDelete.push(key);
92
- }
93
- }
94
- keysToDelete.forEach((key) => this.cache.delete(key));
95
- this.invalidationCallbacks.forEach((callback) => callback(pattern));
96
- }
97
- /**
98
- * Clear all cache entries
99
- */
100
- clear() {
101
- this.cache.clear();
102
- }
103
- /**
104
- * Get cache statistics
105
- */
106
- getStats() {
107
- return {
108
- size: this.cache.size,
109
- ttl: this.TTL,
110
- keys: Array.from(this.cache.keys())
111
- };
112
- }
113
- /**
114
- * Add an invalidation callback
115
- *
116
- * @param callback - Function to call when cache is invalidated
117
- */
118
- onInvalidate(callback) {
119
- this.invalidationCallbacks.add(callback);
120
- return () => {
121
- this.invalidationCallbacks.delete(callback);
122
- };
123
- }
124
- /**
125
- * Generate cache key for permission check
126
- *
127
- * @param key - Permission cache key object
128
- * @returns String cache key
129
- */
130
- static generatePermissionKey(key) {
131
- const parts = [
132
- "perm",
133
- key.userId,
134
- key.organisationId || "null",
135
- key.eventId || "null",
136
- key.appId || "null"
137
- ];
138
- return parts.join(":");
139
- }
140
- /**
141
- * Generate cache key for access level
142
- *
143
- * @param userId - User ID
144
- * @param organisationId - Organisation ID
145
- * @param eventId - Event ID (optional)
146
- * @param appId - App ID (optional)
147
- * @returns String cache key
148
- */
149
- static generateAccessLevelKey(userId, organisationId, eventId, appId) {
150
- const parts = [
151
- "access",
152
- userId,
153
- organisationId,
154
- eventId || "null",
155
- appId || "null"
156
- ];
157
- return parts.join(":");
158
- }
159
- /**
160
- * Generate cache key for permission map
161
- *
162
- * @param userId - User ID
163
- * @param organisationId - Organisation ID
164
- * @param eventId - Event ID (optional)
165
- * @param appId - App ID (optional)
166
- * @returns String cache key
167
- */
168
- static generatePermissionMapKey(userId, organisationId, eventId, appId) {
169
- const parts = [
170
- "map",
171
- userId,
172
- organisationId,
173
- eventId || "null",
174
- appId || "null"
175
- ];
176
- return parts.join(":");
177
- }
178
- };
179
- var rbacCache = new RBACCache();
180
- var CACHE_PATTERNS = {
181
- USER: (userId) => `user:${userId}`,
182
- ORGANISATION: (organisationId) => `org:${organisationId}`,
183
- EVENT: (eventId) => `event:${eventId}`,
184
- APP: (appId) => `app:${appId}`,
185
- PERMISSION: (userId, organisationId) => `perm:${userId}:${organisationId}`
186
- };
187
-
188
40
  // src/rbac/security.ts
189
41
  var RBACSecurityValidator = class {
190
42
  /**
@@ -1231,7 +1083,9 @@ async function isPermittedCached(input) {
1231
1083
  userId,
1232
1084
  organisationId: scope.organisationId,
1233
1085
  eventId: scope.eventId,
1234
- appId: scope.appId
1086
+ appId: scope.appId,
1087
+ permission,
1088
+ pageId
1235
1089
  });
1236
1090
  const cached = rbacCache.get(cacheKey);
1237
1091
  if (cached !== null) {
@@ -1314,9 +1168,6 @@ function clearCache() {
1314
1168
 
1315
1169
  export {
1316
1170
  OrganisationContextRequiredError,
1317
- RBACCache,
1318
- rbacCache,
1319
- CACHE_PATTERNS,
1320
1171
  RBACEngine,
1321
1172
  createRBACEngine,
1322
1173
  createRBACConfig,
@@ -1342,4 +1193,4 @@ export {
1342
1193
  invalidateAppCache,
1343
1194
  clearCache
1344
1195
  };
1345
- //# sourceMappingURL=chunk-6ZQVSHKL.js.map
1196
+ //# sourceMappingURL=chunk-ANE4PDC2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac/types.ts","../src/rbac/security.ts","../src/rbac/engine.ts","../src/rbac/config.ts","../src/rbac/api.ts"],"sourcesContent":["/**\n * RBAC (Role-Based Access Control) Types - Build Contract Compliant\n * @package @jmruthers/pace-core\n * @module RBAC/Types\n * @since 1.0.0\n * \n * This module defines the core types for the RBAC system that match the build contract exactly.\n * All types are designed to be framework-agnostic and provide strong typing for permission operations.\n */\n\nimport type React from 'react';\n\n// ============================================================================\n// CORE TYPES\n// ============================================================================\n\nexport type UUID = string;\n\nexport type Operation = 'read' | 'create' | 'update' | 'delete' | 'manage';\n\nexport type Permission = `${Operation}:${string}`; // e.g. \"manage:base.events\"\n\nexport type AccessLevel =\n | 'viewer'\n | 'participant'\n | 'planner'\n | 'admin'\n | 'super';\n\nexport type Scope = {\n organisationId?: UUID;\n eventId?: string; // event_id is text/varchar\n appId?: UUID;\n};\n\nexport type PermissionCheck = {\n userId: UUID;\n scope: Scope;\n permission: Permission;\n pageId?: UUID | string;\n};\n\nexport type PermissionMap = Record<string /* pageId */, Operation[]>;\n\n// ============================================================================\n// ROLE TYPES\n// ============================================================================\n\nexport type GlobalRole = 'super_admin';\n\nexport type OrganisationRole = 'supporter' | 'member' | 'leader' | 'org_admin';\n\nexport type EventAppRole = 'viewer' | 'participant' | 'planner' | 'event_admin';\n\n// ============================================================================\n// DATABASE TYPES\n// ============================================================================\n\nexport interface RBACGlobalRole {\n id: UUID;\n user_id: UUID;\n role: GlobalRole;\n granted_at: string;\n granted_by: UUID | null;\n valid_from: string;\n valid_to: string | null;\n}\n\nexport interface RBACOrganisationRole {\n id: UUID;\n user_id: UUID;\n organisation_id: UUID;\n role: OrganisationRole;\n status: 'active' | 'inactive' | 'suspended';\n granted_at: string;\n granted_by: UUID | null;\n revoked_at: string | null;\n revoked_by: UUID | null;\n notes: string | null;\n created_at: string;\n updated_at: string;\n valid_from: string;\n valid_to: string | null;\n}\n\nexport interface RBACEventAppRole {\n id: UUID;\n user_id: UUID;\n event_id: string;\n role: EventAppRole;\n status: 'active' | 'inactive' | 'suspended';\n granted_at: string;\n granted_by: UUID | null;\n organisation_id: UUID;\n app_id: UUID;\n valid_from: string;\n valid_to: string | null;\n}\n\nexport interface RBACPagePermission {\n id: UUID;\n app_page_id: UUID;\n operation: Operation;\n role_name: string;\n allowed: boolean;\n created_at: string;\n updated_at: string;\n organisation_id: UUID;\n}\n\nexport interface RBACAppPage {\n id: UUID;\n page_name: string;\n page_description: string | null;\n created_at: string;\n updated_at: string;\n created_by: UUID | null;\n updated_by: UUID | null;\n app_id: UUID;\n}\n\nexport interface RBACApp {\n id: UUID;\n name: string;\n display_name: string;\n description: string | null;\n requires_event: boolean;\n is_active: boolean;\n created_at: string;\n updated_at: string;\n created_by: UUID | null;\n updated_by: UUID | null;\n}\n\n// ============================================================================\n// AUDIT EVENT TYPES\n// ============================================================================\n\nexport type AuditEventType = \n | 'permission_check'\n | 'permission_denied'\n | 'role_granted'\n | 'role_denied'\n | 'rls_denied';\n\nexport type AuditEventSource = 'api' | 'ui' | 'middleware' | 'rls';\n\nexport interface RBACAuditEvent {\n id: UUID;\n event_type: AuditEventType;\n user_id: UUID;\n organisation_id: UUID;\n event_id?: string;\n app_id?: UUID;\n page_id?: UUID;\n permission?: string;\n decision?: boolean;\n source?: AuditEventSource;\n bypass?: boolean;\n duration_ms?: number;\n metadata: Record<string, any>;\n created_at: string;\n}\n\n// ============================================================================\n// CACHE TYPES\n// ============================================================================\n\nexport interface CacheEntry<T> {\n data: T;\n expires: number;\n}\n\nexport interface PermissionCacheKey {\n userId: UUID;\n organisationId?: UUID;\n eventId?: string;\n appId?: UUID;\n permission?: Permission;\n pageId?: UUID | string;\n}\n\n// ============================================================================\n// API TYPES\n// ============================================================================\n\nexport interface GetAccessLevelInput {\n userId: UUID;\n scope: Scope;\n}\n\nexport interface GetPermissionMapInput {\n userId: UUID;\n scope: Scope;\n}\n\nexport interface IsPermittedInput extends PermissionCheck {}\n\n// ============================================================================\n// HOOK TYPES\n// ============================================================================\n\nexport interface UsePermissionsReturn {\n permissions: PermissionMap;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n}\n\nexport interface UseCanReturn {\n can: boolean;\n isLoading: boolean;\n error: Error | null;\n check: () => Promise<void>;\n}\n\n// ============================================================================\n// ADAPTER TYPES\n// ============================================================================\n\nexport interface PermissionGuardConfig {\n permission: Permission;\n pageId?: UUID;\n}\n\nexport interface WithPermissionGuardOptions {\n permission: Permission;\n pageId?: UUID;\n fallback?: React.ReactNode;\n onDenied?: () => void;\n}\n\n// ============================================================================\n// HOOK RETURN TYPES\n// ============================================================================\n\nexport interface UserRBACContext {\n user: any; // User from auth context\n globalRole: GlobalRole | null;\n organisationRole: OrganisationRole | null;\n eventAppRole: EventAppRole | null;\n hasPermission: (operation: Operation, targetPageId?: string) => Promise<boolean>;\n hasGlobalPermission: (permission: Permission) => boolean;\n isSuperAdmin: boolean;\n isOrgAdmin: boolean;\n isEventAdmin: boolean;\n canManageOrganisation: boolean;\n canManageEvent: boolean;\n isLoading: boolean;\n error: Error | null;\n}\n\nexport interface RBACPermission {\n permission_type: string;\n role_name: string;\n [key: string]: any;\n}\n\n// ============================================================================\n// COMPONENT TYPES\n// ============================================================================\n\nexport interface RBACGuardProps {\n children: React.ReactNode;\n operation: Operation;\n pageId?: UUID;\n fallback?: React.ReactNode;\n}\n\nexport interface RoleBasedContentProps {\n children: React.ReactNode;\n globalRoles?: GlobalRole[];\n organisationRoles?: OrganisationRole[];\n eventAppRoles?: EventAppRole[];\n fallback?: React.ReactNode;\n}\n\n// ============================================================================\n// ERROR TYPES\n// ============================================================================\n\nexport class RBACError extends Error {\n constructor(\n message: string,\n public code: string,\n public context?: Record<string, any>\n ) {\n super(message);\n this.name = 'RBACError';\n }\n}\n\nexport class PermissionDeniedError extends RBACError {\n constructor(permission: Permission, context?: Record<string, any>) {\n super(\n `Permission denied: ${permission}`,\n 'PERMISSION_DENIED',\n { permission, ...context }\n );\n this.name = 'PermissionDeniedError';\n }\n}\n\nexport class OrganisationContextRequiredError extends RBACError {\n constructor() {\n super(\n 'Organisation context is required for this operation',\n 'ORGANISATION_CONTEXT_REQUIRED'\n );\n this.name = 'OrganisationContextRequiredError';\n }\n}\n\nexport class RBACNotInitializedError extends RBACError {\n constructor() {\n super(\n 'RBAC system not initialized. Please call setupRBAC(supabase) before using any RBAC components or hooks. See: https://docs.pace-core.dev/rbac/setup',\n 'RBAC_NOT_INITIALIZED'\n );\n this.name = 'RBACNotInitializedError';\n }\n}\n\nexport class InvalidScopeError extends RBACError {\n constructor(scope: Scope, reason: string) {\n super(\n `Invalid scope provided: ${JSON.stringify(scope)}. ${reason}`,\n 'INVALID_SCOPE',\n { scope, reason }\n );\n this.name = 'InvalidScopeError';\n }\n}\n\nexport class MissingUserContextError extends RBACError {\n constructor() {\n super(\n 'User context is required but not available. Make sure to wrap your app with an auth provider.',\n 'MISSING_USER_CONTEXT'\n );\n this.name = 'MissingUserContextError';\n }\n}\n","/**\n * RBAC Security Enhancements\n * @package @jmruthers/pace-core\n * @module RBAC/Security\n * @since 1.0.0\n * \n * Additional security measures for the RBAC system\n */\n\nimport { UUID, Permission, Scope } from './types';\n\n/**\n * Security validation utilities for RBAC operations\n */\nexport class RBACSecurityValidator {\n /**\n * Validate permission string format\n * @param permission - Permission string to validate\n * @returns True if valid, false otherwise\n */\n static validatePermission(permission: string): boolean {\n if (typeof permission !== 'string' || permission.length === 0) {\n return false;\n }\n\n // Permission format: operation:resource[.subresource]\n const permissionRegex = /^(read|create|update|delete|manage):[a-z0-9._-]+$/;\n return permissionRegex.test(permission);\n }\n\n /**\n * Validate UUID format\n * @param uuid - UUID string to validate\n * @returns True if valid, false otherwise\n */\n static validateUUID(uuid: string): boolean {\n if (typeof uuid !== 'string' || uuid.length === 0) {\n return false;\n }\n\n // More permissive UUID regex that allows all valid UUID versions\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n return uuidRegex.test(uuid);\n }\n\n /**\n * Validate scope object\n * @param scope - Scope object to validate\n * @returns True if valid, false otherwise\n */\n static validateScope(scope: Scope): boolean {\n if (!scope || typeof scope !== 'object') {\n return false;\n }\n\n // Organisation ID is required for most operations\n if (scope.organisationId && !this.validateUUID(scope.organisationId)) {\n return false;\n }\n\n // Event ID should be a string if provided\n if (scope.eventId && typeof scope.eventId !== 'string') {\n return false;\n }\n\n // App ID should be a valid UUID if provided\n if (scope.appId && !this.validateUUID(scope.appId)) {\n return false;\n }\n\n // At least one valid field must be present\n return !!(scope.organisationId || scope.eventId || scope.appId);\n }\n\n /**\n * Sanitize input string to prevent injection attacks\n * @param input - Input string to sanitize\n * @returns Sanitized string\n */\n static sanitizeInput(input: string): string {\n if (typeof input !== 'string') {\n return '';\n }\n\n // Remove potentially dangerous characters\n return input\n .replace(/<[^>]*>/g, '') // Remove HTML tags\n .replace(/[<>\\\"'&]/g, '') // Remove HTML/XML characters\n .replace(/[;()]/g, '') // Remove SQL injection characters\n .replace(/javascript:/gi, '') // Remove javascript: protocol\n .replace(/data:/gi, '') // Remove data: protocol\n .trim();\n }\n\n /**\n * Validate user ID format\n * @param userId - User ID to validate\n * @returns True if valid, false otherwise\n */\n static validateUserId(userId: UUID): boolean {\n return this.validateUUID(userId);\n }\n\n /**\n * Check if permission is a wildcard permission\n * @param permission - Permission string to check\n * @returns True if wildcard, false otherwise\n */\n static isWildcardPermission(permission: string): boolean {\n return permission.includes('*') || permission.endsWith(':*');\n }\n\n /**\n * Validate permission hierarchy\n * @param permission - Permission to validate\n * @param requiredOperation - Required operation\n * @returns True if permission matches or is higher in hierarchy\n */\n static validatePermissionHierarchy(permission: string, requiredOperation: string): boolean {\n if (!this.validatePermission(permission)) {\n return false;\n }\n\n const [operation] = permission.split(':');\n const hierarchy = ['read', 'create', 'update', 'delete', 'manage'];\n \n const permissionLevel = hierarchy.indexOf(operation);\n const requiredLevel = hierarchy.indexOf(requiredOperation);\n \n if (permissionLevel === -1 || requiredLevel === -1) {\n return false;\n }\n\n // Higher level permissions include lower level permissions\n return permissionLevel >= requiredLevel;\n }\n\n /**\n * Rate limiting check (placeholder for future implementation)\n * @param userId - User ID\n * @param operation - Operation being performed\n * @returns True if within rate limit, false otherwise\n */\n static async checkRateLimit(userId: UUID, operation: string): Promise<boolean> {\n // TODO: Implement actual rate limiting logic\n // This could use Redis, in-memory cache, or database-based rate limiting\n return true;\n }\n\n /**\n * Validate context requirements for security\n * @param scope - Scope object\n * @param appId - Application ID\n * @returns True if context is valid, false otherwise\n */\n static validateContextRequirements(scope: Scope, appId?: UUID): boolean {\n // Organisation context is always required\n if (!scope.organisationId) {\n return false;\n }\n\n // If app requires event, event context must be provided\n if (appId && scope.appId === appId) {\n // This would need to check app configuration\n // For now, we'll assume event context is required if appId is provided\n if (!scope.eventId) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Log security event for monitoring\n * @param event - Security event details\n */\n static logSecurityEvent(event: {\n type: 'permission_denied' | 'invalid_input' | 'rate_limit_exceeded' | 'suspicious_activity';\n userId: UUID;\n details: Record<string, any>;\n timestamp?: Date;\n }): void {\n const securityEvent = {\n ...event,\n timestamp: event.timestamp || new Date(),\n severity: this.getEventSeverity(event.type),\n };\n\n // Log to console in development, could be sent to security monitoring service in production\n if (process.env.NODE_ENV === 'development') {\n console.warn('[RBAC Security]', securityEvent);\n }\n\n // TODO: Send to security monitoring service\n }\n\n /**\n * Get severity level for security event\n * @param eventType - Type of security event\n * @returns Severity level\n */\n private static getEventSeverity(eventType: string): 'low' | 'medium' | 'high' | 'critical' {\n switch (eventType) {\n case 'permission_denied':\n return 'low';\n case 'invalid_input':\n return 'medium';\n case 'rate_limit_exceeded':\n return 'medium';\n case 'suspicious_activity':\n return 'high';\n default:\n return 'low';\n }\n }\n}\n\n/**\n * Security configuration for RBAC system\n */\nexport interface RBACSecurityConfig {\n enableInputValidation: boolean;\n enableRateLimiting: boolean;\n enableAuditLogging: boolean;\n maxPermissionChecksPerMinute: number;\n suspiciousActivityThreshold: number;\n}\n\n/**\n * Default security configuration\n */\nexport const DEFAULT_SECURITY_CONFIG: RBACSecurityConfig = {\n enableInputValidation: true,\n enableRateLimiting: true,\n enableAuditLogging: true,\n maxPermissionChecksPerMinute: 100,\n suspiciousActivityThreshold: 10,\n};\n\n/**\n * Security context for RBAC operations\n */\nexport interface SecurityContext {\n userId: UUID;\n organisationId: UUID;\n ipAddress?: string;\n userAgent?: string;\n timestamp: Date;\n}\n\n/**\n * Security middleware for RBAC operations\n */\nexport class RBACSecurityMiddleware {\n private config: RBACSecurityConfig;\n\n constructor(config: RBACSecurityConfig = DEFAULT_SECURITY_CONFIG) {\n this.config = config;\n }\n\n /**\n * Validate input before processing\n * @param input - Input to validate\n * @param context - Security context\n * @returns Validation result\n */\n async validateInput(input: any, context: SecurityContext): Promise<{\n isValid: boolean;\n errors: string[];\n }> {\n const errors: string[] = [];\n\n if (!this.config.enableInputValidation) {\n return { isValid: true, errors: [] };\n }\n\n // Validate user ID\n if (!RBACSecurityValidator.validateUserId(context.userId)) {\n errors.push('Invalid user ID format');\n }\n\n // Validate organisation ID\n if (!RBACSecurityValidator.validateUUID(context.organisationId)) {\n errors.push('Invalid organisation ID format');\n }\n\n // Validate permission if present\n if (input.permission && !RBACSecurityValidator.validatePermission(input.permission)) {\n errors.push('Invalid permission format');\n }\n\n // Validate scope if present\n if (input.scope && !RBACSecurityValidator.validateScope(input.scope)) {\n errors.push('Invalid scope format');\n }\n\n // Log suspicious activity\n if (errors.length > 0) {\n RBACSecurityValidator.logSecurityEvent({\n type: 'invalid_input',\n userId: context.userId,\n details: { errors, input: this.sanitizeInput(JSON.stringify(input)) },\n });\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n }\n\n /**\n * Check rate limiting\n * @param context - Security context\n * @returns Rate limit check result\n */\n async checkRateLimit(context: SecurityContext): Promise<{\n isAllowed: boolean;\n remaining: number;\n }> {\n if (!this.config.enableRateLimiting) {\n return { isAllowed: true, remaining: this.config.maxPermissionChecksPerMinute };\n }\n\n // TODO: Implement actual rate limiting logic\n const isAllowed = await RBACSecurityValidator.checkRateLimit(\n context.userId,\n 'permission_check'\n );\n\n return {\n isAllowed,\n remaining: this.config.maxPermissionChecksPerMinute,\n };\n }\n\n /**\n * Sanitize input data\n * @param input - Input to sanitize\n * @returns Sanitized input\n */\n private sanitizeInput(input: string): string {\n return RBACSecurityValidator.sanitizeInput(input);\n }\n}\n","/**\n * RBAC Core Engine\n * @package @jmruthers/pace-core\n * @module RBAC/Engine\n * @since 1.0.0\n * \n * This module implements the core RBAC permission algorithm with deny-overrides-allow precedence.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../types/database';\nimport { \n UUID, \n Permission, \n Scope, \n PermissionCheck, \n AccessLevel, \n PermissionMap,\n Operation,\n OrganisationRole,\n EventAppRole\n} from './types';\nimport { rbacCache, RBACCache } from './cache';\nimport { emitAuditEvent } from './audit';\nimport { getCurrentAppName } from '../utils/appNameResolver';\nimport { \n RBACSecurityValidator, \n RBACSecurityMiddleware, \n SecurityContext,\n DEFAULT_SECURITY_CONFIG \n} from './security';\n\n/**\n * Grant type for permission resolution\n */\ninterface Grant {\n type: 'allow' | 'deny';\n permission: Permission;\n scope: 'global' | 'organisation' | 'eventApp' | 'page';\n source: string;\n}\n\n/**\n * RBAC Engine\n * \n * Implements the core permission algorithm with deny-overrides-allow precedence.\n */\nexport class RBACEngine {\n private supabase: SupabaseClient<Database>;\n private securityMiddleware: RBACSecurityMiddleware;\n\n constructor(supabase: SupabaseClient<Database>) {\n this.supabase = supabase;\n this.securityMiddleware = new RBACSecurityMiddleware(DEFAULT_SECURITY_CONFIG);\n }\n\n /**\n * Check if a user has a specific permission\n * \n * @param input - Permission check input\n * @param securityContext - Optional security context for enhanced validation\n * @returns Promise resolving to permission result\n */\n async isPermitted(input: PermissionCheck, securityContext?: SecurityContext): Promise<boolean> {\n const startTime = Date.now();\n const { userId, permission, scope, pageId } = input;\n\n try {\n // Security validation\n if (securityContext) {\n const validation = await this.securityMiddleware.validateInput(input, securityContext);\n if (!validation.isValid) {\n RBACSecurityValidator.logSecurityEvent({\n type: 'invalid_input',\n userId,\n details: { errors: validation.errors, input: JSON.stringify(input) },\n });\n return false;\n }\n\n const rateLimit = await this.securityMiddleware.checkRateLimit(securityContext);\n if (!rateLimit.isAllowed) {\n RBACSecurityValidator.logSecurityEvent({\n type: 'rate_limit_exceeded',\n userId,\n details: { remaining: rateLimit.remaining },\n });\n return false;\n }\n }\n\n // Input validation (only when security context is provided)\n if (securityContext) {\n if (!RBACSecurityValidator.validateUserId(userId)) {\n RBACSecurityValidator.logSecurityEvent({\n type: 'invalid_input',\n userId,\n details: { error: 'Invalid user ID format' },\n });\n return false;\n }\n\n if (!RBACSecurityValidator.validatePermission(permission)) {\n RBACSecurityValidator.logSecurityEvent({\n type: 'invalid_input',\n userId,\n details: { error: 'Invalid permission format', permission },\n });\n return false;\n }\n\n if (!RBACSecurityValidator.validateScope(scope)) {\n RBACSecurityValidator.logSecurityEvent({\n type: 'invalid_input',\n userId,\n details: { error: 'Invalid scope format', scope },\n });\n return false;\n }\n }\n // 1) If GlobalRole == 'super_admin' → allow (log bypass:true)\n const isSuperAdmin = await this.checkSuperAdmin(userId);\n if (isSuperAdmin) {\n const duration = Date.now() - startTime;\n // Only emit audit event if we have a valid organisation ID\n if (scope.organisationId) {\n // Resolve pageId to UUID if it's a page name\n const resolvedPageId = await this.resolvePageId(pageId, scope.appId);\n await emitAuditEvent({\n type: 'permission_check',\n userId,\n organisationId: scope.organisationId,\n eventId: scope.eventId,\n appId: scope.appId,\n pageId: resolvedPageId,\n permission,\n decision: true,\n source: 'api',\n bypass: true,\n duration_ms: duration,\n });\n }\n return true;\n }\n\n // 2) Validate context requirements based on app configuration\n const validatedScope = await this.validateContextRequirements(scope, scope.appId);\n if (!validatedScope) {\n const duration = Date.now() - startTime;\n // Only emit audit event if we have a valid organisation ID\n if (scope.organisationId) {\n // Resolve pageId to UUID if it's a page name\n const resolvedPageId = await this.resolvePageId(pageId, scope.appId);\n await emitAuditEvent({\n type: 'permission_denied',\n userId,\n organisationId: scope.organisationId,\n eventId: scope.eventId,\n appId: scope.appId,\n pageId: resolvedPageId,\n permission,\n source: 'api',\n metadata: {\n reason: 'invalid_context_requirements',\n app_requires_event: scope.appId ? await this.getAppConfig(scope.appId).then(config => config?.requires_event) : null\n }\n });\n }\n return false;\n }\n\n // 3) Collect active grants at page → eventApp → organisation → global\n const grants = await this.collectActiveGrants(userId, validatedScope, pageId);\n console.log('[RBACEngine] Collected grants:', grants);\n\n // 4) Apply explicit denies at the closest scope first (deny overrides allow)\n const denies = grants.filter(g => g.type === 'deny');\n console.log('[RBACEngine] Deny grants:', denies);\n for (const deny of denies) {\n const matches = this.permissionMatches(deny.permission, permission);\n console.log('[RBACEngine] Checking deny:', { \n denyPermission: deny.permission, \n requestedPermission: permission, \n matches \n });\n if (matches) {\n const duration = Date.now() - startTime;\n console.log('[RBACEngine] Permission DENIED by explicit deny rule');\n // Only emit audit event if we have a valid organisation ID\n if (scope.organisationId) {\n // Resolve pageId to UUID if it's a page name\n const resolvedPageId = await this.resolvePageId(pageId, scope.appId);\n await emitAuditEvent({\n type: 'permission_denied',\n userId,\n organisationId: scope.organisationId,\n eventId: scope.eventId,\n appId: scope.appId,\n pageId: resolvedPageId,\n permission,\n source: 'api',\n });\n }\n return false;\n }\n }\n\n // 5) If no deny, check allows from closest to widest scope\n const allows = grants.filter(g => g.type === 'allow');\n console.log('[RBACEngine] Allow grants:', allows);\n let hasPermission = false;\n \n // Check allows in precedence order: page → eventApp → organisation → global\n const scopeOrder: Array<'page' | 'eventApp' | 'organisation' | 'global'> = ['page', 'eventApp', 'organisation', 'global'];\n \n for (const scopeType of scopeOrder) {\n const scopeAllows = allows.filter(g => g.scope === scopeType);\n console.log(`[RBACEngine] Checking ${scopeType} allows:`, scopeAllows);\n for (const allow of scopeAllows) {\n console.log(`[RBACEngine] About to check permission match for ${scopeType}:`, {\n allowPermission: allow.permission,\n requestedPermission: permission,\n scopeType\n });\n const matches = this.permissionMatches(allow.permission, permission);\n console.log(`[RBACEngine] Permission match result:`, { \n scopeType,\n allowPermission: allow.permission, \n requestedPermission: permission, \n matches \n });\n if (matches) {\n console.log(`[RBACEngine] Permission GRANTED by ${scopeType} allow rule`);\n hasPermission = true;\n break;\n }\n }\n if (hasPermission) break;\n }\n\n // 6) Return final decision; emit audit event\n const finalDecision = hasPermission;\n const _duration = Date.now() - startTime;\n \n console.log('[RBACEngine] Final decision:', {\n userId,\n permission,\n pageId,\n hasPermission,\n grantsCount: grants.length,\n allowsCount: allows.length,\n deniesCount: denies.length,\n duration: _duration\n });\n\n // Only emit audit event if we have a valid organisation ID\n if (scope.organisationId) {\n // Resolve pageId to UUID if it's a page name\n const resolvedPageId = await this.resolvePageId(pageId, scope.appId);\n await emitAuditEvent({\n type: 'permission_check',\n userId,\n organisationId: scope.organisationId,\n eventId: scope.eventId,\n appId: scope.appId,\n pageId: resolvedPageId,\n permission,\n decision: finalDecision,\n source: 'api',\n duration_ms: _duration,\n });\n }\n\n return finalDecision;\n } catch (error) {\n // Log security event for unexpected errors\n RBACSecurityValidator.logSecurityEvent({\n type: 'suspicious_activity',\n userId,\n details: { \n error: error instanceof Error ? error.message : 'Unknown error',\n permission,\n scope: JSON.stringify(scope)\n },\n });\n\n // Permission check failed - error logged via RBAC logger\n return false;\n }\n }\n\n /**\n * Get user's access level in a scope\n * \n * @param input - Access level input\n * @returns Promise resolving to access level\n */\n async getAccessLevel(input: { userId: UUID; scope: Scope }): Promise<AccessLevel> {\n const { userId, scope } = input;\n\n // Check super admin first - super admins don't need context validation\n const isSuperAdmin = await this.checkSuperAdmin(userId);\n if (isSuperAdmin) {\n return 'super';\n }\n\n // Validate context requirements based on app configuration\n const validatedScope = await this.validateContextRequirements(scope, scope.appId);\n if (!validatedScope) {\n return 'viewer'; // Default to lowest access level for invalid context\n }\n\n // Check cache first\n const cacheKey = RBACCache.generateAccessLevelKey(\n userId,\n validatedScope.organisationId!,\n validatedScope.eventId,\n validatedScope.appId\n );\n \n const cached = rbacCache.get<AccessLevel>(cacheKey);\n if (cached) {\n return cached;\n }\n\n // Check organisation role\n const orgRole = await this.getOrganisationRole(userId, validatedScope.organisationId!);\n if (orgRole === 'org_admin') {\n rbacCache.set(cacheKey, 'admin');\n return 'admin';\n }\n\n // Check event-app role\n if (validatedScope.eventId && validatedScope.appId) {\n const eventRole = await this.getEventAppRole(userId, validatedScope.eventId, validatedScope.appId);\n if (eventRole === 'event_admin') {\n rbacCache.set(cacheKey, 'admin');\n return 'admin';\n }\n if (eventRole === 'planner') {\n rbacCache.set(cacheKey, 'planner');\n return 'planner';\n }\n if (eventRole === 'participant') {\n rbacCache.set(cacheKey, 'participant');\n return 'participant';\n }\n }\n\n // Default to viewer\n rbacCache.set(cacheKey, 'viewer');\n return 'viewer';\n }\n\n /**\n * Get user's permission map for a scope\n * \n * @param input - Permission map input\n * @returns Promise resolving to permission map\n */\n async getPermissionMap(input: { userId: UUID; scope: Scope }): Promise<PermissionMap> {\n const { userId, scope } = input;\n\n // Check super admin first - super admins have all permissions\n const isSuperAdmin = await this.checkSuperAdmin(userId);\n if (isSuperAdmin) {\n return {}; // Super admins have all permissions, return empty map\n }\n\n // Validate context requirements based on app configuration\n const validatedScope = await this.validateContextRequirements(scope, scope.appId);\n if (!validatedScope) {\n return {}; // No permissions without valid context\n }\n\n // Check cache first\n const cacheKey = RBACCache.generatePermissionMapKey(\n userId,\n validatedScope.organisationId!,\n validatedScope.eventId,\n validatedScope.appId\n );\n \n const cached = rbacCache.get<PermissionMap>(cacheKey);\n if (cached) {\n return cached;\n }\n\n const permissionMap: PermissionMap = {};\n\n // Get all pages for the app\n if (validatedScope.appId) {\n const { data: pages } = await this.supabase\n .from('rbac_app_pages')\n .select('id, page_name')\n .eq('app_id', validatedScope.appId) as { data: Array<{ id: string; page_name: string }> | null };\n\n if (pages) {\n for (const page of pages) {\n const operations: Operation[] = [];\n \n // Check each operation\n for (const operation of ['read', 'create', 'update', 'delete', 'manage'] as Operation[]) {\n const hasPermission = await this.isPermitted({\n userId,\n scope: validatedScope,\n permission: `${operation}:${page.page_name}`,\n pageId: page.id,\n });\n \n if (hasPermission) {\n operations.push(operation);\n }\n }\n \n permissionMap[page.id] = operations;\n }\n }\n }\n\n rbacCache.set(cacheKey, permissionMap);\n return permissionMap;\n }\n\n /**\n * Check if user is super admin\n * \n * @param userId - User ID\n * @returns Promise resolving to super admin status\n */\n private async checkSuperAdmin(userId: UUID): Promise<boolean> {\n // Check cache first\n const cacheKey = `super_admin:${userId}`;\n const cached = rbacCache.get<boolean>(cacheKey);\n if (cached !== null) {\n return cached;\n }\n\n const { data, error } = await this.supabase\n .from('rbac_global_roles')\n .select('id')\n .eq('user_id', userId)\n .eq('role', 'super_admin')\n .is('valid_to', null)\n .single();\n\n const isSuperAdmin = !error && !!data;\n \n // Cache the result for 60 seconds\n rbacCache.set(cacheKey, isSuperAdmin, 60000);\n \n return isSuperAdmin;\n }\n\n /**\n * Get app configuration including requires_event setting\n * \n * @param appId - App ID\n * @returns Promise resolving to app configuration\n */\n async getAppConfig(appId: UUID): Promise<{ requires_event: boolean } | null> {\n const { data, error } = await this.supabase\n .from('rbac_apps')\n .select('requires_event')\n .eq('id', appId)\n .eq('is_active', true)\n .single() as { data: { requires_event: boolean } | null; error: any };\n\n if (error || !data) {\n return null;\n }\n\n return { requires_event: data.requires_event };\n }\n\n /**\n * Resolve organisation ID from event ID\n * \n * @param eventId - Event ID\n * @returns Promise resolving to organisation ID\n */\n private async resolveOrganisationFromEvent(eventId: string): Promise<UUID | null> {\n const { data, error } = await this.supabase\n .from('event')\n .select('organisation_id')\n .eq('id', eventId)\n .single() as { data: { organisation_id: string } | null; error: any };\n\n if (error || !data) {\n return null;\n }\n\n return data.organisation_id;\n }\n\n /**\n * Validate context requirements based on app configuration\n * \n * @param scope - Permission scope\n * @param appId - Optional app ID\n * @returns Promise resolving to validated scope with resolved organisation ID\n */\n private async validateContextRequirements(scope: Scope, appId?: UUID): Promise<Scope | null> {\n // If we have an app ID, check its requirements\n if (appId) {\n const appConfig = await this.getAppConfig(appId);\n if (!appConfig) {\n return null; // App not found or inactive\n }\n\n if (appConfig.requires_event) {\n // Event-based app: requires eventId, organisationId is resolved from event\n if (!scope.eventId) {\n return null; // Event context required\n }\n\n // Resolve organisation from event if not provided\n if (!scope.organisationId) {\n const resolvedOrgId = await this.resolveOrganisationFromEvent(scope.eventId);\n if (!resolvedOrgId) {\n return null; // Could not resolve organisation from event\n }\n return {\n ...scope,\n organisationId: resolvedOrgId\n };\n }\n\n return scope;\n } else {\n // Organisation-based app: requires organisationId, eventId is optional\n if (!scope.organisationId) {\n return null; // Organisation context required\n }\n\n return scope;\n }\n }\n\n // No app ID provided - use legacy validation (require organisation)\n if (!scope.organisationId) {\n return null; // Organisation context required for legacy behavior\n }\n\n return scope;\n }\n\n /**\n * Collect active grants for a user in a scope\n * \n * @param userId - User ID\n * @param scope - Permission scope\n * @param pageId - Optional page ID\n * @returns Promise resolving to grants array\n * \n * PRECEDENCE ORDER (closest scope first): page → eventApp → organisation → global\n */\n private async collectActiveGrants(\n userId: UUID, \n scope: Scope, \n pageId?: UUID | string\n ): Promise<Grant[]> {\n const grants: Grant[] = [];\n const now = new Date().toISOString();\n \n // Pre-fetch user roles once (to avoid duplicate queries)\n const userRoles: string[] = [];\n \n // Get event-app roles\n if (scope.eventId && scope.appId) {\n const { data: eventRoles } = await this.supabase\n .from('rbac_event_app_roles')\n .select('role, status, valid_from, valid_to')\n .eq('user_id', userId)\n .eq('event_id', scope.eventId)\n .eq('app_id', scope.appId)\n .eq('status', 'active')\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`) as { data: Array<{ role: string; status: string; valid_from: string; valid_to: string | null }> | null };\n \n if (eventRoles) {\n userRoles.push(...eventRoles.map(r => r.role));\n // Also add event-app grants\n for (const role of eventRoles) {\n grants.push({\n type: 'allow',\n permission: this.getPermissionForEventRole(role.role as EventAppRole),\n scope: 'eventApp',\n source: 'rbac_event_app_roles',\n });\n }\n }\n }\n \n // Get organisation roles\n if (scope.organisationId) {\n const { data: orgRoles } = await this.supabase\n .from('rbac_organisation_roles')\n .select('role, status, valid_from, valid_to')\n .eq('user_id', userId)\n .eq('organisation_id', scope.organisationId)\n .eq('status', 'active')\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`) as { data: Array<{ role: string; status: string; valid_from: string; valid_to: string | null }> | null };\n \n if (orgRoles) {\n userRoles.push(...orgRoles.map(r => r.role));\n // Also add org grants\n for (const role of orgRoles) {\n grants.push({\n type: 'allow',\n permission: this.getPermissionForOrgRole(role.role as OrganisationRole),\n scope: 'organisation',\n source: 'rbac_organisation_roles',\n });\n }\n }\n }\n \n console.log('[collectActiveGrants] User roles:', userRoles);\n\n // 1. PAGE GRANTS (closest scope first) - Use RPC to bypass RLS\n if (pageId) {\n // Resolve page ID if it's a page name (string)\n let resolvedPageId: UUID | null = null;\n if (typeof pageId === 'string') {\n // Check if it's already a UUID\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (uuidRegex.test(pageId)) {\n resolvedPageId = pageId as UUID;\n } else {\n // It's a page name, resolve it to a page ID\n const appId = scope.appId;\n if (appId) {\n const { data: page } = await this.supabase\n .from('rbac_app_pages')\n .select('id')\n .eq('app_id', appId)\n .eq('page_name', pageId)\n .single() as { data: { id: UUID } | null };\n resolvedPageId = page?.id || null;\n }\n }\n } else {\n resolvedPageId = pageId;\n }\n\n if (resolvedPageId && scope.appId) {\n console.log('[collectActiveGrants] Fetching page permissions via RPC for page:', resolvedPageId);\n \n // Get the page name for building specific permission strings\n let pageName: string | null = null;\n if (typeof pageId === 'string' && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(pageId)) {\n // pageId is already the page name\n pageName = pageId;\n } else {\n // Fetch page name from database\n const { data: page } = await this.supabase\n .from('rbac_app_pages')\n .select('page_name')\n .eq('id', resolvedPageId)\n .single() as { data: { page_name: string } | null };\n pageName = page?.page_name || null;\n }\n \n // Use RPC to get page permissions (bypasses RLS)\n // @ts-ignore - RPC type inference is incorrect\n const rpcResult = await this.supabase.rpc('get_rbac_permissions', {\n p_user_id: userId,\n p_app_id: scope.appId,\n p_event_id: scope.eventId || null,\n p_organisation_id: scope.organisationId || null,\n p_page_id: resolvedPageId\n });\n \n const { data: rpcPermissions } = rpcResult as { data: Array<{ permission_type: string; role_name: string; has_permission: boolean }> | null; error: any };\n \n console.log('[collectActiveGrants] RPC page permissions:', rpcPermissions);\n \n if (rpcPermissions && pageName) {\n // Filter to only page-level permissions (not org/event-app roles)\n const pagePerms = rpcPermissions.filter(p => \n p.permission_type !== 'all_permissions' && \n p.permission_type !== 'organisation_access' && \n p.permission_type !== 'event_app_access'\n );\n \n for (const perm of pagePerms) {\n // Only add if user has this role\n if (userRoles.includes(perm.role_name)) {\n console.log('[collectActiveGrants] Adding page grant:', { operation: perm.permission_type, role: perm.role_name, allowed: perm.has_permission, pageName });\n grants.push({\n type: perm.has_permission ? 'allow' : 'deny',\n // Use page.${pageName} format for specific page permissions\n permission: `${perm.permission_type}:page.${pageName}` as Permission,\n scope: 'page',\n source: 'rbac_page_permissions',\n });\n }\n }\n }\n }\n }\n\n // 2. GLOBAL GRANTS (widest scope last)\n const { data: globalRoles } = await this.supabase\n .from('rbac_global_roles')\n .select('role, valid_from, valid_to')\n .eq('user_id', userId)\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`) as { data: Array<{ role: string; valid_from: string; valid_to: string | null }> | null };\n\n if (globalRoles) {\n for (const role of globalRoles) {\n if (role.role === 'super_admin') {\n grants.push({\n type: 'allow',\n permission: 'manage:*' as Permission,\n scope: 'global',\n source: 'rbac_global_roles',\n });\n }\n }\n }\n \n console.log('[collectActiveGrants] Final grants:', grants);\n\n return grants;\n }\n\n /**\n * Check page-specific permissions\n * \n * @param userId - User ID\n * @param pageId - Page ID\n * @param permission - Permission to check\n * @param scope - Permission scope\n * @returns Promise resolving to page permission result\n */\n private async checkPagePermissions(\n userId: UUID,\n pageId: UUID | string | undefined,\n permission: Permission,\n scope: Scope\n ): Promise<boolean> {\n if (!pageId) {\n return true; // No page restrictions\n }\n\n const [operation] = permission.split(':') as [Operation, string];\n\n // Get user's roles in this scope\n const userRoles: string[] = [];\n\n // Add organisation role\n if (scope.organisationId) {\n const orgRole = await this.getOrganisationRole(userId, scope.organisationId);\n if (orgRole) {\n userRoles.push(orgRole);\n }\n }\n\n // Add event-app role\n if (scope.eventId && scope.appId) {\n const eventRole = await this.getEventAppRole(userId, scope.eventId, scope.appId);\n if (eventRole) {\n userRoles.push(eventRole);\n }\n }\n\n // Resolve page ID if it's a page name (string)\n let resolvedPageId: UUID | null = null;\n if (typeof pageId === 'string') {\n // Check if it's already a UUID\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (uuidRegex.test(pageId)) {\n resolvedPageId = pageId as UUID;\n } else {\n // It's a page name, resolve it to a page ID\n // First, get the app ID from scope or environment\n let appId = scope.appId;\n if (!appId) {\n // Try to get app ID from environment\n const appName = process.env.VITE_APP_NAME || process.env.REACT_APP_NAME;\n if (appName) {\n const { data: app } = await this.supabase\n .from('rbac_apps')\n .select('id')\n .eq('name', appName)\n .eq('is_active', true)\n .single() as { data: { id: UUID } | null };\n if (app) {\n appId = app.id;\n }\n }\n }\n \n if (appId) {\n const { data: page } = await this.supabase\n .from('rbac_app_pages')\n .select('id')\n .eq('app_id', appId)\n .eq('page_name', pageId)\n .single() as { data: { id: UUID } | null };\n resolvedPageId = page?.id || null;\n }\n }\n } else {\n resolvedPageId = pageId;\n }\n\n if (!resolvedPageId) {\n return false; // Page not found\n }\n\n // Check page permissions\n const { data: pagePermissions } = await this.supabase\n .from('rbac_page_permissions')\n .select('allowed')\n .eq('app_page_id', resolvedPageId)\n .eq('operation', operation)\n .in('role_name', userRoles)\n .single() as { data: { allowed: boolean } | null };\n\n return pagePermissions?.allowed ?? false;\n }\n\n /**\n * Get organisation role for a user\n * \n * @param userId - User ID\n * @param organisationId - Organisation ID\n * @returns Promise resolving to organisation role\n */\n private async getOrganisationRole(userId: UUID, organisationId: UUID): Promise<OrganisationRole | null> {\n const { data, error } = await this.supabase\n .from('rbac_organisation_roles')\n .select('role')\n .eq('user_id', userId)\n .eq('organisation_id', organisationId)\n .eq('status', 'active')\n .single() as { data: { role: string } | null; error: any };\n\n return error ? null : (data?.role as OrganisationRole) || null;\n }\n\n /**\n * Get event-app role for a user\n * \n * @param userId - User ID\n * @param eventId - Event ID\n * @param appId - App ID\n * @returns Promise resolving to event-app role\n */\n private async getEventAppRole(userId: UUID, eventId: string, appId: UUID): Promise<EventAppRole | null> {\n const { data, error } = await this.supabase\n .from('rbac_event_app_roles')\n .select('role, status, valid_from, valid_to')\n .eq('user_id', userId)\n .eq('event_id', eventId)\n .eq('app_id', appId)\n .eq('status', 'active')\n .lte('valid_from', new Date().toISOString())\n .or(`valid_to.is.null,valid_to.gte.${new Date().toISOString()}`)\n .single() as { data: { role: string; status: string; valid_from: string; valid_to: string | null } | null; error: any };\n\n return error ? null : (data?.role as EventAppRole) || null;\n }\n\n /**\n * Get permission for organisation role\n * \n * @param role - Organisation role\n * @returns Permission string\n */\n private getPermissionForOrgRole(role: OrganisationRole): Permission {\n switch (role) {\n case 'org_admin':\n return 'manage:*' as Permission;\n case 'leader':\n return 'manage:organisation.*' as Permission;\n case 'member':\n return 'read:organisation.*' as Permission;\n case 'supporter':\n return 'read:organisation.public' as Permission;\n default:\n return 'read:organisation.public' as Permission;\n }\n }\n\n /**\n * Get permission for event-app role\n * \n * @param role - Event-app role\n * @returns Permission string\n */\n private getPermissionForEventRole(role: EventAppRole): Permission {\n switch (role) {\n case 'event_admin':\n return 'manage:event.*' as Permission;\n case 'planner':\n return 'manage:event.planning' as Permission;\n case 'participant':\n return 'read:event.*' as Permission;\n case 'viewer':\n return 'read:event.public' as Permission;\n default:\n return 'read:event.public' as Permission;\n }\n }\n\n /**\n * Check if a permission matches another permission\n * \n * @param grantPermission - Permission from grant\n * @param requestedPermission - Requested permission\n * @returns True if permissions match\n */\n private permissionMatches(grantPermission: Permission, requestedPermission: Permission): boolean {\n console.log('[permissionMatches] Checking:', { grantPermission, requestedPermission });\n \n // Exact match\n if (grantPermission === requestedPermission) {\n console.log('[permissionMatches] Exact match found');\n return true;\n }\n\n // Wildcard match\n if (grantPermission.endsWith(':*') || grantPermission.endsWith('.*')) {\n const [grantOp, grantResource] = grantPermission.split(':');\n const [requestedOp, requestedResource] = requestedPermission.split(':');\n \n console.log('[permissionMatches] Wildcard check:', { \n grantOp, \n grantResource, \n requestedOp, \n requestedResource,\n operationsMatch: grantOp === requestedOp\n });\n \n if (grantOp === requestedOp) {\n // For wildcard permissions like \"read:*\" or \"read:organisation.*\", grantResource is \"*\" or \"organisation.*\"\n // We need to check if the requested resource starts with the prefix before \"*\"\n const prefix = grantResource.slice(0, -1); // Remove the \"*\"\n const matches = prefix === '' || requestedResource.startsWith(prefix);\n console.log('[permissionMatches] Wildcard match result:', { prefix, matches });\n return matches;\n }\n }\n\n // Check for other wildcard patterns\n if (grantPermission.includes('*')) {\n const [grantOp, grantResource] = grantPermission.split(':');\n const [requestedOp, requestedResource] = requestedPermission.split(':');\n \n console.log('[permissionMatches] Other wildcard check:', { \n grantOp, \n grantResource, \n requestedOp, \n requestedResource,\n operationsMatch: grantOp === requestedOp\n });\n \n if (grantOp === requestedOp) {\n // For wildcard permissions like \"read:event*\", grantResource is \"event*\"\n const prefix = grantResource.replace('*', '');\n const matches = requestedResource.startsWith(prefix);\n console.log('[permissionMatches] Other wildcard match result:', { prefix, matches });\n return matches;\n }\n }\n\n console.log('[permissionMatches] No match found');\n return false;\n }\n\n /**\n * Resolve a page ID to UUID if it's a page name\n * \n * @param pageId - Page ID (UUID) or page name (string)\n * @param appId - App ID to look up the page\n * @returns Resolved page ID (UUID) or original pageId if it's already a UUID or can't be resolved\n */\n private async resolvePageId(pageId?: UUID | string, appId?: UUID): Promise<UUID | string | undefined> {\n if (!pageId) {\n return undefined;\n }\n\n // Check if it's already a UUID\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (uuidRegex.test(pageId)) {\n return pageId as UUID;\n }\n\n // It's a page name, but we need appId to resolve it\n if (!appId) {\n // If we can't resolve it, return the original value\n return pageId;\n }\n\n // It's a page name, resolve it to a page ID\n try {\n const { data: page } = await this.supabase\n .from('rbac_app_pages')\n .select('id')\n .eq('app_id', appId)\n .eq('page_name', pageId)\n .single() as { data: { id: UUID } | null };\n \n return page?.id || pageId; // Return original if not found\n } catch (error) {\n console.warn('[RBAC Engine] Failed to resolve page name to UUID:', { pageId, appId, error });\n return pageId; // Return original on error\n }\n }\n}\n\n/**\n * Create an RBAC engine instance\n * \n * @param supabase - Supabase client\n * @returns RBACEngine instance\n */\nexport function createRBACEngine(supabase: SupabaseClient<Database>): RBACEngine {\n return new RBACEngine(supabase);\n}\n","/**\n * RBAC Configuration\n * @package @jmruthers/pace-core\n * @module RBAC/Config\n * @since 1.0.0\n * \n * This module provides configuration options for the RBAC system.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../types/database';\n\nexport type LogLevel = 'error' | 'warn' | 'info' | 'debug';\n\nexport interface RBACConfig {\n supabase: SupabaseClient<Database>;\n debug?: boolean;\n logLevel?: LogLevel;\n developmentMode?: boolean;\n mockPermissions?: Record<string, boolean>;\n cache?: {\n ttl?: number;\n enabled?: boolean;\n };\n audit?: {\n enabled?: boolean;\n logLevel?: LogLevel;\n };\n}\n\nexport interface RBACLogger {\n error: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n debug: (message: string, ...args: unknown[]) => void;\n}\n\nclass RBACConfigManager {\n private config: RBACConfig | null = null;\n private logger: RBACLogger | null = null;\n\n setConfig(config: RBACConfig): void {\n this.config = config;\n this.setupLogger();\n }\n\n getConfig(): RBACConfig | null {\n return this.config;\n }\n\n getLogger(): RBACLogger {\n if (!this.logger) {\n this.logger = this.createDefaultLogger();\n }\n return this.logger;\n }\n\n private setupLogger(): void {\n if (!this.config) return;\n\n const { debug = false, logLevel = 'warn' } = this.config;\n \n this.logger = {\n error: (message: string, ...args: unknown[]) => {\n console.error(`[RBAC ERROR] ${message}`, ...args);\n },\n warn: (message: string, ...args: unknown[]) => {\n if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug') {\n console.warn(`[RBAC WARN] ${message}`, ...args);\n }\n },\n info: (message: string, ...args: unknown[]) => {\n if (logLevel === 'info' || logLevel === 'debug') {\n console.info(`[RBAC INFO] ${message}`, ...args);\n }\n },\n debug: (message: string, ...args: unknown[]) => {\n if (debug && logLevel === 'debug') {\n console.debug(`[RBAC DEBUG] ${message}`, ...args);\n }\n },\n };\n }\n\n private createDefaultLogger(): RBACLogger {\n return {\n error: (message: string, ...args: unknown[]) => console.error(`[RBAC ERROR] ${message}`, ...args),\n warn: (message: string, ...args: unknown[]) => console.warn(`[RBAC WARN] ${message}`, ...args),\n info: (message: string, ...args: unknown[]) => console.info(`[RBAC INFO] ${message}`, ...args),\n debug: (message: string, ...args: unknown[]) => console.debug(`[RBAC DEBUG] ${message}`, ...args),\n };\n }\n\n isDebugMode(): boolean {\n return this.config?.debug ?? false;\n }\n\n isDevelopmentMode(): boolean {\n return this.config?.developmentMode ?? false;\n }\n\n getMockPermissions(): Record<string, boolean> | null {\n return this.config?.mockPermissions ?? null;\n }\n}\n\n// Global config manager instance\nconst configManager = new RBACConfigManager();\n\nexport function createRBACConfig(config: RBACConfig): RBACConfig {\n configManager.setConfig(config);\n return config;\n}\n\nexport function getRBACConfig(): RBACConfig | null {\n return configManager.getConfig();\n}\n\nexport function getRBACLogger(): RBACLogger {\n return configManager.getLogger();\n}\n\nexport function isDebugMode(): boolean {\n return configManager.isDebugMode();\n}\n\nexport function isDevelopmentMode(): boolean {\n return configManager.isDevelopmentMode();\n}\n\nexport function getMockPermissions(): Record<string, boolean> | null {\n return configManager.getMockPermissions();\n}\n","/**\n * RBAC Main API Functions\n * @package @jmruthers/pace-core\n * @module RBAC/API\n * @since 1.0.0\n * \n * This module provides the main API functions for the RBAC system.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../types/database';\nimport { \n UUID, \n Scope, \n Permission, \n AccessLevel, \n PermissionMap, \n PermissionCheck,\n RBACNotInitializedError\n} from './types';\nimport { createRBACEngine, RBACEngine } from './engine';\nimport { createAuditManager, setGlobalAuditManager } from './audit';\nimport { rbacCache, RBACCache, CACHE_PATTERNS } from './cache';\nimport { createRBACConfig, RBACConfig, getRBACLogger } from './config';\n\n// Global engine instance\nlet globalEngine: RBACEngine | null = null;\n\n/**\n * Setup RBAC system\n * \n * @param supabase - Supabase client\n * @param config - Optional configuration\n */\nexport function setupRBAC(supabase: SupabaseClient<Database>, config?: Partial<RBACConfig>): void {\n const logger = getRBACLogger();\n \n // Create full config\n const fullConfig: RBACConfig = {\n supabase,\n debug: process.env.NODE_ENV === 'development',\n logLevel: 'warn',\n developmentMode: process.env.NODE_ENV === 'development',\n ...config,\n };\n \n createRBACConfig(fullConfig);\n \n globalEngine = createRBACEngine(supabase);\n \n // Setup audit manager\n const auditManager = createAuditManager(supabase);\n setGlobalAuditManager(auditManager);\n \n logger.info('RBAC system initialized successfully');\n}\n\n/**\n * Get the global RBAC engine\n * \n * @returns Global RBAC engine\n * @throws Error if RBAC not initialized\n */\nfunction getEngine(): RBACEngine {\n if (!globalEngine) {\n throw new RBACNotInitializedError();\n }\n return globalEngine;\n}\n\n/**\n * Get user's access level in a scope\n * \n * @param input - Access level input\n * @returns Promise resolving to access level\n * \n * @example\n * ```typescript\n * const accessLevel = await getAccessLevel({\n * userId: 'user-123',\n * scope: { organisationId: 'org-456' }\n * });\n * ```\n */\nexport async function getAccessLevel(input: {\n userId: UUID;\n scope: Scope;\n}): Promise<AccessLevel> {\n const engine = getEngine();\n return engine.getAccessLevel(input);\n}\n\n/**\n * Get user's permission map for a scope\n * \n * @param input - Permission map input\n * @returns Promise resolving to permission map\n * \n * @example\n * ```typescript\n * const permissions = await getPermissionMap({\n * userId: 'user-123',\n * scope: { \n * organisationId: 'org-456',\n * eventId: 'event-789',\n * appId: 'app-101'\n * }\n * });\n * ```\n */\nexport async function getPermissionMap(input: {\n userId: UUID;\n scope: Scope;\n}): Promise<PermissionMap> {\n const engine = getEngine();\n return engine.getPermissionMap(input);\n}\n\n/**\n * Check if user has a specific permission\n * \n * @param input - Permission check input\n * @returns Promise resolving to permission result\n * \n * @example\n * ```typescript\n * const canManage = await isPermitted({\n * userId: 'user-123',\n * scope: { organisationId: 'org-456' },\n * permission: 'manage:events',\n * pageId: 'page-789'\n * });\n * ```\n */\nexport async function isPermitted(input: PermissionCheck): Promise<boolean> {\n const engine = getEngine();\n return engine.isPermitted(input);\n}\n\n/**\n * Check if user has a specific permission (cached version)\n * \n * @param input - Permission check input\n * @returns Promise resolving to permission result\n */\nexport async function isPermittedCached(input: PermissionCheck): Promise<boolean> {\n const { userId, scope, permission, pageId } = input;\n \n // Check cache first\n const cacheKey = RBACCache.generatePermissionKey({\n userId,\n organisationId: scope.organisationId!,\n eventId: scope.eventId,\n appId: scope.appId,\n permission,\n pageId,\n });\n \n const cached = rbacCache.get<boolean>(cacheKey);\n if (cached !== null) {\n return cached;\n }\n\n // Check permission\n const result = await isPermitted(input);\n \n // Cache result\n rbacCache.set(cacheKey, result);\n \n return result;\n}\n\n/**\n * Check if a user has a specific permission (alias for isPermitted)\n * \n * @param input - Permission check parameters\n * @returns Promise<boolean> - True if user has permission\n */\nexport async function hasPermission(input: PermissionCheck): Promise<boolean> {\n return isPermitted(input);\n}\n\n/**\n * Check if user has any of the specified permissions\n * \n * @param input - Permission check input with array of permissions\n * @returns Promise resolving to true if user has any permission\n */\nexport async function hasAnyPermission(input: {\n userId: UUID;\n scope: Scope;\n permissions: Permission[];\n pageId?: UUID;\n}): Promise<boolean> {\n const { permissions, ...baseInput } = input;\n \n for (const permission of permissions) {\n const hasPermission = await isPermitted({\n ...baseInput,\n permission,\n });\n \n if (hasPermission) {\n return true;\n }\n }\n \n return false;\n}\n\n/**\n * Check if user has all of the specified permissions\n * \n * @param input - Permission check input with array of permissions\n * @returns Promise resolving to true if user has all permissions\n */\nexport async function hasAllPermissions(input: {\n userId: UUID;\n scope: Scope;\n permissions: Permission[];\n pageId?: UUID;\n}): Promise<boolean> {\n const { permissions, ...baseInput } = input;\n \n for (const permission of permissions) {\n const hasPermission = await isPermitted({\n ...baseInput,\n permission,\n });\n \n if (!hasPermission) {\n return false;\n }\n }\n \n return true;\n}\n\n/**\n * Check if user is super admin\n * \n * @param userId - User ID\n * @returns Promise resolving to super admin status\n */\nexport async function isSuperAdmin(userId: UUID): Promise<boolean> {\n const engine = getEngine();\n return engine['checkSuperAdmin'](userId);\n}\n\n/**\n * Get app configuration including requires_event setting\n * \n * @param appId - App ID\n * @returns Promise resolving to app configuration\n */\nexport async function getAppConfig(appId: UUID): Promise<{ requires_event: boolean } | null> {\n const engine = getEngine();\n return engine['getAppConfig'](appId);\n}\n\n/**\n * Check if user is organisation admin\n * \n * @param userId - User ID\n * @param organisationId - Organisation ID\n * @returns Promise resolving to organisation admin status\n */\nexport async function isOrganisationAdmin(userId: UUID, organisationId: UUID): Promise<boolean> {\n const accessLevel = await getAccessLevel({\n userId,\n scope: { organisationId },\n });\n \n return accessLevel === 'admin' || accessLevel === 'super';\n}\n\n/**\n * Check if user is event admin\n * \n * @param userId - User ID\n * @param scope - Permission scope with eventId and appId\n * @returns Promise resolving to event admin status\n */\nexport async function isEventAdmin(userId: UUID, scope: Scope): Promise<boolean> {\n if (!scope.eventId || !scope.appId) {\n return false;\n }\n \n const accessLevel = await getAccessLevel({ userId, scope });\n return accessLevel === 'admin' || accessLevel === 'super';\n}\n\n/**\n * Invalidate user's permission cache\n * \n * @param userId - User ID\n * @param organisationId - Organisation ID (optional)\n */\nexport function invalidateUserCache(userId: UUID, organisationId?: UUID): void {\n if (organisationId) {\n rbacCache.invalidate(CACHE_PATTERNS.PERMISSION(userId, organisationId));\n } else {\n rbacCache.invalidate(CACHE_PATTERNS.USER(userId));\n }\n}\n\n/**\n * Invalidate organisation's permission cache\n * \n * @param organisationId - Organisation ID\n */\nexport function invalidateOrganisationCache(organisationId: UUID): void {\n rbacCache.invalidate(CACHE_PATTERNS.ORGANISATION(organisationId));\n}\n\n/**\n * Invalidate event's permission cache\n * \n * @param eventId - Event ID\n */\nexport function invalidateEventCache(eventId: string): void {\n rbacCache.invalidate(CACHE_PATTERNS.EVENT(eventId));\n}\n\n/**\n * Invalidate app's permission cache\n * \n * @param appId - App ID\n */\nexport function invalidateAppCache(appId: UUID): void {\n rbacCache.invalidate(CACHE_PATTERNS.APP(appId));\n}\n\n/**\n * Clear all permission cache\n */\nexport function clearCache(): void {\n rbacCache.clear();\n}\n"],"mappings":";;;;;;;;;;;;AAyRO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YACE,SACO,MACA,SACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAaO,IAAM,mCAAN,cAA+C,UAAU;AAAA,EAC9D,cAAc;AACZ;AAAA,MACE;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,0BAAN,cAAsC,UAAU;AAAA,EACrD,cAAc;AACZ;AAAA,MACE;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AAAA,EACd;AACF;;;ACnTO,IAAM,wBAAN,MAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjC,OAAO,mBAAmB,YAA6B;AACrD,QAAI,OAAO,eAAe,YAAY,WAAW,WAAW,GAAG;AAC7D,aAAO;AAAA,IACT;AAGA,UAAM,kBAAkB;AACxB,WAAO,gBAAgB,KAAK,UAAU;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,aAAa,MAAuB;AACzC,QAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG;AACjD,aAAO;AAAA,IACT;AAGA,UAAM,YAAY;AAClB,WAAO,UAAU,KAAK,IAAI;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,cAAc,OAAuB;AAC1C,QAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,aAAO;AAAA,IACT;AAGA,QAAI,MAAM,kBAAkB,CAAC,KAAK,aAAa,MAAM,cAAc,GAAG;AACpE,aAAO;AAAA,IACT;AAGA,QAAI,MAAM,WAAW,OAAO,MAAM,YAAY,UAAU;AACtD,aAAO;AAAA,IACT;AAGA,QAAI,MAAM,SAAS,CAAC,KAAK,aAAa,MAAM,KAAK,GAAG;AAClD,aAAO;AAAA,IACT;AAGA,WAAO,CAAC,EAAE,MAAM,kBAAkB,MAAM,WAAW,MAAM;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,cAAc,OAAuB;AAC1C,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAGA,WAAO,MACJ,QAAQ,YAAY,EAAE,EACtB,QAAQ,aAAa,EAAE,EACvB,QAAQ,UAAU,EAAE,EACpB,QAAQ,iBAAiB,EAAE,EAC3B,QAAQ,WAAW,EAAE,EACrB,KAAK;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,eAAe,QAAuB;AAC3C,WAAO,KAAK,aAAa,MAAM;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,qBAAqB,YAA6B;AACvD,WAAO,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,IAAI;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,4BAA4B,YAAoB,mBAAoC;AACzF,QAAI,CAAC,KAAK,mBAAmB,UAAU,GAAG;AACxC,aAAO;AAAA,IACT;AAEA,UAAM,CAAC,SAAS,IAAI,WAAW,MAAM,GAAG;AACxC,UAAM,YAAY,CAAC,QAAQ,UAAU,UAAU,UAAU,QAAQ;AAEjE,UAAM,kBAAkB,UAAU,QAAQ,SAAS;AACnD,UAAM,gBAAgB,UAAU,QAAQ,iBAAiB;AAEzD,QAAI,oBAAoB,MAAM,kBAAkB,IAAI;AAClD,aAAO;AAAA,IACT;AAGA,WAAO,mBAAmB;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,eAAe,QAAc,WAAqC;AAG7E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,4BAA4B,OAAc,OAAuB;AAEtE,QAAI,CAAC,MAAM,gBAAgB;AACzB,aAAO;AAAA,IACT;AAGA,QAAI,SAAS,MAAM,UAAU,OAAO;AAGlC,UAAI,CAAC,MAAM,SAAS;AAClB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,iBAAiB,OAKf;AACP,UAAM,gBAAgB;AAAA,MACpB,GAAG;AAAA,MACH,WAAW,MAAM,aAAa,oBAAI,KAAK;AAAA,MACvC,UAAU,KAAK,iBAAiB,MAAM,IAAI;AAAA,IAC5C;AAGA,QAAI,OAAwC;AAC1C,cAAQ,KAAK,mBAAmB,aAAa;AAAA,IAC/C;AAAA,EAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAe,iBAAiB,WAA2D;AACzF,YAAQ,WAAW;AAAA,MACjB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AACF;AAgBO,IAAM,0BAA8C;AAAA,EACzD,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,8BAA8B;AAAA,EAC9B,6BAA6B;AAC/B;AAgBO,IAAM,yBAAN,MAA6B;AAAA,EAGlC,YAAY,SAA6B,yBAAyB;AAChE,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAc,OAAY,SAG7B;AACD,UAAM,SAAmB,CAAC;AAE1B,QAAI,CAAC,KAAK,OAAO,uBAAuB;AACtC,aAAO,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;AAAA,IACrC;AAGA,QAAI,CAAC,sBAAsB,eAAe,QAAQ,MAAM,GAAG;AACzD,aAAO,KAAK,wBAAwB;AAAA,IACtC;AAGA,QAAI,CAAC,sBAAsB,aAAa,QAAQ,cAAc,GAAG;AAC/D,aAAO,KAAK,gCAAgC;AAAA,IAC9C;AAGA,QAAI,MAAM,cAAc,CAAC,sBAAsB,mBAAmB,MAAM,UAAU,GAAG;AACnF,aAAO,KAAK,2BAA2B;AAAA,IACzC;AAGA,QAAI,MAAM,SAAS,CAAC,sBAAsB,cAAc,MAAM,KAAK,GAAG;AACpE,aAAO,KAAK,sBAAsB;AAAA,IACpC;AAGA,QAAI,OAAO,SAAS,GAAG;AACrB,4BAAsB,iBAAiB;AAAA,QACrC,MAAM;AAAA,QACN,QAAQ,QAAQ;AAAA,QAChB,SAAS,EAAE,QAAQ,OAAO,KAAK,cAAc,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,MACtE,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,SAAS,OAAO,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAe,SAGlB;AACD,QAAI,CAAC,KAAK,OAAO,oBAAoB;AACnC,aAAO,EAAE,WAAW,MAAM,WAAW,KAAK,OAAO,6BAA6B;AAAA,IAChF;AAGA,UAAM,YAAY,MAAM,sBAAsB;AAAA,MAC5C,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,cAAc,OAAuB;AAC3C,WAAO,sBAAsB,cAAc,KAAK;AAAA,EAClD;AACF;;;AC1SO,IAAM,aAAN,MAAiB;AAAA,EAItB,YAAY,UAAoC;AAC9C,SAAK,WAAW;AAChB,SAAK,qBAAqB,IAAI,uBAAuB,uBAAuB;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,YAAY,OAAwB,iBAAqD;AAC7F,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,EAAE,QAAQ,YAAY,OAAO,OAAO,IAAI;AAE9C,QAAI;AAEF,UAAI,iBAAiB;AACnB,cAAM,aAAa,MAAM,KAAK,mBAAmB,cAAc,OAAO,eAAe;AACrF,YAAI,CAAC,WAAW,SAAS;AACvB,gCAAsB,iBAAiB;AAAA,YACrC,MAAM;AAAA,YACN;AAAA,YACA,SAAS,EAAE,QAAQ,WAAW,QAAQ,OAAO,KAAK,UAAU,KAAK,EAAE;AAAA,UACrE,CAAC;AACD,iBAAO;AAAA,QACT;AAEA,cAAM,YAAY,MAAM,KAAK,mBAAmB,eAAe,eAAe;AAC9E,YAAI,CAAC,UAAU,WAAW;AACxB,gCAAsB,iBAAiB;AAAA,YACrC,MAAM;AAAA,YACN;AAAA,YACA,SAAS,EAAE,WAAW,UAAU,UAAU;AAAA,UAC5C,CAAC;AACD,iBAAO;AAAA,QACT;AAAA,MACF;AAGA,UAAI,iBAAiB;AACnB,YAAI,CAAC,sBAAsB,eAAe,MAAM,GAAG;AACjD,gCAAsB,iBAAiB;AAAA,YACrC,MAAM;AAAA,YACN;AAAA,YACA,SAAS,EAAE,OAAO,yBAAyB;AAAA,UAC7C,CAAC;AACD,iBAAO;AAAA,QACT;AAEA,YAAI,CAAC,sBAAsB,mBAAmB,UAAU,GAAG;AACzD,gCAAsB,iBAAiB;AAAA,YACrC,MAAM;AAAA,YACN;AAAA,YACA,SAAS,EAAE,OAAO,6BAA6B,WAAW;AAAA,UAC5D,CAAC;AACD,iBAAO;AAAA,QACT;AAEA,YAAI,CAAC,sBAAsB,cAAc,KAAK,GAAG;AAC/C,gCAAsB,iBAAiB;AAAA,YACrC,MAAM;AAAA,YACN;AAAA,YACA,SAAS,EAAE,OAAO,wBAAwB,MAAM;AAAA,UAClD,CAAC;AACD,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,YAAMA,gBAAe,MAAM,KAAK,gBAAgB,MAAM;AACtD,UAAIA,eAAc;AAChB,cAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,YAAI,MAAM,gBAAgB;AAExB,gBAAM,iBAAiB,MAAM,KAAK,cAAc,QAAQ,MAAM,KAAK;AACnE,gBAAM,eAAe;AAAA,YACnB,MAAM;AAAA,YACN;AAAA,YACA,gBAAgB,MAAM;AAAA,YACtB,SAAS,MAAM;AAAA,YACf,OAAO,MAAM;AAAA,YACb,QAAQ;AAAA,YACR;AAAA,YACA,UAAU;AAAA,YACV,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,aAAa;AAAA,UACf,CAAC;AAAA,QACH;AACA,eAAO;AAAA,MACT;AAGA,YAAM,iBAAiB,MAAM,KAAK,4BAA4B,OAAO,MAAM,KAAK;AAChF,UAAI,CAAC,gBAAgB;AACnB,cAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,YAAI,MAAM,gBAAgB;AAExB,gBAAM,iBAAiB,MAAM,KAAK,cAAc,QAAQ,MAAM,KAAK;AACnE,gBAAM,eAAe;AAAA,YACnB,MAAM;AAAA,YACN;AAAA,YACA,gBAAgB,MAAM;AAAA,YACtB,SAAS,MAAM;AAAA,YACf,OAAO,MAAM;AAAA,YACb,QAAQ;AAAA,YACR;AAAA,YACA,QAAQ;AAAA,YACR,UAAU;AAAA,cACR,QAAQ;AAAA,cACR,oBAAoB,MAAM,QAAQ,MAAM,KAAK,aAAa,MAAM,KAAK,EAAE,KAAK,YAAU,QAAQ,cAAc,IAAI;AAAA,YAClH;AAAA,UACF,CAAC;AAAA,QACH;AACA,eAAO;AAAA,MACT;AAGA,YAAM,SAAS,MAAM,KAAK,oBAAoB,QAAQ,gBAAgB,MAAM;AAC5E,cAAQ,IAAI,kCAAkC,MAAM;AAGpD,YAAM,SAAS,OAAO,OAAO,OAAK,EAAE,SAAS,MAAM;AACnD,cAAQ,IAAI,6BAA6B,MAAM;AAC/C,iBAAW,QAAQ,QAAQ;AACzB,cAAM,UAAU,KAAK,kBAAkB,KAAK,YAAY,UAAU;AAClE,gBAAQ,IAAI,+BAA+B;AAAA,UACzC,gBAAgB,KAAK;AAAA,UACrB,qBAAqB;AAAA,UACrB;AAAA,QACF,CAAC;AACD,YAAI,SAAS;AACX,gBAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,kBAAQ,IAAI,sDAAsD;AAElE,cAAI,MAAM,gBAAgB;AAExB,kBAAM,iBAAiB,MAAM,KAAK,cAAc,QAAQ,MAAM,KAAK;AACnE,kBAAM,eAAe;AAAA,cACnB,MAAM;AAAA,cACN;AAAA,cACA,gBAAgB,MAAM;AAAA,cACtB,SAAS,MAAM;AAAA,cACf,OAAO,MAAM;AAAA,cACb,QAAQ;AAAA,cACR;AAAA,cACA,QAAQ;AAAA,YACV,CAAC;AAAA,UACH;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAGA,YAAM,SAAS,OAAO,OAAO,OAAK,EAAE,SAAS,OAAO;AACpD,cAAQ,IAAI,8BAA8B,MAAM;AAChD,UAAIC,iBAAgB;AAGpB,YAAM,aAAqE,CAAC,QAAQ,YAAY,gBAAgB,QAAQ;AAExH,iBAAW,aAAa,YAAY;AAClC,cAAM,cAAc,OAAO,OAAO,OAAK,EAAE,UAAU,SAAS;AAC5D,gBAAQ,IAAI,yBAAyB,SAAS,YAAY,WAAW;AACrE,mBAAW,SAAS,aAAa;AAC/B,kBAAQ,IAAI,oDAAoD,SAAS,KAAK;AAAA,YAC5E,iBAAiB,MAAM;AAAA,YACvB,qBAAqB;AAAA,YACrB;AAAA,UACF,CAAC;AACD,gBAAM,UAAU,KAAK,kBAAkB,MAAM,YAAY,UAAU;AACnE,kBAAQ,IAAI,yCAAyC;AAAA,YACnD;AAAA,YACA,iBAAiB,MAAM;AAAA,YACvB,qBAAqB;AAAA,YACrB;AAAA,UACF,CAAC;AACD,cAAI,SAAS;AACX,oBAAQ,IAAI,sCAAsC,SAAS,aAAa;AACxE,YAAAA,iBAAgB;AAChB;AAAA,UACF;AAAA,QACF;AACA,YAAIA,eAAe;AAAA,MACrB;AAGA,YAAM,gBAAgBA;AACtB,YAAM,YAAY,KAAK,IAAI,IAAI;AAE/B,cAAQ,IAAI,gCAAgC;AAAA,QAC1C;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAAA;AAAA,QACA,aAAa,OAAO;AAAA,QACpB,aAAa,OAAO;AAAA,QACpB,aAAa,OAAO;AAAA,QACpB,UAAU;AAAA,MACZ,CAAC;AAGD,UAAI,MAAM,gBAAgB;AAExB,cAAM,iBAAiB,MAAM,KAAK,cAAc,QAAQ,MAAM,KAAK;AACnE,cAAM,eAAe;AAAA,UACnB,MAAM;AAAA,UACN;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,SAAS,MAAM;AAAA,UACf,OAAO,MAAM;AAAA,UACb,QAAQ;AAAA,UACR;AAAA,UACA,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,aAAa;AAAA,QACf,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,4BAAsB,iBAAiB;AAAA,QACrC,MAAM;AAAA,QACN;AAAA,QACA,SAAS;AAAA,UACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAChD;AAAA,UACA,OAAO,KAAK,UAAU,KAAK;AAAA,QAC7B;AAAA,MACF,CAAC;AAGD,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,eAAe,OAA6D;AAChF,UAAM,EAAE,QAAQ,MAAM,IAAI;AAG1B,UAAMD,gBAAe,MAAM,KAAK,gBAAgB,MAAM;AACtD,QAAIA,eAAc;AAChB,aAAO;AAAA,IACT;AAGA,UAAM,iBAAiB,MAAM,KAAK,4BAA4B,OAAO,MAAM,KAAK;AAChF,QAAI,CAAC,gBAAgB;AACnB,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,UAAU;AAAA,MACzB;AAAA,MACA,eAAe;AAAA,MACf,eAAe;AAAA,MACf,eAAe;AAAA,IACjB;AAEA,UAAM,SAAS,UAAU,IAAiB,QAAQ;AAClD,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAGA,UAAM,UAAU,MAAM,KAAK,oBAAoB,QAAQ,eAAe,cAAe;AACrF,QAAI,YAAY,aAAa;AAC3B,gBAAU,IAAI,UAAU,OAAO;AAC/B,aAAO;AAAA,IACT;AAGA,QAAI,eAAe,WAAW,eAAe,OAAO;AAClD,YAAM,YAAY,MAAM,KAAK,gBAAgB,QAAQ,eAAe,SAAS,eAAe,KAAK;AACjG,UAAI,cAAc,eAAe;AAC/B,kBAAU,IAAI,UAAU,OAAO;AAC/B,eAAO;AAAA,MACT;AACA,UAAI,cAAc,WAAW;AAC3B,kBAAU,IAAI,UAAU,SAAS;AACjC,eAAO;AAAA,MACT;AACA,UAAI,cAAc,eAAe;AAC/B,kBAAU,IAAI,UAAU,aAAa;AACrC,eAAO;AAAA,MACT;AAAA,IACF;AAGA,cAAU,IAAI,UAAU,QAAQ;AAChC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,iBAAiB,OAA+D;AACpF,UAAM,EAAE,QAAQ,MAAM,IAAI;AAG1B,UAAMA,gBAAe,MAAM,KAAK,gBAAgB,MAAM;AACtD,QAAIA,eAAc;AAChB,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,iBAAiB,MAAM,KAAK,4BAA4B,OAAO,MAAM,KAAK;AAChF,QAAI,CAAC,gBAAgB;AACnB,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,WAAW,UAAU;AAAA,MACzB;AAAA,MACA,eAAe;AAAA,MACf,eAAe;AAAA,MACf,eAAe;AAAA,IACjB;AAEA,UAAM,SAAS,UAAU,IAAmB,QAAQ;AACpD,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,UAAM,gBAA+B,CAAC;AAGtC,QAAI,eAAe,OAAO;AACxB,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,SAChC,KAAK,gBAAgB,EACrB,OAAO,eAAe,EACtB,GAAG,UAAU,eAAe,KAAK;AAEpC,UAAI,OAAO;AACT,mBAAW,QAAQ,OAAO;AACxB,gBAAM,aAA0B,CAAC;AAGjC,qBAAW,aAAa,CAAC,QAAQ,UAAU,UAAU,UAAU,QAAQ,GAAkB;AACvF,kBAAMC,iBAAgB,MAAM,KAAK,YAAY;AAAA,cAC3C;AAAA,cACA,OAAO;AAAA,cACP,YAAY,GAAG,SAAS,IAAI,KAAK,SAAS;AAAA,cAC1C,QAAQ,KAAK;AAAA,YACf,CAAC;AAED,gBAAIA,gBAAe;AACjB,yBAAW,KAAK,SAAS;AAAA,YAC3B;AAAA,UACF;AAEA,wBAAc,KAAK,EAAE,IAAI;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAEA,cAAU,IAAI,UAAU,aAAa;AACrC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,gBAAgB,QAAgC;AAE5D,UAAM,WAAW,eAAe,MAAM;AACtC,UAAM,SAAS,UAAU,IAAa,QAAQ;AAC9C,QAAI,WAAW,MAAM;AACnB,aAAO;AAAA,IACT;AAEA,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,SAChC,KAAK,mBAAmB,EACxB,OAAO,IAAI,EACX,GAAG,WAAW,MAAM,EACpB,GAAG,QAAQ,aAAa,EACxB,GAAG,YAAY,IAAI,EACnB,OAAO;AAEV,UAAMD,gBAAe,CAAC,SAAS,CAAC,CAAC;AAGjC,cAAU,IAAI,UAAUA,eAAc,GAAK;AAE3C,WAAOA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,aAAa,OAA0D;AAC3E,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,SAChC,KAAK,WAAW,EAChB,OAAO,gBAAgB,EACvB,GAAG,MAAM,KAAK,EACd,GAAG,aAAa,IAAI,EACpB,OAAO;AAEV,QAAI,SAAS,CAAC,MAAM;AAClB,aAAO;AAAA,IACT;AAEA,WAAO,EAAE,gBAAgB,KAAK,eAAe;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,6BAA6B,SAAuC;AAChF,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,SAChC,KAAK,OAAO,EACZ,OAAO,iBAAiB,EACxB,GAAG,MAAM,OAAO,EAChB,OAAO;AAEV,QAAI,SAAS,CAAC,MAAM;AAClB,aAAO;AAAA,IACT;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,4BAA4B,OAAc,OAAqC;AAE3F,QAAI,OAAO;AACT,YAAM,YAAY,MAAM,KAAK,aAAa,KAAK;AAC/C,UAAI,CAAC,WAAW;AACd,eAAO;AAAA,MACT;AAEA,UAAI,UAAU,gBAAgB;AAE5B,YAAI,CAAC,MAAM,SAAS;AAClB,iBAAO;AAAA,QACT;AAGA,YAAI,CAAC,MAAM,gBAAgB;AACzB,gBAAM,gBAAgB,MAAM,KAAK,6BAA6B,MAAM,OAAO;AAC3E,cAAI,CAAC,eAAe;AAClB,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,gBAAgB;AAAA,UAClB;AAAA,QACF;AAEA,eAAO;AAAA,MACT,OAAO;AAEL,YAAI,CAAC,MAAM,gBAAgB;AACzB,iBAAO;AAAA,QACT;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,CAAC,MAAM,gBAAgB;AACzB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,oBACZ,QACA,OACA,QACkB;AAClB,UAAM,SAAmB,CAAC;AAC1B,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AAGnC,UAAM,YAAsB,CAAC;AAG7B,QAAI,MAAM,WAAW,MAAM,OAAO;AAChC,YAAM,EAAE,MAAM,WAAW,IAAI,MAAM,KAAK,SACrC,KAAK,sBAAsB,EAC3B,OAAO,oCAAoC,EAC3C,GAAG,WAAW,MAAM,EACpB,GAAG,YAAY,MAAM,OAAO,EAC5B,GAAG,UAAU,MAAM,KAAK,EACxB,GAAG,UAAU,QAAQ,EACrB,IAAI,cAAc,GAAG,EACrB,GAAG,iCAAiC,GAAG,EAAE;AAE5C,UAAI,YAAY;AACd,kBAAU,KAAK,GAAG,WAAW,IAAI,OAAK,EAAE,IAAI,CAAC;AAE7C,mBAAW,QAAQ,YAAY;AAC7B,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,YAAY,KAAK,0BAA0B,KAAK,IAAoB;AAAA,YACpE,OAAO;AAAA,YACP,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,QAAI,MAAM,gBAAgB;AACxB,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,KAAK,SACnC,KAAK,yBAAyB,EAC9B,OAAO,oCAAoC,EAC3C,GAAG,WAAW,MAAM,EACpB,GAAG,mBAAmB,MAAM,cAAc,EAC1C,GAAG,UAAU,QAAQ,EACrB,IAAI,cAAc,GAAG,EACrB,GAAG,iCAAiC,GAAG,EAAE;AAE5C,UAAI,UAAU;AACZ,kBAAU,KAAK,GAAG,SAAS,IAAI,OAAK,EAAE,IAAI,CAAC;AAE3C,mBAAW,QAAQ,UAAU;AAC3B,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,YAAY,KAAK,wBAAwB,KAAK,IAAwB;AAAA,YACtE,OAAO;AAAA,YACP,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,IAAI,qCAAqC,SAAS;AAG1D,QAAI,QAAQ;AAEV,UAAI,iBAA8B;AAClC,UAAI,OAAO,WAAW,UAAU;AAE9B,cAAM,YAAY;AAClB,YAAI,UAAU,KAAK,MAAM,GAAG;AAC1B,2BAAiB;AAAA,QACnB,OAAO;AAEL,gBAAM,QAAQ,MAAM;AACpB,cAAI,OAAO;AACT,kBAAM,EAAE,MAAM,KAAK,IAAI,MAAM,KAAK,SAC/B,KAAK,gBAAgB,EACrB,OAAO,IAAI,EACX,GAAG,UAAU,KAAK,EAClB,GAAG,aAAa,MAAM,EACtB,OAAO;AACV,6BAAiB,MAAM,MAAM;AAAA,UAC/B;AAAA,QACF;AAAA,MACF,OAAO;AACL,yBAAiB;AAAA,MACnB;AAEA,UAAI,kBAAkB,MAAM,OAAO;AACjC,gBAAQ,IAAI,qEAAqE,cAAc;AAG/F,YAAI,WAA0B;AAC9B,YAAI,OAAO,WAAW,YAAY,CAAC,kEAAkE,KAAK,MAAM,GAAG;AAEjH,qBAAW;AAAA,QACb,OAAO;AAEL,gBAAM,EAAE,MAAM,KAAK,IAAI,MAAM,KAAK,SAC/B,KAAK,gBAAgB,EACrB,OAAO,WAAW,EAClB,GAAG,MAAM,cAAc,EACvB,OAAO;AACV,qBAAW,MAAM,aAAa;AAAA,QAChC;AAIA,cAAM,YAAY,MAAM,KAAK,SAAS,IAAI,wBAAwB;AAAA,UAChE,WAAW;AAAA,UACX,UAAU,MAAM;AAAA,UAChB,YAAY,MAAM,WAAW;AAAA,UAC7B,mBAAmB,MAAM,kBAAkB;AAAA,UAC3C,WAAW;AAAA,QACb,CAAC;AAED,cAAM,EAAE,MAAM,eAAe,IAAI;AAEjC,gBAAQ,IAAI,+CAA+C,cAAc;AAEzE,YAAI,kBAAkB,UAAU;AAE9B,gBAAM,YAAY,eAAe;AAAA,YAAO,OACtC,EAAE,oBAAoB,qBACtB,EAAE,oBAAoB,yBACtB,EAAE,oBAAoB;AAAA,UACxB;AAEA,qBAAW,QAAQ,WAAW;AAE5B,gBAAI,UAAU,SAAS,KAAK,SAAS,GAAG;AACtC,sBAAQ,IAAI,4CAA4C,EAAE,WAAW,KAAK,iBAAiB,MAAM,KAAK,WAAW,SAAS,KAAK,gBAAgB,SAAS,CAAC;AACzJ,qBAAO,KAAK;AAAA,gBACV,MAAM,KAAK,iBAAiB,UAAU;AAAA;AAAA,gBAEtC,YAAY,GAAG,KAAK,eAAe,SAAS,QAAQ;AAAA,gBACpD,OAAO;AAAA,gBACP,QAAQ;AAAA,cACV,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,EAAE,MAAM,YAAY,IAAI,MAAM,KAAK,SACtC,KAAK,mBAAmB,EACxB,OAAO,4BAA4B,EACnC,GAAG,WAAW,MAAM,EACpB,IAAI,cAAc,GAAG,EACrB,GAAG,iCAAiC,GAAG,EAAE;AAE5C,QAAI,aAAa;AACf,iBAAW,QAAQ,aAAa;AAC9B,YAAI,KAAK,SAAS,eAAe;AAC/B,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,YAAY;AAAA,YACZ,OAAO;AAAA,YACP,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,IAAI,uCAAuC,MAAM;AAEzD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,qBACZ,QACA,QACA,YACA,OACkB;AAClB,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,IACT;AAEA,UAAM,CAAC,SAAS,IAAI,WAAW,MAAM,GAAG;AAGxC,UAAM,YAAsB,CAAC;AAG7B,QAAI,MAAM,gBAAgB;AACxB,YAAM,UAAU,MAAM,KAAK,oBAAoB,QAAQ,MAAM,cAAc;AAC3E,UAAI,SAAS;AACX,kBAAU,KAAK,OAAO;AAAA,MACxB;AAAA,IACF;AAGA,QAAI,MAAM,WAAW,MAAM,OAAO;AAChC,YAAM,YAAY,MAAM,KAAK,gBAAgB,QAAQ,MAAM,SAAS,MAAM,KAAK;AAC/E,UAAI,WAAW;AACb,kBAAU,KAAK,SAAS;AAAA,MAC1B;AAAA,IACF;AAGA,QAAI,iBAA8B;AAClC,QAAI,OAAO,WAAW,UAAU;AAE9B,YAAM,YAAY;AAClB,UAAI,UAAU,KAAK,MAAM,GAAG;AAC1B,yBAAiB;AAAA,MACnB,OAAO;AAGL,YAAI,QAAQ,MAAM;AAClB,YAAI,CAAC,OAAO;AAEV,gBAAM,UAAU,QAAQ,IAAI,iBAAiB,QAAQ,IAAI;AACzD,cAAI,SAAS;AACX,kBAAM,EAAE,MAAM,IAAI,IAAI,MAAM,KAAK,SAC9B,KAAK,WAAW,EAChB,OAAO,IAAI,EACX,GAAG,QAAQ,OAAO,EAClB,GAAG,aAAa,IAAI,EACpB,OAAO;AACV,gBAAI,KAAK;AACP,sBAAQ,IAAI;AAAA,YACd;AAAA,UACF;AAAA,QACF;AAEA,YAAI,OAAO;AACT,gBAAM,EAAE,MAAM,KAAK,IAAI,MAAM,KAAK,SAC/B,KAAK,gBAAgB,EACrB,OAAO,IAAI,EACX,GAAG,UAAU,KAAK,EAClB,GAAG,aAAa,MAAM,EACtB,OAAO;AACV,2BAAiB,MAAM,MAAM;AAAA,QAC/B;AAAA,MACF;AAAA,IACF,OAAO;AACL,uBAAiB;AAAA,IACnB;AAEA,QAAI,CAAC,gBAAgB;AACnB,aAAO;AAAA,IACT;AAGA,UAAM,EAAE,MAAM,gBAAgB,IAAI,MAAM,KAAK,SAC1C,KAAK,uBAAuB,EAC5B,OAAO,SAAS,EAChB,GAAG,eAAe,cAAc,EAChC,GAAG,aAAa,SAAS,EACzB,GAAG,aAAa,SAAS,EACzB,OAAO;AAEV,WAAO,iBAAiB,WAAW;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,oBAAoB,QAAc,gBAAwD;AACtG,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,SAChC,KAAK,yBAAyB,EAC9B,OAAO,MAAM,EACb,GAAG,WAAW,MAAM,EACpB,GAAG,mBAAmB,cAAc,EACpC,GAAG,UAAU,QAAQ,EACrB,OAAO;AAEV,WAAO,QAAQ,OAAQ,MAAM,QAA6B;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,gBAAgB,QAAc,SAAiB,OAA2C;AACtG,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,SAChC,KAAK,sBAAsB,EAC3B,OAAO,oCAAoC,EAC3C,GAAG,WAAW,MAAM,EACpB,GAAG,YAAY,OAAO,EACtB,GAAG,UAAU,KAAK,EAClB,GAAG,UAAU,QAAQ,EACrB,IAAI,eAAc,oBAAI,KAAK,GAAE,YAAY,CAAC,EAC1C,GAAG,kCAAiC,oBAAI,KAAK,GAAE,YAAY,CAAC,EAAE,EAC9D,OAAO;AAEV,WAAO,QAAQ,OAAQ,MAAM,QAAyB;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,wBAAwB,MAAoC;AAClE,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,0BAA0B,MAAgC;AAChE,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,kBAAkB,iBAA6B,qBAA0C;AAC/F,YAAQ,IAAI,iCAAiC,EAAE,iBAAiB,oBAAoB,CAAC;AAGrF,QAAI,oBAAoB,qBAAqB;AAC3C,cAAQ,IAAI,uCAAuC;AACnD,aAAO;AAAA,IACT;AAGA,QAAI,gBAAgB,SAAS,IAAI,KAAK,gBAAgB,SAAS,IAAI,GAAG;AACpE,YAAM,CAAC,SAAS,aAAa,IAAI,gBAAgB,MAAM,GAAG;AAC1D,YAAM,CAAC,aAAa,iBAAiB,IAAI,oBAAoB,MAAM,GAAG;AAEtE,cAAQ,IAAI,uCAAuC;AAAA,QACjD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAiB,YAAY;AAAA,MAC/B,CAAC;AAED,UAAI,YAAY,aAAa;AAG3B,cAAM,SAAS,cAAc,MAAM,GAAG,EAAE;AACxC,cAAM,UAAU,WAAW,MAAM,kBAAkB,WAAW,MAAM;AACpE,gBAAQ,IAAI,8CAA8C,EAAE,QAAQ,QAAQ,CAAC;AAC7E,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,gBAAgB,SAAS,GAAG,GAAG;AACjC,YAAM,CAAC,SAAS,aAAa,IAAI,gBAAgB,MAAM,GAAG;AAC1D,YAAM,CAAC,aAAa,iBAAiB,IAAI,oBAAoB,MAAM,GAAG;AAEtE,cAAQ,IAAI,6CAA6C;AAAA,QACvD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAiB,YAAY;AAAA,MAC/B,CAAC;AAED,UAAI,YAAY,aAAa;AAE3B,cAAM,SAAS,cAAc,QAAQ,KAAK,EAAE;AAC5C,cAAM,UAAU,kBAAkB,WAAW,MAAM;AACnD,gBAAQ,IAAI,oDAAoD,EAAE,QAAQ,QAAQ,CAAC;AACnF,eAAO;AAAA,MACT;AAAA,IACF;AAEA,YAAQ,IAAI,oCAAoC;AAChD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,cAAc,QAAwB,OAAkD;AACpG,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,IACT;AAGA,UAAM,YAAY;AAClB,QAAI,UAAU,KAAK,MAAM,GAAG;AAC1B,aAAO;AAAA,IACT;AAGA,QAAI,CAAC,OAAO;AAEV,aAAO;AAAA,IACT;AAGA,QAAI;AACF,YAAM,EAAE,MAAM,KAAK,IAAI,MAAM,KAAK,SAC/B,KAAK,gBAAgB,EACrB,OAAO,IAAI,EACX,GAAG,UAAU,KAAK,EAClB,GAAG,aAAa,MAAM,EACtB,OAAO;AAEV,aAAO,MAAM,MAAM;AAAA,IACrB,SAAS,OAAO;AACd,cAAQ,KAAK,sDAAsD,EAAE,QAAQ,OAAO,MAAM,CAAC;AAC3F,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAQO,SAAS,iBAAiB,UAAgD;AAC/E,SAAO,IAAI,WAAW,QAAQ;AAChC;;;AC59BA,IAAM,oBAAN,MAAwB;AAAA,EAAxB;AACE,SAAQ,SAA4B;AACpC,SAAQ,SAA4B;AAAA;AAAA,EAEpC,UAAU,QAA0B;AAClC,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,YAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAwB;AACtB,QAAI,CAAC,KAAK,QAAQ;AAChB,WAAK,SAAS,KAAK,oBAAoB;AAAA,IACzC;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAM,EAAE,QAAQ,OAAO,WAAW,OAAO,IAAI,KAAK;AAElD,SAAK,SAAS;AAAA,MACZ,OAAO,CAAC,YAAoB,SAAoB;AAC9C,gBAAQ,MAAM,gBAAgB,OAAO,IAAI,GAAG,IAAI;AAAA,MAClD;AAAA,MACA,MAAM,CAAC,YAAoB,SAAoB;AAC7C,YAAI,aAAa,UAAU,aAAa,UAAU,aAAa,SAAS;AACtE,kBAAQ,KAAK,eAAe,OAAO,IAAI,GAAG,IAAI;AAAA,QAChD;AAAA,MACF;AAAA,MACA,MAAM,CAAC,YAAoB,SAAoB;AAC7C,YAAI,aAAa,UAAU,aAAa,SAAS;AAC/C,kBAAQ,KAAK,eAAe,OAAO,IAAI,GAAG,IAAI;AAAA,QAChD;AAAA,MACF;AAAA,MACA,OAAO,CAAC,YAAoB,SAAoB;AAC9C,YAAI,SAAS,aAAa,SAAS;AACjC,kBAAQ,MAAM,gBAAgB,OAAO,IAAI,GAAG,IAAI;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,sBAAkC;AACxC,WAAO;AAAA,MACL,OAAO,CAAC,YAAoB,SAAoB,QAAQ,MAAM,gBAAgB,OAAO,IAAI,GAAG,IAAI;AAAA,MAChG,MAAM,CAAC,YAAoB,SAAoB,QAAQ,KAAK,eAAe,OAAO,IAAI,GAAG,IAAI;AAAA,MAC7F,MAAM,CAAC,YAAoB,SAAoB,QAAQ,KAAK,eAAe,OAAO,IAAI,GAAG,IAAI;AAAA,MAC7F,OAAO,CAAC,YAAoB,SAAoB,QAAQ,MAAM,gBAAgB,OAAO,IAAI,GAAG,IAAI;AAAA,IAClG;AAAA,EACF;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,oBAA6B;AAC3B,WAAO,KAAK,QAAQ,mBAAmB;AAAA,EACzC;AAAA,EAEA,qBAAqD;AACnD,WAAO,KAAK,QAAQ,mBAAmB;AAAA,EACzC;AACF;AAGA,IAAM,gBAAgB,IAAI,kBAAkB;AAErC,SAAS,iBAAiB,QAAgC;AAC/D,gBAAc,UAAU,MAAM;AAC9B,SAAO;AACT;AAEO,SAAS,gBAAmC;AACjD,SAAO,cAAc,UAAU;AACjC;AAEO,SAAS,gBAA4B;AAC1C,SAAO,cAAc,UAAU;AACjC;AAEO,SAAS,cAAuB;AACrC,SAAO,cAAc,YAAY;AACnC;AAEO,SAAS,oBAA6B;AAC3C,SAAO,cAAc,kBAAkB;AACzC;;;ACtGA,IAAI,eAAkC;AAQ/B,SAAS,UAAU,UAAoC,QAAoC;AAChG,QAAM,SAAS,cAAc;AAG7B,QAAM,aAAyB;AAAA,IAC7B;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,GAAG;AAAA,EACL;AAEA,mBAAiB,UAAU;AAE3B,iBAAe,iBAAiB,QAAQ;AAGxC,QAAM,eAAe,mBAAmB,QAAQ;AAChD,wBAAsB,YAAY;AAElC,SAAO,KAAK,sCAAsC;AACpD;AAQA,SAAS,YAAwB;AAC/B,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,wBAAwB;AAAA,EACpC;AACA,SAAO;AACT;AAgBA,eAAsB,eAAe,OAGZ;AACvB,QAAM,SAAS,UAAU;AACzB,SAAO,OAAO,eAAe,KAAK;AACpC;AAoBA,eAAsB,iBAAiB,OAGZ;AACzB,QAAM,SAAS,UAAU;AACzB,SAAO,OAAO,iBAAiB,KAAK;AACtC;AAkBA,eAAsB,YAAY,OAA0C;AAC1E,QAAM,SAAS,UAAU;AACzB,SAAO,OAAO,YAAY,KAAK;AACjC;AAQA,eAAsB,kBAAkB,OAA0C;AAChF,QAAM,EAAE,QAAQ,OAAO,YAAY,OAAO,IAAI;AAG9C,QAAM,WAAW,UAAU,sBAAsB;AAAA,IAC/C;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,SAAS,UAAU,IAAa,QAAQ;AAC9C,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,MAAM,YAAY,KAAK;AAGtC,YAAU,IAAI,UAAU,MAAM;AAE9B,SAAO;AACT;AAQA,eAAsB,cAAc,OAA0C;AAC5E,SAAO,YAAY,KAAK;AAC1B;AAQA,eAAsB,iBAAiB,OAKlB;AACnB,QAAM,EAAE,aAAa,GAAG,UAAU,IAAI;AAEtC,aAAW,cAAc,aAAa;AACpC,UAAME,iBAAgB,MAAM,YAAY;AAAA,MACtC,GAAG;AAAA,MACH;AAAA,IACF,CAAC;AAED,QAAIA,gBAAe;AACjB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAQA,eAAsB,kBAAkB,OAKnB;AACnB,QAAM,EAAE,aAAa,GAAG,UAAU,IAAI;AAEtC,aAAW,cAAc,aAAa;AACpC,UAAMA,iBAAgB,MAAM,YAAY;AAAA,MACtC,GAAG;AAAA,MACH;AAAA,IACF,CAAC;AAED,QAAI,CAACA,gBAAe;AAClB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAQA,eAAsB,aAAa,QAAgC;AACjE,QAAM,SAAS,UAAU;AACzB,SAAO,OAAO,iBAAiB,EAAE,MAAM;AACzC;AAQA,eAAsB,aAAa,OAA0D;AAC3F,QAAM,SAAS,UAAU;AACzB,SAAO,OAAO,cAAc,EAAE,KAAK;AACrC;AASA,eAAsB,oBAAoB,QAAc,gBAAwC;AAC9F,QAAM,cAAc,MAAM,eAAe;AAAA,IACvC;AAAA,IACA,OAAO,EAAE,eAAe;AAAA,EAC1B,CAAC;AAED,SAAO,gBAAgB,WAAW,gBAAgB;AACpD;AASA,eAAsB,aAAa,QAAc,OAAgC;AAC/E,MAAI,CAAC,MAAM,WAAW,CAAC,MAAM,OAAO;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,MAAM,eAAe,EAAE,QAAQ,MAAM,CAAC;AAC1D,SAAO,gBAAgB,WAAW,gBAAgB;AACpD;AAQO,SAAS,oBAAoB,QAAc,gBAA6B;AAC7E,MAAI,gBAAgB;AAClB,cAAU,WAAW,eAAe,WAAW,QAAQ,cAAc,CAAC;AAAA,EACxE,OAAO;AACL,cAAU,WAAW,eAAe,KAAK,MAAM,CAAC;AAAA,EAClD;AACF;AAOO,SAAS,4BAA4B,gBAA4B;AACtE,YAAU,WAAW,eAAe,aAAa,cAAc,CAAC;AAClE;AAOO,SAAS,qBAAqB,SAAuB;AAC1D,YAAU,WAAW,eAAe,MAAM,OAAO,CAAC;AACpD;AAOO,SAAS,mBAAmB,OAAmB;AACpD,YAAU,WAAW,eAAe,IAAI,KAAK,CAAC;AAChD;AAKO,SAAS,aAAmB;AACjC,YAAU,MAAM;AAClB;","names":["isSuperAdmin","hasPermission","hasPermission"]}
@@ -0,0 +1,161 @@
1
+ // src/rbac/cache.ts
2
+ var RBACCache = class {
3
+ constructor() {
4
+ this.cache = /* @__PURE__ */ new Map();
5
+ this.TTL = 60 * 1e3;
6
+ // 60 seconds
7
+ this.invalidationCallbacks = /* @__PURE__ */ new Set();
8
+ }
9
+ /**
10
+ * Get a value from the cache
11
+ *
12
+ * @param key - Cache key
13
+ * @returns Cached value or null if not found/expired
14
+ */
15
+ get(key) {
16
+ const entry = this.cache.get(key);
17
+ if (!entry) {
18
+ return null;
19
+ }
20
+ if (Date.now() > entry.expires) {
21
+ this.cache.delete(key);
22
+ return null;
23
+ }
24
+ return entry.data;
25
+ }
26
+ /**
27
+ * Set a value in the cache
28
+ *
29
+ * @param key - Cache key
30
+ * @param data - Data to cache
31
+ * @param ttl - Time to live in milliseconds (defaults to 60s)
32
+ */
33
+ set(key, data, ttl = this.TTL) {
34
+ const expires = ttl <= 0 ? Date.now() - 1 : Date.now() + ttl;
35
+ this.cache.set(key, {
36
+ data,
37
+ expires
38
+ });
39
+ }
40
+ /**
41
+ * Delete a specific key from the cache
42
+ *
43
+ * @param key - Cache key to delete
44
+ */
45
+ delete(key) {
46
+ this.cache.delete(key);
47
+ }
48
+ /**
49
+ * Invalidate cache entries matching a pattern
50
+ *
51
+ * @param pattern - Pattern to match against cache keys
52
+ */
53
+ invalidate(pattern) {
54
+ const keysToDelete = [];
55
+ for (const key of this.cache.keys()) {
56
+ if (key.includes(pattern)) {
57
+ keysToDelete.push(key);
58
+ }
59
+ }
60
+ keysToDelete.forEach((key) => this.cache.delete(key));
61
+ this.invalidationCallbacks.forEach((callback) => callback(pattern));
62
+ }
63
+ /**
64
+ * Clear all cache entries
65
+ */
66
+ clear() {
67
+ this.cache.clear();
68
+ }
69
+ /**
70
+ * Get cache statistics
71
+ */
72
+ getStats() {
73
+ return {
74
+ size: this.cache.size,
75
+ ttl: this.TTL,
76
+ keys: Array.from(this.cache.keys())
77
+ };
78
+ }
79
+ /**
80
+ * Add an invalidation callback
81
+ *
82
+ * @param callback - Function to call when cache is invalidated
83
+ */
84
+ onInvalidate(callback) {
85
+ this.invalidationCallbacks.add(callback);
86
+ return () => {
87
+ this.invalidationCallbacks.delete(callback);
88
+ };
89
+ }
90
+ /**
91
+ * Generate cache key for permission check
92
+ *
93
+ * @param key - Permission cache key object
94
+ * @returns String cache key
95
+ */
96
+ static generatePermissionKey(key) {
97
+ const parts = [
98
+ "perm",
99
+ key.userId,
100
+ key.organisationId || "null",
101
+ key.eventId || "null",
102
+ key.appId || "null",
103
+ key.permission || "null",
104
+ key.pageId || "null"
105
+ ];
106
+ return parts.join(":");
107
+ }
108
+ /**
109
+ * Generate cache key for access level
110
+ *
111
+ * @param userId - User ID
112
+ * @param organisationId - Organisation ID
113
+ * @param eventId - Event ID (optional)
114
+ * @param appId - App ID (optional)
115
+ * @returns String cache key
116
+ */
117
+ static generateAccessLevelKey(userId, organisationId, eventId, appId) {
118
+ const parts = [
119
+ "access",
120
+ userId,
121
+ organisationId,
122
+ eventId || "null",
123
+ appId || "null"
124
+ ];
125
+ return parts.join(":");
126
+ }
127
+ /**
128
+ * Generate cache key for permission map
129
+ *
130
+ * @param userId - User ID
131
+ * @param organisationId - Organisation ID
132
+ * @param eventId - Event ID (optional)
133
+ * @param appId - App ID (optional)
134
+ * @returns String cache key
135
+ */
136
+ static generatePermissionMapKey(userId, organisationId, eventId, appId) {
137
+ const parts = [
138
+ "map",
139
+ userId,
140
+ organisationId,
141
+ eventId || "null",
142
+ appId || "null"
143
+ ];
144
+ return parts.join(":");
145
+ }
146
+ };
147
+ var rbacCache = new RBACCache();
148
+ var CACHE_PATTERNS = {
149
+ USER: (userId) => `user:${userId}`,
150
+ ORGANISATION: (organisationId) => `org:${organisationId}`,
151
+ EVENT: (eventId) => `event:${eventId}`,
152
+ APP: (appId) => `app:${appId}`,
153
+ PERMISSION: (userId, organisationId) => `perm:${userId}:${organisationId}`
154
+ };
155
+
156
+ export {
157
+ RBACCache,
158
+ rbacCache,
159
+ CACHE_PATTERNS
160
+ };
161
+ //# sourceMappingURL=chunk-MRRFJ6SA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac/cache.ts"],"sourcesContent":["/**\n * RBAC Cache Implementation\n * @package @jmruthers/pace-core\n * @module RBAC/Cache\n * @since 1.0.0\n * \n * This module provides caching functionality for RBAC operations with TTL and invalidation.\n */\n\nimport { UUID } from './types';\nimport { CacheEntry, PermissionCacheKey } from './types';\n\n/**\n * In-memory cache for RBAC operations\n * \n * Provides 60-second TTL and pattern-based invalidation for permission checks.\n */\nexport class RBACCache {\n private cache = new Map<string, CacheEntry<any>>();\n private readonly TTL = 60 * 1000; // 60 seconds\n private invalidationCallbacks: Set<(pattern: string) => void> = new Set();\n\n /**\n * Get a value from the cache\n * \n * @param key - Cache key\n * @returns Cached value or null if not found/expired\n */\n get<T>(key: string): T | null {\n const entry = this.cache.get(key);\n \n if (!entry) {\n return null;\n }\n\n if (Date.now() > entry.expires) {\n this.cache.delete(key);\n return null;\n }\n\n return entry.data as T;\n }\n\n /**\n * Set a value in the cache\n * \n * @param key - Cache key\n * @param data - Data to cache\n * @param ttl - Time to live in milliseconds (defaults to 60s)\n */\n set<T>(key: string, data: T, ttl: number = this.TTL): void {\n // For zero or negative TTL, set expires to current time to make it immediately expired\n const expires = ttl <= 0 ? Date.now() - 1 : Date.now() + ttl;\n this.cache.set(key, {\n data,\n expires,\n });\n }\n\n /**\n * Delete a specific key from the cache\n * \n * @param key - Cache key to delete\n */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /**\n * Invalidate cache entries matching a pattern\n * \n * @param pattern - Pattern to match against cache keys\n */\n invalidate(pattern: string): void {\n const keysToDelete: string[] = [];\n \n for (const key of this.cache.keys()) {\n if (key.includes(pattern)) {\n keysToDelete.push(key);\n }\n }\n\n keysToDelete.forEach(key => this.cache.delete(key));\n \n // Notify invalidation callbacks\n this.invalidationCallbacks.forEach(callback => callback(pattern));\n }\n\n /**\n * Clear all cache entries\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n size: number;\n ttl: number;\n keys: string[];\n } {\n return {\n size: this.cache.size,\n ttl: this.TTL,\n keys: Array.from(this.cache.keys()),\n };\n }\n\n /**\n * Add an invalidation callback\n * \n * @param callback - Function to call when cache is invalidated\n */\n onInvalidate(callback: (pattern: string) => void): () => void {\n this.invalidationCallbacks.add(callback);\n \n // Return unsubscribe function\n return () => {\n this.invalidationCallbacks.delete(callback);\n };\n }\n\n /**\n * Generate cache key for permission check\n * \n * @param key - Permission cache key object\n * @returns String cache key\n */\n static generatePermissionKey(key: PermissionCacheKey): string {\n const parts = [\n 'perm',\n key.userId,\n key.organisationId || 'null',\n key.eventId || 'null',\n key.appId || 'null',\n key.permission || 'null',\n key.pageId || 'null',\n ];\n \n return parts.join(':');\n }\n\n /**\n * Generate cache key for access level\n * \n * @param userId - User ID\n * @param organisationId - Organisation ID\n * @param eventId - Event ID (optional)\n * @param appId - App ID (optional)\n * @returns String cache key\n */\n static generateAccessLevelKey(\n userId: UUID,\n organisationId: UUID,\n eventId?: string,\n appId?: UUID\n ): string {\n const parts = [\n 'access',\n userId,\n organisationId,\n eventId || 'null',\n appId || 'null',\n ];\n \n return parts.join(':');\n }\n\n /**\n * Generate cache key for permission map\n * \n * @param userId - User ID\n * @param organisationId - Organisation ID\n * @param eventId - Event ID (optional)\n * @param appId - App ID (optional)\n * @returns String cache key\n */\n static generatePermissionMapKey(\n userId: UUID,\n organisationId: UUID,\n eventId?: string,\n appId?: UUID\n ): string {\n const parts = [\n 'map',\n userId,\n organisationId,\n eventId || 'null',\n appId || 'null',\n ];\n \n return parts.join(':');\n }\n}\n\n/**\n * Global cache instance\n * \n * This is the default cache instance used by the RBAC system.\n * You can create additional instances if needed for different contexts.\n */\nexport const rbacCache = new RBACCache();\n\n/**\n * Cache key patterns for invalidation\n */\nexport const CACHE_PATTERNS = {\n USER: (userId: UUID) => `user:${userId}`,\n ORGANISATION: (organisationId: UUID) => `org:${organisationId}`,\n EVENT: (eventId: string) => `event:${eventId}`,\n APP: (appId: UUID) => `app:${appId}`,\n PERMISSION: (userId: UUID, organisationId: UUID) => `perm:${userId}:${organisationId}`,\n} as const;\n"],"mappings":"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","names":[]}
@@ -170,7 +170,7 @@ var useOrganisationSecurity = () => {
170
170
  const targetOrgId = orgId || selectedOrganisation?.id;
171
171
  if (!targetOrgId || !user) return false;
172
172
  try {
173
- const { isPermitted } = await import("./api-GZHIDA4X.js");
173
+ const { isPermitted } = await import("./api-T6CBS7IO.js");
174
174
  const scope = {
175
175
  organisationId: targetOrgId,
176
176
  eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,
@@ -193,7 +193,7 @@ var useOrganisationSecurity = () => {
193
193
  const targetOrgId = orgId || selectedOrganisation?.id;
194
194
  if (!targetOrgId || !user) return [];
195
195
  try {
196
- const { getPermissionMap } = await import("./api-GZHIDA4X.js");
196
+ const { getPermissionMap } = await import("./api-T6CBS7IO.js");
197
197
  const scope = {
198
198
  organisationId: targetOrgId,
199
199
  eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,
@@ -519,4 +519,4 @@ export {
519
519
  generatePublicRoutePath,
520
520
  extractEventCodeFromPath
521
521
  };
522
- //# sourceMappingURL=chunk-OKXMUYIB.js.map
522
+ //# sourceMappingURL=chunk-O4T53L7X.js.map