@jmruthers/pace-core 0.5.193 → 0.6.2

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 (577) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +299 -0
  4. package/cursor-rules/01-standards-compliance.mdc +244 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +222 -0
  7. package/cursor-rules/04-testing-standards.mdc +268 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +309 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
  11. package/cursor-rules/08-markup-quality.mdc +452 -0
  12. package/cursor-rules/CHANGELOG.md +119 -0
  13. package/cursor-rules/README.md +192 -0
  14. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  15. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
  16. package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
  17. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
  18. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  19. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  20. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  21. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  22. package/dist/chunk-24UVZUZG.js.map +1 -0
  23. package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
  24. package/dist/chunk-2UOI2FG5.js.map +1 -0
  25. package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
  26. package/dist/chunk-3XC4CPTD.js.map +1 -0
  27. package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
  28. package/dist/chunk-6J4GEEJR.js.map +1 -0
  29. package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
  30. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  31. package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
  32. package/dist/chunk-EHMR7VYL.js.map +1 -0
  33. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  34. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  35. package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
  36. package/dist/chunk-FFQEQTNW.js.map +1 -0
  37. package/dist/chunk-FMUCXFII.js +76 -0
  38. package/dist/chunk-FMUCXFII.js.map +1 -0
  39. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  40. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  41. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  42. package/dist/chunk-L4OXEN46.js.map +1 -0
  43. package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
  44. package/dist/chunk-M43Y4SSO.js.map +1 -0
  45. package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
  46. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  47. package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
  48. package/dist/chunk-NECFR5MM.js.map +1 -0
  49. package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
  50. package/dist/chunk-SFZUDBL5.js.map +1 -0
  51. package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
  52. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  53. package/dist/components.d.ts +6 -6
  54. package/dist/components.js +15 -12
  55. package/dist/components.js.map +1 -1
  56. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  57. package/dist/hooks.d.ts +59 -126
  58. package/dist/hooks.js +19 -28
  59. package/dist/hooks.js.map +1 -1
  60. package/dist/index.d.ts +63 -16
  61. package/dist/index.js +23 -24
  62. package/dist/index.js.map +1 -1
  63. package/dist/providers.d.ts +21 -3
  64. package/dist/providers.js +2 -2
  65. package/dist/rbac/index.d.ts +146 -115
  66. package/dist/rbac/index.js +8 -11
  67. package/dist/theming/runtime.d.ts +1 -13
  68. package/dist/theming/runtime.js +1 -1
  69. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  70. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  71. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
  74. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  75. package/dist/utils.d.ts +4 -5
  76. package/dist/utils.js +15 -15
  77. package/dist/utils.js.map +1 -1
  78. package/docs/api/README.md +7 -1
  79. package/docs/api/classes/ColumnFactory.md +8 -8
  80. package/docs/api/classes/InvalidScopeError.md +4 -4
  81. package/docs/api/classes/Logger.md +1 -1
  82. package/docs/api/classes/MissingUserContextError.md +4 -4
  83. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  84. package/docs/api/classes/PermissionDeniedError.md +4 -4
  85. package/docs/api/classes/RBACAuditManager.md +1 -1
  86. package/docs/api/classes/RBACCache.md +1 -1
  87. package/docs/api/classes/RBACEngine.md +1 -1
  88. package/docs/api/classes/RBACError.md +4 -4
  89. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  90. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  91. package/docs/api/classes/StorageUtils.md +1 -1
  92. package/docs/api/enums/FileCategory.md +1 -1
  93. package/docs/api/enums/LogLevel.md +1 -1
  94. package/docs/api/enums/RBACErrorCode.md +1 -1
  95. package/docs/api/enums/RPCFunction.md +1 -1
  96. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  97. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  98. package/docs/api/interfaces/AggregateConfig.md +4 -4
  99. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  100. package/docs/api/interfaces/AvatarProps.md +1 -1
  101. package/docs/api/interfaces/BadgeProps.md +9 -2
  102. package/docs/api/interfaces/ButtonProps.md +7 -4
  103. package/docs/api/interfaces/CalendarProps.md +8 -5
  104. package/docs/api/interfaces/CardProps.md +8 -5
  105. package/docs/api/interfaces/ColorPalette.md +1 -1
  106. package/docs/api/interfaces/ColorShade.md +1 -1
  107. package/docs/api/interfaces/ComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  109. package/docs/api/interfaces/DataRecord.md +1 -1
  110. package/docs/api/interfaces/DataTableAction.md +24 -21
  111. package/docs/api/interfaces/DataTableColumn.md +31 -31
  112. package/docs/api/interfaces/DataTableProps.md +1 -1
  113. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  114. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  115. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  116. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  117. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  118. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  119. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  120. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  121. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  122. package/docs/api/interfaces/ExportColumn.md +1 -1
  123. package/docs/api/interfaces/ExportOptions.md +8 -8
  124. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  125. package/docs/api/interfaces/FileMetadata.md +1 -1
  126. package/docs/api/interfaces/FileReference.md +1 -1
  127. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  128. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  129. package/docs/api/interfaces/FileUploadProps.md +26 -23
  130. package/docs/api/interfaces/FooterProps.md +10 -8
  131. package/docs/api/interfaces/FormFieldProps.md +10 -10
  132. package/docs/api/interfaces/FormProps.md +1 -1
  133. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  134. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  135. package/docs/api/interfaces/InputProps.md +7 -4
  136. package/docs/api/interfaces/LabelProps.md +1 -1
  137. package/docs/api/interfaces/LoggerConfig.md +1 -1
  138. package/docs/api/interfaces/LoginFormProps.md +14 -11
  139. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  140. package/docs/api/interfaces/NavigationContextType.md +1 -1
  141. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  142. package/docs/api/interfaces/NavigationItem.md +11 -11
  143. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  144. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  145. package/docs/api/interfaces/Organisation.md +1 -1
  146. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  147. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  148. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  149. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  150. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  151. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  152. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  153. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  154. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  155. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  156. package/docs/api/interfaces/PaletteData.md +1 -1
  157. package/docs/api/interfaces/ParsedAddress.md +1 -1
  158. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  159. package/docs/api/interfaces/ProgressProps.md +1 -1
  160. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  161. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  162. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  163. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  164. package/docs/api/interfaces/QuickFix.md +1 -1
  165. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  166. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  167. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  168. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  169. package/docs/api/interfaces/RBACConfig.md +1 -1
  170. package/docs/api/interfaces/RBACContext.md +1 -1
  171. package/docs/api/interfaces/RBACLogger.md +1 -1
  172. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  173. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  174. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  175. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  176. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  177. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  178. package/docs/api/interfaces/RBACResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  181. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  182. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  183. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  184. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  185. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  186. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  187. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  188. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  189. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  190. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  191. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  192. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  193. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  194. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  195. package/docs/api/interfaces/RouteConfig.md +1 -1
  196. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  197. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  198. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  199. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  200. package/docs/api/interfaces/SetupIssue.md +1 -1
  201. package/docs/api/interfaces/StorageConfig.md +1 -1
  202. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  203. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  204. package/docs/api/interfaces/StorageListOptions.md +1 -1
  205. package/docs/api/interfaces/StorageListResult.md +1 -1
  206. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  207. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  208. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  209. package/docs/api/interfaces/StyleImport.md +1 -1
  210. package/docs/api/interfaces/SwitchProps.md +1 -1
  211. package/docs/api/interfaces/TabsContentProps.md +1 -1
  212. package/docs/api/interfaces/TabsListProps.md +1 -1
  213. package/docs/api/interfaces/TabsProps.md +1 -1
  214. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  215. package/docs/api/interfaces/TextareaProps.md +1 -1
  216. package/docs/api/interfaces/ToastActionElement.md +4 -1
  217. package/docs/api/interfaces/ToastProps.md +1 -1
  218. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  219. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  220. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  221. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  222. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  223. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  224. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  225. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  226. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  227. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  228. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  229. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  230. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  231. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  232. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  233. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  234. package/docs/api/interfaces/UserEventAccess.md +14 -11
  235. package/docs/api/interfaces/UserMenuProps.md +8 -6
  236. package/docs/api/interfaces/UserProfile.md +1 -1
  237. package/docs/api/modules.md +575 -634
  238. package/docs/architecture/database-schema-requirements.md +161 -0
  239. package/docs/core-concepts/rbac-system.md +3 -3
  240. package/docs/documentation-index.md +2 -4
  241. package/docs/getting-started/cursor-rules.md +263 -0
  242. package/docs/getting-started/installation-guide.md +6 -1
  243. package/docs/getting-started/quick-start.md +6 -1
  244. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  245. package/docs/migration/MIGRATION_GUIDE.md +6 -28
  246. package/docs/migration/README.md +52 -6
  247. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  248. package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
  249. package/docs/migration/database-changes-december-2025.md +3 -3
  250. package/docs/rbac/event-based-apps.md +1 -1
  251. package/docs/rbac/getting-started.md +1 -1
  252. package/docs/rbac/quick-start.md +1 -1
  253. package/docs/standards/README.md +40 -0
  254. package/docs/troubleshooting/migration.md +4 -4
  255. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  256. package/package.json +12 -6
  257. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  258. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  259. package/scripts/audit/core/checks/bundle.cjs +142 -0
  260. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
  261. package/scripts/audit/core/checks/config.cjs +54 -0
  262. package/scripts/audit/core/checks/coverage.cjs +84 -0
  263. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  264. package/scripts/audit/core/checks/documentation.cjs +203 -0
  265. package/scripts/audit/core/checks/environment.cjs +128 -0
  266. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  267. package/scripts/audit/core/checks/forms.cjs +172 -0
  268. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  269. package/scripts/audit/core/checks/hooks.cjs +334 -0
  270. package/scripts/audit/core/checks/imports.cjs +244 -0
  271. package/scripts/audit/core/checks/performance.cjs +325 -0
  272. package/scripts/audit/core/checks/routes.cjs +117 -0
  273. package/scripts/audit/core/checks/state.cjs +130 -0
  274. package/scripts/audit/core/checks/structure.cjs +65 -0
  275. package/scripts/audit/core/checks/style.cjs +584 -0
  276. package/scripts/audit/core/checks/testing.cjs +122 -0
  277. package/scripts/audit/core/checks/typescript.cjs +61 -0
  278. package/scripts/audit/core/scanner.cjs +199 -0
  279. package/scripts/audit/core/utils.cjs +137 -0
  280. package/scripts/audit/index.cjs +223 -0
  281. package/scripts/audit/reporters/console.cjs +151 -0
  282. package/scripts/audit/reporters/json.cjs +54 -0
  283. package/scripts/audit/reporters/markdown.cjs +124 -0
  284. package/scripts/audit-consuming-app.cjs +86 -0
  285. package/scripts/build-docs/build-decision.js +240 -0
  286. package/scripts/build-docs/cache-utils.js +105 -0
  287. package/scripts/build-docs/content-normalization.js +150 -0
  288. package/scripts/build-docs/file-utils.js +105 -0
  289. package/scripts/build-docs/git-utils.js +86 -0
  290. package/scripts/build-docs/hash-utils.js +116 -0
  291. package/scripts/build-docs/typedoc-runner.js +220 -0
  292. package/scripts/build-docs-incremental.js +77 -913
  293. package/scripts/install-cursor-rules.cjs +236 -0
  294. package/scripts/utils/command-runner.js +16 -11
  295. package/scripts/validate-formats.js +61 -56
  296. package/scripts/validate-master.js +74 -69
  297. package/scripts/validate-pre-publish.js +70 -65
  298. package/src/__tests__/helpers/test-providers.tsx +1 -1
  299. package/src/__tests__/helpers/test-utils.tsx +1 -1
  300. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  301. package/src/components/Alert/Alert.test.tsx +12 -18
  302. package/src/components/Alert/Alert.tsx +5 -7
  303. package/src/components/Avatar/Avatar.test.tsx +4 -4
  304. package/src/components/Badge/Badge.tsx +16 -4
  305. package/src/components/Button/Button.tsx +27 -4
  306. package/src/components/Calendar/Calendar.tsx +9 -3
  307. package/src/components/Card/Card.tsx +4 -0
  308. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  309. package/src/components/Checkbox/Checkbox.tsx +2 -2
  310. package/src/components/DataTable/DataTable.test.tsx +57 -93
  311. package/src/components/DataTable/DataTable.tsx +40 -6
  312. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  313. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
  314. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  315. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  316. package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
  317. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  318. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  319. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  320. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  321. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  322. package/src/components/DataTable/components/DataTableCore.tsx +200 -561
  323. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  324. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  325. package/src/components/DataTable/components/DataTableModals.tsx +9 -1
  326. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  327. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  328. package/src/components/DataTable/components/EditFields.tsx +307 -0
  329. package/src/components/DataTable/components/EditableRow.tsx +9 -1
  330. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  331. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  332. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  333. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  334. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  335. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  336. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  337. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  338. package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
  339. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  340. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  341. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  342. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  343. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  344. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  345. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  346. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  347. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  348. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  349. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  350. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  351. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  352. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  353. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  354. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  355. package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
  356. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  357. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  358. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  359. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  360. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  361. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  362. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  363. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  364. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  365. package/src/components/DataTable/styles.ts +6 -6
  366. package/src/components/DataTable/types.ts +6 -10
  367. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  368. package/src/components/DataTable/utils/debugTools.ts +18 -113
  369. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  370. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  371. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  372. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  373. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  374. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  375. package/src/components/Dialog/Dialog.tsx +8 -7
  376. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  377. package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
  378. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  379. package/src/components/ErrorBoundary/index.ts +27 -2
  380. package/src/components/EventSelector/EventSelector.tsx +4 -1
  381. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  382. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  383. package/src/components/FileUpload/FileUpload.tsx +22 -2
  384. package/src/components/Footer/Footer.test.tsx +16 -16
  385. package/src/components/Footer/Footer.tsx +15 -12
  386. package/src/components/Form/Form.test.tsx +36 -15
  387. package/src/components/Form/Form.tsx +31 -26
  388. package/src/components/Header/Header.tsx +22 -11
  389. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  390. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  391. package/src/components/Input/Input.test.tsx +2 -2
  392. package/src/components/Input/Input.tsx +36 -34
  393. package/src/components/Label/Label.tsx +1 -1
  394. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  395. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  396. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  397. package/src/components/LoginForm/LoginForm.tsx +12 -8
  398. package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
  399. package/src/components/NavigationMenu/types.ts +56 -0
  400. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  401. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  402. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  403. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
  404. package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
  405. package/src/components/PaceAppLayout/README.md +1 -1
  406. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  407. package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
  408. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  409. package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
  410. package/src/components/Progress/Progress.tsx +1 -1
  411. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  412. package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
  413. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  414. package/src/components/Select/Select.tsx +95 -438
  415. package/src/components/Select/context.ts +23 -0
  416. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  417. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  418. package/src/components/Select/hooks/useSelectState.ts +104 -0
  419. package/src/components/Select/index.ts +9 -1
  420. package/src/components/Select/types.ts +123 -0
  421. package/src/components/Select/utils/text.ts +26 -0
  422. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
  423. package/src/components/Switch/Switch.tsx +4 -4
  424. package/src/components/Table/Table.tsx +1 -1
  425. package/src/components/Tabs/Tabs.tsx +1 -1
  426. package/src/components/Textarea/Textarea.tsx +27 -29
  427. package/src/components/Toast/Toast.tsx +5 -1
  428. package/src/components/Tooltip/Tooltip.tsx +3 -3
  429. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  430. package/src/components/UserMenu/UserMenu.tsx +22 -19
  431. package/src/components/index.ts +2 -2
  432. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  433. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  434. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  435. package/src/hooks/index.ts +1 -2
  436. package/src/hooks/public/usePublicEvent.ts +5 -1
  437. package/src/hooks/public/usePublicEventLogo.ts +5 -1
  438. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  439. package/src/hooks/public/usePublicRouteParams.ts +5 -1
  440. package/src/hooks/services/useAuth.ts +32 -0
  441. package/src/hooks/services/useCurrentEvent.ts +6 -0
  442. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  443. package/src/hooks/useDataTableState.ts +8 -18
  444. package/src/hooks/useDebounce.ts +9 -0
  445. package/src/hooks/useEventTheme.ts +6 -0
  446. package/src/hooks/useFileDisplay.ts +4 -0
  447. package/src/hooks/useFileReference.ts +25 -7
  448. package/src/hooks/useFileUrl.ts +11 -1
  449. package/src/hooks/useFocusManagement.ts +16 -2
  450. package/src/hooks/useFocusTrap.ts +7 -4
  451. package/src/hooks/useFormDialog.ts +8 -7
  452. package/src/hooks/useInactivityTracker.ts +4 -1
  453. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  454. package/src/hooks/useOrganisationPermissions.ts +4 -0
  455. package/src/hooks/useOrganisationSecurity.ts +4 -0
  456. package/src/hooks/usePerformanceMonitor.ts +4 -0
  457. package/src/hooks/usePermissionCache.ts +8 -1
  458. package/src/hooks/useQueryCache.ts +12 -1
  459. package/src/hooks/useSessionRestoration.ts +4 -0
  460. package/src/hooks/useStorage.ts +4 -0
  461. package/src/hooks/useToast.ts +3 -3
  462. package/src/index.ts +2 -1
  463. package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
  464. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  465. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  466. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  467. package/src/providers/services/EventServiceProvider.tsx +18 -0
  468. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  469. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  470. package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
  471. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
  472. package/src/rbac/README.md +1 -1
  473. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
  474. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  475. package/src/rbac/adapters.tsx +14 -5
  476. package/src/rbac/api.ts +100 -67
  477. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  478. package/src/rbac/components/NavigationGuard.tsx +1 -1
  479. package/src/rbac/components/NavigationProvider.tsx +5 -2
  480. package/src/rbac/components/PagePermissionGuard.tsx +158 -18
  481. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  482. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  483. package/src/rbac/components/RoleBasedRouter.tsx +6 -2
  484. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  485. package/src/rbac/components/SecureDataProvider.tsx +21 -6
  486. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  487. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  488. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  489. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  490. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  491. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  492. package/src/rbac/engine.ts +38 -14
  493. package/src/rbac/hooks/permissions/index.ts +7 -0
  494. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  495. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  496. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  497. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  498. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  499. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  500. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  501. package/src/rbac/hooks/useCan.test.ts +71 -64
  502. package/src/rbac/hooks/usePermissions.ts +14 -995
  503. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  504. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  505. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  506. package/src/rbac/permissions.ts +0 -30
  507. package/src/rbac/secureClient.ts +212 -61
  508. package/src/rbac/types.ts +8 -0
  509. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  510. package/src/theming/parseEventColours.ts +5 -19
  511. package/src/types/vitest-globals.d.ts +51 -26
  512. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  513. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  514. package/src/utils/__tests__/index.unit.test.ts +2 -2
  515. package/src/utils/audit/audit.ts +0 -3
  516. package/src/utils/core/cn.ts +1 -1
  517. package/src/utils/file-reference/index.ts +53 -1
  518. package/src/utils/formatting/formatting.ts +8 -18
  519. package/src/utils/index.ts +0 -1
  520. package/src/utils/security/secureDataAccess.test.ts +31 -20
  521. package/src/utils/security/secureDataAccess.ts +4 -3
  522. package/dist/chunk-6C4YBBJM.js +0 -628
  523. package/dist/chunk-6C4YBBJM.js.map +0 -1
  524. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  525. package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
  526. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  527. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  528. package/dist/chunk-7FLMSG37.js.map +0 -1
  529. package/dist/chunk-BC4IJKSL.js.map +0 -1
  530. package/dist/chunk-E3SPN4VZ.js +0 -12917
  531. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  532. package/dist/chunk-E66EQZE6 5.js +0 -37
  533. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  534. package/dist/chunk-HWIIPPNI.js.map +0 -1
  535. package/dist/chunk-I7PSE6JW 5.js +0 -191
  536. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  537. package/dist/chunk-I7PSE6JW.js.map +0 -1
  538. package/dist/chunk-IIELH4DL.js.map +0 -1
  539. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  540. package/dist/chunk-KNC55RTG.js.map +0 -1
  541. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  542. package/dist/chunk-LFNCN2SP.js +0 -412
  543. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  544. package/dist/chunk-LFNCN2SP.js.map +0 -1
  545. package/dist/chunk-LMC26NLJ 2.js +0 -84
  546. package/dist/chunk-NOAYCWCX.js +0 -4993
  547. package/dist/chunk-NOAYCWCX.js.map +0 -1
  548. package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
  549. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  550. package/dist/chunk-QXHPKYJV 3.js +0 -113
  551. package/dist/chunk-R77UEZ4E.js +0 -68
  552. package/dist/chunk-R77UEZ4E.js.map +0 -1
  553. package/dist/chunk-SQGMNID3.js.map +0 -1
  554. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  555. package/dist/chunk-XNXXZ43G.js.map +0 -1
  556. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  557. package/dist/components.js 5.map +0 -1
  558. package/dist/styles/index 2.js +0 -12
  559. package/dist/styles/index.js 5.map +0 -1
  560. package/dist/theming/runtime 5.js +0 -19
  561. package/dist/theming/runtime.js 5.map +0 -1
  562. package/docs/api/classes/ErrorBoundary.md +0 -144
  563. package/docs/migration/quick-migration-guide.md +0 -356
  564. package/docs/migration/service-architecture.md +0 -281
  565. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  566. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  567. package/src/hooks/useSecureDataAccess.ts +0 -666
  568. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  569. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  570. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  571. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  572. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  573. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  574. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  575. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  576. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  577. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -1,37 +0,0 @@
1
- import {
2
- createLogger
3
- } from "./chunk-PWLANIRT.js";
4
-
5
- // src/hooks/useComponentPerformance.ts
6
- import { useEffect, useRef } from "react";
7
- var log = createLogger("useComponentPerformance");
8
- function useComponentPerformance({
9
- componentName,
10
- enableLogging = import.meta.env.MODE === "development",
11
- threshold = 16
12
- // 60fps = 16ms per frame
13
- }) {
14
- const renderCount = useRef(0);
15
- const lastRenderTime = useRef(0);
16
- useEffect(() => {
17
- if (!enableLogging) return;
18
- const now = performance.now();
19
- const renderTime = now - lastRenderTime.current;
20
- renderCount.current += 1;
21
- if (renderCount.current > 1 && renderTime > threshold) {
22
- log.warn(
23
- `Performance warning: ${componentName} rendered in ${renderTime.toFixed(2)}ms (render #${renderCount.current}). Consider optimizing.`
24
- );
25
- }
26
- lastRenderTime.current = now;
27
- });
28
- return {
29
- renderCount: renderCount.current,
30
- componentName
31
- };
32
- }
33
-
34
- export {
35
- useComponentPerformance
36
- };
37
- //# sourceMappingURL=chunk-E66EQZE6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/useComponentPerformance.ts"],"sourcesContent":["\nimport { useEffect, useRef } from 'react';\nimport { createLogger } from '../utils/core/logger';\n\nconst log = createLogger('useComponentPerformance');\n\ninterface PerformanceConfig {\n componentName: string;\n enableLogging?: boolean;\n threshold?: number;\n}\n\nexport function useComponentPerformance({ \n componentName, \n enableLogging = import.meta.env.MODE === 'development',\n threshold = 16 // 60fps = 16ms per frame\n}: PerformanceConfig) {\n const renderCount = useRef(0);\n const lastRenderTime = useRef<number>(0);\n\n useEffect(() => {\n if (!enableLogging) return;\n\n const now = performance.now();\n const renderTime = now - lastRenderTime.current;\n renderCount.current += 1;\n\n if (renderCount.current > 1 && renderTime > threshold) {\n log.warn(\n `Performance warning: ${componentName} rendered in ${renderTime.toFixed(2)}ms ` +\n `(render #${renderCount.current}). Consider optimizing.`\n );\n }\n\n lastRenderTime.current = now;\n });\n\n return {\n renderCount: renderCount.current,\n componentName\n };\n}\n"],"mappings":";;;;;AACA,SAAS,WAAW,cAAc;AAGlC,IAAM,MAAM,aAAa,yBAAyB;AAQ3C,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA,gBAAgB,YAAY,IAAI,SAAS;AAAA,EACzC,YAAY;AAAA;AACd,GAAsB;AACpB,QAAM,cAAc,OAAO,CAAC;AAC5B,QAAM,iBAAiB,OAAe,CAAC;AAEvC,YAAU,MAAM;AACd,QAAI,CAAC,cAAe;AAEpB,UAAM,MAAM,YAAY,IAAI;AAC5B,UAAM,aAAa,MAAM,eAAe;AACxC,gBAAY,WAAW;AAEvB,QAAI,YAAY,UAAU,KAAK,aAAa,WAAW;AACrD,UAAI;AAAA,QACF,wBAAwB,aAAa,gBAAgB,WAAW,QAAQ,CAAC,CAAC,eAC9D,YAAY,OAAO;AAAA,MACjC;AAAA,IACF;AAEA,mBAAe,UAAU;AAAA,EAC3B,CAAC;AAED,SAAO;AAAA,IACL,aAAa,YAAY;AAAA,IACzB;AAAA,EACF;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/useZodForm.ts","../src/hooks/useFormDialog.ts","../src/hooks/useOrganisationPermissions.ts","../src/utils/storage/index.ts","../src/hooks/public/usePublicEvent.ts","../src/hooks/public/usePublicEventLogo.ts","../src/hooks/public/usePublicRouteParams.ts"],"sourcesContent":["\n/**\n * Zod Form Hook\n * \n * Enhanced form hook with Zod validation\n */\n\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { z } from 'zod';\n\ninterface UseZodFormProps<T extends z.ZodTypeAny> {\n schema: T;\n defaultValues?: Partial<z.infer<T>>;\n mode?: 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';\n}\n\nexport function useZodForm<T extends z.ZodTypeAny>({\n schema,\n defaultValues,\n mode = 'onSubmit'\n}: UseZodFormProps<T>) {\n return useForm<z.infer<T>>({\n resolver: zodResolver(schema),\n defaultValues: defaultValues as any,\n mode\n });\n}\n","/**\n * @file useFormDialog Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n * \n * Generic React hook for managing form dialog state (open/close, form data, reset behavior).\n * \n * @example\n * ```ts\n * interface ContactFormData {\n * name: string;\n * email: string;\n * }\n * \n * function ContactDialog() {\n * const { isOpen, formData, openDialog, closeDialog, handleOpenChange } = \n * useFormDialog<ContactFormData>();\n * \n * // Use in Dialog component\n * // <Dialog open={isOpen} onOpenChange={handleOpenChange}>...</Dialog>\n * }\n * ```\n */\n\nimport { useState, useCallback } from 'react';\n\n/**\n * Options for configuring the useFormDialog hook behavior\n */\nexport interface UseFormDialogOptions<T = unknown> {\n /**\n * Callback invoked when the dialog open state changes\n * @param open - The new open state\n */\n onOpenChange?: (open: boolean) => void;\n \n /**\n * Whether to reset form data when the dialog closes\n * @default true\n */\n resetOnClose?: boolean;\n}\n\n/**\n * Return type for the useFormDialog hook\n */\nexport interface UseFormDialogReturn<T = unknown> {\n /**\n * Current open state of the dialog\n */\n isOpen: boolean;\n \n /**\n * Current form data (null when dialog is closed or no data provided)\n */\n formData: T | null;\n \n /**\n * Open the dialog with optional form data\n * @param data - Optional form data to populate the dialog\n */\n openDialog: (data?: T | null) => void;\n \n /**\n * Close the dialog\n */\n closeDialog: () => void;\n \n /**\n * Handler for controlled dialog components (e.g., Radix UI Dialog)\n * @param open - The new open state\n */\n handleOpenChange: (open: boolean) => void;\n}\n\n/**\n * Generic React hook for managing form dialog state.\n * \n * Provides state management for form dialogs including:\n * - Open/close state\n * - Form data management\n * - Automatic reset on close (configurable)\n * - Controlled and uncontrolled usage patterns\n * \n * @param options - Configuration options for the hook\n * @returns Object containing dialog state and control functions\n * \n * @example\n * ```ts\n * // Basic usage\n * const { isOpen, openDialog, closeDialog } = useFormDialog();\n * \n * // With form data\n * interface UserData {\n * id: string;\n * name: string;\n * }\n * const { isOpen, formData, openDialog } = useFormDialog<UserData>();\n * \n * // Open with data\n * openDialog({ id: '1', name: 'John' });\n * \n * // Controlled usage with callback\n * const { handleOpenChange, isOpen } = useFormDialog({\n * onOpenChange: (open) => console.log('Dialog state:', open),\n * resetOnClose: false\n * });\n * ```\n */\nexport function useFormDialog<T = unknown>({\n onOpenChange,\n resetOnClose = true,\n}: UseFormDialogOptions<T> = {}): UseFormDialogReturn<T> {\n const [isOpen, setIsOpen] = useState(false);\n const [formData, setFormData] = useState<T | null>(null);\n\n const openDialog = useCallback((data: T | null = null) => {\n setFormData(data);\n setIsOpen(true);\n onOpenChange?.(true);\n }, [onOpenChange]);\n\n const closeDialog = useCallback(() => {\n setIsOpen(false);\n if (resetOnClose) {\n setFormData(null);\n }\n onOpenChange?.(false);\n }, [resetOnClose, onOpenChange]);\n\n const handleOpenChange = useCallback((open: boolean) => {\n setIsOpen(open);\n if (!open && resetOnClose) {\n setFormData(null);\n }\n onOpenChange?.(open);\n }, [resetOnClose, onOpenChange]);\n\n return {\n isOpen,\n formData,\n openDialog,\n closeDialog,\n handleOpenChange,\n };\n}\n","/**\n * @file useOrganisationPermissions Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useOrganisationPermissions\n * @since 0.4.0\n *\n * Hook for managing organisation-specific permissions and role validation.\n * Provides secure access to user's role and permissions within organisations.\n *\n * @example\n * ```tsx\n * function OrganisationComponent() {\n * const { \n * isOrgAdmin, \n * canManageMembers,\n * userRole,\n * hasOrganisationAccess\n * } = useOrganisationPermissions();\n * \n * return (\n * <div>\n * {isOrgAdmin && <AdminPanel />}\n * {canManageMembers && <MemberManagement />}\n * <p>Your role: {userRole}</p>\n * </div>\n * );\n * }\n * \n * // For specific organisation\n * function MultiOrgComponent() {\n * const permissions = useOrganisationPermissions('org-123');\n * \n * if (!permissions.hasOrganisationAccess) {\n * return <div>No access to this organisation</div>;\n * }\n * \n * return <div>Role in org-123: {permissions.userRole}</div>;\n * }\n * ```\n *\n * @security\n * - Validates user membership in organisation\n * - Provides role-based permission checks\n * - Ensures secure access to organisation data\n * - Real-time permission validation\n */\n\nimport { useMemo } from 'react';\nimport { useOrganisations } from './useOrganisations';\nimport { useOrganisationSecurity } from './useOrganisationSecurity';\nimport type { OrganisationRole, OrganisationPermission } from '../types/organisation';\n\nexport interface UseOrganisationPermissionsReturn {\n /** User's role in the organisation */\n userRole: OrganisationRole | 'no_access';\n \n /** Whether user has organisation admin role */\n isOrgAdmin: boolean;\n \n /** Whether user is a super admin */\n isSuperAdmin: boolean;\n \n /** Whether user can moderate content */\n canModerate: boolean;\n \n /** Whether user can manage members */\n canManageMembers: boolean;\n \n /** Whether user can manage organisation settings */\n canManageSettings: boolean;\n \n /** Whether user can manage events */\n canManageEvents: boolean;\n \n /** Whether user has any admin privileges */\n hasAdminPrivileges: boolean;\n \n /** Whether user has access to the organisation */\n hasOrganisationAccess: boolean;\n \n /** Check if user has specific permission */\n hasPermission: (permission: OrganisationPermission) => boolean;\n \n /** Get all permissions for the user */\n getAllPermissions: () => OrganisationPermission[];\n \n /** Organisation ID being checked */\n organisationId: string;\n}\n\n/**\n * Hook to access organisation-specific permissions and roles\n * \n * @param orgId - Optional organisation ID. Defaults to currently selected organisation\n * @returns Organisation permissions and role information\n */\nexport function useOrganisationPermissions(orgId?: string): UseOrganisationPermissionsReturn {\n const { \n selectedOrganisation, \n getUserRole, \n validateOrganisationAccess,\n ensureOrganisationContext\n } = useOrganisations();\n \n // Get super admin context if available (may not be available in all contexts)\n let superAdminContext: { isSuperAdmin: boolean } = { isSuperAdmin: false };\n try {\n superAdminContext = useOrganisationSecurity().superAdminContext;\n } catch {\n // Not available in this context, default to false\n }\n\n const organisationId = useMemo(() => {\n if (orgId) {\n return orgId;\n }\n try {\n const currentOrg = ensureOrganisationContext();\n return currentOrg.id;\n } catch {\n return '';\n }\n }, [orgId, ensureOrganisationContext]);\n\n const userRole = useMemo(() => {\n if (!organisationId) return 'no_access';\n const role = getUserRole(organisationId);\n // Map to valid OrganisationRole or 'no_access'\n if (role === 'org_admin' || role === 'leader' || role === 'member' || role === 'supporter') {\n return role as OrganisationRole;\n }\n return 'no_access';\n }, [organisationId, getUserRole]);\n\n const hasOrganisationAccess = useMemo(() => {\n if (!organisationId) return false;\n return validateOrganisationAccess(organisationId);\n }, [organisationId, validateOrganisationAccess]);\n\n const permissions = useMemo(() => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return {\n isOrgAdmin: false,\n isSuperAdmin: false,\n canModerate: false,\n canManageMembers: false,\n canManageSettings: false,\n canManageEvents: false,\n hasAdminPrivileges: false\n };\n }\n\n const isOrgAdmin = userRole === 'org_admin';\n const isLeader = userRole === 'leader';\n const isMember = userRole === 'member';\n const isSupporter = userRole === 'supporter';\n\n // Super admin status - database backed (user_metadata can be spoofed)\n // Get super admin status from the security hook\n const isSuperAdmin = superAdminContext.isSuperAdmin;\n\n return {\n isOrgAdmin,\n isSuperAdmin,\n canModerate: isSuperAdmin || isOrgAdmin || isLeader,\n canManageMembers: isSuperAdmin || isOrgAdmin || isLeader, // Leaders can manage members\n canManageSettings: isSuperAdmin || isOrgAdmin,\n canManageEvents: isSuperAdmin || isOrgAdmin || isLeader,\n hasAdminPrivileges: isSuperAdmin || isOrgAdmin || isLeader // Leaders have admin privileges\n };\n }, [hasOrganisationAccess, userRole]);\n\n const hasPermission = useMemo(() => {\n return (permission: OrganisationPermission): boolean => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return false;\n }\n\n // Super admin has all permissions (org_admin acts as super admin within org)\n if (userRole === 'org_admin' || permission === '*') {\n return true;\n }\n\n // Map permissions to roles using the defined permissions from organisation types\n const rolePermissions: Record<OrganisationRole, OrganisationPermission[]> = {\n supporter: ['view_basic'],\n member: ['view_basic', 'view_details'],\n leader: ['view_basic', 'view_details', 'moderate_content', 'manage_events'],\n org_admin: ['view_basic', 'view_details', 'moderate_content', 'manage_events', 'manage_members', 'manage_settings']\n };\n\n const userPermissions = rolePermissions[userRole as OrganisationRole] || [];\n return userPermissions.includes(permission) || userPermissions.includes('*');\n };\n }, [hasOrganisationAccess, userRole]);\n\n const getAllPermissions = useMemo(() => {\n return (): OrganisationPermission[] => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return [];\n }\n\n const rolePermissions: Record<OrganisationRole, OrganisationPermission[]> = {\n supporter: ['view_basic'],\n member: ['view_basic', 'view_details'],\n leader: ['view_basic', 'view_details', 'moderate_content', 'manage_events'],\n org_admin: ['view_basic', 'view_details', 'moderate_content', 'manage_events', 'manage_members', 'manage_settings']\n };\n\n return rolePermissions[userRole as OrganisationRole] || [];\n };\n }, [hasOrganisationAccess, userRole]);\n\n return useMemo(() => ({\n userRole,\n organisationId,\n hasOrganisationAccess,\n hasPermission,\n getAllPermissions,\n ...permissions\n }), [userRole, organisationId, hasOrganisationAccess, hasPermission, getAllPermissions, permissions]);\n} ","/**\n * Storage utilities for pace-core\n * \n * Provides app-segregated file storage with organisation-scoped access\n */\n\nexport * from './types';\nexport * from './config';\nexport * from './helpers';\n\n// Import functions for StorageUtils class\nimport {\n uploadFile,\n getPublicUrl,\n getSignedUrl,\n deleteFile,\n downloadFile,\n listFiles,\n archiveFile,\n generateFilePath,\n generateUniqueFileName,\n extractFileMetadata\n} from './helpers';\n\n// Re-export commonly used functions for convenience\nexport {\n uploadFile,\n getPublicUrl,\n getSignedUrl,\n deleteFile,\n downloadFile,\n listFiles,\n archiveFile,\n generateFilePath,\n generateUniqueFileName,\n extractFileMetadata\n};\n\nexport {\n validateFileSize,\n getFileSizeLimit,\n formatFileSize\n} from './config';\n\n\nexport {\n FILE_SIZE_LIMITS,\n DEFAULT_FILE_SIZE_LIMIT,\n STORAGE_CONFIG,\n APP_PATH_MAPPING\n} from './config';\n\n/**\n * StorageUtils class for convenient access to storage functions\n */\nexport class StorageUtils {\n static generateFilePath = generateFilePath;\n static generateUniqueFileName = generateUniqueFileName;\n static extractFileMetadata = extractFileMetadata;\n static uploadFile = uploadFile;\n static getPublicUrl = getPublicUrl;\n static getSignedUrl = getSignedUrl;\n static deleteFile = deleteFile;\n static downloadFile = downloadFile;\n static listFiles = listFiles;\n static archiveFile = archiveFile;\n}\n","/**\n * @file Public Event Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing public event data without authentication.\n * Provides event information by event_code for public pages.\n *\n * Features:\n * - No authentication required\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Automatic refetch capabilities\n *\n * @example\n * ```tsx\n * import { usePublicEvent } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * const { eventCode } = usePublicRouteParams();\n * const { event, isLoading, error, refetch } = usePublicEvent(eventCode);\n *\n * if (isLoading) return <div>Loading event...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (!event) return <div>Event not found</div>;\n *\n * return (\n * <div>\n * <h1>{event.event_name}</h1>\n * <p>Date: {event.event_date}</p>\n * <p>Venue: {event.event_venue}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible public event display\n * - Supports screen reader friendly loading states\n *\n * @security\n * - Only returns public-safe event data\n * - Validates event_code before querying\n * - No sensitive information exposed\n * - Rate limiting applied at database level\n *\n * @performance\n * - Built-in caching with TTL\n * - Minimal re-renders with stable references\n * - Lazy loading support\n * - Error boundary integration\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - @supabase/supabase-js - Database integration\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Event } from '../../types/event';\nimport type { Database } from '../../types/database';\nimport { assertOrganisationId } from '../../types/core';\nimport { usePublicPageContext } from '../../components/PublicLayout/PublicPageProvider';\nimport { logger } from '../../utils/core/logger';\n\n// Simple in-memory cache for public data\nconst publicDataCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicEventReturn {\n /** The event data, null if not loaded or not found */\n event: Event | null;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicEventOptions {\n /** Cache TTL in milliseconds (default: 5 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n}\n\n/**\n * Hook for accessing public event data by event_code\n * \n * This hook provides access to public event information without requiring\n * authentication. It includes caching, error handling, and loading states.\n * \n * @param eventCode - The event code to look up\n * @param options - Configuration options for caching and behavior\n * @returns Object containing event data, loading state, error, and refetch function\n */\nexport function usePublicEvent(\n eventCode: string,\n options: UsePublicEventOptions = {}\n): UsePublicEventReturn {\n const {\n cacheTtl = 5 * 60 * 1000, // 5 minutes\n enableCache = true\n } = options;\n\n const [event, setEvent] = useState<Event | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Get environment variables from public page context or fallback to direct access\n let environment: { supabaseUrl: string | null; supabaseKey: string | null };\n \n try {\n environment = usePublicPageContext().environment;\n } catch {\n // Fallback to direct environment variable access if not in PublicPageProvider\n environment = {\n supabaseUrl: (import.meta as any).env?.VITE_SUPABASE_URL || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_URL || null,\n supabaseKey: (import.meta as any).env?.VITE_SUPABASE_PUBLISHABLE_KEY || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || null\n };\n }\n \n // Create a simple Supabase client for public access\n const supabase = useMemo(() => {\n if (typeof window === 'undefined') return null;\n \n if (!environment.supabaseUrl || !environment.supabaseKey) {\n logger.warn('usePublicEvent', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are set in your environment.');\n return null;\n }\n\n return createClient<Database>(environment.supabaseUrl, environment.supabaseKey);\n }, [environment.supabaseUrl, environment.supabaseKey]);\n\n // Helper function to try refreshing schema cache\n const refreshSchemaCache = useCallback(async () => {\n try {\n // Try to trigger a schema refresh by querying a system table\n await (supabase as any).from('information_schema.routines').select('routine_name').limit(1);\n } catch (error) {\n // Ignore errors, this is just an attempt to refresh cache\n logger.debug('usePublicEvent', 'Schema cache refresh attempt failed:', error);\n }\n }, [supabase]);\n\n const fetchEvent = useCallback(async (): Promise<void> => {\n if (!eventCode || !supabase) {\n setError(new Error('Invalid event code or Supabase client not available'));\n setIsLoading(false);\n return;\n }\n\n // Check cache first\n const cacheKey = `public_event_${eventCode}`;\n if (enableCache) {\n const cached = publicDataCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n setEvent(cached.data);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n let eventData: any = null;\n\n try {\n // Try to call the public event RPC function first\n const response = await (supabase as any).rpc('get_public_event_by_code', {\n event_code_param: eventCode\n });\n \n const data = response?.data;\n const rpcError = response?.error;\n\n if (rpcError) {\n // If RPC function doesn't exist or schema cache issue, try refresh first, then fallback\n if (rpcError.message?.includes('Could not find the function') || \n rpcError.message?.includes('does not exist') ||\n rpcError.message?.includes('schema cache')) {\n logger.warn('usePublicEvent', 'RPC function not found or schema cache issue, attempting refresh:', rpcError.message);\n \n // Try to refresh schema cache first\n await refreshSchemaCache();\n \n // Try RPC call one more time after refresh\n try {\n const retryResponse = await (supabase as any).rpc('get_public_event_by_code', {\n event_code_param: eventCode\n });\n \n const retryData = retryResponse?.data;\n const retryError = retryResponse?.error;\n \n if (!retryError && retryData && retryData.length > 0) {\n eventData = retryData[0];\n } else {\n throw new Error('RPC still failing after cache refresh');\n }\n } catch (retryError) {\n logger.warn('usePublicEvent', 'RPC still failing after cache refresh, falling back to direct table access');\n \n // Fallback: Direct table access with public RLS policy\n const tableResponse2 = await (supabase as any)\n .from('core_events')\n .select(`\n event_id,\n event_name,\n event_date,\n event_venue,\n event_participants,\n event_colours,\n organisation_id,\n event_days,\n event_typicalunit,\n event_rounddown,\n event_youthmultiplier,\n event_catering_email,\n event_news,\n event_billing,\n event_email\n `)\n .eq('event_code', eventCode)\n .eq('is_visible', true)\n .not('organisation_id', 'is', null)\n .limit(1)\n .single();\n\n const tableData = tableResponse2?.data;\n const tableError = tableResponse2?.error;\n\n if (tableError) {\n throw new Error(tableError?.message || 'Failed to fetch event from table');\n }\n\n if (!tableData) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n\n // Get event logo from core_file_references\n const logoResponse = await (supabase as any)\n .from('core_file_references')\n .select('file_path')\n .eq('table_name', 'core_events')\n .eq('record_id', tableData.event_id)\n .eq('is_public', true)\n .eq('file_metadata->>category', 'event_logos')\n .limit(1)\n .single();\n \n const logoData = logoResponse?.data;\n\n eventData = {\n ...tableData,\n event_logo: logoData?.file_path || null\n };\n }\n } else {\n // For RPC errors that aren't schema cache issues, throw immediately without fallback\n const errorMessage = rpcError?.message || rpcError?.toString() || 'Failed to fetch event';\n setEvent(null);\n setError(new Error(errorMessage));\n setIsLoading(false);\n return;\n }\n } else {\n if (!data || data.length === 0 || !data[0]) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n eventData = data[0];\n }\n } catch (rpcError) {\n // If RPC call fails for any reason (including schema cache issues), try direct table access\n logger.warn('usePublicEvent', 'RPC call failed, falling back to direct table access:', rpcError);\n \n const tableResponse = await (supabase as any)\n .from('core_events')\n .select(`\n event_id,\n event_name,\n event_date,\n event_venue,\n event_participants,\n event_colours,\n organisation_id,\n event_days,\n event_typicalunit,\n event_rounddown,\n event_youthmultiplier,\n event_catering_email,\n event_news,\n event_billing,\n event_email\n `)\n .eq('event_code', eventCode)\n .eq('is_visible', true)\n .not('organisation_id', 'is', null)\n .limit(1)\n .single();\n\n const tableData = tableResponse?.data;\n const tableError = tableResponse?.error;\n\n if (tableError) {\n throw new Error(tableError?.message || 'Failed to fetch event from table');\n }\n\n if (!tableData) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n\n // Get event logo from core_file_references\n const logoResponse = await (supabase as any)\n .from('core_file_references')\n .select('file_path')\n .eq('table_name', 'core_events')\n .eq('record_id', tableData.event_id)\n .eq('is_public', true)\n .eq('file_metadata->>category', 'event_logos')\n .limit(1)\n .single();\n \n const logoData = logoResponse?.data;\n\n eventData = {\n ...tableData,\n event_logo: logoData?.file_path || null\n };\n }\n \n // Transform to Event type\n const transformedEvent: Event = {\n id: eventData.event_id,\n event_id: eventData.event_id,\n event_name: eventData.event_name,\n event_code: eventCode,\n event_date: eventData.event_date,\n event_venue: eventData.event_venue,\n event_participants: eventData.event_participants,\n event_logo: eventData.event_logo,\n event_colours: eventData.event_colours,\n organisation_id: assertOrganisationId(eventData.organisation_id),\n is_visible: true,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString()\n };\n\n setEvent(transformedEvent);\n\n // Cache the result\n if (enableCache) {\n publicDataCache.set(cacheKey, {\n data: transformedEvent,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n logger.error('usePublicEvent', 'Error fetching event:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setEvent(null);\n } finally {\n setIsLoading(false);\n }\n }, [eventCode, supabase, cacheTtl, enableCache]);\n\n // Fetch event when eventCode changes\n useEffect(() => {\n fetchEvent();\n }, [fetchEvent]);\n\n const refetch = useCallback(async (): Promise<void> => {\n // Clear cache for this event\n if (enableCache) {\n const cacheKey = `public_event_${eventCode}`;\n publicDataCache.delete(cacheKey);\n }\n await fetchEvent();\n }, [fetchEvent, eventCode, enableCache]);\n\n return {\n event,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public event data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicEventCache(): void {\n for (const [key] of publicDataCache) {\n if (key.startsWith('public_event_')) {\n publicDataCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicEventCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicDataCache.keys()).filter(key => key.startsWith('public_event_'));\n return {\n size: keys.length,\n keys\n };\n}\n","/**\n * @file Public Event Logo Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing public event logo URLs without authentication.\n * Provides logo URLs with fallback handling for public pages.\n *\n * Features:\n * - No authentication required\n * - Automatic fallback to event initials\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Image validation\n *\n * @example\n * ```tsx\n * import { usePublicEventLogo } from '@jmruthers/pace-core';\n *\n * function EventHeader() {\n * const { logoUrl, fallbackText, isLoading, error } = usePublicEventLogo(\n * eventId, \n * eventName, \n * organisationId\n * );\n *\n * if (isLoading) return <div>Loading logo...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return (\n * <div>\n * {logoUrl ? (\n * <img src={logoUrl} alt={`${eventName} logo`} />\n * ) : (\n * <div className=\"logo-fallback\">{fallbackText}</div>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible logo display with proper alt text\n * - Supports screen reader friendly fallbacks\n *\n * @security\n * - Only returns public-safe logo URLs\n * - Validates image existence before returning URL\n * - No sensitive information exposed\n * - Rate limiting applied at storage level\n *\n * @performance\n * - Built-in caching with TTL\n * - Image validation and optimization\n * - Minimal re-renders with stable references\n * - Lazy loading support\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - @supabase/supabase-js - Storage integration\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { createLogger } from '../../utils/core/logger';\n\nconst log = createLogger('usePublicEventLogo');\n\n// Simple in-memory cache for public data\nconst publicDataCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicEventLogoReturn {\n /** The logo URL if available, null if not found or error */\n logoUrl: string | null;\n /** Fallback text (event initials) if no logo is available */\n fallbackText: string;\n /** Whether the logo is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the logo */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicEventLogoOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Whether to validate image existence (default: true) */\n validateImage?: boolean;\n /** Custom fallback text generator */\n generateFallbackText?: (eventName: string) => string;\n /** Supabase client instance (required) */\n supabase: SupabaseClient<Database>;\n}\n\n/**\n * Generate fallback text from event name (first letter of each word)\n */\nfunction defaultGenerateFallbackText(eventName: string): string {\n if (!eventName) return 'EV';\n \n return eventName\n .split(' ')\n .map(word => word.charAt(0).toUpperCase())\n .join('')\n .substring(0, 3); // Max 3 characters\n}\n\n/**\n * Hook for accessing public event logo URLs\n * \n * This hook provides access to event logo URLs without requiring\n * authentication. It includes fallback handling and image validation.\n * \n * @param eventId - The event ID to fetch logo for\n * @param eventName - The event name for fallback text generation\n * @param organisationId - The organisation ID for storage path\n * @param options - Configuration options for caching and behavior\n * @returns Object containing logo URL, fallback text, loading state, error, and refetch function\n */\nexport function usePublicEventLogo(\n eventId: string | undefined,\n eventName: string | undefined,\n organisationId: string | undefined,\n options: UsePublicEventLogoOptions\n): UsePublicEventLogoReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n validateImage = true,\n generateFallbackText = defaultGenerateFallbackText,\n supabase\n } = options;\n\n const [logoUrl, setLogoUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Generate fallback text\n const fallbackText = useMemo(() => {\n return eventName ? generateFallbackText(eventName) : 'EV';\n }, [eventName, generateFallbackText]);\n\n const fetchLogo = useCallback(async (): Promise<void> => {\n if (!eventId || !organisationId || !supabase) {\n setLogoUrl(null);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisationId)) {\n log.warn('Invalid organisationId format (not a valid UUID):', organisationId);\n // Don't return early - let the database handle the validation\n // This allows for more graceful error handling\n }\n\n // Check cache first\n const cacheKey = `public_logo_${eventId}_${organisationId}`;\n if (enableCache) {\n const cached = publicDataCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n setLogoUrl(cached.data);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n // Call the public logo RPC function\n const { data, error: rpcError } = await (supabase as any).rpc('get_public_event_logo', {\n event_id_param: eventId,\n organisation_id_param: organisationId\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to fetch logo');\n }\n\n if (!data || data.length === 0 || !data[0] || !data[0].logo_url) {\n setLogoUrl(null);\n return;\n }\n\n const logoUrl = data[0].logo_url;\n\n // Validate image existence if requested\n if (validateImage) {\n try {\n const response = await fetch(logoUrl, { method: 'HEAD' });\n if (!response.ok) {\n log.warn('Logo URL not accessible:', logoUrl);\n setLogoUrl(null);\n return;\n }\n } catch (fetchError) {\n log.warn('Error validating logo URL:', fetchError);\n setLogoUrl(null);\n return;\n }\n }\n\n setLogoUrl(logoUrl);\n\n // Cache the result\n if (enableCache) {\n publicDataCache.set(cacheKey, {\n data: logoUrl,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n log.error('Error fetching logo:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setLogoUrl(null);\n } finally {\n setIsLoading(false);\n }\n }, [eventId, organisationId, supabase, cacheTtl, enableCache, validateImage]);\n\n // Fetch logo when parameters change\n useEffect(() => {\n if (eventId && organisationId) {\n fetchLogo();\n } else {\n setLogoUrl(null);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchLogo, eventId, organisationId]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!eventId || !organisationId) return;\n \n // Clear cache for this logo\n if (enableCache) {\n const cacheKey = `public_logo_${eventId}_${organisationId}`;\n publicDataCache.delete(cacheKey);\n }\n await fetchLogo();\n }, [fetchLogo, eventId, organisationId, enableCache]);\n\n return {\n logoUrl,\n fallbackText,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public logo data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicLogoCache(): void {\n for (const [key] of publicDataCache) {\n if (key.startsWith('public_logo_')) {\n publicDataCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicLogoCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicDataCache.keys()).filter(key => key.startsWith('public_logo_'));\n return {\n size: keys.length,\n keys\n };\n}\n","/**\n * @file Public Route Params Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for extracting and validating public route parameters.\n * Provides event code extraction and validation for public pages.\n *\n * Features:\n * - URL parameter extraction\n * - Event code validation\n * - TypeScript support\n * - Error handling\n * - Route pattern support\n *\n * @example\n * ```tsx\n * import { usePublicRouteParams } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * const { eventCode, eventId, event, error, isLoading } = usePublicRouteParams();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (!event) return <div>Event not found</div>;\n *\n * return (\n * <div>\n * <h1>{event.event_name}</h1>\n * <p>Event Code: {eventCode}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible route parameter handling\n * - Supports screen reader friendly error states\n *\n * @security\n * - Validates event codes before processing\n * - Sanitizes URL parameters\n * - No sensitive information exposed\n * - Rate limiting applied at route level\n *\n * @performance\n * - Minimal re-renders with stable references\n * - Efficient parameter extraction\n * - Caching integration\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - React Router - URL parameter extraction\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useParams, useLocation } from 'react-router-dom';\nimport type { Event } from '../../types/event';\nimport { usePublicEvent } from './usePublicEvent';\n\nexport interface UsePublicRouteParamsReturn {\n /** The event code from the URL */\n eventCode: string | null;\n /** The event ID (resolved from event code) */\n eventId: string | null;\n /** The full event object */\n event: Event | null;\n /** Whether the route parameters are being processed */\n isLoading: boolean;\n /** Any error that occurred during processing */\n error: Error | null;\n /** Function to manually refetch the event data */\n refetch: () => Promise<void>;\n}\n\ninterface UsePublicRouteParamsOptions {\n /** Whether to automatically fetch event data (default: true) */\n fetchEventData?: boolean;\n /** Custom event code parameter name (default: 'eventCode') */\n eventCodeParam?: string;\n /** Whether to validate event code format (default: true) */\n validateEventCode?: boolean;\n}\n\n/**\n * Validate event code format\n * Event codes should be alphanumeric with optional hyphens/underscores in the middle\n * Supports 2-50 characters\n */\nfunction validateEventCodeFormat(eventCode: string): boolean {\n if (!eventCode || typeof eventCode !== 'string') return false;\n \n // Length check: 2-50 characters\n if (eventCode.length < 2 || eventCode.length > 50) return false;\n \n // For 2-character codes: both must be alphanumeric\n if (eventCode.length === 2) {\n return /^[a-zA-Z0-9]{2}$/.test(eventCode);\n }\n \n // For 3+ character codes: alphanumeric at start and end, hyphens/underscores allowed in middle\n // Must not start or end with hyphen/underscore\n const eventCodeRegex = /^[a-zA-Z0-9][a-zA-Z0-9_-]{1,48}[a-zA-Z0-9]$/;\n const matchesFormat = eventCodeRegex.test(eventCode);\n \n if (!matchesFormat) return false;\n \n // Additional check: no consecutive hyphens or underscores\n if (eventCode.includes('--') || eventCode.includes('__') || eventCode.includes('-_') || eventCode.includes('_-')) {\n return false;\n }\n \n return true;\n}\n\n/**\n * Hook for extracting and validating public route parameters\n * \n * This hook extracts event codes from URL parameters and optionally\n * fetches the corresponding event data. It provides validation and\n * error handling for public routes.\n * \n * @param options - Configuration options for behavior\n * @returns Object containing route parameters, event data, loading state, error, and refetch function\n */\nexport function usePublicRouteParams(\n options: UsePublicRouteParamsOptions = {}\n): UsePublicRouteParamsReturn {\n const {\n fetchEventData = true,\n eventCodeParam = 'eventCode',\n validateEventCode = true\n } = options;\n\n const params = useParams();\n const location = useLocation();\n \n const [error, setError] = useState<Error | null>(null);\n\n // Extract event code from URL parameters\n const eventCode = useMemo(() => {\n const code = params[eventCodeParam] as string;\n \n if (!code) {\n // Don't set error immediately - let the component handle missing eventCode gracefully\n return null;\n }\n\n // Validate event code format if requested\n if (validateEventCode && !validateEventCodeFormat(code)) {\n setError(new Error(`Invalid event code format: ${code}`));\n return null;\n }\n\n setError(null);\n return code;\n }, [params, eventCodeParam, validateEventCode]);\n\n // Use the public event hook to fetch event data\n const {\n event,\n isLoading: eventLoading,\n error: eventError,\n refetch: refetchEvent\n } = usePublicEvent(eventCode || '', {\n enableCache: true,\n cacheTtl: 5 * 60 * 1000 // 5 minutes\n });\n\n // Determine if we should show loading state\n const isLoading = useMemo(() => {\n if (!fetchEventData) return false;\n return eventLoading;\n }, [fetchEventData, eventLoading]);\n\n // Determine the final error state\n const finalError = useMemo(() => {\n if (error) return error;\n if (eventError) return eventError;\n return null;\n }, [error, eventError]);\n\n // Extract event ID from event data\n const eventId = useMemo(() => {\n if (!event) return null;\n return event.event_id || event.id;\n }, [event]);\n\n // Refetch function\n const refetch = useCallback(async (): Promise<void> => {\n if (!fetchEventData) return;\n await refetchEvent();\n }, [fetchEventData, refetchEvent]);\n\n return {\n eventCode,\n eventId,\n event: fetchEventData ? event : null,\n isLoading,\n error: finalError,\n refetch\n };\n}\n\n/**\n * Hook for extracting just the event code without fetching event data\n * Useful when you only need the event code and will fetch data separately\n */\nexport function usePublicEventCode(\n eventCodeParam: string = 'eventCode'\n): { eventCode: string | null; error: Error | null } {\n const params = useParams();\n \n const eventCode = useMemo(() => {\n const code = params[eventCodeParam] as string;\n \n if (!code) {\n return null;\n }\n\n // Validate event code format\n if (!validateEventCodeFormat(code)) {\n return null;\n }\n\n return code;\n }, [params, eventCodeParam]);\n\n const error = useMemo(() => {\n if (!eventCode) {\n return new Error(`Event code parameter '${eventCodeParam}' not found or invalid`);\n }\n return null;\n }, [eventCode, eventCodeParam]);\n\n return {\n eventCode,\n error\n };\n}\n\n/**\n * Utility function to generate public route paths\n */\nexport function generatePublicRoutePath(\n eventCode: string,\n pageName: string = 'index'\n): string {\n if (!eventCode || !validateEventCodeFormat(eventCode)) {\n throw new Error('Invalid event code for route generation');\n }\n \n return `/public/event/${eventCode}/${pageName}`;\n}\n\n/**\n * Utility function to extract event code from a public route path\n * Supports 2-50 character event codes\n */\nexport function extractEventCodeFromPath(path: string): string | null {\n const match = path.match(/^\\/public\\/event\\/([a-zA-Z0-9_-]{2,50})(?:\\/.*)?$/);\n if (!match) return null;\n \n const eventCode = match[1];\n // Validate the extracted code using the same validation function\n if (!validateEventCodeFormat(eventCode)) {\n return null;\n }\n \n return eventCode;\n}\n"],"mappings":"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","names":["useState","useCallback","useMemo","useState","useMemo","useCallback","error","useState","useEffect","useCallback","useMemo","publicDataCache","useState","useMemo","useCallback","logoUrl","error","useEffect","useState","useCallback","useMemo","useState","useMemo","useCallback"]}
@@ -1,191 +0,0 @@
1
- import {
2
- createLogger
3
- } from "./chunk-PWLANIRT.js";
4
- import {
5
- __require
6
- } from "./chunk-7D4SUZUM.js";
7
-
8
- // src/utils/app/appNameResolver.ts
9
- var log = createLogger("AppNameResolver");
10
- function getAppNameFromPackageJson() {
11
- if (typeof window === "undefined" && typeof __require !== "undefined") {
12
- try {
13
- const fs = __require("fs");
14
- const path = __require("path");
15
- const possiblePaths = [
16
- // Only use process.cwd() if we're in a Node.js environment
17
- ...typeof process !== "undefined" && process.cwd ? [path.join(process.cwd(), "package.json")] : [],
18
- path.join(__dirname, "../../package.json"),
19
- path.join(__dirname, "../../../package.json")
20
- ];
21
- for (const packagePath of possiblePaths) {
22
- try {
23
- if (fs.existsSync(packagePath)) {
24
- const packageJsonContent = fs.readFileSync(packagePath, "utf8");
25
- const packageJson = JSON.parse(packageJsonContent);
26
- if (packageJson.name) {
27
- const name = packageJson.name.split("/").pop() || packageJson.name;
28
- return name;
29
- }
30
- }
31
- } catch (error) {
32
- continue;
33
- }
34
- }
35
- } catch (error) {
36
- log.warn("Could not read app name from package.json:", error);
37
- }
38
- }
39
- return null;
40
- }
41
- function getAppNameFromBuildTime() {
42
- if (typeof window !== "undefined") {
43
- try {
44
- const buildTimeEnv = globalThis.__RBAC_APP_NAME__;
45
- if (buildTimeEnv && buildTimeEnv.trim()) {
46
- return buildTimeEnv.trim();
47
- }
48
- } catch (error) {
49
- }
50
- }
51
- return null;
52
- }
53
- function getAppNameFromGlobal() {
54
- if (typeof window !== "undefined") {
55
- try {
56
- const globalAppName = globalThis.RBAC_APP_NAME;
57
- if (globalAppName && globalAppName.trim()) {
58
- return globalAppName.trim();
59
- }
60
- } catch (error) {
61
- }
62
- }
63
- return null;
64
- }
65
- function getAppNameFromEnvironment() {
66
- const envVars = [
67
- "VITE_APP_NAME",
68
- "REACT_APP_NAME",
69
- "NEXT_PUBLIC_APP_NAME",
70
- "APP_NAME",
71
- "NODE_APP_NAME"
72
- ];
73
- for (const envVar of envVars) {
74
- const value = import.meta.env[envVar];
75
- if (value && value.trim()) {
76
- return value.trim();
77
- }
78
- }
79
- return null;
80
- }
81
- function getCurrentAppName() {
82
- const globalName = getAppNameFromGlobal();
83
- if (globalName) {
84
- return globalName;
85
- }
86
- const buildTimeName = getAppNameFromBuildTime();
87
- if (buildTimeName) {
88
- return buildTimeName;
89
- }
90
- const packageJsonName = getAppNameFromPackageJson();
91
- if (packageJsonName) {
92
- return packageJsonName;
93
- }
94
- const envName = getAppNameFromEnvironment();
95
- if (envName) {
96
- return envName;
97
- }
98
- return null;
99
- }
100
- function setRBACAppName(appName) {
101
- if (typeof window !== "undefined") {
102
- globalThis.RBAC_APP_NAME = appName.trim();
103
- }
104
- }
105
- function getCurrentAppNameWithFallback(fallback = "default-app") {
106
- return getCurrentAppName() || fallback;
107
- }
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
-
180
- export {
181
- getAppNameFromPackageJson,
182
- getAppNameFromBuildTime,
183
- getAppNameFromGlobal,
184
- getAppNameFromEnvironment,
185
- getCurrentAppName,
186
- setRBACAppName,
187
- getCurrentAppNameWithFallback,
188
- PERFORMANCE_BUDGETS,
189
- performanceBudgetMonitor
190
- };
191
- //# sourceMappingURL=chunk-I7PSE6JW.js.map
@@ -1 +0,0 @@
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":[]}
@@ -1 +0,0 @@
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":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/useEvents.ts","../src/rbac/hooks/useResolvedScope.ts","../src/components/ErrorBoundary/ErrorBoundary.tsx","../src/components/PublicLayout/PublicPageProvider.tsx","../src/hooks/useAppConfig.ts","../src/hooks/useOrganisationSecurity.ts"],"sourcesContent":["/**\n * @file Event Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n *\n * Convenience hook for accessing event context.\n * This hook provides a simple interface for working with events.\n * \n * Note: This is a convenience wrapper around the EventService.\n * For better performance, consider using useEventService directly.\n */\n\nimport { useMemo, useRef } from 'react';\nimport { useEventService } from './services/useEventService';\nimport { Event } from '../types/event';\n\nexport interface EventContextType {\n events: Event[];\n selectedEvent: Event | null;\n isLoading: boolean;\n error: Error | null;\n setSelectedEvent: (event: Event | null) => void;\n refreshEvents: () => Promise<void>;\n clearEventSelection: () => Promise<void>;\n}\n\n/**\n * Hook to access event context\n * \n * @returns Event context with events, selected event, and helper methods\n * @throws {Error} If used outside EventServiceProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { events, selectedEvent, setSelectedEvent } = useEvents();\n * \n * return (\n * <div>\n * {events.map(event => (\n * <button key={event.id} onClick={() => setSelectedEvent(event)}>\n * {event.event_name}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useEvents(): EventContextType {\n const eventService = useEventService();\n\n // Get current state - service subscription will trigger re-renders when state changes\n const rawEvents = eventService.getEvents();\n const selectedEvent = eventService.getSelectedEvent();\n const isLoading = eventService.isLoading();\n const error = eventService.getError();\n\n // Use refs to track previous values and only create new array reference when events actually change\n const prevEventsRef = useRef<Event[]>([]);\n const prevEventsIdsRef = useRef<string>('');\n \n // Compare event IDs to detect actual changes (more efficient than deep comparison)\n // Do this outside useMemo to avoid dependency on rawEvents array\n const currentEventsIds = rawEvents.map(e => e.event_id || e.id).join(',');\n const eventsChanged = currentEventsIds !== prevEventsIdsRef.current;\n \n // Only create new array reference when events actually changed\n // Update refs synchronously, then memoize based on the ID string\n if (eventsChanged) {\n prevEventsRef.current = rawEvents;\n prevEventsIdsRef.current = currentEventsIds;\n }\n \n // Return stable array reference - only changes when event IDs change\n const events = useMemo(() => {\n return prevEventsRef.current;\n }, [currentEventsIds]);\n\n // Memoize callbacks to prevent unnecessary re-renders in child components\n const setSelectedEventCallback = useMemo(\n () => (event: Event | null) => eventService.setSelectedEvent(event),\n [eventService]\n );\n const refreshEventsCallback = useMemo(\n () => () => eventService.refreshEvents(),\n [eventService]\n );\n const clearEventSelectionCallback = useMemo(\n () => () => eventService.clearEventSelection(),\n [eventService]\n );\n\n // Memoize the return object - only recreate when actual values change\n return useMemo(() => ({\n events,\n selectedEvent,\n isLoading,\n error,\n setSelectedEvent: setSelectedEventCallback,\n refreshEvents: refreshEventsCallback,\n clearEventSelection: clearEventSelectionCallback,\n }), [events, selectedEvent?.event_id, isLoading, error?.message, setSelectedEventCallback, refreshEventsCallback, clearEventSelectionCallback]);\n}\n\n","/**\n * @file useResolvedScope Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * Shared hook for resolving RBAC scope from various contexts.\n * This hook is used by both DataTable and PagePermissionGuard to ensure\n * consistent scope resolution logic.\n */\n\nimport { useEffect, useState, useRef, useMemo } from 'react';\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport type { Scope } from '../types';\nimport { ContextValidator } from '../utils/contextValidator';\nimport type { AppConfig } from '../utils/contextValidator';\nimport { getCurrentAppName } from '../../utils/app/appNameResolver';\nimport { createLogger } from '../../utils/core/logger';\n\nconst log = createLogger('useResolvedScope');\n\n// Cache app config to avoid repeated database queries\n// App config rarely changes during a session, so we can cache it\nconst appConfigCache = new Map<string, { appId: string; appConfig: AppConfig; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\n// Export function to clear cache (for testing)\nexport function clearAppConfigCache(): void {\n appConfigCache.clear();\n}\n\nexport interface UseResolvedScopeOptions {\n /** Supabase client instance */\n supabase: SupabaseClient<Database> | null;\n /** Selected organisation ID */\n selectedOrganisationId: string | null;\n /** Selected event ID */\n selectedEventId: string | null;\n}\n\nexport interface UseResolvedScopeReturn {\n /** Resolved scope, or null if not yet resolved */\n resolvedScope: Scope | null;\n /** Whether the scope resolution is in progress */\n isLoading: boolean;\n /** Error if scope resolution failed */\n error: Error | null;\n}\n\n/**\n * Resolves RBAC scope from organisation and event context\n * \n * This hook handles the complex logic of determining the correct RBAC scope\n * based on available context (organisation, event, app). It ensures consistent\n * scope resolution across the application.\n * \n * @param options - Hook options\n * @returns Resolved scope and loading state\n * \n * @example\n * ```tsx\n * const { resolvedScope, isLoading } = useResolvedScope({\n * supabase,\n * selectedOrganisationId,\n * selectedEventId\n * });\n * \n * if (isLoading) return <Loading />;\n * if (!resolvedScope) return <Error />;\n * \n * const permission = useCan(userId, resolvedScope, permission);\n * ```\n */\nexport function useResolvedScope({\n supabase,\n selectedOrganisationId,\n selectedEventId\n}: UseResolvedScopeOptions): UseResolvedScopeReturn {\n const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n \n // Use a ref to track the stable scope and only update it when it actually changes\n const stableScopeRef = useRef<{ organisationId: string; appId: string; eventId: string | undefined }>({ \n organisationId: '', \n appId: '', \n eventId: undefined \n });\n \n // Update stable scope ref in useEffect to avoid updates during render\n // For PORTAL, allow scopes without organisationId\n useEffect(() => {\n if (resolvedScope) {\n const newScope = {\n organisationId: resolvedScope.organisationId || '',\n appId: resolvedScope.appId || '',\n eventId: resolvedScope.eventId\n };\n \n // Only update if the scope has actually changed\n if (stableScopeRef.current.organisationId !== newScope.organisationId ||\n stableScopeRef.current.eventId !== newScope.eventId ||\n stableScopeRef.current.appId !== newScope.appId) {\n stableScopeRef.current = {\n organisationId: newScope.organisationId,\n appId: newScope.appId,\n eventId: newScope.eventId\n };\n }\n } else {\n // Reset to empty scope when no resolved scope\n stableScopeRef.current = { organisationId: '', appId: '', eventId: undefined };\n }\n }, [resolvedScope]);\n \n // Get app name to check if it's PORTAL (needed for return logic)\n const appName = getCurrentAppName();\n \n const stableScope = stableScopeRef.current;\n \n useEffect(() => {\n let cancelled = false;\n \n const resolveScope = async () => {\n // OPTIMIZATION: If all inputs are null/undefined, immediately return empty scope\n // This indicates pre-filtered mode where we don't need to resolve scope\n if (!supabase && !selectedOrganisationId && !selectedEventId) {\n if (!cancelled) {\n setResolvedScope(null);\n setIsLoading(false);\n setError(null);\n }\n return;\n }\n \n setIsLoading(true);\n setError(null);\n \n try {\n // Get app name and config\n const appName = getCurrentAppName();\n let appId: string | undefined = undefined;\n let appConfig: AppConfig | null = null;\n \n // Try to resolve app config from database (with caching)\n // Only query if user is authenticated (RLS policies require authentication)\n if (supabase && appName) {\n try {\n // Check if user is authenticated before querying (RLS requires auth)\n // HTTP 406 errors are expected when not authenticated, so we skip the query\n const { data: session } = await supabase.auth.getSession();\n if (!session?.session) {\n // User not authenticated - skip app resolution, will retry after login\n // This is expected on login pages, so don't log as error\n log.debug(`Skipping app resolution for \"${appName}\" - user not authenticated`);\n } else {\n // Check cache first\n const cached = appConfigCache.get(appName);\n const now = Date.now();\n if (cached && (now - cached.timestamp) < CACHE_TTL) {\n appId = cached.appId;\n appConfig = cached.appConfig;\n } else {\n // Cache miss or expired - fetch from database\n const { data: app, error } = await supabase\n .from('rbac_apps')\n .select('id, name, requires_event, is_active')\n .eq('name', appName)\n .eq('is_active', true)\n .single() as { data: { id: string; name: string; requires_event: boolean; is_active: boolean } | null; error: any };\n \n if (error) {\n // HTTP 406 is expected when not authenticated (RLS blocks query)\n // Don't log as error if it's a 406 - this is expected behavior\n if (error.code === '406' || error.code === 'PGRST116' || error.message?.includes('406')) {\n log.debug(`App resolution blocked by RLS for \"${appName}\" - user may not be authenticated`);\n // Don't cache - will retry after authentication\n appId = undefined;\n } else {\n // Check if app exists but is inactive\n const { data: inactiveApp } = await supabase\n .from('rbac_apps')\n .select('id, name, is_active')\n .eq('name', appName)\n .single() as { data: { id: string; name: string; is_active: boolean } | null };\n \n if (inactiveApp) {\n log.error(`App \"${appName}\" exists but is inactive (is_active: ${inactiveApp.is_active})`);\n // Don't cache inactive apps - set appId to undefined\n appId = undefined;\n } else {\n log.error(`App \"${appName}\" not found in rbac_apps table`, { error });\n // Don't cache missing apps - set appId to undefined\n appId = undefined;\n }\n }\n } else if (app) {\n appId = app.id;\n appConfig = { requires_event: app.requires_event ?? false };\n // Only cache successful lookups of active apps\n appConfigCache.set(appName, { appId, appConfig, timestamp: now });\n }\n }\n }\n } catch (error) {\n // Handle network errors or other unexpected errors gracefully\n // Don't log 406 errors as they're expected when not authenticated\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (!errorMessage.includes('406') && !errorMessage.includes('PGRST116')) {\n log.error('Unexpected error resolving app config:', error);\n } else {\n log.debug('App resolution skipped - authentication required');\n }\n }\n }\n\n // Build initial scope from available context\n // For event-required apps: Only use eventId (org will be derived)\n // For org-required apps: Only use organisationId (event is optional)\n const initialScope: Scope = {\n organisationId: appConfig?.requires_event ? undefined : (selectedOrganisationId || undefined),\n eventId: selectedEventId || undefined,\n appId: appId\n };\n\n // Use ContextValidator to resolve required context\n // For PORTAL, always allow scope resolution even if appConfig is null\n const validation = await ContextValidator.resolveRequiredContext(\n initialScope,\n appConfig,\n appName || undefined,\n supabase\n );\n\n if (!validation.isValid) {\n // For PORTAL/ADMIN apps, allow scope without org/event even if validation fails\n if (appName === 'PORTAL' || appName === 'ADMIN') {\n if (!cancelled) {\n // For PORTAL/ADMIN, we need at least an appId. If we don't have it from the query,\n // we'll set it to undefined and let the component handle it (it can use contextAppId)\n const optionalContextScope: Scope = {\n organisationId: undefined,\n eventId: undefined,\n appId: appId || undefined // appId might be undefined if query failed, that's OK\n };\n setResolvedScope(optionalContextScope);\n setError(null);\n setIsLoading(false);\n }\n return;\n }\n \n // For event-required apps: if validation fails but we have an eventId, return scope with eventId\n // The organisation will be derived later during permission checks\n if (appConfig?.requires_event && selectedEventId) {\n if (!cancelled) {\n const eventScope: Scope = {\n organisationId: undefined, // Will be derived from event during permission check\n eventId: selectedEventId,\n appId: appId || undefined\n };\n setResolvedScope(eventScope);\n setError(null); // Don't set error - let permission check handle derivation\n setIsLoading(false);\n }\n return;\n }\n \n if (!cancelled) {\n setResolvedScope(null);\n setError(validation.error || new Error('Context validation failed'));\n setIsLoading(false);\n }\n return;\n }\n\n // Set resolved scope\n if (!cancelled) {\n setResolvedScope(validation.resolvedScope);\n setError(null);\n setIsLoading(false);\n }\n } catch (err) {\n if (!cancelled) {\n setError(err as Error);\n setIsLoading(false);\n }\n }\n };\n\n resolveScope();\n \n return () => {\n cancelled = true;\n };\n }, [selectedOrganisationId, selectedEventId, supabase]);\n \n // Return scope if it has appId (for PORTAL/ADMIN) or organisationId (for other apps)\n // For PORTAL/ADMIN, always return a scope (even if empty) so components can use contextAppId\n const allowsOptionalContexts = appName === 'PORTAL' || appName === 'ADMIN';\n const hasValidScope = allowsOptionalContexts\n ? true // PORTAL/ADMIN always have valid scope (even without org/event/appId)\n : (stableScope.appId || stableScope.organisationId);\n \n // CRITICAL FIX: Memoize finalScope to prevent creating new object references on every render\n // This prevents infinite loops in useCan and other hooks that depend on scope equality\n const finalScope: Scope | null = useMemo(() => {\n if (!hasValidScope) {\n return allowsOptionalContexts ? {} : null;\n }\n \n // Build scope object only with defined values to ensure stable reference\n const scope: Scope = {};\n if (stableScope.organisationId) {\n scope.organisationId = stableScope.organisationId;\n }\n if (stableScope.eventId) {\n scope.eventId = stableScope.eventId;\n }\n if (stableScope.appId) {\n scope.appId = stableScope.appId;\n }\n \n return scope;\n }, [hasValidScope, allowsOptionalContexts, stableScope.organisationId, stableScope.eventId, stableScope.appId]);\n \n return {\n resolvedScope: finalScope,\n isLoading,\n error\n };\n}\n","/**\n * @file Error Boundary Component\n * @package @jmruthers/pace-core\n * @module Components/ErrorBoundary\n * @since 0.1.0\n *\n * A comprehensive error boundary component that catches JavaScript errors in child components\n * and provides fallback UI with retry functionality and error reporting.\n *\n * Features:\n * - Catches JavaScript errors in component tree\n * - Custom fallback UI with retry functionality\n * - Error reporting and logging\n * - Performance monitoring integration\n * - Retry mechanism with configurable attempts\n * - Development mode error details\n * - Accessibility compliant error display\n * - Component-specific error tracking\n * - Error ID generation for tracking\n *\n * @example\n * ```tsx\n * // Basic error boundary\n * <ErrorBoundary>\n * <MyComponent />\n * </ErrorBoundary>\n * \n * // Error boundary with custom fallback\n * <ErrorBoundary\n * componentName=\"UserProfile\"\n * fallback={<div>Something went wrong loading the profile.</div>}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId, error);\n * }}\n * >\n * <UserProfile />\n * </ErrorBoundary>\n * \n * // Error boundary with retry functionality\n * <ErrorBoundary\n * componentName=\"DataTable\"\n * maxRetries={3}\n * enableRetry={true}\n * enableReporting={true}\n * onError={(error, errorInfo, errorId) => {\n * // Send to error reporting service\n * errorReportingService.report({ error, errorInfo, errorId });\n * }}\n * >\n * <DataTable data={data} />\n * </ErrorBoundary>\n * \n * // Wrapping multiple components\n * <ErrorBoundary componentName=\"App\">\n * <Header />\n * <ErrorBoundary componentName=\"MainContent\">\n * <MainContent />\n * </ErrorBoundary>\n * <Footer />\n * </ErrorBoundary>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"alert\" for error announcements\n * - Screen reader friendly error messages\n * - Keyboard accessible retry buttons\n * - High contrast error styling\n * - Clear error identification\n *\n * @performance\n * - Efficient error state management\n * - Performance monitoring integration\n * - Minimal impact on normal rendering\n * - Configurable error reporting\n *\n * @dependencies\n * - React 18+ - Component lifecycle\n * - Performance monitoring utilities\n * - Tailwind CSS - Styling\n */\n\nimport React, { Component, ReactNode } from 'react';\nimport { performanceBudgetMonitor } from '../../utils/performance/performanceBudgets';\nimport { logger } from '../../utils/core/logger';\n\n/**\n * State interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryState {\n /** Whether an error has been caught */\n hasError: boolean;\n /** The error that was caught */\n error?: Error;\n /** Additional error information from React */\n errorInfo?: React.ErrorInfo;\n /** Unique identifier for the error */\n errorId?: string;\n /** Number of retry attempts made */\n retryCount: number;\n}\n\n/**\n * Props interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryProps {\n /** Child components to wrap with error boundary */\n children: ReactNode;\n /** Name of the component for error reporting */\n componentName?: string;\n /** Custom fallback UI to display when error occurs */\n fallback?: ReactNode;\n /** Callback function called when an error is caught */\n onError?: (error: Error, errorInfo: React.ErrorInfo, errorId: string) => void;\n /** Maximum number of retry attempts */\n maxRetries?: number;\n /** Whether to enable retry functionality */\n enableRetry?: boolean;\n /** Whether to enable error reporting */\n enableReporting?: boolean;\n}\n\n/**\n * ErrorBoundary component\n * Catches JavaScript errors in child components and provides fallback UI\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * componentName=\"MyComponent\"\n * maxRetries={3}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId);\n * }}\n * >\n * <MyComponent />\n * </ErrorBoundary>\n * ```\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n private retryTimeoutId: NodeJS.Timeout | null = null;\n\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { \n hasError: false, \n retryCount: 0 \n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n const errorId = `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n return { \n hasError: true, \n error,\n errorId \n };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;\n const errorId = this.state.errorId!;\n \n this.setState({ errorInfo });\n \n // Enhanced logging with component name and error ID\n logger.error('ErrorBoundary', `[${componentName}] Caught error ${errorId}:`, error, errorInfo);\n \n // Performance monitoring - track error occurrence\n performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {\n componentName,\n errorId,\n errorMessage: error.message,\n stack: error.stack?.substring(0, 200), // Truncated stack trace\n });\n\n // Report error if enabled\n if (enableReporting) {\n this.reportError(errorId, componentName);\n }\n \n // Call custom error handler if provided\n if (onError) {\n onError(error, errorInfo, errorId);\n }\n }\n\n private reportError = (errorId: string, componentName: string) => {\n // In production, this would send to error reporting service\n if (import.meta.env.MODE === 'production') {\n // Example: Send to error reporting service\n // errorReportingService.report({ error, errorInfo, errorId, componentName });\n logger.warn('ErrorBoundary', 'Error reporting would be triggered in production:', { errorId, componentName });\n }\n };\n\n private handleRetry = () => {\n const { maxRetries = 3 } = this.props;\n const { retryCount } = this.state;\n\n if (retryCount < maxRetries) {\n logger.debug('ErrorBoundary', `Retrying component render (attempt ${retryCount + 1}/${maxRetries})`);\n \n this.setState(prevState => ({\n hasError: false,\n error: undefined,\n errorInfo: undefined,\n errorId: undefined,\n retryCount: prevState.retryCount + 1\n }));\n }\n };\n\n componentWillUnmount() {\n if (this.retryTimeoutId) {\n clearTimeout(this.retryTimeoutId);\n }\n }\n\n render() {\n if (this.state.hasError) {\n const { \n componentName = 'Component', \n fallback, \n enableRetry = true, \n maxRetries = 3 \n } = this.props;\n const { retryCount, errorId } = this.state;\n\n // Use custom fallback if provided\n if (fallback) {\n return fallback;\n }\n\n // Enhanced error UI with retry functionality\n return (\n <div \n role=\"alert\" \n className=\"p-6 bg-destructive/10 border border-destructive/20 rounded-lg\"\n data-error-boundary={errorId}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0\">\n <svg className=\"w-5 h-5 text-destructive\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z\" clipRule=\"evenodd\" />\n </svg>\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-destructive\">\n Error in {componentName}\n </h3>\n <p className=\"text-destructive/80\">\n {this.state.error?.message || 'An unexpected error occurred.'}\n </p>\n \n {enableRetry && retryCount < maxRetries && (\n <div className=\"flex gap-3 mb-4\">\n <button\n onClick={this.handleRetry}\n className=\"px-4 py-2 bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors text-sm font-medium\"\n >\n Retry ({retryCount + 1}/{maxRetries})\n </button>\n <button\n onClick={() => window.location.reload()}\n className=\"px-4 py-2 bg-sec-600 text-main-50 rounded-md hover:bg-sec-700 transition-colors text-sm font-medium\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {retryCount >= maxRetries && (\n <div className=\"mb-4 p-3 bg-acc-50 border border-acc-200 rounded-md\">\n <p className=\"text-acc-800\">\n Maximum retry attempts reached. Please reload the page or contact support.\n </p>\n <button\n onClick={() => window.location.reload()}\n className=\"mt-2 px-3 py-1 bg-acc-600 text-main-50 rounded text-sm hover:bg-acc-700\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {import.meta.env.MODE === 'development' && this.state.error && (\n <details className=\"text-sm text-destructive/70\">\n <summary className=\"cursor-pointer font-medium mb-2\">\n Error Details (Development)\n </summary>\n <div className=\"bg-destructive/5 p-3 rounded border\">\n <p className=\"font-mono\">Error ID: {errorId}</p>\n <pre className=\"whitespace-pre-wrap text-xs overflow-auto max-h-32\">\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </div>\n </details>\n )}\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n","/**\n * @file Public Page Provider\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * A completely isolated provider for public pages that doesn't trigger\n * any authentication context. This ensures public pages work independently\n * of the main application's authentication system.\n *\n * Features:\n * - No authentication required\n * - No organisation context\n * - No event context\n * - Completely isolated from main app context\n * - Environment variable access for public data\n * - Error boundary integration\n *\n * @example\n * ```tsx\n * import { PublicPageProvider } from '@jmruthers/pace-core';\n * \n * const APP_NAME = 'CORE';\n * \n * function PublicApp() {\n * return (\n * <PublicPageProvider appName={APP_NAME}>\n * <Routes>\n * <Route path=\"/events/:eventCode/recipe-grid-report\" element={<PublicRecipePage />} />\n * </Routes>\n * </PublicPageProvider>\n * );\n * }\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { ErrorBoundary } from '../ErrorBoundary';\nimport { logger } from '../../utils/core/logger';\n\ninterface PublicPageContextType {\n isPublicPage: true;\n supabase: ReturnType<typeof createClient<Database>> | null;\n appName: string | null;\n environment: {\n supabaseUrl: string | null;\n supabaseKey: string | null;\n };\n}\n\nexport const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);\n\nexport interface PublicPageProviderProps {\n children: ReactNode;\n /** Application name for logo display and branding. Should match the APP_NAME constant used in App.tsx */\n appName?: string;\n}\n\n/**\n * Provider for public pages that completely isolates them from authentication context\n * \n * This provider:\n * - Does not initialize any authentication providers\n * - Provides environment variables for public data access\n * - Includes error boundary for graceful error handling\n * - Is completely separate from the main app context\n * - Provides appName for consistent logo display\n */\nexport function PublicPageProvider({ children, appName }: PublicPageProviderProps) {\n // Get environment variables for public data access\n // Handle both Vite (import.meta.env) and Node.js (process.env) environments\n const getEnvVar = (key: string): string | undefined => {\n // Check Vite environment first (browser)\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const env = import.meta.env as Record<string, string | undefined>;\n return env[key];\n }\n // Check Node.js environment (server-side)\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 getEnvVar('VITE_SUPABASE_ANON_KEY') ||\n getEnvVar('NEXT_PUBLIC_SUPABASE_ANON_KEY') || \n null;\n\n // Create Supabase client if environment variables are available\n // Note: VITE_SUPABASE_PUBLISHABLE_KEY should be your publishable key (sb_publishable_...)\n const supabase = useMemo(() => {\n if (!supabaseUrl || !supabaseKey) {\n logger.warn('PublicPageProvider', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are set in your environment.');\n return null;\n }\n const client = createClient<Database>(supabaseUrl, supabaseKey);\n return client;\n }, [supabaseUrl, supabaseKey]);\n\n const contextValue: PublicPageContextType = {\n isPublicPage: true,\n supabase,\n appName: appName || null,\n environment: {\n supabaseUrl,\n supabaseKey\n }\n };\n\n return (\n <PublicPageContext.Provider value={contextValue}>\n <ErrorBoundary componentName=\"PublicPageProvider\">\n {children}\n </ErrorBoundary>\n </PublicPageContext.Provider>\n );\n}\n\n/**\n * Hook to access public page context\n * \n * @returns Public page context with environment variables\n */\nexport function usePublicPageContext(): PublicPageContextType {\n const context = useContext(PublicPageContext);\n \n if (!context) {\n throw new Error('usePublicPageContext must be used within a PublicPageProvider');\n }\n \n return context;\n}\n\n/**\n * Hook to check if we're in a public page context\n * \n * @returns True if we're in a public page context\n */\nexport function useIsPublicPage(): boolean {\n const context = useContext(PublicPageContext);\n return context !== undefined;\n}\n","/**\n * @file useAppConfig Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useAppConfig\n * @since 0.4.0\n *\n * Hook for accessing app configuration like direct access support and event requirements.\n * This is a convenience hook that extracts app config from the UnifiedAuthProvider.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { supportsDirectAccess, requiresEvent, isLoading } = useAppConfig();\n * \n * if (isLoading) return <div>Loading...</div>;\n * \n * return (\n * <div>\n * {supportsDirectAccess && (\n * <div>This app supports direct access!</div>\n * )}\n * {requiresEvent && (\n * <EventSelector />\n * )}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useMemo, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';\nimport { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';\n\nexport interface UseAppConfigReturn {\n supportsDirectAccess: boolean;\n requiresEvent: boolean;\n isLoading: boolean;\n appName: string;\n}\n\n/**\n * Hook to access app configuration\n * Works in both authenticated and public contexts\n * @returns App configuration and loading state\n */\nexport function useAppConfig(): UseAppConfigReturn {\n // Check if we're in a public page context first\n const isPublicPage = useIsPublicPage();\n \n // Try to get appName from PublicPageContext (access context directly to avoid hook rule violations)\n const publicPageContext = useContext(PublicPageContext);\n const hasPublicContext = publicPageContext !== undefined;\n const contextAppName = publicPageContext?.appName || null;\n const getNodeEnvVar = (key: string): string | undefined =>\n typeof process !== 'undefined' && process.env ? process.env[key] : undefined;\n \n if (isPublicPage) {\n const getAppName = (): string => {\n // When tests mock public mode without the provider, default to PACE\n if (!hasPublicContext) {\n return 'PACE';\n }\n \n // Priority 1: Use appName from PublicPageContext (passed from App.tsx)\n if (contextAppName) {\n return contextAppName;\n }\n \n // Priority 2: Check environment variables\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n const env = (import.meta as any).env;\n return env.VITE_APP_NAME || \n env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.VITE_APP_NAME || \n import.meta.env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n \n const nodeEnvAppName =\n getNodeEnvVar('VITE_APP_NAME') ||\n getNodeEnvVar('NEXT_PUBLIC_APP_NAME');\n if (nodeEnvAppName) {\n return nodeEnvAppName;\n }\n \n // Priority 3: Default fallback\n return 'PACE';\n };\n \n return useMemo(() => ({\n supportsDirectAccess: false, // Public pages don't support direct access\n requiresEvent: true, // Public pages always require an event\n isLoading: false,\n appName: getAppName()\n }), [contextAppName, hasPublicContext]);\n }\n \n // For authenticated pages, use UnifiedAuthProvider\n try {\n const { appConfig, appName } = useUnifiedAuth();\n return useMemo(() => ({\n supportsDirectAccess: !(appConfig?.requires_event ?? true),\n requiresEvent: appConfig?.requires_event ?? true,\n isLoading: appConfig === null,\n appName\n }), [appConfig?.requires_event, appName]);\n } catch (error) {\n // Fallback if UnifiedAuthProvider is not available\n return useMemo(() => ({\n supportsDirectAccess: false,\n requiresEvent: true,\n isLoading: false,\n appName: contextAppName ||\n getNodeEnvVar('VITE_APP_NAME') ||\n getNodeEnvVar('NEXT_PUBLIC_APP_NAME') ||\n 'PACE'\n }), [contextAppName]);\n }\n} ","/**\n * @file Organisation Security Hook\n * @package @jmruthers/pace-core\n * @module Hooks/OrganisationSecurity\n * @since 0.4.0\n *\n * Security-focused hook for organisation access validation and super admin functionality.\n * Provides utilities for validating user access to organisations and checking permissions.\n */\n\nimport { useCallback, useMemo, useEffect, useState } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\n// Legacy useRBAC hook removed - use new RBAC system instead\nimport type { OrganisationSecurityError, SuperAdminContext } from '../types/organisation';\nimport type { Permission } from '../rbac/types';\nimport { logger } from '../utils/core/logger';\n\nexport interface OrganisationSecurityHook {\n // Super admin context\n superAdminContext: SuperAdminContext;\n \n // Access validation\n validateOrganisationAccess: (orgId: string) => Promise<boolean>;\n hasMinimumRole: (minRole: string, orgId?: string) => boolean;\n canAccessChildOrganisations: (orgId?: string) => boolean;\n \n // Permission checks\n hasPermission: (permission: string, orgId?: string) => Promise<boolean>;\n getUserPermissions: (orgId?: string) => Promise<string[]>;\n \n // Audit logging\n logOrganisationAccess: (action: string, details?: Record<string, unknown>) => Promise<void>;\n \n // Security utilities\n ensureOrganisationAccess: (orgId: string) => Promise<void>;\n validateUserAccess: (userId: string, orgId: string) => Promise<boolean>;\n}\n\nexport const useOrganisationSecurity = (): OrganisationSecurityHook => {\n const { user, session, supabase } = useUnifiedAuth();\n const { selectedOrganisation, getUserRole, validateOrganisationAccess: validateAccess } = useOrganisations();\n \n // Super admin status - query database for security (user_metadata can be spoofed)\n const [isSuperAdmin, setIsSuperAdmin] = useState<boolean>(false);\n const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState(false);\n\n // Check super admin status from database\n useEffect(() => {\n if (!user || !session || !supabase) {\n setIsSuperAdmin(false);\n return;\n }\n\n const checkSuperAdmin = async () => {\n setIsCheckingSuperAdmin(true);\n try {\n const now = new Date().toISOString();\n const { data, error } = await supabase\n .from('rbac_global_roles')\n .select('role')\n .eq('user_id', user.id)\n .eq('role', 'super_admin')\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`)\n .limit(1);\n\n setIsSuperAdmin(!error && data && data.length > 0);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error checking super admin status:', error);\n setIsSuperAdmin(false);\n } finally {\n setIsCheckingSuperAdmin(false);\n }\n };\n\n checkSuperAdmin();\n }, [user, session, supabase]);\n\n // Super admin context\n const superAdminContext = useMemo((): SuperAdminContext => {\n return {\n isSuperAdmin,\n hasGlobalAccess: isSuperAdmin,\n canManageAllOrganisations: isSuperAdmin\n };\n }, [isSuperAdmin]);\n\n // Validate organisation access with database check\n const validateOrganisationAccess = useCallback(async (orgId: string): Promise<boolean> => {\n if (!user || !session || !supabase) return false;\n \n try {\n // Super admin has access to all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Check organisation membership using consolidated rbac_organisation_roles table\n const { data, error } = await supabase\n .from('rbac_organisation_roles')\n .select('id')\n .eq('user_id', user.id)\n .eq('organisation_id', orgId)\n .eq('status', 'active')\n .is('revoked_at', null)\n .in('role', ['org_admin', 'leader', 'member']) // Only actual members, not supporters\n .single();\n\n if (error) {\n logger.error('useOrganisationSecurity', 'Error validating organisation access:', error);\n return false;\n }\n\n return !!data;\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating organisation access:', error);\n return false;\n }\n }, [user, session, supabase, superAdminContext.isSuperAdmin]);\n\n // Check if user has minimum role\n const hasMinimumRole = useCallback((minRole: string, orgId?: string): boolean => {\n // Super admin has all roles\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n const roleHierarchy = ['supporter', 'member', 'leader', 'org_admin'];\n \n const userRoleIndex = roleHierarchy.indexOf(userRole);\n const minRoleIndex = roleHierarchy.indexOf(minRole);\n \n return userRoleIndex >= minRoleIndex;\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check if user can access child organisations\n const canAccessChildOrganisations = useCallback((orgId?: string): boolean => {\n // Super admin can access all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n return userRole === 'org_admin';\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check specific permission using the new RBAC system\n const hasPermission = useCallback(async (permission: string, orgId?: string): Promise<boolean> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return false;\n\n try {\n // Use the new RBAC system with caching\n const { isPermittedCached } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n return await isPermittedCached({\n userId: user.id,\n scope,\n permission: permission as Permission\n });\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception checking permission:', error);\n return false;\n }\n }, [selectedOrganisation, user, superAdminContext.isSuperAdmin]);\n\n // Get user's permissions for organisation using the new RBAC system\n const getUserPermissions = useCallback(async (orgId?: string): Promise<string[]> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return ['*']; // All permissions\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return [];\n\n try {\n // Use the new RBAC system\n const { getPermissionMap } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n const permissionMap = await getPermissionMap({\n userId: user.id,\n scope\n });\n \n // Flatten all permissions from all pages\n const allPermissions = Object.entries(permissionMap)\n .filter(([, allowed]) => allowed)\n .map(([permission]) => permission);\n return [...new Set(allPermissions)]; // Remove duplicates\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception getting user permissions:', error);\n return [];\n }\n }, [selectedOrganisation, user, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Log organisation access for audit using the new RBAC audit system\n const logOrganisationAccess = useCallback(async (action: string, details?: Record<string, unknown>): Promise<void> => {\n if (!user || !selectedOrganisation) return;\n\n try {\n // Use the new RBAC audit system - only if we have a valid organisation ID\n if (selectedOrganisation.id) {\n const { emitAuditEvent } = await import('../rbac/audit');\n \n await emitAuditEvent({\n type: 'permission_check',\n userId: user.id,\n organisationId: selectedOrganisation.id,\n permission: action,\n decision: true, // Assume access was granted if we're logging it\n source: 'api',\n duration_ms: 0, // No actual permission check performed here\n metadata: details || {}\n });\n }\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error logging organisation access:', error);\n }\n }, [user, selectedOrganisation]);\n\n // Ensure organisation access (throws if no access)\n const ensureOrganisationAccess = useCallback(async (orgId: string): Promise<void> => {\n const hasAccess = await validateOrganisationAccess(orgId);\n \n if (!hasAccess) {\n const error = new Error(`User does not have access to organisation ${orgId}`) as OrganisationSecurityError;\n error.name = 'OrganisationSecurityError';\n error.code = 'ACCESS_DENIED';\n error.organisationId = orgId;\n error.userId = user?.id;\n throw error;\n }\n }, [validateOrganisationAccess, user]);\n\n // Validate user access (for admin functions)\n const validateUserAccess = useCallback(async (userId: string, orgId: string): Promise<boolean> => {\n if (!supabase) return false;\n\n try {\n // Super admin can validate any user\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Regular users can only validate their own access\n if (userId !== user?.id) {\n return false;\n }\n\n return await validateOrganisationAccess(orgId);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating user access:', error);\n return false;\n }\n }, [supabase, superAdminContext.isSuperAdmin, user, validateOrganisationAccess]);\n\n return {\n superAdminContext,\n validateOrganisationAccess,\n hasMinimumRole,\n canAccessChildOrganisations,\n hasPermission,\n getUserPermissions,\n logOrganisationAccess,\n ensureOrganisationAccess,\n validateUserAccess\n };\n}; "],"mappings":"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","names":["useRef","useMemo","useRef","appName","error","useMemo","useMemo","jsx","useMemo","useMemo","useContext","useContext","useMemo","useMemo","useEffect","useState","useState","useEffect","useMemo"]}