@jmruthers/pace-core 0.5.189 → 0.5.190

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 (420) hide show
  1. package/core-usage-manifest.json +0 -4
  2. package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
  3. package/dist/{DataTable-GUFUNZ3N.js → DataTable-ON3IXISJ.js} +8 -8
  4. package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-C4uxosp6.d.ts} +83 -24
  5. package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
  6. package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-X5NXANVI.js} +4 -2
  7. package/dist/{api-YP7XD5L6.js → api-I6UCQ5S6.js} +4 -2
  8. package/dist/{chunk-DDM4CCYT.js → chunk-4QYC5L4K.js} +60 -35
  9. package/dist/chunk-4QYC5L4K.js.map +1 -0
  10. package/dist/{chunk-IM4QE42D.js → chunk-73HSNNOQ.js} +141 -326
  11. package/dist/chunk-73HSNNOQ.js.map +1 -0
  12. package/dist/{chunk-YHCN776L.js → chunk-DZWK57KZ.js} +2 -75
  13. package/dist/chunk-DZWK57KZ.js.map +1 -0
  14. package/dist/{chunk-3GOZZZYH.js → chunk-HQVPB5MZ.js} +238 -301
  15. package/dist/chunk-HQVPB5MZ.js.map +1 -0
  16. package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
  17. package/dist/chunk-HW3OVDUF.js.map +1 -0
  18. package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
  19. package/dist/chunk-I7PSE6JW.js.map +1 -0
  20. package/dist/{chunk-VGZZXKBR.js → chunk-J2XXC7R5.js} +280 -52
  21. package/dist/chunk-J2XXC7R5.js.map +1 -0
  22. package/dist/{chunk-UCQSRW7Z.js → chunk-NIU6J6OX.js} +425 -378
  23. package/dist/chunk-NIU6J6OX.js.map +1 -0
  24. package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
  25. package/dist/{chunk-HEHYGYOX.js → chunk-RUYZKXOD.js} +401 -46
  26. package/dist/chunk-RUYZKXOD.js.map +1 -0
  27. package/dist/{chunk-2UUZZJFT.js → chunk-SDMHPX3X.js} +176 -160
  28. package/dist/{chunk-2UUZZJFT.js.map → chunk-SDMHPX3X.js.map} +1 -1
  29. package/dist/{chunk-MX64ZF6I.js → chunk-STYK4OH2.js} +11 -11
  30. package/dist/chunk-STYK4OH2.js.map +1 -0
  31. package/dist/{chunk-YGPFYGA6.js → chunk-VVBAW5A5.js} +822 -498
  32. package/dist/chunk-VVBAW5A5.js.map +1 -0
  33. package/dist/chunk-Y4BUBBHD.js +614 -0
  34. package/dist/chunk-Y4BUBBHD.js.map +1 -0
  35. package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
  36. package/dist/chunk-ZSAAAMVR.js.map +1 -0
  37. package/dist/components.d.ts +3 -4
  38. package/dist/components.js +19 -19
  39. package/dist/components.js.map +1 -1
  40. package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
  41. package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
  42. package/dist/hooks.d.ts +10 -5
  43. package/dist/hooks.js +14 -8
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +13 -11
  46. package/dist/index.js +79 -69
  47. package/dist/index.js.map +1 -1
  48. package/dist/providers.d.ts +3 -3
  49. package/dist/providers.js +3 -1
  50. package/dist/rbac/index.d.ts +76 -12
  51. package/dist/rbac/index.js +12 -9
  52. package/dist/types.d.ts +1 -1
  53. package/dist/types.js +1 -1
  54. package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-DxIDS4bC.d.ts} +16 -9
  55. package/dist/utils.js +16 -16
  56. package/docs/README.md +2 -2
  57. package/docs/api/classes/ColumnFactory.md +1 -1
  58. package/docs/api/classes/ErrorBoundary.md +1 -1
  59. package/docs/api/classes/InvalidScopeError.md +2 -2
  60. package/docs/api/classes/Logger.md +1 -1
  61. package/docs/api/classes/MissingUserContextError.md +2 -2
  62. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  63. package/docs/api/classes/PermissionDeniedError.md +1 -1
  64. package/docs/api/classes/RBACAuditManager.md +1 -1
  65. package/docs/api/classes/RBACCache.md +1 -1
  66. package/docs/api/classes/RBACEngine.md +4 -4
  67. package/docs/api/classes/RBACError.md +1 -1
  68. package/docs/api/classes/RBACNotInitializedError.md +2 -2
  69. package/docs/api/classes/SecureSupabaseClient.md +21 -16
  70. package/docs/api/classes/StorageUtils.md +7 -4
  71. package/docs/api/enums/FileCategory.md +1 -1
  72. package/docs/api/enums/LogLevel.md +1 -1
  73. package/docs/api/enums/RBACErrorCode.md +1 -1
  74. package/docs/api/enums/RPCFunction.md +1 -1
  75. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  76. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  77. package/docs/api/interfaces/AggregateConfig.md +1 -1
  78. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  79. package/docs/api/interfaces/AvatarProps.md +1 -1
  80. package/docs/api/interfaces/BadgeProps.md +1 -1
  81. package/docs/api/interfaces/ButtonProps.md +1 -1
  82. package/docs/api/interfaces/CalendarProps.md +20 -6
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/ComplianceResult.md +1 -1
  87. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  88. package/docs/api/interfaces/DataRecord.md +1 -1
  89. package/docs/api/interfaces/DataTableAction.md +1 -1
  90. package/docs/api/interfaces/DataTableColumn.md +1 -1
  91. package/docs/api/interfaces/DataTableProps.md +1 -1
  92. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  93. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  94. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  95. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  96. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  97. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  98. package/docs/api/interfaces/ExportColumn.md +1 -1
  99. package/docs/api/interfaces/ExportOptions.md +1 -1
  100. package/docs/api/interfaces/FileDisplayProps.md +62 -16
  101. package/docs/api/interfaces/FileMetadata.md +1 -1
  102. package/docs/api/interfaces/FileReference.md +2 -2
  103. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  104. package/docs/api/interfaces/FileUploadOptions.md +26 -12
  105. package/docs/api/interfaces/FileUploadProps.md +30 -19
  106. package/docs/api/interfaces/FooterProps.md +1 -1
  107. package/docs/api/interfaces/FormFieldProps.md +1 -1
  108. package/docs/api/interfaces/FormProps.md +1 -1
  109. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  110. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  111. package/docs/api/interfaces/InputProps.md +1 -1
  112. package/docs/api/interfaces/LabelProps.md +1 -1
  113. package/docs/api/interfaces/LoggerConfig.md +1 -1
  114. package/docs/api/interfaces/LoginFormProps.md +1 -1
  115. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  116. package/docs/api/interfaces/NavigationContextType.md +9 -9
  117. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  118. package/docs/api/interfaces/NavigationItem.md +1 -1
  119. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  120. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  121. package/docs/api/interfaces/Organisation.md +1 -1
  122. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  123. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  124. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  125. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  126. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  127. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  128. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  129. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  130. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  131. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  132. package/docs/api/interfaces/PaletteData.md +1 -1
  133. package/docs/api/interfaces/ParsedAddress.md +1 -1
  134. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  135. package/docs/api/interfaces/ProgressProps.md +3 -11
  136. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  137. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  138. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  139. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  140. package/docs/api/interfaces/QuickFix.md +1 -1
  141. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  142. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  143. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  144. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  145. package/docs/api/interfaces/RBACConfig.md +1 -1
  146. package/docs/api/interfaces/RBACContext.md +1 -1
  147. package/docs/api/interfaces/RBACLogger.md +1 -1
  148. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  149. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  150. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  151. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  152. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  153. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  154. package/docs/api/interfaces/RBACResult.md +1 -1
  155. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  156. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  157. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  158. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  160. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  161. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  162. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  163. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  164. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  165. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  166. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  167. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  168. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  169. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  170. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  171. package/docs/api/interfaces/RouteConfig.md +10 -10
  172. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  173. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  174. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  175. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  176. package/docs/api/interfaces/SetupIssue.md +1 -1
  177. package/docs/api/interfaces/StorageConfig.md +4 -4
  178. package/docs/api/interfaces/StorageFileInfo.md +7 -7
  179. package/docs/api/interfaces/StorageFileMetadata.md +25 -14
  180. package/docs/api/interfaces/StorageListOptions.md +22 -9
  181. package/docs/api/interfaces/StorageListResult.md +4 -4
  182. package/docs/api/interfaces/StorageUploadOptions.md +21 -8
  183. package/docs/api/interfaces/StorageUploadResult.md +6 -6
  184. package/docs/api/interfaces/StorageUrlOptions.md +19 -6
  185. package/docs/api/interfaces/StyleImport.md +1 -1
  186. package/docs/api/interfaces/SwitchProps.md +1 -1
  187. package/docs/api/interfaces/TabsContentProps.md +1 -1
  188. package/docs/api/interfaces/TabsListProps.md +1 -1
  189. package/docs/api/interfaces/TabsProps.md +1 -1
  190. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  191. package/docs/api/interfaces/TextareaProps.md +1 -1
  192. package/docs/api/interfaces/ToastActionElement.md +1 -1
  193. package/docs/api/interfaces/ToastProps.md +1 -1
  194. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  195. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  196. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  197. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  198. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  199. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  200. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  201. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  202. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  203. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  205. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  207. package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
  208. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  209. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  210. package/docs/api/interfaces/UserEventAccess.md +11 -11
  211. package/docs/api/interfaces/UserMenuProps.md +1 -1
  212. package/docs/api/interfaces/UserProfile.md +1 -1
  213. package/docs/api/modules.md +151 -92
  214. package/docs/api-reference/components.md +15 -7
  215. package/docs/api-reference/providers.md +2 -2
  216. package/docs/api-reference/rpc-functions.md +1 -0
  217. package/docs/best-practices/README.md +1 -1
  218. package/docs/best-practices/deployment.md +8 -8
  219. package/docs/getting-started/examples/README.md +2 -2
  220. package/docs/getting-started/installation-guide.md +4 -4
  221. package/docs/getting-started/quick-start.md +3 -3
  222. package/docs/migration/MIGRATION_GUIDE.md +3 -3
  223. package/docs/rbac/compliance/compliance-guide.md +2 -2
  224. package/docs/rbac/event-based-apps.md +2 -2
  225. package/docs/rbac/getting-started.md +2 -2
  226. package/docs/rbac/quick-start.md +2 -2
  227. package/docs/security/README.md +4 -4
  228. package/docs/standards/07-rbac-and-rls-standard.md +430 -7
  229. package/docs/troubleshooting/README.md +2 -2
  230. package/docs/troubleshooting/migration.md +3 -3
  231. package/package.json +1 -3
  232. package/scripts/check-pace-core-compliance.cjs +1 -1
  233. package/scripts/check-pace-core-compliance.js +1 -1
  234. package/src/__tests__/fixtures/supabase.ts +301 -0
  235. package/src/__tests__/public-recipe-view.test.ts +9 -9
  236. package/src/__tests__/rls-policies.test.ts +197 -61
  237. package/src/components/AddressField/AddressField.test.tsx +42 -0
  238. package/src/components/AddressField/AddressField.tsx +71 -60
  239. package/src/components/AddressField/README.md +1 -0
  240. package/src/components/Alert/Alert.test.tsx +50 -10
  241. package/src/components/Alert/Alert.tsx +5 -3
  242. package/src/components/Avatar/Avatar.test.tsx +95 -43
  243. package/src/components/Avatar/Avatar.tsx +16 -16
  244. package/src/components/Button/Button.test.tsx +2 -1
  245. package/src/components/Button/Button.tsx +3 -3
  246. package/src/components/Calendar/Calendar.test.tsx +53 -37
  247. package/src/components/Calendar/Calendar.tsx +409 -82
  248. package/src/components/Card/Card.test.tsx +7 -4
  249. package/src/components/Card/Card.tsx +3 -6
  250. package/src/components/Checkbox/Checkbox.tsx +2 -2
  251. package/src/components/DataTable/components/ActionButtons.tsx +5 -5
  252. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  253. package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
  254. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
  255. package/src/components/DataTable/components/DataTableBody.tsx +12 -12
  256. package/src/components/DataTable/components/DataTableCore.tsx +3 -3
  257. package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
  258. package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
  259. package/src/components/DataTable/components/EditableRow.tsx +2 -2
  260. package/src/components/DataTable/components/EmptyState.tsx +3 -3
  261. package/src/components/DataTable/components/GroupHeader.tsx +2 -2
  262. package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +4 -4
  264. package/src/components/DataTable/components/LoadingState.tsx +1 -1
  265. package/src/components/DataTable/components/PaginationControls.tsx +11 -11
  266. package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
  267. package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
  268. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
  269. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
  270. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
  271. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
  272. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
  273. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
  274. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
  275. package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
  276. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
  277. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
  278. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
  280. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
  281. package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
  282. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
  283. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
  284. package/src/components/Dialog/Dialog.tsx +2 -2
  285. package/src/components/EventSelector/EventSelector.tsx +7 -7
  286. package/src/components/FileDisplay/FileDisplay.tsx +291 -179
  287. package/src/components/FileUpload/FileUpload.tsx +7 -4
  288. package/src/components/Header/Header.test.tsx +28 -0
  289. package/src/components/Header/Header.tsx +22 -9
  290. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
  291. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
  292. package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
  293. package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
  294. package/src/components/OrganisationSelector/OrganisationSelector.tsx +8 -8
  295. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
  296. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
  297. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
  298. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
  299. package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
  300. package/src/components/PaceAppLayout/test-setup.tsx +1 -0
  301. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
  302. package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
  303. package/src/components/Progress/Progress.test.tsx +18 -19
  304. package/src/components/Progress/Progress.tsx +31 -32
  305. package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
  306. package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
  307. package/src/components/Select/Select.tsx +5 -5
  308. package/src/components/Switch/Switch.test.tsx +2 -1
  309. package/src/components/Switch/Switch.tsx +1 -1
  310. package/src/components/Toast/Toast.tsx +1 -1
  311. package/src/components/Tooltip/Tooltip.test.tsx +8 -2
  312. package/src/components/UserMenu/UserMenu.tsx +3 -3
  313. package/src/eslint-rules/pace-core-compliance.cjs +0 -2
  314. package/src/eslint-rules/pace-core-compliance.js +0 -2
  315. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
  316. package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
  317. package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
  318. package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
  319. package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
  320. package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
  321. package/src/hooks/__tests__/{usePublicEvent.unit.test.ts → usePublicEvent.test.ts} +28 -1
  322. package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
  323. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +58 -16
  324. package/src/hooks/index.ts +1 -1
  325. package/src/hooks/public/usePublicEvent.ts +2 -2
  326. package/src/hooks/public/usePublicFileDisplay.ts +173 -87
  327. package/src/hooks/useAppConfig.ts +24 -5
  328. package/src/hooks/useFileDisplay.ts +297 -34
  329. package/src/hooks/useFileReference.ts +56 -11
  330. package/src/hooks/useFileUrl.ts +1 -1
  331. package/src/hooks/useInactivityTracker.ts +16 -7
  332. package/src/hooks/usePermissionCache.test.ts +85 -8
  333. package/src/hooks/useQueryCache.ts +21 -0
  334. package/src/hooks/useSecureDataAccess.test.ts +80 -35
  335. package/src/hooks/useSecureDataAccess.ts +80 -37
  336. package/src/providers/services/EventServiceProvider.tsx +37 -17
  337. package/src/providers/services/InactivityServiceProvider.tsx +4 -4
  338. package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
  339. package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
  340. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
  341. package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
  342. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
  343. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
  344. package/src/rbac/api.ts +240 -36
  345. package/src/rbac/cache-invalidation.ts +21 -7
  346. package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
  347. package/src/rbac/components/NavigationGuard.tsx +23 -63
  348. package/src/rbac/components/NavigationProvider.test.tsx +52 -23
  349. package/src/rbac/components/NavigationProvider.tsx +13 -11
  350. package/src/rbac/components/PagePermissionGuard.tsx +77 -203
  351. package/src/rbac/components/PagePermissionProvider.tsx +13 -11
  352. package/src/rbac/components/PermissionEnforcer.tsx +24 -62
  353. package/src/rbac/components/RoleBasedRouter.tsx +14 -12
  354. package/src/rbac/components/SecureDataProvider.tsx +13 -11
  355. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
  356. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
  357. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
  358. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
  359. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
  360. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
  361. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
  362. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
  363. package/src/rbac/engine.ts +4 -2
  364. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
  365. package/src/rbac/hooks/index.ts +3 -0
  366. package/src/rbac/hooks/useCan.test.ts +101 -53
  367. package/src/rbac/hooks/usePermissions.ts +108 -41
  368. package/src/rbac/hooks/useRBAC.test.ts +11 -3
  369. package/src/rbac/hooks/useRBAC.ts +83 -40
  370. package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
  371. package/src/rbac/hooks/useResolvedScope.ts +128 -70
  372. package/src/rbac/hooks/useSecureSupabase.ts +36 -19
  373. package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
  374. package/src/rbac/request-deduplication.ts +1 -1
  375. package/src/rbac/secureClient.ts +72 -12
  376. package/src/rbac/security.ts +29 -23
  377. package/src/rbac/types.ts +10 -0
  378. package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
  379. package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
  380. package/src/rbac/utils/__tests__/eventContext.test.ts +6 -1
  381. package/src/rbac/utils/contextValidator.ts +288 -0
  382. package/src/rbac/utils/eventContext.ts +48 -2
  383. package/src/services/EventService.ts +165 -21
  384. package/src/services/OrganisationService.ts +37 -2
  385. package/src/services/__tests__/EventService.test.ts +26 -21
  386. package/src/types/file-reference.ts +13 -10
  387. package/src/utils/app/appNameResolver.test.ts +346 -73
  388. package/src/utils/context/superAdminOverride.ts +58 -0
  389. package/src/utils/file-reference/index.ts +61 -33
  390. package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
  391. package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
  392. package/src/utils/storage/helpers.test.ts +1 -1
  393. package/src/utils/storage/helpers.ts +38 -19
  394. package/src/utils/storage/types.ts +15 -8
  395. package/src/utils/validation/__tests__/csrf.test.ts +105 -0
  396. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
  397. package/src/vite-env.d.ts +2 -2
  398. package/dist/chunk-3GOZZZYH.js.map +0 -1
  399. package/dist/chunk-DDM4CCYT.js.map +0 -1
  400. package/dist/chunk-E7UAOUMY.js +0 -75
  401. package/dist/chunk-E7UAOUMY.js.map +0 -1
  402. package/dist/chunk-F2IMUDXZ.js.map +0 -1
  403. package/dist/chunk-HEHYGYOX.js.map +0 -1
  404. package/dist/chunk-IM4QE42D.js.map +0 -1
  405. package/dist/chunk-MX64ZF6I.js.map +0 -1
  406. package/dist/chunk-SAUPYVLF.js.map +0 -1
  407. package/dist/chunk-THRPYOFK.js.map +0 -1
  408. package/dist/chunk-UCQSRW7Z.js.map +0 -1
  409. package/dist/chunk-VGZZXKBR.js.map +0 -1
  410. package/dist/chunk-YGPFYGA6.js.map +0 -1
  411. package/dist/chunk-YHCN776L.js.map +0 -1
  412. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  413. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  414. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -703
  415. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  416. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -428
  417. /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-ON3IXISJ.js.map} +0 -0
  418. /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-X5NXANVI.js.map} +0 -0
  419. /package/dist/{api-YP7XD5L6.js.map → api-I6UCQ5S6.js.map} +0 -0
  420. /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac/secureClient.ts","../src/rbac/hooks/useRBAC.ts","../src/rbac/hooks/usePermissions.ts","../src/rbac/utils/deep-equal.ts","../src/rbac/hooks/useResourcePermissions.ts","../src/rbac/hooks/useRoleManagement.ts","../src/rbac/hooks/useSecureSupabase.ts"],"sourcesContent":["/**\n * Secure Supabase Client for RBAC\n * @package @jmruthers/pace-core\n * @module RBAC/SecureClient\n * @since 1.0.0\n * \n * This module provides a secure Supabase client that enforces organisation context\n * and prevents direct database access outside of the RBAC system.\n */\n\nimport { createClient, SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../types/database';\nimport { UUID } from './types';\nimport { OrganisationContextRequiredError } from './types';\n\n/**\n * Secure Supabase Client that enforces organisation context\n * \n * This client automatically injects organisation context into all requests\n * and prevents queries that don't have the required context.\n * \n * Note: Callers should derive organisationId from eventId before creating this client\n * if working with event-required apps. The client requires organisationId.\n */\nexport class SecureSupabaseClient {\n private supabase: SupabaseClient<Database>;\n private edgeFunctionClient: SupabaseClient<Database> | null = null;\n private supabaseUrl: string;\n private supabaseKey: string;\n private organisationId: UUID;\n private eventId?: string;\n private appId?: UUID;\n private isSuperAdmin: boolean;\n\n constructor(\n supabaseUrl: string,\n supabaseKey: string,\n organisationId: UUID,\n eventId?: string,\n appId?: UUID,\n isSuperAdmin: boolean = false\n ) {\n this.supabaseUrl = supabaseUrl;\n this.supabaseKey = supabaseKey;\n this.organisationId = organisationId;\n this.eventId = eventId;\n this.appId = appId;\n this.isSuperAdmin = isSuperAdmin;\n\n // Create the base Supabase client with context headers\n // Note: We'll override functions.invoke to exclude headers for Edge Functions\n // as they may not have CORS configured to accept custom headers\n this.supabase = createClient<Database>(supabaseUrl, supabaseKey, {\n global: {\n headers: {\n 'x-organisation-id': organisationId,\n 'x-event-id': eventId || '',\n 'x-app-id': appId || '',\n },\n },\n });\n\n // Override the auth methods to inject context\n this.setupContextInjection();\n \n // Override functions.invoke to exclude custom headers for Edge Functions\n // Edge Functions may not have CORS configured to accept custom headers\n this.setupEdgeFunctionHandling();\n }\n\n /**\n * Setup context injection for all database operations\n */\n private setupContextInjection() {\n const originalFrom = this.supabase.from.bind(this.supabase);\n \n (this.supabase as any).from = (table: string): any => {\n // Validate context before allowing any database operations\n this.validateContext();\n \n // Type assertion needed because table is a string but Supabase expects specific table names\n const query = originalFrom(table as any);\n \n // Store table name on query object so we can access it in filter methods\n (query as any)._tableName = table;\n \n // Inject organisation context into all queries\n return this.injectContext(query, table);\n };\n\n const originalRpc = this.supabase.rpc.bind(this.supabase);\n \n // Override rpc method to inject context\n // Type assertion needed because we're wrapping the generic rpc method\n // The fn parameter is typed as string to match Supabase's rpc signature\n (this.supabase as any).rpc = (fn: string, args?: any, options?: any): any => {\n // Validate context before allowing any RPC calls\n this.validateContext();\n \n // Inject context into RPC calls\n const contextArgs = {\n ...args,\n p_organisation_id: this.organisationId,\n p_event_id: this.eventId,\n p_app_id: this.appId,\n };\n \n return originalRpc(fn as any, contextArgs, options);\n };\n }\n\n /**\n * Setup Edge Function handling to bypass custom headers\n * Edge Functions may not have CORS configured to accept custom headers,\n * so we create a separate client without custom headers for Edge Function calls\n * \n * NOTE: We store the edge function client but don't override functions here.\n * Instead, we provide a method to get the edge function client for direct use.\n * This avoids interfering with the main client's operations.\n */\n private setupEdgeFunctionHandling() {\n // Create a separate client without custom headers for Edge Functions\n // This prevents CORS errors when Edge Functions don't accept custom headers\n // Store it as an instance variable to avoid creating multiple clients\n // We'll use this client directly for Edge Function calls instead of overriding\n this.edgeFunctionClient = createClient<Database>(this.supabaseUrl, this.supabaseKey);\n }\n\n /**\n * Get a client for Edge Function calls without custom headers\n * Edge Functions may not have CORS configured to accept custom headers\n * @returns Supabase client without custom headers for Edge Function calls\n */\n getEdgeFunctionClient(): SupabaseClient<Database> {\n return this.edgeFunctionClient || this.supabase;\n }\n\n /**\n * Inject organisation context into a query\n */\n private injectContext(query: any, tableName: string) {\n const originalSelect = query.select.bind(query);\n const originalInsert = query.insert.bind(query);\n const originalUpdate = query.update.bind(query);\n const originalDelete = query.delete.bind(query);\n\n // Override select to add organisation filter\n query.select = (columns?: string) => {\n const result = originalSelect(columns);\n return this.addOrganisationFilter(result, tableName);\n };\n\n // Override insert to add organisation context\n query.insert = (values: any) => {\n // For rbac_user_profiles, only add organisation_id if not super admin\n // Super admins can create users in any org, non-super-admins are restricted\n if (tableName === 'rbac_user_profiles') {\n if (this.isSuperAdmin) {\n // Super admin: Don't force organisation_id (can be set explicitly if needed)\n return originalInsert(values);\n }\n // Non-super-admin: Add organisation_id as defense in depth\n const contextValues = Array.isArray(values) \n ? values.map(v => ({ ...v, organisation_id: this.organisationId }))\n : { ...values, organisation_id: this.organisationId };\n return originalInsert(contextValues);\n }\n \n // For other tables, always add organisation_id\n const contextValues = Array.isArray(values) \n ? values.map(v => ({ ...v, organisation_id: this.organisationId }))\n : { ...values, organisation_id: this.organisationId };\n \n return originalInsert(contextValues);\n };\n\n // Override update to add organisation filter\n query.update = (values: any) => {\n const result = originalUpdate(values);\n return this.addOrganisationFilter(result, tableName);\n };\n\n // Override delete to add organisation filter\n query.delete = () => {\n const result = originalDelete();\n return this.addOrganisationFilter(result, tableName);\n };\n\n return query;\n }\n\n /**\n * Add organisation filter to a query\n * \n * Defense in depth strategy:\n * - RLS policies are the primary security layer (cannot be bypassed)\n * - Application-level filtering adds an additional layer of protection\n * \n * For rbac_user_profiles:\n * - Super admins: No org filter (see all users) - RLS will allow access\n * - Non-super-admins: Apply org filter as defense in depth - RLS will also filter\n * \n * For other tables:\n * - Always apply org filter unless super admin bypasses it\n */\n private addOrganisationFilter(query: any, tableName: string) {\n // For rbac_user_profiles, use conditional filtering based on super admin status\n if (tableName === 'rbac_user_profiles') {\n // Super admins: No org filter (see all users via RLS)\n // Non-super-admins: Apply org filter as defense in depth (RLS also filters)\n if (this.isSuperAdmin) {\n return query; // No filter - RLS handles access control\n }\n // Apply org filter for non-super-admins as additional security layer\n return query.eq('organisation_id', this.organisationId);\n }\n \n // For all other tables, apply organisation filter\n // Super admins can still bypass via RLS if needed, but we filter at app level too\n if (this.isSuperAdmin) {\n // Super admins might want to see cross-org data, but for most tables\n // we still apply the filter as a safety measure (they can override if needed)\n // For now, we'll apply it - super admins can use direct queries if they need cross-org access\n return query.eq('organisation_id', this.organisationId);\n }\n \n // Non-super-admins: Always apply org filter\n return query.eq('organisation_id', this.organisationId);\n }\n\n /**\n * Validate that required context is present\n */\n private validateContext() {\n if (!this.organisationId) {\n throw new OrganisationContextRequiredError();\n }\n }\n\n /**\n * Get the current organisation ID\n */\n getOrganisationId(): UUID {\n return this.organisationId;\n }\n\n /**\n * Get the current event ID\n */\n getEventId(): string | undefined {\n return this.eventId;\n }\n\n /**\n * Get the current app ID\n */\n getAppId(): UUID | undefined {\n return this.appId;\n }\n\n /**\n * Create a new client with updated context\n */\n withContext(updates: {\n organisationId?: UUID;\n eventId?: string;\n appId?: UUID;\n isSuperAdmin?: boolean;\n }): SecureSupabaseClient {\n return new SecureSupabaseClient(\n this.supabaseUrl,\n this.supabaseKey,\n updates.organisationId || this.organisationId,\n updates.eventId !== undefined ? updates.eventId : this.eventId,\n updates.appId !== undefined ? updates.appId : this.appId,\n updates.isSuperAdmin !== undefined ? updates.isSuperAdmin : this.isSuperAdmin\n );\n }\n\n /**\n * Get the underlying Supabase client (for internal use only)\n * @internal\n */\n getClient(): SupabaseClient<Database> {\n // Return a proxy that intercepts functions.invoke calls to use edge function client\n // This avoids CORS issues with Edge Functions while keeping the main client intact\n return new Proxy(this.supabase, {\n get: (target, prop) => {\n if (prop === 'functions' && this.edgeFunctionClient) {\n // Return the edge function client's functions for invoke calls\n // This bypasses custom headers that cause CORS errors\n return this.edgeFunctionClient.functions;\n }\n // For all other properties, return the original\n return (target as any)[prop];\n }\n }) as SupabaseClient<Database>;\n }\n}\n\n/**\n * Create a secure Supabase client with organisation context\n * \n * @param supabaseUrl - Supabase project URL\n * @param supabaseKey - Supabase publishable key or anon key (accepts both legacy anon keys and modern publishable keys)\n * @param organisationId - Required organisation ID\n * @param eventId - Optional event ID\n * @param appId - Optional app ID\n * @param isSuperAdmin - Optional super admin flag (defaults to false)\n * @returns SecureSupabaseClient instance\n * \n * @example\n * ```typescript\n * const client = createSecureClient(\n * 'https://your-project.supabase.co',\n * 'your-publishable-key-or-anon-key',\n * 'org-123',\n * 'event-456',\n * 'app-789',\n * false // isSuperAdmin\n * );\n * ```\n */\nexport function createSecureClient(\n supabaseUrl: string,\n supabaseKey: string,\n organisationId: UUID,\n eventId?: string,\n appId?: UUID,\n isSuperAdmin: boolean = false\n): SecureSupabaseClient {\n return new SecureSupabaseClient(supabaseUrl, supabaseKey, organisationId, eventId, appId, isSuperAdmin);\n}\n\n/**\n * Create a secure client from an existing Supabase client\n * \n * @param client - Existing Supabase client\n * @param organisationId - Required organisation ID\n * @param eventId - Optional event ID\n * @param appId - Optional app ID\n * @returns SecureSupabaseClient instance\n */\nexport function fromSupabaseClient(\n client: SupabaseClient<Database>,\n organisationId: UUID,\n eventId?: string,\n appId?: UUID\n): SecureSupabaseClient {\n // We need the URL and key to create a new client, but they're not accessible\n // This function should be used with createSecureClient instead\n throw new Error('fromSupabaseClient is not supported. Use createSecureClient instead.');\n}\n","/**\n * @file RBAC Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 0.3.0\n *\n * A React hook that provides access to the RBAC (Role-Based Access Control) system\n * through the hardened RBAC engine API. The hook defers all permission and role\n * resolution to the shared engine to ensure consistent security behaviour across\n * applications.\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEvents } from '../../hooks/useEvents';\nimport {\n getPermissionMap,\n getAccessLevel,\n resolveAppContext,\n getRoleContext,\n} from '../api';\nimport { getRBACLogger } from '../config';\nimport { ContextValidator } from '../utils/contextValidator';\nimport type {\n UserRBACContext,\n GlobalRole,\n OrganisationRole,\n EventAppRole,\n Permission,\n Scope,\n PermissionMap,\n UUID,\n} from '../types';\n\nfunction mapAccessLevelToEventRole(level: string | null): EventAppRole | null {\n switch (level) {\n case 'viewer':\n return 'viewer';\n case 'participant':\n return 'participant';\n case 'planner':\n return 'planner';\n case 'admin':\n case 'super':\n return 'event_admin';\n default:\n return null;\n }\n}\n\nexport function useRBAC(pageId?: string): UserRBACContext {\n const logger = getRBACLogger();\n // Get all context from UnifiedAuth - it already provides selectedOrganisation, isContextReady, selectedEvent, and eventLoading\n // This is more reliable than calling useOrganisations()/useEvents() separately which might throw or return stale values\n const { \n user, \n session, \n supabase,\n appName, \n appConfig,\n appId: contextAppId,\n selectedOrganisation,\n isContextReady: orgContextReady,\n organisationLoading: orgLoading,\n selectedEvent,\n eventLoading\n } = useUnifiedAuth();\n \n // Removed excessive logging - hook initialization logged only on first mount or significant changes\n\n const [globalRole, setGlobalRole] = useState<GlobalRole | null>(null);\n const [organisationRole, setOrganisationRole] = useState<OrganisationRole | null>(null);\n const [eventAppRole, setEventAppRole] = useState<EventAppRole | null>(null);\n const [permissionMap, setPermissionMap] = useState<PermissionMap>({} as PermissionMap);\n const [currentScope, setCurrentScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const resetState = useCallback(() => {\n setGlobalRole(null);\n setOrganisationRole(null);\n setEventAppRole(null);\n setPermissionMap({} as PermissionMap);\n setCurrentScope(null);\n }, []);\n\n const loadRBACContext = useCallback(async () => {\n // Early return if user is not authenticated - don't do anything\n if (!user || !session) {\n resetState();\n setIsLoading(false);\n return;\n }\n\n // Build initial scope from available context\n // For event-required apps: use organisation_id from selectedEvent if available (faster than deriving)\n // For org-required apps: use selectedOrganisation.id\n const initialScope: Scope = {\n organisationId: appConfig?.requires_event \n ? (selectedEvent?.organisation_id || selectedOrganisation?.id) \n : selectedOrganisation?.id,\n eventId: selectedEvent?.event_id || undefined,\n appId: undefined\n };\n\n // Check if context is ready using ContextValidator\n const contextReady = ContextValidator.isContextReady(\n initialScope,\n appConfig,\n appName,\n !!selectedEvent,\n !!selectedOrganisation\n );\n\n // PORTAL/ADMIN special case: context is always ready\n if (appName !== 'PORTAL' && appName !== 'ADMIN' && !contextReady) {\n // Wait for appropriate context based on app config\n setIsLoading(true);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n // Only log at debug level - loading RBAC context is normal operation\n logger.debug('[useRBAC] Loading RBAC context', {\n appName,\n appConfig,\n hasSelectedEvent: !!selectedEvent,\n selectedEventId: selectedEvent?.event_id,\n organisationId: selectedOrganisation?.id\n });\n\n try {\n let appId: UUID | undefined = contextAppId; // Use contextAppId as default (already resolved in UnifiedAuthProvider)\n \n if (appName && !appId) {\n // Wrap RPC call in try-catch to handle NetworkError gracefully\n try {\n const resolved = await resolveAppContext({ userId: user.id as UUID, appName });\n // For PORTAL/ADMIN apps, allow access even if hasAccess is false (users can view their own profile, super admins have global access)\n if (!resolved) {\n if (appName === 'PORTAL' || appName === 'ADMIN') {\n // For PORTAL/ADMIN, try to get appId directly from database\n logger.debug(`[useRBAC] ${appName} app context not resolved, attempting direct lookup`);\n try {\n const { getAppConfigByName } = await import('../api');\n const config = await getAppConfigByName(appName);\n // We can't get appId from config, but that's OK - use contextAppId or proceed without\n logger.debug(`[useRBAC] ${appName} app - proceeding without appId for page-level permissions`);\n } catch (err) {\n logger.debug(`[useRBAC] ${appName} app - proceeding without appId for page-level permissions`);\n }\n } else {\n throw new Error(`User does not have access to app \"${appName}\"`);\n }\n } else if (!resolved.hasAccess && appName !== 'PORTAL' && appName !== 'ADMIN') {\n throw new Error(`User does not have access to app \"${appName}\"`);\n } else {\n appId = resolved.appId;\n }\n } catch (rpcError: any) {\n // Handle NetworkError - might be due to timing issue\n if (rpcError?.message?.includes('NetworkError') || rpcError?.message?.includes('fetch')) {\n logger.warn('[useRBAC] NetworkError resolving app context - may be timing issue, will retry when context is ready', {\n appName,\n error: rpcError.message,\n eventLoading,\n hasSelectedEvent: !!selectedEvent\n });\n // Don't throw - let it retry when dependencies change\n setIsLoading(false);\n return;\n }\n // For PORTAL/ADMIN, allow proceeding without app access check (for profile pages, super admin access)\n if (appName === 'PORTAL' || appName === 'ADMIN') {\n logger.debug(`[useRBAC] ${appName} app - allowing access despite app context resolution failure`);\n // appId will use contextAppId or be undefined, which is OK for PORTAL/ADMIN page-level permissions\n } else {\n // Re-throw other errors for non-PORTAL apps\n throw rpcError;\n }\n }\n }\n\n // Update scope with appId (use contextAppId as fallback for PORTAL)\n const scope: Scope = {\n ...initialScope,\n appId: appId || contextAppId,\n };\n \n // Resolve required context using ContextValidator\n // Pass supabase client to allow deriving organisation from event for event-required apps\n const validation = await ContextValidator.resolveRequiredContext(\n scope,\n appConfig,\n appName,\n supabase || null\n );\n\n if (!validation.isValid || !validation.resolvedScope) {\n throw validation.error || new Error('Context validation failed');\n }\n\n const resolvedScope = validation.resolvedScope;\n setCurrentScope(resolvedScope);\n\n // Pass appConfig and appName to API calls for context validation\n const [map, roleContext, accessLevel] = await Promise.all([\n getPermissionMap({ userId: user.id as UUID, scope: resolvedScope }, appConfig, appName),\n getRoleContext({ userId: user.id as UUID, scope: resolvedScope }, appConfig, appName),\n getAccessLevel({ userId: user.id as UUID, scope: resolvedScope }, appConfig, appName),\n ]);\n\n setPermissionMap(map);\n setGlobalRole(roleContext.globalRole);\n setOrganisationRole(roleContext.organisationRole);\n setEventAppRole(roleContext.eventAppRole || mapAccessLevelToEventRole(accessLevel));\n \n // Only log on first successful load or if there's an issue\n const permissionCount = Object.keys(map).length;\n if (permissionCount === 0) {\n logger.warn('[useRBAC] RBAC context loaded but returned 0 permissions', {\n appName,\n organisationId: resolvedScope.organisationId,\n eventId: resolvedScope.eventId\n });\n }\n } catch (err) {\n const handledError = err instanceof Error ? err : new Error('Failed to load RBAC context');\n logger.error('[useRBAC] Error loading RBAC context:', handledError);\n setError(handledError);\n resetState();\n } finally {\n setIsLoading(false);\n }\n }, [appName, logger, resetState, selectedEvent?.event_id, selectedOrganisation?.id, session, user, eventLoading, appConfig, orgContextReady, orgLoading]);\n\n const hasGlobalPermission = useCallback(\n (permission: string): boolean => {\n if (globalRole === 'super_admin' || permissionMap['*']) {\n return true;\n }\n\n if (permission === 'super_admin') {\n return globalRole === 'super_admin';\n }\n\n if (permission === 'org_admin') {\n return organisationRole === 'org_admin';\n }\n\n return permissionMap[permission as Permission] === true;\n },\n [globalRole, organisationRole, permissionMap],\n );\n\n const isSuperAdmin = useMemo(() => globalRole === 'super_admin' || permissionMap['*'] === true, [globalRole, permissionMap]);\n const isOrgAdmin = useMemo(() => organisationRole === 'org_admin' || isSuperAdmin, [organisationRole, isSuperAdmin]);\n const isEventAdmin = useMemo(() => eventAppRole === 'event_admin' || isSuperAdmin, [eventAppRole, isSuperAdmin]);\n const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === 'org_admin', [isSuperAdmin, organisationRole]);\n const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === 'event_admin', [isSuperAdmin, eventAppRole]);\n\n useEffect(() => {\n loadRBACContext();\n }, [loadRBACContext, appName, appConfig, eventLoading, selectedEvent?.event_id, user, session, selectedOrganisation?.id, orgContextReady, orgLoading]);\n\n return {\n user,\n globalRole,\n organisationRole,\n eventAppRole,\n hasGlobalPermission,\n isSuperAdmin,\n isOrgAdmin,\n isEventAdmin,\n canManageOrganisation,\n canManageEvent,\n isLoading,\n error,\n };\n}\n","/**\n * @file RBAC Permission Hooks\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * This module provides React hooks for RBAC functionality.\n */\n\nimport React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { \n UUID, \n Scope, \n Permission, \n PermissionMap,\n OrganisationContextRequiredError\n} from '../types';\nimport { AccessLevel as AccessLevelType } from '../types';\nimport { \n getAccessLevel, \n getPermissionMap, \n isPermitted,\n isPermittedCached \n} from '../api';\nimport { getRBACLogger } from '../config';\nimport { scopeEqual } from '../utils/deep-equal';\nimport { useAppConfig } from '../../hooks/useAppConfig';\n\n/**\n * Hook to get user's permissions in a scope\n * \n * @param userId - User ID\n * @param organisationId - Organisation ID\n * @param eventId - Event ID (optional)\n * @param appId - Application ID (optional)\n * @returns Permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error } = usePermissions(\n * userId, \n * organisationId, \n * eventId, \n * appId\n * );\n * \n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * {permissions['create:users'] && <CreateUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePermissions(\n userId: UUID, \n organisationId: string | undefined, \n eventId: string | undefined, \n appId: string | undefined\n) {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [fetchTrigger, setFetchTrigger] = useState(0);\n const isFetchingRef = useRef(false);\n const logger = getRBACLogger();\n \n // Track previous values to detect changes imperatively\n const prevValuesRef = useRef({ userId, organisationId, eventId, appId });\n \n // Normalize organisationId to empty string if undefined\n const orgId = organisationId || '';\n \n // Removed excessive logging - only log when scope actually changes (not on every render)\n\n // Add timeout for missing organisation context (3 seconds)\n // OPTIMIZATION: Skip timeout if userId is null/undefined (indicates pre-filtered mode)\n useEffect(() => {\n // If userId is null/undefined, skip the timeout - this indicates items are pre-filtered\n // and we don't need to wait for organisation context\n if (!userId) {\n return; // Skip timeout when userId is null (pre-filtered mode)\n }\n \n if (!orgId || orgId === null || (typeof orgId === 'string' && orgId.trim() === '')) {\n const timeoutId = setTimeout(() => {\n setError(new Error('Organisation context is required for permission checks'));\n setIsLoading(false);\n }, 3000); // 3 seconds - typical permission check is < 1 second\n \n return () => clearTimeout(timeoutId);\n }\n // Clear error if organisation context becomes available\n if (error?.message === 'Organisation context is required for permission checks') {\n setError(null);\n }\n }, [userId, organisationId, error, orgId]);\n\n // CRITICAL: Detect parameter changes and trigger fetch\n // Moved to useEffect to prevent render-time state updates that could cause render loops\n useEffect(() => {\n const paramsChanged = \n prevValuesRef.current.userId !== userId ||\n prevValuesRef.current.organisationId !== organisationId ||\n prevValuesRef.current.eventId !== eventId ||\n prevValuesRef.current.appId !== appId;\n \n if (paramsChanged) {\n // Only log significant changes (appId changes are most important)\n if (prevValuesRef.current.appId !== appId) {\n logger.debug('[usePermissions] AppId changed - triggering fetch', {\n prevAppId: prevValuesRef.current.appId,\n newAppId: appId\n });\n }\n prevValuesRef.current = { userId, organisationId, eventId, appId };\n // Increment counter to force fetch useEffect to run\n setFetchTrigger(prev => prev + 1);\n }\n }, [userId, organisationId, eventId, appId, logger]);\n\n useEffect(() => {\n const fetchPermissions = async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is null/empty)\n // Wait for organisation context to resolve\n // IMPORTANT: Don't clear existing permissions here - keep them until we have new ones\n // OPTIMIZATION: If userId is null/undefined, immediately set loading to false\n // This indicates pre-filtered mode where we don't need to wait for organisation context\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n \n if (!orgId || orgId === null || (typeof orgId === 'string' && orgId.trim() === '')) {\n // Keep existing permissions, just mark as loading\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n // Build scope object for API call\n const scope: Scope = {\n organisationId: orgId,\n eventId: eventId,\n appId: appId\n };\n \n // Fetch new permissions - don't clear old ones until we have new ones\n const permissionMap = await getPermissionMap({ userId, scope });\n \n // Only log if there's a significant change or error\n const permissionCount = Object.keys(permissionMap).length;\n if (permissionCount === 0 && Object.keys(permissions).length > 0) {\n logger.warn('[usePermissions] Permissions fetched but returned empty map', {\n scope: { organisationId: orgId, eventId, appId }\n });\n }\n \n // Only update permissions if fetch was successful\n setPermissions(permissionMap);\n } catch (err) {\n // On error, keep existing permissions but set error state\n // This prevents the UI from losing all items when there's a transient error\n logger.error('[usePermissions] Failed to fetch permissions:', err);\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n // Don't clear permissions on error - keep what we had\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n };\n\n fetchPermissions();\n }, [fetchTrigger, userId, organisationId, eventId, appId]);\n\n const hasPermission = useCallback((permission: Permission): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissions[permission] === true;\n }, [permissions]);\n\n const hasAnyPermission = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.some(p => permissions[p] === true);\n }, [permissions]);\n\n const hasAllPermissions = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.every(p => permissions[p] === true);\n }, [permissions]);\n\n const refetch = useCallback(async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is null/empty)\n // IMPORTANT: Don't clear existing permissions - keep them until we have new ones\n if (!orgId || orgId === null || (typeof orgId === 'string' && orgId.trim() === '')) {\n // Keep existing permissions, just mark as loading\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n // Build scope object for API call\n const scope: Scope = {\n organisationId: orgId,\n eventId: eventId,\n appId: appId\n };\n \n // Fetch new permissions - don't clear old ones until we have new ones\n const permissionMap = await getPermissionMap({ userId, scope });\n \n // Only update permissions if fetch was successful\n setPermissions(permissionMap);\n } catch (err) {\n // On error, keep existing permissions but set error state\n // This prevents the UI from losing all items when there's a transient error\n const logger = getRBACLogger();\n logger.error('Failed to refetch permissions:', err);\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n // Don't clear permissions on error - keep what we had\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n }, [userId, organisationId, eventId, appId]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n refetch\n }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);\n}\n\n/**\n * Hook to check if user can perform an action\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permission - Permission to check\n * @param pageId - Optional page ID\n * @param useCache - Whether to use cached results\n * @param appName - Optional app name (for PORTAL/ADMIN special case)\n * @returns Permission check state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { can, isLoading, error } = useCan(userId, scope, 'read:users');\n * \n * if (isLoading) return <div>Checking permission...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return can ? <UserList /> : <div>Access denied</div>;\n * }\n * ```\n */\nexport function useCan(\n userId: UUID, \n scope: Scope, \n permission: Permission, \n pageId?: UUID,\n useCache: boolean = true,\n appName?: string\n) {\n const [can, setCan] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Validate scope parameter - handle undefined/null scope gracefully\n const isValidScope = scope && typeof scope === 'object';\n const organisationId = isValidScope ? scope.organisationId : undefined;\n const eventId = isValidScope ? scope.eventId : undefined;\n const appId = isValidScope ? scope.appId : undefined;\n\n // Add timeout for missing organisation context (3 seconds)\n // Only apply timeout for resource-level permissions, not page-level (which can handle null orgs)\n useEffect(() => {\n const isPagePermission = permission.includes(':page.') || !!pageId;\n const requiresOrgId = !isPagePermission;\n \n if (requiresOrgId && (!isValidScope || !organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === ''))) {\n const timeoutId = setTimeout(() => {\n setError(new Error('Organisation context is required for permission checks'));\n setIsLoading(false);\n setCan(false);\n }, 3000); // 3 seconds - typical permission check is < 1 second\n \n return () => clearTimeout(timeoutId);\n }\n // Clear error if organisation context becomes available\n if (error?.message === 'Organisation context is required for permission checks') {\n setError(null);\n }\n }, [isValidScope, organisationId, error, permission, pageId]);\n\n // Use refs to track the last values to prevent unnecessary re-runs\n const lastUserIdRef = useRef<UUID | null>(null);\n const lastScopeRef = useRef<string | null>(null);\n const lastPermissionRef = useRef<Permission | null>(null);\n const lastPageIdRef = useRef<UUID | undefined | null>(null);\n const lastUseCacheRef = useRef<boolean | null>(null);\n\n // Create a stable scope object for comparison\n const stableScope = useMemo(() => {\n if (!isValidScope) {\n return null;\n }\n return {\n organisationId,\n eventId,\n appId,\n };\n }, [isValidScope, organisationId, eventId, appId]);\n\n // Track previous scope for deep equality comparison\n const prevScopeRef = useRef<Scope | null>(null);\n\n useEffect(() => {\n // Use deep equality check for scope to prevent unnecessary re-runs\n const scopeChanged = !scopeEqual(prevScopeRef.current, stableScope);\n \n // Only run if something has actually changed\n if (\n lastUserIdRef.current !== userId ||\n scopeChanged ||\n lastPermissionRef.current !== permission ||\n lastPageIdRef.current !== pageId ||\n lastUseCacheRef.current !== useCache\n ) {\n lastUserIdRef.current = userId;\n prevScopeRef.current = stableScope;\n lastPermissionRef.current = permission;\n lastPageIdRef.current = pageId;\n lastUseCacheRef.current = useCache;\n \n // Inline the permission check logic to avoid useCallback dependency issues\n const checkPermission = async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Validate scope before accessing properties\n if (!isValidScope) {\n setIsLoading(true);\n setCan(false);\n setError(null);\n // Timeout is handled in separate useEffect\n return;\n }\n\n // For page-level permissions, allow undefined/null organisationId (database function handles it)\n // For resource-level permissions, organisationId is required\n const isPagePermission = permission.includes(':page.') || !!pageId;\n const requiresOrgId = !isPagePermission;\n \n // Check if pageId is a pageName (not a UUID) - if so, we need appId to resolve it\n const isPageName = pageId && 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 const needsAppIdForPageName = isPagePermission && isPageName;\n \n // Don't check permissions if scope is invalid and orgId is required\n // Wait for organisation context to resolve (unless it's a page permission that can handle null orgs)\n if (requiresOrgId && (!organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === ''))) {\n setIsLoading(true);\n setCan(false);\n setError(null);\n // Timeout is handled in separate useEffect (Phase 1.4)\n return;\n }\n \n // For page-level permissions with pageName (not UUID), we need appId to resolve the pageName to pageId\n // Wait for appId to be available before checking permissions\n if (needsAppIdForPageName && (!appId || appId === null || (typeof appId === 'string' && appId.trim() === ''))) {\n setIsLoading(true);\n setCan(false);\n setError(null);\n // Will re-run when appId becomes available (via scope change detection)\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n // Create a valid scope object for the API call\n // For page-level permissions, organisationId can be undefined (database handles it)\n const validScope: Scope = {\n ...(organisationId ? { organisationId } : {}),\n ...(eventId ? { eventId } : {}),\n ...(appId ? { appId } : {})\n };\n \n const result = useCache \n ? await isPermittedCached({ userId, scope: validScope, permission, pageId }, undefined, appName)\n : await isPermitted({ userId, scope: validScope, permission, pageId }, undefined, appName);\n \n setCan(result);\n } catch (err) {\n const logger = getRBACLogger();\n logger.error('Permission check error:', { permission, error: err });\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n };\n \n checkPermission();\n }\n }, [userId, stableScope, permission, pageId, useCache, appName]);\n\n const refetch = useCallback(async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Validate scope before accessing properties\n if (!isValidScope) {\n setCan(false);\n setIsLoading(true);\n setError(null);\n return;\n }\n\n // For page-level permissions, allow undefined/null organisationId (database function handles it)\n // For resource-level permissions, organisationId is required\n const isPagePermission = permission.includes(':page.') || !!pageId;\n const requiresOrgId = !isPagePermission;\n \n // Don't check permissions if scope is invalid and orgId is required\n if (requiresOrgId && (!organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === ''))) {\n setCan(false);\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n // Create a valid scope object for the API call\n // For page-level permissions, organisationId can be undefined (database handles it)\n const validScope: Scope = {\n ...(organisationId ? { organisationId } : {}),\n ...(eventId ? { eventId } : {}),\n ...(appId ? { appId } : {})\n };\n \n const result = useCache \n ? await isPermittedCached({ userId, scope: validScope, permission, pageId }, undefined, appName)\n : await isPermitted({ userId, scope: validScope, permission, pageId }, undefined, appName);\n \n setCan(result);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, isValidScope, organisationId, eventId, appId, permission, pageId, useCache, appName]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n can,\n isLoading,\n error,\n refetch\n }), [can, isLoading, error, refetch]);\n}\n\n/**\n * Hook to get user's access level in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for access level checking\n * @returns Access level state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { accessLevel, isLoading, error } = useAccessLevel(userId, scope);\n * \n * if (isLoading) return <div>Loading access level...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * Access Level: {accessLevel}\n * {accessLevel >= AccessLevel.ADMIN && <AdminPanel />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAccessLevel(userId: UUID, scope: Scope): {\n accessLevel: AccessLevelType;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [accessLevel, setAccessLevel] = useState<AccessLevelType>('viewer');\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Get appName from context if available (safely handles missing context)\n let appName: string | undefined;\n try {\n const { appName: contextAppName } = useAppConfig();\n appName = contextAppName;\n } catch {\n // Not available, will use undefined\n }\n\n const fetchAccessLevel = useCallback(async () => {\n if (!userId) {\n setAccessLevel('viewer');\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n // Check super admin status first - super admins bypass context requirements\n // This allows super admins to check their access level without organisation context\n const { isSuperAdmin: checkSuperAdmin } = await import('../api');\n const isSuperAdminUser = await checkSuperAdmin(userId);\n \n if (isSuperAdminUser) {\n setAccessLevel('super');\n setIsLoading(false);\n return;\n }\n\n // Early validation: check if scope has required context\n // PORTAL/ADMIN apps allow both contexts to be optional\n if (appName !== 'PORTAL' && appName !== 'ADMIN' && !scope.organisationId && !scope.eventId) {\n const orgError = new OrganisationContextRequiredError();\n setError(orgError);\n setAccessLevel('viewer');\n setIsLoading(false);\n return;\n }\n \n const level = await getAccessLevel({ userId, scope }, null, appName);\n setAccessLevel(level);\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch access level');\n setError(error);\n setAccessLevel('viewer');\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, appName]);\n\n useEffect(() => {\n fetchAccessLevel();\n }, [fetchAccessLevel]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n accessLevel,\n isLoading,\n error,\n refetch: fetchAccessLevel\n }), [accessLevel, isLoading, error, fetchAccessLevel]);\n}\n\n/**\n * Hook to check multiple permissions at once\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Multiple permission check results\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { results, isLoading, error } = useMultiplePermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {results['read:users'] && <UserList />}\n * {results['create:users'] && <CreateUserButton />}\n * {results['update:users'] && <EditUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMultiplePermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n results: Record<Permission, boolean>;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [results, setResults] = useState<Record<Permission, boolean>>({} as Record<Permission, boolean>);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setResults({} as Record<Permission, boolean>);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionResults: Record<Permission, boolean> = {} as Record<Permission, boolean>;\n \n // Check each permission\n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n permissionResults[permission] = result;\n }\n \n setResults(permissionResults);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setResults({} as Record<Permission, boolean>);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkPermissions();\n }, [checkPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n results,\n isLoading,\n error,\n refetch: checkPermissions\n }), [results, isLoading, error, checkPermissions]);\n}\n\n/**\n * Hook to check if user has any of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has any of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAny, isLoading, error } = useHasAnyPermission(\n * userId, \n * scope, \n * ['read:users', 'create:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAny ? <UserManagementPanel /> : <div>No user permissions</div>;\n * }\n * ```\n */\nexport function useHasAnyPermission(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAny: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAny, setHasAny] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAnyPermission = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAny(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAnyPermission = false;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (result) {\n hasAnyPermission = true;\n break;\n }\n }\n \n setHasAny(hasAnyPermission);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAny(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAnyPermission();\n }, [checkAnyPermission]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAny,\n isLoading,\n error,\n refetch: checkAnyPermission\n }), [hasAny, isLoading, error, checkAnyPermission]);\n}\n\n/**\n * Hook to check if user has all of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has all of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAll, isLoading, error } = useHasAllPermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAll ? <FullUserManagementPanel /> : <div>Insufficient permissions</div>;\n * }\n * ```\n */\nexport function useHasAllPermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAll: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAll, setHasAll] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAllPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAll(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAllPermissions = true;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (!result) {\n hasAllPermissions = false;\n break;\n }\n }\n \n setHasAll(hasAllPermissions);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAll(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAllPermissions();\n }, [checkAllPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAll,\n isLoading,\n error,\n refetch: checkAllPermissions\n }), [hasAll, isLoading, error, checkAllPermissions]);\n}\n\n/**\n * Hook to get cached permissions with TTL management\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Cached permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error, invalidateCache } = useCachedPermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading cached permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * <button onClick={invalidateCache}>Refresh Permissions</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCachedPermissions(userId: UUID, scope: Scope): {\n permissions: PermissionMap;\n isLoading: boolean;\n error: Error | null;\n invalidateCache: () => void;\n refetch: () => Promise<void>;\n} {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchCachedPermissions = useCallback(async () => {\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch cached permissions'));\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const invalidateCache = useCallback(() => {\n // This would typically invalidate the cache in the actual implementation\n // For now, we'll just refetch\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n useEffect(() => {\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n invalidateCache,\n refetch: fetchCachedPermissions\n }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);\n}\n","/**\n * Deep equality check utility for RBAC\n * @package @jmruthers/pace-core\n * @module RBAC/Utils/DeepEqual\n * @since 2.0.0\n * \n * Provides deep equality checking for scope objects and other RBAC data structures.\n */\n\nimport { Scope } from '../types';\n\n/**\n * Deep equality check for two values\n * \n * @param a - First value\n * @param b - Second value\n * @returns True if values are deeply equal\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) {\n return true;\n }\n\n if (a == null || b == null) {\n return a === b;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n if (typeof a !== 'object') {\n return false;\n }\n\n if (Array.isArray(a) !== Array.isArray(b)) {\n return false;\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) {\n return false;\n }\n }\n return true;\n }\n\n const keysA = Object.keys(a as Record<string, unknown>);\n const keysB = Object.keys(b as Record<string, unknown>);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n for (const key of keysA) {\n if (!keysB.includes(key)) {\n return false;\n }\n if (!deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Deep equality check for Scope objects\n * \n * @param a - First scope\n * @param b - Second scope\n * @returns True if scopes are deeply equal\n */\nexport function scopeEqual(a: Scope | null | undefined, b: Scope | null | undefined): boolean {\n if (a === b) {\n return true;\n }\n\n if (a == null || b == null) {\n return a === b;\n }\n\n return (\n a.organisationId === b.organisationId &&\n a.eventId === b.eventId &&\n a.appId === b.appId\n );\n}\n\n","/**\n * @file useResourcePermissions Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * Hook to check permissions for a specific resource type.\n * This hook centralizes the common pattern of checking create/update/delete/read\n * permissions, eliminating ~30 lines of boilerplate code per hook usage.\n * \n * @example\n * ```tsx\n * import { useResourcePermissions } from '@jmruthers/pace-core/rbac';\n * \n * function ContactsHook() {\n * const { canCreate, canUpdate, canDelete } = useResourcePermissions('contacts');\n * \n * const addContact = async (data: ContactData) => {\n * if (!canCreate('contacts')) {\n * throw new Error(\"Permission denied: You do not have permission to create contacts.\");\n * }\n * // ... perform mutation\n * };\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With read permissions enabled\n * const { canRead } = useResourcePermissions('contacts', { enableRead: true });\n * \n * if (!canRead('contacts')) {\n * return <PermissionDenied />;\n * }\n * ```\n * \n * @security\n * - Requires organisation context (handled by useResolvedScope)\n * - All permission checks are scoped to the current organisation/event/app context\n * - Missing user context results in all permissions being denied\n */\n\nimport { useMemo } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEvents } from '../../hooks/useEvents';\nimport { useResolvedScope } from './useResolvedScope';\nimport { useCan } from './usePermissions';\nimport type { Scope } from '../types';\n\nexport interface UseResourcePermissionsOptions {\n /** Whether to check read permissions (default: false) */\n enableRead?: boolean;\n /** Whether scope resolution is required (default: true) */\n requireScope?: boolean;\n}\n\nexport interface ResourcePermissions {\n /** Check if user can create resources of this type */\n canCreate: (resource: string) => boolean;\n /** Check if user can update resources of this type */\n canUpdate: (resource: string) => boolean;\n /** Check if user can delete resources of this type */\n canDelete: (resource: string) => boolean;\n /** Check if user can read resources of this type */\n canRead: (resource: string) => boolean;\n /** The resolved scope object (for advanced use cases) */\n scope: Scope;\n /** Whether any permission check is currently loading */\n isLoading: boolean;\n /** Error from any permission check or scope resolution */\n error: Error | null;\n}\n\n/**\n * Hook to check permissions for a specific resource\n * \n * This hook encapsulates the common pattern of checking create/update/delete/read\n * permissions for a resource type. It handles scope resolution, user context,\n * and provides a simple API for permission checking.\n * \n * **Page Permission Support:**\n * When an `appId` is available in the resolved scope, the resource name is passed\n * as `pageId` to enable page-based permission checks. This allows the hook to work\n * with both resource-based permissions (when appId is not available) and page-based\n * permissions (when appId is available and the resource is a registered page).\n * \n * The RPC function `rbac_check_permission_simplified` will automatically resolve\n * the page name to a page ID and check page permissions if the resource matches\n * a registered page in `rbac_app_pages`. If the resource is not a registered page,\n * it will fall back to resource-based permission checking.\n * \n * @param resource - The resource name (e.g., 'contacts', 'risks', 'planning')\n * Can be a resource name or a page name registered in rbac_app_pages\n * @param options - Optional configuration\n * @param options.enableRead - Whether to check read permissions (default: false)\n * @param options.requireScope - Whether scope resolution is required (default: true)\n * @returns Object with permission check functions and scope\n * \n * @example\n * ```tsx\n * function useContacts() {\n * const { canCreate, canUpdate, canDelete } = useResourcePermissions('contacts');\n * \n * const addContact = async (data: ContactData) => {\n * if (!canCreate('contacts')) {\n * throw new Error(\"Permission denied\");\n * }\n * // ... perform mutation\n * };\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Works with page names when appId is available in scope\n * function usePlanning() {\n * const { canCreate, canUpdate, canDelete } = useResourcePermissions('planning');\n * \n * // Will check page permissions if 'planning' is registered in rbac_app_pages\n * // Falls back to resource permissions if not a registered page\n * const deleteItem = async (id: string) => {\n * if (!canDelete('planning')) {\n * throw new Error(\"Permission denied\");\n * }\n * // ... perform deletion\n * };\n * }\n * ```\n */\nexport function useResourcePermissions(\n resource: string,\n options: UseResourcePermissionsOptions = {}\n): ResourcePermissions {\n const { enableRead = false, requireScope = true } = options;\n\n // Get user and supabase client from UnifiedAuth\n const { user, supabase } = useUnifiedAuth();\n \n // Get selected organisation\n const { selectedOrganisation } = useOrganisations();\n \n // Get selected event (optional - wrap in try/catch)\n let selectedEvent: { event_id: string } | null = null;\n try {\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } catch (error) {\n // Event provider not available - continue without event context\n // This is expected in some apps that don't use events\n }\n\n // Resolve scope for permission checks\n const { resolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({\n supabase,\n selectedOrganisationId: selectedOrganisation?.id || null,\n selectedEventId: selectedEvent?.event_id || null\n });\n\n // Create fallback scope if resolvedScope is not available\n const scope: Scope = resolvedScope || {\n organisationId: selectedOrganisation?.id || '',\n eventId: selectedEvent?.event_id || undefined,\n appId: undefined\n };\n\n // If we have an appId in scope, pass the resource name as pageId to enable page permission checks\n // The RPC function rbac_check_permission_simplified will resolve the page name to a page ID\n // and check page permissions if the resource is a registered page\n // This allows useResourcePermissions to work with both resource-based and page-based permissions\n const pageId = scope.appId ? resource : undefined;\n\n // Permission checks for create, update, delete\n const { can: canCreateResult, isLoading: createLoading, error: createError } = useCan(\n user?.id || '',\n scope,\n `create:${resource}` as const,\n pageId, // Pass resource name as pageId when appId is available to enable page permission checks\n true // useCache\n );\n\n const { can: canUpdateResult, isLoading: updateLoading, error: updateError } = useCan(\n user?.id || '',\n scope,\n `update:${resource}` as const,\n pageId, // Pass resource name as pageId when appId is available to enable page permission checks\n true // useCache\n );\n\n const { can: canDeleteResult, isLoading: deleteLoading, error: deleteError } = useCan(\n user?.id || '',\n scope,\n `delete:${resource}` as const,\n pageId, // Pass resource name as pageId when appId is available to enable page permission checks\n true // useCache\n );\n\n // Optional read permission check\n const { can: canReadResult, isLoading: readLoading, error: readError } = useCan(\n user?.id || '',\n scope,\n `read:${resource}` as const,\n pageId, // Pass resource name as pageId when appId is available to enable page permission checks\n true // useCache\n );\n\n // Aggregate loading states - any permission check or scope resolution loading\n const isLoading = useMemo(() => {\n return scopeLoading || createLoading || updateLoading || deleteLoading || (enableRead && readLoading);\n }, [scopeLoading, createLoading, updateLoading, deleteLoading, readLoading, enableRead]);\n\n // Aggregate errors - prefer scope error, then any permission error\n const error = useMemo(() => {\n if (scopeError) return scopeError;\n if (createError) return createError;\n if (updateError) return updateError;\n if (deleteError) return deleteError;\n if (enableRead && readError) return readError;\n return null;\n }, [scopeError, createError, updateError, deleteError, readError, enableRead]);\n\n // Return wrapper functions that take resource name and return permission result\n // Note: The resource parameter in the function is for consistency with the API,\n // but we're checking permissions for the resource passed to the hook\n return useMemo(() => ({\n canCreate: (res: string) => {\n // For now, we only check the resource passed to the hook\n // Future enhancement could support checking different resources\n if (res !== resource) {\n return false;\n }\n return canCreateResult;\n },\n canUpdate: (res: string) => {\n if (res !== resource) {\n return false;\n }\n return canUpdateResult;\n },\n canDelete: (res: string) => {\n if (res !== resource) {\n return false;\n }\n return canDeleteResult;\n },\n canRead: (res: string) => {\n if (!enableRead) {\n return true; // If read checking is disabled, allow read\n }\n if (res !== resource) {\n return false;\n }\n return canReadResult;\n },\n scope,\n isLoading,\n error\n }), [\n resource,\n canCreateResult,\n canUpdateResult,\n canDeleteResult,\n canReadResult,\n enableRead,\n scope,\n isLoading,\n error\n ]);\n}\n\n","/**\n * @file RBAC Role Management Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 2.1.0\n *\n * React hook for managing RBAC roles safely using RPC functions.\n * This hook provides a secure, type-safe interface for granting and revoking roles\n * that ensures proper audit trails and security checks.\n *\n * @example\n * ```tsx\n * import { useRoleManagement } from '@jmruthers/pace-core/rbac';\n *\n * function UserRolesComponent() {\n * const { \n * revokeEventAppRole, \n * grantEventAppRole,\n * grantGlobalRole,\n * revokeGlobalRole,\n * grantOrganisationRole,\n * revokeOrganisationRole,\n * isLoading, \n * error \n * } = useRoleManagement();\n *\n * // Grant a global role\n * const handleGrantGlobalRole = async () => {\n * const result = await grantGlobalRole({\n * user_id: userId,\n * role: 'super_admin'\n * });\n * if (result.success) {\n * toast({ title: 'Role granted successfully' });\n * }\n * };\n *\n * // Grant an organisation role\n * const handleGrantOrgRole = async () => {\n * const result = await grantOrganisationRole({\n * user_id: userId,\n * organisation_id: orgId,\n * role: 'org_admin'\n * });\n * if (result.success) {\n * toast({ title: 'Role granted successfully' });\n * }\n * };\n *\n * return (\n * <div>\n * <button onClick={handleGrantGlobalRole}>Grant Super Admin</button>\n * <button onClick={handleGrantOrgRole}>Grant Org Admin</button>\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useState, useCallback } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport type { UUID } from '../types';\n\nexport interface EventAppRoleData {\n user_id: UUID;\n organisation_id: UUID;\n event_id: string;\n app_id: UUID;\n role: 'viewer' | 'participant' | 'planner' | 'event_admin';\n}\n\nexport interface OrganisationRoleData {\n user_id: UUID;\n organisation_id: UUID;\n role: 'supporter' | 'member' | 'leader' | 'org_admin';\n}\n\nexport interface GlobalRoleData {\n user_id: UUID;\n role: 'super_admin';\n}\n\nexport interface RevokeEventAppRoleParams extends EventAppRoleData {\n revoked_by?: UUID;\n}\n\nexport interface GrantEventAppRoleParams extends EventAppRoleData {\n granted_by?: UUID;\n valid_from?: string;\n valid_to?: string | null;\n}\n\nexport interface RevokeOrganisationRoleParams extends OrganisationRoleData {\n revoked_by?: UUID;\n}\n\nexport interface GrantOrganisationRoleParams extends OrganisationRoleData {\n granted_by?: UUID;\n valid_from?: string;\n valid_to?: string | null;\n}\n\nexport interface RevokeGlobalRoleParams extends GlobalRoleData {\n revoked_by?: UUID;\n}\n\nexport interface GrantGlobalRoleParams extends GlobalRoleData {\n granted_by?: UUID;\n valid_from?: string;\n valid_to?: string | null;\n}\n\nexport interface RoleManagementResult {\n success: boolean;\n message?: string;\n error?: string;\n roleId?: UUID;\n}\n\nexport function useRoleManagement() {\n const { user, supabase } = useUnifiedAuth();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n if (!supabase) {\n throw new Error('useRoleManagement requires a Supabase client. Ensure UnifiedAuthProvider is configured.');\n }\n\n /**\n * Revoke an event app role using the secure RPC function\n * \n * This function uses the `revoke_event_app_role` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (revoked_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role revocation parameters\n * @returns Promise resolving to operation result\n */\n const revokeEventAppRole = useCallback(async (\n params: RevokeEventAppRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('revoke_event_app_role', {\n p_user_id: params.user_id,\n p_organisation_id: params.organisation_id,\n p_event_id: params.event_id,\n p_app_id: params.app_id,\n p_role: params.role,\n p_revoked_by: params.revoked_by || user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n return {\n success: data === true,\n message: data === true ? 'Role revoked successfully' : 'No role found to revoke',\n error: data === false ? 'No matching role found' : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id]);\n\n /**\n * Grant an event app role using the secure RPC function\n * \n * This function uses the `grant_event_app_role` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (granted_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role grant parameters\n * @returns Promise resolving to operation result with role ID\n */\n const grantEventAppRole = useCallback(async (\n params: GrantEventAppRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('grant_event_app_role', {\n p_user_id: params.user_id,\n p_organisation_id: params.organisation_id,\n p_event_id: params.event_id,\n p_app_id: params.app_id,\n p_role: params.role,\n p_granted_by: params.granted_by || user?.id || undefined,\n p_valid_from: params.valid_from,\n p_valid_to: params.valid_to\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to grant role');\n }\n\n if (!data) {\n return {\n success: false,\n error: 'Failed to grant role - no role ID returned'\n };\n }\n\n return {\n success: true,\n message: 'Role granted successfully',\n roleId: data as UUID\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id]);\n\n /**\n * Revoke an event app role by role ID (alternative method)\n * \n * This fetches the role by ID first to get the required context (role name, event_id, app_id),\n * then uses the unified `rbac_role_revoke` function to revoke it.\n * \n * @param roleId - The role ID to revoke\n * @returns Promise resolving to operation result\n */\n const revokeRoleById = useCallback(async (\n roleId: UUID\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n // First, fetch the role by ID to get the required context\n const { data: roleData, error: fetchError } = await supabase\n .from('rbac_event_app_roles')\n .select('user_id, role, event_id, app_id')\n .eq('id', roleId)\n .single();\n\n if (fetchError || !roleData) {\n throw new Error(fetchError?.message || 'Role not found');\n }\n\n // Construct context_id in the format required by rbac_role_revoke: \"event_id:app_id\"\n const contextId = `${roleData.event_id}:${roleData.app_id}`;\n\n // Now call rbac_role_revoke with the required parameters\n const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', {\n p_user_id: roleData.user_id,\n p_role_type: 'event_app',\n p_role_name: roleData.role,\n p_context_id: contextId,\n p_revoked_by: user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n // rbac_role_revoke returns a table with success, message, revoked_count, error_code\n const result = Array.isArray(data) && data.length > 0 ? data[0] : null;\n\n return {\n success: result?.success === true,\n message: result?.message || undefined,\n error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, supabase]);\n\n /**\n * Grant a global role using the unified RPC function\n * \n * This function uses the `rbac_role_grant` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (granted_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role grant parameters\n * @returns Promise resolving to operation result with role ID\n */\n const grantGlobalRole = useCallback(async (\n params: GrantGlobalRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('rbac_role_grant', {\n p_user_id: params.user_id,\n p_role_type: 'global',\n p_role_name: params.role,\n p_context_id: null, // Global roles don't need context\n p_granted_by: params.granted_by || user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to grant role');\n }\n\n // rbac_role_grant returns a table with success, message, role_id, error_code\n const result = Array.isArray(data) && data.length > 0 ? data[0] : null;\n\n if (!result || !result.success) {\n return {\n success: false,\n error: result?.message || result?.error_code || 'Failed to grant role',\n message: result?.message\n };\n }\n\n return {\n success: true,\n message: result.message || 'Role granted successfully',\n roleId: result.role_id\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, supabase]);\n\n /**\n * Revoke a global role using the unified RPC function\n * \n * This function uses the `rbac_role_revoke` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (revoked_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role revocation parameters\n * @returns Promise resolving to operation result\n */\n const revokeGlobalRole = useCallback(async (\n params: RevokeGlobalRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', {\n p_user_id: params.user_id,\n p_role_type: 'global',\n p_role_name: params.role,\n p_context_id: null, // Global roles don't need context\n p_revoked_by: params.revoked_by || user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n // rbac_role_revoke returns a table with success, message, revoked_count, error_code\n const result = Array.isArray(data) && data.length > 0 ? data[0] : null;\n\n return {\n success: result?.success === true,\n message: result?.message || undefined,\n error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, supabase]);\n\n /**\n * Grant an organisation role using the unified RPC function\n * \n * This function uses the `rbac_role_grant` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (granted_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role grant parameters\n * @returns Promise resolving to operation result with role ID\n */\n const grantOrganisationRole = useCallback(async (\n params: GrantOrganisationRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('rbac_role_grant', {\n p_user_id: params.user_id,\n p_role_type: 'organisation',\n p_role_name: params.role,\n p_context_id: params.organisation_id, // Organisation ID as context\n p_granted_by: params.granted_by || user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to grant role');\n }\n\n // rbac_role_grant returns a table with success, message, role_id, error_code\n const result = Array.isArray(data) && data.length > 0 ? data[0] : null;\n\n if (!result || !result.success) {\n return {\n success: false,\n error: result?.message || result?.error_code || 'Failed to grant role',\n message: result?.message\n };\n }\n\n return {\n success: true,\n message: result.message || 'Role granted successfully',\n roleId: result.role_id\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, supabase]);\n\n /**\n * Revoke an organisation role using the unified RPC function\n * \n * This function uses the `rbac_role_revoke` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (revoked_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role revocation parameters\n * @returns Promise resolving to operation result\n */\n const revokeOrganisationRole = useCallback(async (\n params: RevokeOrganisationRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', {\n p_user_id: params.user_id,\n p_role_type: 'organisation',\n p_role_name: params.role,\n p_context_id: params.organisation_id, // Organisation ID as context\n p_revoked_by: params.revoked_by || user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n // rbac_role_revoke returns a table with success, message, revoked_count, error_code\n const result = Array.isArray(data) && data.length > 0 ? data[0] : null;\n\n return {\n success: result?.success === true,\n message: result?.message || undefined,\n error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, supabase]);\n\n return {\n // Event app roles (existing)\n revokeEventAppRole,\n grantEventAppRole,\n revokeRoleById,\n // Global roles (new)\n grantGlobalRole,\n revokeGlobalRole,\n // Organisation roles (new)\n grantOrganisationRole,\n revokeOrganisationRole,\n // Shared state\n isLoading,\n error\n };\n}\n\n","/**\n * @file Secure Supabase Client Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n *\n * React hook for getting a secure Supabase client with automatic context injection\n * and caching to prevent multiple client instances.\n *\n * ## Overview\n *\n * This hook provides a secure Supabase client that automatically injects\n * organisation and event context for all database operations, while preventing\n * the creation of multiple Supabase client instances (which causes the\n * \"Multiple GoTrueClient instances\" warning).\n *\n * ## Features\n *\n * - **Automatic Context Injection**: Organisation, event, and app context are\n * automatically injected into all database queries\n * - **Client Instance Caching**: Prevents creating duplicate Supabase clients\n * for the same context, eliminating the \"Multiple GoTrueClient instances\" warning\n * - **Automatic Fallback**: Falls back to base client when context is unavailable\n * - **Type-Safe**: Full TypeScript support with proper type inference\n *\n * ## Usage\n *\n * ### Basic Usage\n *\n * ```tsx\n * import { useSecureSupabase } from '@jmruthers/pace-core/rbac';\n *\n * function MyComponent() {\n * const supabase = useSecureSupabase();\n *\n * if (!supabase) {\n * return <div>Loading context...</div>;\n * }\n *\n * const fetchData = async () => {\n * const { data, error } = await supabase\n * .from('users')\n * .select('*');\n * // Organisation context is automatically injected\n * };\n *\n * return <div>...</div>;\n * }\n * ```\n *\n * ### With Base Client Fallback\n *\n * ```tsx\n * import { useSecureSupabase } from '@jmruthers/pace-core/rbac';\n * import { supabase } from './lib/supabase';\n *\n * function MyComponent() {\n * // Provide base client as fallback\n * const secureSupabase = useSecureSupabase(supabase);\n *\n * // secureSupabase will be the secure client when context is available,\n * // or the base client when context is unavailable\n * }\n * ```\n *\n * ## How It Works\n *\n * 1. **Context Resolution**: The hook uses `useResolvedScope` to get the current\n * organisation, event, and app context\n * 2. **Client Caching**: Clients are cached by context key (organisationId-eventId-appId)\n * to prevent duplicate instances\n * 3. **Automatic Injection**: The secure client automatically injects context headers\n * into all database operations\n * 4. **Fallback Behavior**: When context is unavailable or event is loading, the hook\n * returns the base client (or null if no base client provided)\n *\n * ## Security\n *\n * - **Organisation Context Enforcement**: All queries automatically include organisation\n * context, preventing cross-organisation data access\n * - **Event Context Injection**: Event context is automatically included when available\n * - **App Context Support**: App context is included when resolved from scope\n *\n * ## Performance\n *\n * - **Client Instance Caching**: Prevents creating multiple Supabase clients for the\n * same context, reducing memory usage and eliminating the \"Multiple GoTrueClient\n * instances\" warning\n * - **Cache Management**: Automatically cleans up old cache entries (keeps last 5)\n * to prevent memory leaks\n * - **Stable References**: Returns stable client references to prevent unnecessary\n * re-renders in consuming components\n *\n * ## Requirements\n *\n * - Must be used within `UnifiedAuthProvider` context\n * - Requires `useOrganisations` and `useEvents` hooks to be available\n * - Environment variables `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_KEY` must be set\n * (or `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` for Next.js)\n *\n * ## See Also\n *\n * - {@link SecureSupabaseClient} - The underlying secure client class\n * - {@link createSecureClient} - Function to create secure clients manually\n * - {@link useResolvedScope} - Hook for resolving RBAC scope\n *\n * @security\n * - Enforces organisation context on all queries\n * - Prevents cross-organisation data access\n * - Automatic context injection\n *\n * @performance\n * - Client instance caching prevents duplicate creation\n * - Reuses cached clients for same context\n * - Automatic cache cleanup\n */\n\nimport { useMemo, useRef } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEvents } from '../../hooks/useEvents';\nimport { useResolvedScope } from './useResolvedScope';\nimport { useSuperAdminBypass } from './useSuperAdminBypass';\nimport { createSecureClient, SecureSupabaseClient } from '../secureClient';\nimport type { Database } from '../../types/database';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport { logger } from '../../utils/core/logger';\n\n// Cache secure clients by context to prevent creating multiple instances\n// This prevents the \"Multiple GoTrueClient instances\" warning\nconst secureClientCache = new Map<string, SecureSupabaseClient>();\n\n// Maximum cache size to prevent memory leaks\nconst MAX_CACHE_SIZE = 5;\n\n/**\n * Get cache key for secure client based on context\n * CRITICAL: Must include isSuperAdmin in cache key to ensure correct filtering behavior\n */\nfunction getCacheKey(\n organisationId: string,\n eventId: string | undefined,\n appId: string | undefined,\n isSuperAdmin: boolean\n): string {\n return `${organisationId}-${eventId || 'no-event'}-${appId || 'no-app'}-${isSuperAdmin ? 'super' : 'regular'}`;\n}\n\n/**\n * Get Supabase URL and key from environment\n */\nfunction getSupabaseConfig(): { url: string; key: string } | null {\n // Try to get from environment variables\n const getEnvVar = (key: string): string | undefined => {\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n return (import.meta as any).env[key];\n }\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key];\n }\n return undefined;\n };\n\n const supabaseUrl = getEnvVar('VITE_SUPABASE_URL') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_URL') || \n null;\n \n const supabaseKey = getEnvVar('VITE_SUPABASE_PUBLISHABLE_KEY') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY') || \n null;\n\n if (!supabaseUrl || !supabaseKey) {\n return null;\n }\n\n return { url: supabaseUrl, key: supabaseKey };\n}\n\n/**\n * Hook to get a secure Supabase client with automatic context injection\n * \n * Returns a secure client when organisation context is available,\n * otherwise returns null. The client automatically injects organisation\n * and event context into all database operations.\n * \n * Uses caching to prevent creating multiple Supabase client instances,\n * which causes the \"Multiple GoTrueClient instances\" warning.\n * \n * @param baseClient - Optional base Supabase client to use as fallback\n * @returns Secure Supabase client or null if context is not available\n * \n * @example\n * ```tsx\n * import { useSecureSupabase } from '@jmruthers/pace-core/rbac';\n * \n * function MyComponent() {\n * const supabase = useSecureSupabase();\n * \n * if (!supabase) {\n * return <div>Loading context...</div>;\n * }\n * \n * // Use supabase client - organisation context is automatically injected\n * const { data } = await supabase.from('users').select('*');\n * }\n * ```\n */\nexport function useSecureSupabase(\n baseClient?: SupabaseClient<Database> | null\n): SupabaseClient<Database> | null {\n const { user, supabase: authSupabase } = useUnifiedAuth();\n const { selectedOrganisation } = useOrganisations();\n const eventsContext = useEvents();\n const { selectedEvent } = eventsContext;\n const eventLoading = 'eventLoading' in eventsContext ? eventsContext.eventLoading : false;\n \n // Check super admin status for conditional filtering\n // Use both verified status and metadata hint to avoid race conditions\n // Strategy: Use verified status if available, otherwise use metadata hint during verification\n // This prevents creating clients with wrong super admin status before verification completes\n const { isSuperAdmin: verifiedIsSuperAdmin, isLoading: isVerifyingSuperAdmin } = useSuperAdminBypass();\n const metadataHint = Boolean(user?.app_metadata?.is_super_admin) || Boolean(user?.user_metadata?.is_super_admin);\n // If verified as super admin, use that. If verification in progress, use metadata hint optimistically.\n // Once verification completes and user is not super admin, verifiedIsSuperAdmin will be false.\n const isSuperAdmin = verifiedIsSuperAdmin || (isVerifyingSuperAdmin && metadataHint);\n\n // Resolve scope to get appId\n const { resolvedScope } = useResolvedScope({\n supabase: authSupabase || null,\n selectedOrganisationId: selectedOrganisation?.id || null,\n selectedEventId: selectedEvent?.event_id || null\n });\n\n // Track previous context to detect changes\n const prevContextRef = useRef<{\n organisationId: string | undefined;\n eventId: string | undefined;\n appId: string | undefined;\n }>({\n organisationId: undefined,\n eventId: undefined,\n appId: undefined\n });\n\n return useMemo(() => {\n // If event is loading, return base client or null to avoid recreating client unnecessarily\n if (eventLoading) {\n return baseClient || authSupabase || null;\n }\n \n // Use resolved scope to get organisationId (derived from event if needed)\n // For event-required apps, resolvedScope.organisationId is derived from event\n // For org-required apps, resolvedScope.organisationId comes from selectedOrganisation\n const organisationId = resolvedScope?.organisationId;\n const eventId = resolvedScope?.eventId || selectedEvent?.event_id;\n const appId = resolvedScope?.appId;\n \n // If we have organisation context (from resolved scope), create or reuse a secure client\n if (organisationId && user?.id) {\n // Update previous context\n prevContextRef.current = { organisationId, eventId, appId };\n\n // Check cache first (must include isSuperAdmin in key for correct filtering)\n const cacheKey = getCacheKey(organisationId, eventId, appId, isSuperAdmin);\n const cachedClient = secureClientCache.get(cacheKey);\n\n if (cachedClient) {\n // Reuse cached client - prevents creating multiple instances\n return cachedClient.getClient();\n }\n\n // Get Supabase configuration\n const config = getSupabaseConfig();\n if (!config || !config.url || !config.key) {\n logger.warn('useSecureSupabase', 'Missing Supabase environment variables. Falling back to base client.', {\n note: 'Ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are set in your environment.'\n });\n return baseClient || authSupabase || null;\n }\n\n try {\n const secureClient = createSecureClient(\n config.url,\n config.key,\n organisationId as any, // organisationId is string, UUID is string alias\n eventId,\n appId as any, // appId is string | undefined, UUID is string alias\n isSuperAdmin // Pass super admin status for conditional filtering\n );\n\n // Cache the client for reuse\n secureClientCache.set(cacheKey, secureClient);\n\n // Clean up old cache entries to prevent memory leaks\n if (secureClientCache.size > MAX_CACHE_SIZE) {\n const firstKey = secureClientCache.keys().next().value;\n if (firstKey) {\n secureClientCache.delete(firstKey);\n }\n }\n\n // Return the underlying client for compatibility\n return secureClient.getClient();\n } catch (error) {\n logger.error('useSecureSupabase', 'Failed to create secure client', error);\n // Fallback to base client\n return baseClient || authSupabase || null;\n }\n }\n\n // Fallback to base client when context is not available\n return baseClient || authSupabase || null;\n }, [\n resolvedScope?.organisationId,\n resolvedScope?.eventId,\n resolvedScope?.appId,\n selectedEvent?.event_id,\n user?.id,\n eventLoading,\n isSuperAdmin,\n baseClient,\n authSupabase\n ]);\n}\n\n"],"mappings":"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","names":["contextValues","logger","useState","useEffect","useCallback","useMemo","useState","logger","useEffect","useCallback","useMemo","error","useMemo","error","useMemo","useState","useCallback","useState","useCallback","useMemo","useRef","useRef","useMemo"]}
@@ -9,13 +9,13 @@ var LoadingSpinner = ({
9
9
  className = ""
10
10
  }) => {
11
11
  const sizeClasses = {
12
- sm: "w-4 h-4",
13
- md: "w-6 h-6",
14
- lg: "w-8 h-8"
12
+ sm: "size-4",
13
+ md: "size-6",
14
+ lg: "size-8"
15
15
  };
16
16
  const validSize = size && size in sizeClasses ? size : "md";
17
17
  const sizeClass = sizeClasses[validSize];
18
- return /* @__PURE__ */ jsx("div", { className: `inline-block animate-spin rounded-full border-2 border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite] ${sizeClass} ${className}`.trim(), role: "status", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading..." }) });
18
+ return /* @__PURE__ */ jsx("canvas", { className: `inline-block animate-spin rounded-full border-2 border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite] ${sizeClass} ${className}`.trim(), role: "status", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading..." }) });
19
19
  };
20
20
 
21
21
  // src/utils/timezone/timezone.ts
@@ -212,4 +212,4 @@ export {
212
212
  CachedAppIdResolver,
213
213
  cachedAppIdResolver
214
214
  };
215
- //# sourceMappingURL=chunk-THRPYOFK.js.map
215
+ //# sourceMappingURL=chunk-HW3OVDUF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/LoadingSpinner/LoadingSpinner.tsx","../src/utils/timezone/timezone.ts","../src/utils/app/appIdResolver.ts"],"sourcesContent":["/**\n * @file LoadingSpinner Component\n * @package @jmruthers/pace-core\n * @module Components/LoadingSpinner\n * @since 0.1.0\n *\n * A simple, accessible loading spinner component for indicating loading states.\n * Provides smooth animations with reduced motion support for accessibility.\n *\n * Features:\n * - Multiple size variants (sm, md, lg)\n * - Smooth CSS animations\n * - Reduced motion support for accessibility\n * - Screen reader friendly with proper ARIA attributes\n * - Customizable styling\n * - Lightweight and performant\n *\n * @example\n * ```tsx\n * // Basic loading spinner\n * <LoadingSpinner />\n * \n * // Different sizes\n * <LoadingSpinner size=\"sm\" />\n * <LoadingSpinner size=\"md\" />\n * <LoadingSpinner size=\"lg\" />\n * \n * // With custom styling\n * <LoadingSpinner \n * size=\"lg\" \n * className=\"text-main-500\" \n * />\n * \n * // In a button\n * <Button disabled>\n * <LoadingSpinner size=\"sm\" className=\"mr-2\" />\n * Loading...\n * </Button>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"status\"\n * - Screen reader announcement with \"Loading...\" text\n * - Reduced motion support for users with vestibular disorders\n * - High contrast support\n *\n * @performance\n * - CSS-only animations for optimal performance\n * - No JavaScript dependencies\n * - Minimal DOM structure\n * - Efficient rendering\n *\n * @dependencies\n * - React 18+ - Component framework\n * - Tailwind CSS - Styling and animations\n */\n\nimport React from 'react';\n\n/**\n * Props for the LoadingSpinner component\n */\nexport interface LoadingSpinnerProps {\n /** Size variant of the spinner */\n size?: 'sm' | 'md' | 'lg';\n /** Additional CSS classes for styling */\n className?: string;\n}\n\n/**\n * LoadingSpinner component\n * A simple, accessible loading spinner for indicating loading states\n * \n * @param props - Spinner configuration and styling\n * @returns JSX.Element - The rendered loading spinner\n * \n * @example\n * ```tsx\n * <LoadingSpinner size=\"lg\" className=\"text-main-500\" />\n * ```\n */\nexport const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({ \n size = 'md', \n className = '' \n}) => {\n const sizeClasses: Record<'sm' | 'md' | 'lg', string> = {\n sm: 'size-4',\n md: 'size-6',\n lg: 'size-8'\n };\n\n // Ensure we always have a valid size class, defaulting to 'md' if invalid\n const validSize = size && size in sizeClasses ? size : 'md';\n const sizeClass = sizeClasses[validSize];\n\n return (\n <canvas className={`inline-block animate-spin rounded-full border-2 border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite] ${sizeClass} ${className}`.trim()} role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </canvas>\n );\n};\n","/**\n * @file Timezone Utilities\n * @package @jmruthers/pace-core\n * @module Utils/Timezone\n * @since 0.1.0\n *\n * Comprehensive timezone conversion and formatting utilities using date-fns-tz and native Intl APIs.\n * Provides functions for timezone-aware date operations, conversions, and formatting.\n *\n * Features:\n * - Format dates in specific timezones\n * - Get timezone abbreviations\n * - Convert between UTC and timezone local times\n * - Round dates to nearest minutes\n * - Calculate timezone differences\n * - Get user's browser timezone\n *\n * @example\n * ```ts\n * import { toZonedTime, fromZonedTime, formatInTimeZone, getUserTimeZone } from '@jmruthers/pace-core/utils/timezone';\n *\n * // Convert UTC to local timezone\n * const utcDate = new Date('2024-01-15T10:00:00Z');\n * const localDate = toZonedTime(utcDate, 'America/New_York');\n *\n * // Convert local time to UTC\n * const localInput = new Date(2024, 0, 15, 10, 0);\n * const utcDate = fromZonedTime(localInput, 'America/New_York');\n *\n * // Format with timezone\n * const formatted = formatInTimeZone(utcDate, 'America/New_York', 'MMM dd, yyyy HH:mm');\n * ```\n */\n\nimport { format, parseISO, addMinutes, differenceInHours, isValid } from 'date-fns';\nimport { formatInTimeZone as fnsFormatInTimeZone, toZonedTime as fnsToZonedTime, fromZonedTime as fnsFromZonedTime } from 'date-fns-tz';\n\n/**\n * Format a date in a specific timezone using date-fns format strings\n *\n * @param date - Date to format (Date object, ISO string, or timestamp)\n * @param timeZone - IANA timezone string (e.g., 'America/New_York')\n * @param formatStr - date-fns format string (e.g., 'MMM dd, yyyy HH:mm')\n * @returns Formatted date string or 'Invalid date' on error\n *\n * @example\n * ```ts\n * formatInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York', 'MMM dd, yyyy HH:mm');\n * // \"Jan 15, 2024 05:00\"\n * ```\n */\nexport function formatInTimeZone(\n date: Date | string | number,\n timeZone: string,\n formatStr: string\n): string {\n try {\n if (!timeZone || typeof timeZone !== 'string') {\n return 'Invalid date';\n }\n\n let dateObj: Date;\n if (typeof date === 'string') {\n dateObj = parseISO(date);\n } else if (typeof date === 'number') {\n dateObj = new Date(date);\n } else {\n dateObj = date;\n }\n\n if (!isValid(dateObj)) {\n return 'Invalid date';\n }\n\n return fnsFormatInTimeZone(dateObj, timeZone, formatStr);\n } catch {\n return 'Invalid date';\n }\n}\n\n/**\n * Get the timezone abbreviation (EST, PST, etc.) for a date in a specific timezone\n *\n * @param date - Date to get abbreviation for\n * @param timeZone - IANA timezone string\n * @returns Timezone abbreviation or timezone name on error\n *\n * @example\n * ```ts\n * getTimezoneAbbreviation(new Date(), 'America/New_York');\n * // \"EST\" or \"EDT\" depending on date\n * ```\n */\nexport function getTimezoneAbbreviation(date: Date, timeZone: string): string {\n try {\n if (!timeZone || typeof timeZone !== 'string') {\n return timeZone || 'UTC';\n }\n\n if (!isValid(date)) {\n return timeZone;\n }\n\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n timeZoneName: 'short'\n });\n\n const parts = formatter.formatToParts(date);\n const timeZoneName = parts.find(part => part.type === 'timeZoneName');\n\n return timeZoneName?.value || timeZone;\n } catch {\n return timeZone || 'UTC';\n }\n}\n\n/**\n * Format time only in a specific timezone\n *\n * @param date - Date to format\n * @param timeZone - IANA timezone string\n * @returns Formatted time string (HH:mm format) or 'Invalid date' on error\n *\n * @example\n * ```ts\n * formatTimeInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York');\n * // \"05:00\"\n * ```\n */\nexport function formatTimeInTimeZone(date: Date | string, timeZone: string): string {\n return formatInTimeZone(date, timeZone, 'HH:mm');\n}\n\n/**\n * Get the user's local timezone from the browser\n *\n * @returns IANA timezone string (e.g., 'America/New_York')\n *\n * @example\n * ```ts\n * const userTz = getUserTimeZone();\n * // \"America/New_York\" or user's actual timezone\n * ```\n */\nexport function getUserTimeZone(): string {\n try {\n if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n return 'UTC';\n } catch {\n return 'UTC';\n }\n}\n\n/**\n * Convert a UTC date to a specific timezone's local time representation\n *\n * @param date - UTC date to convert\n * @param timezone - IANA timezone string\n * @returns Date object representing local time in the timezone\n *\n * @example\n * ```ts\n * const utcDate = new Date('2024-01-15T10:00:00Z');\n * const localDate = toZonedTime(utcDate, 'America/New_York');\n * // Returns Date object representing 5:00 AM in New York\n * ```\n */\nexport function toZonedTime(date: Date, timezone: string): Date {\n try {\n if (!timezone || typeof timezone !== 'string') {\n return date;\n }\n\n if (!isValid(date)) {\n return date;\n }\n\n return fnsToZonedTime(date, timezone);\n } catch {\n return date;\n }\n}\n\n/**\n * Convert a local time in a specific timezone to UTC\n *\n * @param localDate - Local date in the timezone\n * @param timezone - IANA timezone string\n * @returns Date object in UTC\n *\n * @example\n * ```ts\n * const localDate = new Date(2024, 0, 15, 10, 0); // Jan 15, 2024 10:00 AM local\n * const utcDate = fromZonedTime(localDate, 'America/New_York');\n * // Returns Date object representing 3:00 PM UTC (if EST) or 2:00 PM UTC (if EDT)\n * ```\n */\nexport function fromZonedTime(localDate: Date, timezone: string): Date {\n try {\n if (!timezone || typeof timezone !== 'string') {\n return localDate;\n }\n\n if (!isValid(localDate)) {\n return localDate;\n }\n\n return fnsFromZonedTime(localDate, timezone);\n } catch {\n return localDate;\n }\n}\n\n/**\n * Round a date to the nearest X minutes\n *\n * @param date - Date to round\n * @param minutesStep - Number of minutes to round to (default: 5)\n * @returns Rounded date\n *\n * @example\n * ```ts\n * const date = new Date('2024-01-15T10:23:00Z');\n * roundToNearestMinutes(date, 5);\n * // Returns Date object for 10:25:00\n * ```\n */\nexport function roundToNearestMinutes(date: Date, minutesStep: number = 5): Date {\n try {\n if (!isValid(date)) {\n return date;\n }\n\n if (minutesStep <= 0 || !Number.isInteger(minutesStep)) {\n return date;\n }\n\n const minutes = date.getMinutes();\n const roundedMinutes = Math.round(minutes / minutesStep) * minutesStep;\n const diff = roundedMinutes - minutes;\n\n return addMinutes(date, diff);\n } catch {\n return date;\n }\n}\n\n/**\n * Calculate the time difference between two timezones in hours\n *\n * @param fromTimeZone - Source timezone (IANA string)\n * @param toTimeZone - Target timezone (IANA string)\n * @returns Difference in hours (positive if toTimeZone is ahead)\n *\n * @example\n * ```ts\n * getTimeZoneDifference('America/New_York', 'America/Los_Angeles');\n * // -3 (Los Angeles is 3 hours behind New York)\n * ```\n */\nexport function getTimeZoneDifference(fromTimeZone: string, toTimeZone: string): number {\n try {\n if (!fromTimeZone || !toTimeZone || typeof fromTimeZone !== 'string' || typeof toTimeZone !== 'string') {\n return 0;\n }\n\n const now = new Date();\n const fromDate = toZonedTime(now, fromTimeZone);\n const toDate = toZonedTime(now, toTimeZone);\n\n const diff = differenceInHours(toDate, fromDate);\n // Handle NaN case (differenceInHours can return NaN for invalid dates)\n return isNaN(diff) ? 0 : diff;\n } catch {\n return 0;\n }\n}\n\n","/**\n * App ID Resolution Utility\n * @package @jmruthers/pace-core\n * @module Utils/AppIdResolver\n * @since 1.0.0\n * \n * This module provides utilities to resolve app names to app IDs for database operations.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('AppIdResolver');\n\n/**\n * Resolves an app name to its corresponding app ID\n * \n * @param supabase - Supabase client instance\n * @param appName - The app name to resolve\n * @returns Promise resolving to the app ID or null if not found\n */\nexport async function getAppId(\n supabase: SupabaseClient<Database>,\n appName: string\n): Promise<string | null> {\n try {\n const { data, error } = await supabase\n .from('rbac_apps')\n .select('id')\n .ilike('name', appName)\n .eq('is_active', true)\n .single();\n\n if (error) {\n log.error('Failed to resolve app ID for app name:', appName, error);\n return null;\n }\n\n return (data as { id: string } | null)?.id || null;\n } catch (error) {\n log.error('Error resolving app ID for app name:', appName, error);\n return null;\n }\n}\n\n/**\n * Resolves multiple app names to their corresponding app IDs\n * \n * @param supabase - Supabase client instance\n * @param appNames - Array of app names to resolve\n * @returns Promise resolving to a map of app names to app IDs\n */\nexport async function getAppIds(\n supabase: SupabaseClient<Database>,\n appNames: string[]\n): Promise<Record<string, string | null>> {\n try {\n // For case-insensitive matching with multiple values, we need to use OR conditions\n // since PostgreSQL doesn't support case-insensitive IN with ILIKE\n const orConditions = appNames.map(name => `name.ilike.${name}`).join(',');\n \n const { data, error } = await supabase\n .from('rbac_apps')\n .select('id, name')\n .or(orConditions)\n .eq('is_active', true);\n\n if (error) {\n log.error('Failed to resolve app IDs for app names:', appNames, error);\n return {};\n }\n\n const result: Record<string, string | null> = {};\n \n // Initialize all app names with null\n appNames.forEach(name => {\n result[name] = null;\n });\n\n // Set resolved app IDs - match case-insensitively\n (data as { id: string; name: string }[] | null)?.forEach(app => {\n // Find the original app name that matches (case-insensitive)\n const originalName = appNames.find(name => \n name.toLowerCase() === app.name.toLowerCase()\n );\n if (originalName) {\n result[originalName] = app.id;\n }\n });\n\n return result;\n } catch (error) {\n log.error('Error resolving app IDs for app names:', appNames, error);\n return {};\n }\n}\n\n/**\n * Cached app ID resolver with TTL\n */\nexport class CachedAppIdResolver {\n private cache = new Map<string, { id: string | null; expires: number }>();\n private ttl = 5 * 60 * 1000; // 5 minutes\n\n async getAppId(\n supabase: SupabaseClient<Database>,\n appName: string\n ): Promise<string | null> {\n const now = Date.now();\n const cached = this.cache.get(appName);\n\n if (cached && cached.expires > now) {\n return cached.id;\n }\n\n const id = await getAppId(supabase, appName);\n this.cache.set(appName, { id, expires: now + this.ttl });\n\n return id;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n clearCacheForApp(appName: string): void {\n this.cache.delete(appName);\n }\n}\n\n// Export singleton instance\nexport const cachedAppIdResolver = new CachedAppIdResolver();\n"],"mappings":"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","names":[]}
@@ -106,6 +106,77 @@ function getCurrentAppNameWithFallback(fallback = "default-app") {
106
106
  return getCurrentAppName() || fallback;
107
107
  }
108
108
 
109
+ // src/utils/performance/performanceBudgets.ts
110
+ var PERFORMANCE_BUDGETS = {
111
+ COMPONENT_RENDER: { threshold: 50 },
112
+ BUNDLE_SIZE: { threshold: 15e4 },
113
+ CHUNK_COUNT: { threshold: 10 },
114
+ TREESHAKING_SCORE: { threshold: 70 },
115
+ ERROR_BOUNDARY_TRIGGER: { threshold: 5 },
116
+ MEMORY_INCREASE: { threshold: 1e3 },
117
+ LARGE_LIST_RENDER: { threshold: 500 }
118
+ };
119
+ var PerformanceBudgetMonitor = class {
120
+ constructor() {
121
+ this.metrics = /* @__PURE__ */ new Map();
122
+ this.budgets = [];
123
+ }
124
+ measure(metric, value, metadata) {
125
+ this.metrics.set(metric, value);
126
+ if (import.meta.env.MODE === "development" || import.meta.env.MODE === "test" || false) {
127
+ console.log("\u{1F4CA} Performance Metric: " + metric + " = " + value, metadata);
128
+ }
129
+ const budgetConfig = PERFORMANCE_BUDGETS[metric];
130
+ const threshold = budgetConfig?.threshold || 100;
131
+ const passed = value <= threshold;
132
+ return {
133
+ passed,
134
+ value,
135
+ threshold
136
+ };
137
+ }
138
+ setBudget(metric, budget, threshold = "warning") {
139
+ this.budgets.push({ metric, budget, actual: 0, threshold });
140
+ }
141
+ checkBudgets() {
142
+ const violations = [];
143
+ for (const budget of this.budgets) {
144
+ const actual = this.metrics.get(budget.metric) || 0;
145
+ budget.actual = actual;
146
+ if (actual > budget.budget) {
147
+ violations.push(budget);
148
+ if (import.meta.env.MODE === "development" || import.meta.env.MODE === "test" || false) {
149
+ if (budget.threshold === "error") {
150
+ console.error("\u274C Performance budget exceeded: " + budget.metric + " (" + actual + " > " + budget.budget + ")");
151
+ } else if (budget.threshold === "warning") {
152
+ console.warn("\u26A0\uFE0F Performance budget exceeded: " + budget.metric + " (" + actual + " > " + budget.budget + ")");
153
+ } else {
154
+ console.info("\u2139\uFE0F Performance budget exceeded: " + budget.metric + " (" + actual + " > " + budget.budget + ")");
155
+ }
156
+ }
157
+ }
158
+ }
159
+ return violations;
160
+ }
161
+ getMetrics() {
162
+ return {
163
+ bundleSize: this.metrics.get("BUNDLE_SIZE") || 0,
164
+ chunkCount: this.metrics.get("CHUNK_COUNT") || 0,
165
+ treeshakingEffectiveness: this.metrics.get("TREESHAKING_SCORE") || 0,
166
+ dynamicImportUsage: this.metrics.get("DYNAMIC_IMPORTS") || 0
167
+ };
168
+ }
169
+ reset() {
170
+ this.metrics.clear();
171
+ this.budgets.length = 0;
172
+ }
173
+ };
174
+ var performanceBudgetMonitor = new PerformanceBudgetMonitor();
175
+ performanceBudgetMonitor.setBudget("BUNDLE_SIZE", 15e4, "error");
176
+ performanceBudgetMonitor.setBudget("CHUNK_COUNT", 10, "warning");
177
+ performanceBudgetMonitor.setBudget("TREESHAKING_SCORE", 70, "warning");
178
+ performanceBudgetMonitor.setBudget("ERROR_BOUNDARY_TRIGGER", 5, "error");
179
+
109
180
  export {
110
181
  getAppNameFromPackageJson,
111
182
  getAppNameFromBuildTime,
@@ -113,6 +184,8 @@ export {
113
184
  getAppNameFromEnvironment,
114
185
  getCurrentAppName,
115
186
  setRBACAppName,
116
- getCurrentAppNameWithFallback
187
+ getCurrentAppNameWithFallback,
188
+ PERFORMANCE_BUDGETS,
189
+ performanceBudgetMonitor
117
190
  };
118
- //# sourceMappingURL=chunk-F2IMUDXZ.js.map
191
+ //# sourceMappingURL=chunk-I7PSE6JW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/app/appNameResolver.ts","../src/utils/performance/performanceBudgets.ts"],"sourcesContent":["/**\n * Utility to resolve the current app name from various sources\n * Priority: package.json > environment variables > fallback\n */\n\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('AppNameResolver');\n\ninterface PackageJson {\n name: string;\n [key: string]: unknown;\n}\n\n/**\n * Get the app name from package.json at build time\n * This works by reading package.json during the build process\n */\nexport function getAppNameFromPackageJson(): string | null {\n // Check if we're in a Node.js environment (build time)\n if (typeof window === 'undefined' && typeof require !== 'undefined') {\n try {\n // Try to read package.json from the current working directory\n // This works in most Node.js environments\n const fs = require('fs');\n const path = require('path');\n \n // Look for package.json in common locations\n const possiblePaths = [\n // Only use process.cwd() if we're in a Node.js environment\n ...(typeof process !== 'undefined' && process.cwd ? [path.join(process.cwd(), 'package.json')] : []),\n path.join(__dirname, '../../package.json'),\n path.join(__dirname, '../../../package.json'),\n ];\n \n for (const packagePath of possiblePaths) {\n try {\n if (fs.existsSync(packagePath)) {\n const packageJsonContent = fs.readFileSync(packagePath, 'utf8');\n const packageJson: PackageJson = JSON.parse(packageJsonContent);\n \n if (packageJson.name) {\n // Extract the app name from the package name\n // Handle scoped packages like @org/app-name\n const name = packageJson.name.split('/').pop() || packageJson.name;\n return name;\n }\n }\n } catch (error) {\n // Continue to next path\n continue;\n }\n }\n } catch (error) {\n // package.json not found or not readable\n log.warn('Could not read app name from package.json:', error);\n }\n }\n \n // In browser environments, we can't read package.json\n // This will fall back to environment variables\n return null;\n}\n\n/**\n * Get the app name from build-time injected variables\n * This is the preferred method for browser environments\n */\nexport function getAppNameFromBuildTime(): string | null {\n // Check for build-time injected app name\n // This would be set by the build process reading package.json\n if (typeof window !== 'undefined') {\n // Try to access build-time injected variables\n try {\n // @ts-ignore - These are injected at build time\n const buildTimeEnv = (globalThis as any).__RBAC_APP_NAME__;\n if (buildTimeEnv && buildTimeEnv.trim()) {\n return buildTimeEnv.trim();\n }\n } catch (error) {\n // Build-time injection not available\n }\n }\n \n return null;\n}\n\n/**\n * Get the app name from a global variable set by the consuming app\n * This is the simplest approach for browser environments\n */\nexport function getAppNameFromGlobal(): string | null {\n if (typeof window !== 'undefined') {\n // Check for global app name set by consuming app\n try {\n // @ts-ignore - This is set by the consuming app\n const globalAppName = (globalThis as any).RBAC_APP_NAME;\n if (globalAppName && globalAppName.trim()) {\n return globalAppName.trim();\n }\n } catch (error) {\n // Global variable not set\n }\n }\n \n return null;\n}\n\n/**\n * Get the app name from environment variables\n * Fallback method for when package.json is not available\n */\nexport function getAppNameFromEnvironment(): string | null {\n // Try different environment variable patterns used by various frameworks\n const envVars = [\n 'VITE_APP_NAME',\n 'REACT_APP_NAME', \n 'NEXT_PUBLIC_APP_NAME',\n 'APP_NAME',\n 'NODE_APP_NAME'\n ];\n \n for (const envVar of envVars) {\n const value = import.meta.env[envVar];\n if (value && value.trim()) {\n return value.trim();\n }\n }\n \n return null;\n}\n\n/**\n * Get the current app name from the most reliable source\n * Priority: global variable > build-time injection > package.json > environment variables > null\n */\nexport function getCurrentAppName(): string | null {\n // First try global variable (set by consuming app)\n const globalName = getAppNameFromGlobal();\n if (globalName) {\n return globalName;\n }\n \n // Then try build-time injection (most reliable for browser)\n const buildTimeName = getAppNameFromBuildTime();\n if (buildTimeName) {\n return buildTimeName;\n }\n \n // Then try package.json (works in Node.js environments)\n const packageJsonName = getAppNameFromPackageJson();\n if (packageJsonName) {\n return packageJsonName;\n }\n \n // Fallback to environment variables\n const envName = getAppNameFromEnvironment();\n if (envName) {\n return envName;\n }\n \n return null;\n}\n\n/**\n * Set the app name globally for RBAC resolution\n * Call this in your app's main entry point (e.g., main.tsx, index.tsx)\n * \n * @param appName - The app name from your package.json\n * \n * @example\n * ```tsx\n * // In your main.tsx or index.tsx\n * import { setRBACAppName } from '@jmruthers/pace-core/utils';\n * \n * // Set the app name from package.json\n * setRBACAppName('CAKE');\n * \n * // Rest of your app setup...\n * ```\n */\nexport function setRBACAppName(appName: string): void {\n if (typeof window !== 'undefined') {\n // @ts-ignore - Setting global variable\n (globalThis as any).RBAC_APP_NAME = appName.trim();\n }\n}\n\n/**\n * Get the app name with fallback to a default\n * Useful when you need a guaranteed app name\n */\nexport function getCurrentAppNameWithFallback(fallback: string = 'default-app'): string {\n return getCurrentAppName() || fallback;\n}\n","\ninterface PerformanceBudget {\n metric: string;\n budget: number;\n actual: number;\n threshold: 'error' | 'warning' | 'info';\n}\n\ninterface PerformanceMetrics {\n bundleSize: number;\n chunkCount: number;\n treeshakingEffectiveness: number;\n dynamicImportUsage: number;\n}\n\ninterface MeasurementResult {\n passed: boolean;\n value: number;\n threshold: number;\n}\n\n// Performance budget thresholds with index signature\nexport const PERFORMANCE_BUDGETS: { [key: string]: { threshold: number } } = {\n COMPONENT_RENDER: { threshold: 50 },\n BUNDLE_SIZE: { threshold: 150000 },\n CHUNK_COUNT: { threshold: 10 },\n TREESHAKING_SCORE: { threshold: 70 },\n ERROR_BOUNDARY_TRIGGER: { threshold: 5 },\n MEMORY_INCREASE: { threshold: 1000 },\n LARGE_LIST_RENDER: { threshold: 500 },\n} as const;\n\nclass PerformanceBudgetMonitor {\n private metrics: Map<string, number> = new Map();\n private budgets: PerformanceBudget[] = [];\n\n measure(metric: string, value: number, metadata?: Record<string, unknown>): MeasurementResult {\n this.metrics.set(metric, value);\n \n // In production, this would send to a proper logging service\n // For now, we'll log to console for testing purposes\n if (import.meta.env.MODE === 'development' || import.meta.env.MODE === 'test' || process.env.NODE_ENV === 'test') {\n console.log('📊 Performance Metric: ' + metric + ' = ' + value, metadata);\n }\n\n // Get threshold for this metric\n const budgetConfig = PERFORMANCE_BUDGETS[metric];\n const threshold = budgetConfig?.threshold || 100;\n const passed = value <= threshold;\n\n return {\n passed,\n value,\n threshold\n };\n }\n\n setBudget(metric: string, budget: number, threshold: 'error' | 'warning' | 'info' = 'warning'): void {\n this.budgets.push({ metric, budget, actual: 0, threshold });\n }\n\n checkBudgets(): PerformanceBudget[] {\n const violations: PerformanceBudget[] = [];\n \n for (const budget of this.budgets) {\n const actual = this.metrics.get(budget.metric) || 0;\n budget.actual = actual;\n \n if (actual > budget.budget) {\n violations.push(budget);\n \n // In production, this would send to a proper logging service\n // For now, we'll log to console for testing purposes\n if (import.meta.env.MODE === 'development' || import.meta.env.MODE === 'test' || process.env.NODE_ENV === 'test') {\n if (budget.threshold === 'error') {\n console.error('❌ Performance budget exceeded: ' + budget.metric + ' (' + actual + ' > ' + budget.budget + ')');\n } else if (budget.threshold === 'warning') {\n console.warn('⚠️ Performance budget exceeded: ' + budget.metric + ' (' + actual + ' > ' + budget.budget + ')');\n } else {\n console.info('ℹ️ Performance budget exceeded: ' + budget.metric + ' (' + actual + ' > ' + budget.budget + ')');\n }\n }\n }\n }\n \n return violations;\n }\n\n getMetrics(): PerformanceMetrics {\n return {\n bundleSize: this.metrics.get('BUNDLE_SIZE') || 0,\n chunkCount: this.metrics.get('CHUNK_COUNT') || 0,\n treeshakingEffectiveness: this.metrics.get('TREESHAKING_SCORE') || 0,\n dynamicImportUsage: this.metrics.get('DYNAMIC_IMPORTS') || 0,\n };\n }\n\n reset(): void {\n this.metrics.clear();\n this.budgets.length = 0;\n }\n}\n\nexport const performanceBudgetMonitor = new PerformanceBudgetMonitor();\n\n// Set default performance budgets\nperformanceBudgetMonitor.setBudget('BUNDLE_SIZE', 150000, 'error'); // 150KB\nperformanceBudgetMonitor.setBudget('CHUNK_COUNT', 10, 'warning');\nperformanceBudgetMonitor.setBudget('TREESHAKING_SCORE', 70, 'warning');\nperformanceBudgetMonitor.setBudget('ERROR_BOUNDARY_TRIGGER', 5, 'error');\n"],"mappings":"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","names":[]}