@jmruthers/pace-core 0.6.1 → 0.6.3

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 (549) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +46 -87
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
  17. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
  18. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
  19. package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
  20. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  21. package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
  22. package/dist/chunk-2T2IG7T7.js.map +1 -0
  23. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  24. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  25. package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
  26. package/dist/chunk-6Z7LTB3D.js.map +1 -0
  27. package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
  28. package/dist/chunk-CNCQDFLN.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
  31. package/dist/chunk-DWUBLJJM.js.map +1 -0
  32. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  33. package/dist/chunk-FFQEQTNW.js.map +1 -0
  34. package/dist/chunk-FMUCXFII.js +76 -0
  35. package/dist/chunk-FMUCXFII.js.map +1 -0
  36. package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
  37. package/dist/chunk-HFZBI76P.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
  43. package/dist/chunk-M7MPQISP.js.map +1 -0
  44. package/dist/chunk-PQBSKX33.js +7793 -0
  45. package/dist/chunk-PQBSKX33.js.map +1 -0
  46. package/dist/chunk-QRPVRXYT.js +226 -0
  47. package/dist/chunk-QRPVRXYT.js.map +1 -0
  48. package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
  49. package/dist/chunk-RWEBCB47.js.map +1 -0
  50. package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
  51. package/dist/chunk-YDQHOZNA.js.map +1 -0
  52. package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
  53. package/dist/chunk-ZNIWI3UC.js.map +1 -0
  54. package/dist/components.d.ts +5 -5
  55. package/dist/components.js +18 -16
  56. package/dist/components.js.map +1 -1
  57. package/dist/contextValidator-3JNZKUTX.js +9 -0
  58. package/dist/contextValidator-3JNZKUTX.js.map +1 -0
  59. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  60. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  61. package/dist/hooks.d.ts +55 -122
  62. package/dist/hooks.js +10 -13
  63. package/dist/hooks.js.map +1 -1
  64. package/dist/index.d.ts +60 -13
  65. package/dist/index.js +30 -25
  66. package/dist/index.js.map +1 -1
  67. package/dist/providers.d.ts +21 -3
  68. package/dist/providers.js +4 -3
  69. package/dist/rbac/index.d.ts +210 -139
  70. package/dist/rbac/index.js +17 -13
  71. package/dist/styles/index.js +1 -1
  72. package/dist/theming/runtime.d.ts +1 -13
  73. package/dist/theming/runtime.js +2 -2
  74. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  75. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  76. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  77. package/dist/types.d.ts +2 -2
  78. package/dist/types.js +1 -1
  79. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
  80. package/dist/utils.d.ts +4 -5
  81. package/dist/utils.js +17 -19
  82. package/dist/utils.js.map +1 -1
  83. package/docs/api/README.md +21 -17
  84. package/docs/api/modules.md +4191 -2967
  85. package/docs/architecture/database-schema-requirements.md +161 -0
  86. package/docs/components/context-selector.md +126 -0
  87. package/docs/core-concepts/rbac-system.md +3 -3
  88. package/docs/documentation-index.md +2 -4
  89. package/docs/getting-started/cursor-rules.md +2 -1
  90. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  91. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  92. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  93. package/docs/migration/README.md +52 -6
  94. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  95. package/docs/migration/database-changes-december-2025.md +3 -3
  96. package/docs/pace-mint-fix-auto-selection.md +218 -0
  97. package/docs/pace-mint-rbac-setup.md +391 -0
  98. package/docs/rbac/event-based-apps.md +1 -1
  99. package/docs/rbac/getting-started.md +1 -1
  100. package/docs/rbac/quick-start.md +1 -1
  101. package/docs/rbac/secure-client-protection.md +330 -0
  102. package/docs/standards/README.md +1 -0
  103. package/package.json +4 -3
  104. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  105. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  106. package/scripts/audit/core/checks/bundle.cjs +142 -0
  107. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
  108. package/scripts/audit/core/checks/config.cjs +54 -0
  109. package/scripts/audit/core/checks/coverage.cjs +84 -0
  110. package/scripts/audit/core/checks/dependencies.cjs +985 -0
  111. package/scripts/audit/core/checks/documentation.cjs +268 -0
  112. package/scripts/audit/core/checks/environment.cjs +116 -0
  113. package/scripts/audit/core/checks/error-handling.cjs +340 -0
  114. package/scripts/audit/core/checks/forms.cjs +172 -0
  115. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  116. package/scripts/audit/core/checks/hooks.cjs +334 -0
  117. package/scripts/audit/core/checks/imports.cjs +244 -0
  118. package/scripts/audit/core/checks/performance.cjs +325 -0
  119. package/scripts/audit/core/checks/routes.cjs +117 -0
  120. package/scripts/audit/core/checks/state.cjs +130 -0
  121. package/scripts/audit/core/checks/structure.cjs +65 -0
  122. package/scripts/audit/core/checks/style.cjs +584 -0
  123. package/scripts/audit/core/checks/testing.cjs +122 -0
  124. package/scripts/audit/core/checks/typescript.cjs +61 -0
  125. package/scripts/audit/core/scanner.cjs +199 -0
  126. package/scripts/audit/core/utils.cjs +137 -0
  127. package/scripts/audit/index.cjs +223 -0
  128. package/scripts/audit/reporters/console.cjs +151 -0
  129. package/scripts/audit/reporters/json.cjs +54 -0
  130. package/scripts/audit/reporters/markdown.cjs +124 -0
  131. package/scripts/audit-consuming-app.cjs +61 -936
  132. package/scripts/build-docs/build-decision.js +240 -0
  133. package/scripts/build-docs/cache-utils.js +105 -0
  134. package/scripts/build-docs/content-normalization.js +150 -0
  135. package/scripts/build-docs/file-utils.js +105 -0
  136. package/scripts/build-docs/git-utils.js +86 -0
  137. package/scripts/build-docs/hash-utils.js +116 -0
  138. package/scripts/build-docs/typedoc-runner.js +220 -0
  139. package/scripts/build-docs-incremental.js +77 -913
  140. package/scripts/utils/command-runner.js +16 -11
  141. package/scripts/validate-formats.js +61 -56
  142. package/scripts/validate-master.js +74 -69
  143. package/scripts/validate-pre-publish.js +70 -65
  144. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  145. package/src/components/Alert/Alert.test.tsx +12 -18
  146. package/src/components/Alert/Alert.tsx +5 -7
  147. package/src/components/Avatar/Avatar.test.tsx +4 -4
  148. package/src/components/Badge/Badge.tsx +14 -0
  149. package/src/components/Button/Button.tsx +22 -0
  150. package/src/components/Calendar/Calendar.tsx +8 -2
  151. package/src/components/Card/Card.tsx +4 -0
  152. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  153. package/src/components/Checkbox/Checkbox.tsx +2 -2
  154. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  155. package/src/components/ContextSelector/index.ts +3 -0
  156. package/src/components/DataTable/DataTable.tsx +38 -4
  157. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  158. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  159. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  160. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  161. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  162. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  163. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  164. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  165. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  166. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  167. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  168. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  169. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  170. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  171. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  172. package/src/components/DataTable/components/EditFields.tsx +307 -0
  173. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  174. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  176. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  177. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  178. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  179. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  180. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  181. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  182. package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
  183. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  184. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  185. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  186. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  187. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  188. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  189. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  190. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  191. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  192. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  193. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  194. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  195. package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
  196. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  197. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  198. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  199. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  200. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  201. package/src/components/DataTable/styles.ts +6 -6
  202. package/src/components/DataTable/types.ts +6 -10
  203. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  204. package/src/components/DataTable/utils/debugTools.ts +18 -113
  205. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  206. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  207. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  208. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  209. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  210. package/src/components/Dialog/Dialog.tsx +31 -3
  211. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  212. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  213. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  214. package/src/components/ErrorBoundary/index.ts +27 -2
  215. package/src/components/FileDisplay/FileDisplay.tsx +74 -28
  216. package/src/components/FileUpload/FileUpload.tsx +22 -2
  217. package/src/components/Footer/Footer.test.tsx +16 -16
  218. package/src/components/Footer/Footer.tsx +14 -11
  219. package/src/components/Form/Form.tsx +1 -0
  220. package/src/components/Header/Header.test.tsx +43 -73
  221. package/src/components/Header/Header.tsx +59 -49
  222. package/src/components/Input/Input.test.tsx +2 -2
  223. package/src/components/Input/Input.tsx +8 -4
  224. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  225. package/src/components/LoginForm/LoginForm.tsx +4 -0
  226. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  227. package/src/components/NavigationMenu/types.ts +56 -0
  228. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  229. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  230. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  231. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  232. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
  233. package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
  234. package/src/components/PaceAppLayout/README.md +14 -17
  235. package/src/components/PaceAppLayout/test-setup.tsx +3 -4
  236. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  237. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  238. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  239. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  240. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  241. package/src/components/Select/Select.tsx +80 -434
  242. package/src/components/Select/context.ts +23 -0
  243. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  244. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  245. package/src/components/Select/hooks/useSelectState.ts +104 -0
  246. package/src/components/Select/index.ts +9 -1
  247. package/src/components/Select/types.ts +123 -0
  248. package/src/components/Select/utils/text.ts +26 -0
  249. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  250. package/src/components/Switch/Switch.tsx +4 -4
  251. package/src/components/Tabs/Tabs.tsx +1 -1
  252. package/src/components/Toast/Toast.tsx +4 -0
  253. package/src/components/Tooltip/Tooltip.tsx +2 -2
  254. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  255. package/src/components/UserMenu/UserMenu.tsx +21 -18
  256. package/src/components/index.ts +7 -7
  257. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  258. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  259. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  260. package/src/hooks/index.ts +1 -2
  261. package/src/hooks/public/usePublicEvent.ts +4 -0
  262. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  263. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  264. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  265. package/src/hooks/services/useAuth.ts +32 -0
  266. package/src/hooks/services/useCurrentEvent.ts +6 -0
  267. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  268. package/src/hooks/useAppConfig.ts +15 -30
  269. package/src/hooks/useDebounce.ts +9 -0
  270. package/src/hooks/useEventTheme.ts +6 -0
  271. package/src/hooks/useFileDisplay.ts +81 -50
  272. package/src/hooks/useFileReference.ts +25 -7
  273. package/src/hooks/useFileUrl.ts +11 -1
  274. package/src/hooks/useFocusManagement.ts +14 -0
  275. package/src/hooks/useFocusTrap.ts +3 -0
  276. package/src/hooks/useInactivityTracker.ts +3 -0
  277. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  278. package/src/hooks/useOrganisationPermissions.ts +4 -0
  279. package/src/hooks/useOrganisationSecurity.ts +4 -0
  280. package/src/hooks/usePerformanceMonitor.ts +4 -0
  281. package/src/hooks/usePermissionCache.ts +7 -0
  282. package/src/hooks/useQueryCache.ts +12 -1
  283. package/src/hooks/useSessionRestoration.ts +4 -0
  284. package/src/hooks/useStorage.ts +4 -0
  285. package/src/hooks/useToast.ts +1 -1
  286. package/src/index.ts +6 -6
  287. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  288. package/src/providers/services/AuthServiceProvider.tsx +35 -7
  289. package/src/providers/services/EventServiceProvider.tsx +51 -5
  290. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  291. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  292. package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
  293. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  294. package/src/rbac/README.md +1 -1
  295. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  296. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  297. package/src/rbac/adapters.tsx +12 -3
  298. package/src/rbac/api.test.ts +59 -51
  299. package/src/rbac/api.ts +246 -167
  300. package/src/rbac/components/NavigationProvider.tsx +4 -1
  301. package/src/rbac/components/PagePermissionGuard.tsx +185 -17
  302. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  303. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  304. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  305. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  306. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  307. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  308. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  309. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  310. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  311. package/src/rbac/engine.ts +38 -14
  312. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  313. package/src/rbac/hooks/permissions/index.ts +7 -0
  314. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  315. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  316. package/src/rbac/hooks/permissions/useCan.ts +377 -0
  317. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  318. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  319. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  320. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  321. package/src/rbac/hooks/useCan.test.ts +64 -66
  322. package/src/rbac/hooks/usePermissions.ts +14 -995
  323. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  324. package/src/rbac/hooks/useRBAC.ts +36 -37
  325. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  326. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  327. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  328. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  329. package/src/rbac/hooks/useSecureSupabase.ts +27 -7
  330. package/src/rbac/index.ts +7 -0
  331. package/src/rbac/permissions.ts +0 -30
  332. package/src/rbac/secureClient.test.ts +22 -18
  333. package/src/rbac/secureClient.ts +294 -68
  334. package/src/rbac/security.ts +0 -17
  335. package/src/rbac/types.ts +9 -0
  336. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  337. package/src/rbac/utils/clientSecurity.ts +93 -0
  338. package/src/rbac/utils/contextValidator.ts +77 -168
  339. package/src/services/AuthService.ts +39 -7
  340. package/src/services/EventService.ts +186 -54
  341. package/src/services/OrganisationService.ts +81 -14
  342. package/src/services/__tests__/EventService.test.ts +1 -2
  343. package/src/services/base/BaseService.ts +3 -0
  344. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  345. package/src/theming/parseEventColours.ts +5 -19
  346. package/src/types/vitest-globals.d.ts +51 -26
  347. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  348. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  349. package/src/utils/__tests__/index.unit.test.ts +2 -2
  350. package/src/utils/audit/audit.ts +0 -3
  351. package/src/utils/core/cn.ts +1 -1
  352. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  353. package/src/utils/file-reference/index.ts +53 -1
  354. package/src/utils/formatting/formatting.ts +8 -18
  355. package/src/utils/index.ts +0 -1
  356. package/dist/chunk-3QRJFVBR.js.map +0 -1
  357. package/dist/chunk-3XTALGJF.js.map +0 -1
  358. package/dist/chunk-4N5C5XZU.js.map +0 -1
  359. package/dist/chunk-4ZC4GX36.js.map +0 -1
  360. package/dist/chunk-7D4SUZUM.js +0 -38
  361. package/dist/chunk-BYFSK72L.js.map +0 -1
  362. package/dist/chunk-EXUD6RNJ.js +0 -451
  363. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  364. package/dist/chunk-GLK6VM3F.js.map +0 -1
  365. package/dist/chunk-I7PSE6JW.js.map +0 -1
  366. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  367. package/dist/chunk-KNC55RTG.js.map +0 -1
  368. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  369. package/dist/chunk-R77UEZ4E.js.map +0 -1
  370. package/dist/chunk-SQGMNID3.js.map +0 -1
  371. package/dist/chunk-T33XF5ZC.js +0 -12922
  372. package/dist/chunk-T33XF5ZC.js.map +0 -1
  373. package/dist/chunk-XM25TVIE.js.map +0 -1
  374. package/docs/api/classes/ColumnFactory.md +0 -243
  375. package/docs/api/classes/ErrorBoundary.md +0 -144
  376. package/docs/api/classes/InvalidScopeError.md +0 -73
  377. package/docs/api/classes/Logger.md +0 -178
  378. package/docs/api/classes/MissingUserContextError.md +0 -66
  379. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  380. package/docs/api/classes/PermissionDeniedError.md +0 -73
  381. package/docs/api/classes/RBACAuditManager.md +0 -297
  382. package/docs/api/classes/RBACCache.md +0 -322
  383. package/docs/api/classes/RBACEngine.md +0 -171
  384. package/docs/api/classes/RBACError.md +0 -76
  385. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  386. package/docs/api/classes/SecureSupabaseClient.md +0 -160
  387. package/docs/api/classes/StorageUtils.md +0 -328
  388. package/docs/api/enums/FileCategory.md +0 -184
  389. package/docs/api/enums/LogLevel.md +0 -54
  390. package/docs/api/enums/RBACErrorCode.md +0 -228
  391. package/docs/api/enums/RPCFunction.md +0 -118
  392. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  393. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  394. package/docs/api/interfaces/AggregateConfig.md +0 -43
  395. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  396. package/docs/api/interfaces/AvatarProps.md +0 -128
  397. package/docs/api/interfaces/BadgeProps.md +0 -27
  398. package/docs/api/interfaces/ButtonProps.md +0 -53
  399. package/docs/api/interfaces/CalendarProps.md +0 -70
  400. package/docs/api/interfaces/CardProps.md +0 -66
  401. package/docs/api/interfaces/ColorPalette.md +0 -7
  402. package/docs/api/interfaces/ColorShade.md +0 -66
  403. package/docs/api/interfaces/ComplianceResult.md +0 -30
  404. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  405. package/docs/api/interfaces/DataRecord.md +0 -11
  406. package/docs/api/interfaces/DataTableAction.md +0 -249
  407. package/docs/api/interfaces/DataTableColumn.md +0 -504
  408. package/docs/api/interfaces/DataTableProps.md +0 -625
  409. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  410. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  411. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  412. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  413. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  414. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  415. package/docs/api/interfaces/ExportColumn.md +0 -90
  416. package/docs/api/interfaces/ExportOptions.md +0 -126
  417. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  418. package/docs/api/interfaces/FileMetadata.md +0 -129
  419. package/docs/api/interfaces/FileReference.md +0 -118
  420. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  421. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  422. package/docs/api/interfaces/FileUploadProps.md +0 -293
  423. package/docs/api/interfaces/FooterProps.md +0 -105
  424. package/docs/api/interfaces/FormFieldProps.md +0 -166
  425. package/docs/api/interfaces/FormProps.md +0 -113
  426. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  427. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  428. package/docs/api/interfaces/InputProps.md +0 -53
  429. package/docs/api/interfaces/LabelProps.md +0 -107
  430. package/docs/api/interfaces/LoggerConfig.md +0 -62
  431. package/docs/api/interfaces/LoginFormProps.md +0 -184
  432. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  433. package/docs/api/interfaces/NavigationContextType.md +0 -164
  434. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  435. package/docs/api/interfaces/NavigationItem.md +0 -120
  436. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  437. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  438. package/docs/api/interfaces/Organisation.md +0 -140
  439. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  440. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  441. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  442. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  443. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
  444. package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
  445. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  446. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  447. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  448. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  449. package/docs/api/interfaces/PaletteData.md +0 -41
  450. package/docs/api/interfaces/ParsedAddress.md +0 -120
  451. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  452. package/docs/api/interfaces/ProgressProps.md +0 -42
  453. package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
  454. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  455. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  456. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
  457. package/docs/api/interfaces/QuickFix.md +0 -52
  458. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  459. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  460. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  461. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  462. package/docs/api/interfaces/RBACConfig.md +0 -133
  463. package/docs/api/interfaces/RBACContext.md +0 -52
  464. package/docs/api/interfaces/RBACLogger.md +0 -112
  465. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  466. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  467. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  468. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  469. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  470. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  471. package/docs/api/interfaces/RBACResult.md +0 -58
  472. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  473. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  474. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  475. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  476. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  477. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  478. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  479. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  480. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  481. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  482. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  483. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  484. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  485. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  486. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  487. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  488. package/docs/api/interfaces/RouteConfig.md +0 -134
  489. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  490. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  491. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  492. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  493. package/docs/api/interfaces/SetupIssue.md +0 -41
  494. package/docs/api/interfaces/StorageConfig.md +0 -41
  495. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  496. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  497. package/docs/api/interfaces/StorageListOptions.md +0 -99
  498. package/docs/api/interfaces/StorageListResult.md +0 -41
  499. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  500. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  501. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  502. package/docs/api/interfaces/StyleImport.md +0 -19
  503. package/docs/api/interfaces/SwitchProps.md +0 -34
  504. package/docs/api/interfaces/TabsContentProps.md +0 -9
  505. package/docs/api/interfaces/TabsListProps.md +0 -9
  506. package/docs/api/interfaces/TabsProps.md +0 -9
  507. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  508. package/docs/api/interfaces/TextareaProps.md +0 -53
  509. package/docs/api/interfaces/ToastActionElement.md +0 -9
  510. package/docs/api/interfaces/ToastProps.md +0 -9
  511. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
  512. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
  513. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  514. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  515. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
  516. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  517. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  518. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
  519. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  520. package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
  521. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  522. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
  523. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
  524. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  525. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  526. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  527. package/docs/api/interfaces/UserEventAccess.md +0 -118
  528. package/docs/api/interfaces/UserMenuProps.md +0 -86
  529. package/docs/api/interfaces/UserProfile.md +0 -63
  530. package/docs/migration/quick-migration-guide.md +0 -356
  531. package/docs/migration/service-architecture.md +0 -281
  532. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  533. package/src/components/EventSelector/EventSelector.tsx +0 -420
  534. package/src/components/EventSelector/index.ts +0 -3
  535. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  536. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
  537. package/src/components/OrganisationSelector/index.ts +0 -9
  538. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  539. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  540. package/src/hooks/useSecureDataAccess.ts +0 -681
  541. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
  542. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
  543. /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
  544. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  545. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  546. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  547. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  548. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  549. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -25,10 +25,35 @@
25
25
  * <MyComponent />
26
26
  * </ErrorBoundary>
27
27
  *
28
+ * // Using global error handler (recommended for applications)
29
+ * import { ErrorBoundaryProvider, ErrorBoundary } from '@jmruthers/pace-core';
30
+ *
31
+ * // Setup once in main.tsx
32
+ * <ErrorBoundaryProvider
33
+ * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
34
+ * if (import.meta.env.DEV) {
35
+ * console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);
36
+ * }
37
+ * errorTracking.captureException(error, {
38
+ * componentStack: errorInfo.componentStack,
39
+ * errorBoundary: true,
40
+ * errorId,
41
+ * componentName,
42
+ * });
43
+ * }}
44
+ * >
45
+ * <App />
46
+ * </ErrorBoundaryProvider>
47
+ *
48
+ * // Then use ErrorBoundary without onError prop - uses global handler
49
+ * <ErrorBoundary componentName="AppRoot">
50
+ * <AppContent />
51
+ * </ErrorBoundary>
52
+ *
28
53
  * // Error boundary with custom fallback
29
54
  * <ErrorBoundary
30
55
  * componentName="UserProfile"
31
- * fallback={<div>Something went wrong loading the profile.</div>}
56
+ * fallback={<section>Something went wrong loading the profile.</section>}
32
57
  * onError={(error, errorInfo, errorId) => {
33
58
  * console.log('Error caught:', errorId, error);
34
59
  * }}
@@ -58,6 +83,16 @@
58
83
  * </ErrorBoundary>
59
84
  * <Footer />
60
85
  * </ErrorBoundary>
86
+ *
87
+ * // Per-instance onError overrides global handler
88
+ * <ErrorBoundary
89
+ * componentName="SpecialCase"
90
+ * onError={(error, errorInfo, errorId) => {
91
+ * // Custom handling for this specific case - global handler won't be called
92
+ * }}
93
+ * >
94
+ * <SpecialComponent />
95
+ * </ErrorBoundary>
61
96
  * ```
62
97
  *
63
98
  * @accessibility
@@ -120,6 +155,8 @@ export interface ErrorBoundaryProps {
120
155
  enableRetry?: boolean;
121
156
  /** Whether to enable error reporting */
122
157
  enableReporting?: boolean;
158
+ /** Internal: Global error handler from context (not part of public API) */
159
+ _globalErrorHandler?: (error: Error, errorInfo: React.ErrorInfo, errorId: string, componentName: string) => void;
123
160
  }
124
161
 
125
162
  /**
@@ -162,15 +199,16 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
162
199
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
163
200
  const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;
164
201
  const errorId = this.state.errorId!;
202
+ const componentNameForHandler = componentName || 'Unknown Component';
165
203
 
166
204
  this.setState({ errorInfo });
167
205
 
168
206
  // Enhanced logging with component name and error ID
169
- logger.error('ErrorBoundary', `[${componentName}] Caught error ${errorId}:`, error, errorInfo);
207
+ logger.error('ErrorBoundary', `[${componentNameForHandler}] Caught error ${errorId}:`, error, errorInfo);
170
208
 
171
209
  // Performance monitoring - track error occurrence
172
210
  performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {
173
- componentName,
211
+ componentName: componentNameForHandler,
174
212
  errorId,
175
213
  errorMessage: error.message,
176
214
  stack: error.stack?.substring(0, 200), // Truncated stack trace
@@ -178,12 +216,14 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
178
216
 
179
217
  // Report error if enabled
180
218
  if (enableReporting) {
181
- this.reportError(errorId, componentName);
219
+ this.reportError(errorId, componentNameForHandler);
182
220
  }
183
221
 
184
- // Call custom error handler if provided
222
+ // Call error handler: prefer prop onError, fall back to global handler from props
185
223
  if (onError) {
186
224
  onError(error, errorInfo, errorId);
225
+ } else if (this.props._globalErrorHandler) {
226
+ this.props._globalErrorHandler(error, errorInfo, errorId, componentNameForHandler);
187
227
  }
188
228
  }
189
229
 
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @file Error Boundary Context
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/ErrorBoundary
5
+ * @since 0.6.2
6
+ *
7
+ * Context provider for global error handler configuration in ErrorBoundary components.
8
+ * Allows applications to register a default error handler once, which is then used by
9
+ * all ErrorBoundary instances unless overridden.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // Setup once in main.tsx
14
+ * <ErrorBoundaryProvider
15
+ * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
16
+ * if (import.meta.env.DEV) {
17
+ * console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);
18
+ * }
19
+ * errorTracking.captureException(error, {
20
+ * componentStack: errorInfo.componentStack,
21
+ * errorBoundary: true,
22
+ * errorId,
23
+ * componentName,
24
+ * });
25
+ * }}
26
+ * >
27
+ * <App />
28
+ * </ErrorBoundaryProvider>
29
+ *
30
+ * // Usage - no onError needed, uses global handler
31
+ * <ErrorBoundary componentName="AppRoot">
32
+ * <AppContent />
33
+ * </ErrorBoundary>
34
+ * ```
35
+ */
36
+
37
+ import React, { createContext, useContext, ReactNode } from 'react';
38
+
39
+ /**
40
+ * Type definition for the global error handler function
41
+ * @public
42
+ */
43
+ export type GlobalErrorHandler = (
44
+ error: Error,
45
+ errorInfo: React.ErrorInfo,
46
+ errorId: string,
47
+ componentName: string
48
+ ) => void;
49
+
50
+ /**
51
+ * Context value type for ErrorBoundary configuration
52
+ * @internal
53
+ */
54
+ interface ErrorBoundaryContextType {
55
+ /** Global error handler that will be used by all ErrorBoundary instances */
56
+ defaultErrorHandler?: GlobalErrorHandler;
57
+ }
58
+
59
+ /**
60
+ * Context for ErrorBoundary global configuration
61
+ * @internal
62
+ */
63
+ const ErrorBoundaryContext = createContext<ErrorBoundaryContextType | undefined>(undefined);
64
+
65
+ /**
66
+ * Props for ErrorBoundaryProvider component
67
+ * @public
68
+ */
69
+ export interface ErrorBoundaryProviderProps {
70
+ /** Children to wrap with the provider */
71
+ children: ReactNode;
72
+ /** Global error handler that will be used by all ErrorBoundary instances unless overridden */
73
+ defaultErrorHandler?: GlobalErrorHandler;
74
+ }
75
+
76
+ /**
77
+ * Provider component for ErrorBoundary global error handler configuration
78
+ *
79
+ * Wrap your application with this provider to set a default error handler that will
80
+ * be used by all ErrorBoundary instances unless they provide their own onError prop.
81
+ *
82
+ * @example
83
+ * ```tsx
84
+ * <ErrorBoundaryProvider
85
+ * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
86
+ * errorTracking.captureException(error, {
87
+ * componentStack: errorInfo.componentStack,
88
+ * errorBoundary: true,
89
+ * errorId,
90
+ * componentName,
91
+ * });
92
+ * }}
93
+ * >
94
+ * <App />
95
+ * </ErrorBoundaryProvider>
96
+ * ```
97
+ *
98
+ * @public
99
+ */
100
+ /**
101
+ * Error boundary provider component.
102
+ * Provides global error handling configuration to child ErrorBoundary components.
103
+ *
104
+ * @param props - Error boundary provider configuration
105
+ * @returns The error boundary provider
106
+ */
107
+ export function ErrorBoundaryProvider({
108
+ children,
109
+ defaultErrorHandler,
110
+ }: ErrorBoundaryProviderProps) {
111
+ const contextValue: ErrorBoundaryContextType = {
112
+ defaultErrorHandler,
113
+ };
114
+
115
+ return (
116
+ <ErrorBoundaryContext.Provider value={contextValue}>
117
+ {children}
118
+ </ErrorBoundaryContext.Provider>
119
+ );
120
+ }
121
+
122
+ /**
123
+ * Hook to access ErrorBoundary context
124
+ * @internal
125
+ */
126
+ export function useErrorBoundaryContext(): ErrorBoundaryContextType | undefined {
127
+ return useContext(ErrorBoundaryContext);
128
+ }
129
+
@@ -2,7 +2,32 @@
2
2
  * @file Error Boundary exports
3
3
  */
4
4
 
5
- export { ErrorBoundary } from './ErrorBoundary';
5
+ // Re-export ErrorBoundary with automatic context support
6
+ import { ErrorBoundary as ErrorBoundaryClass } from './ErrorBoundary';
7
+ import { useErrorBoundaryContext } from './ErrorBoundaryContext';
8
+ import React from 'react';
9
+ import type { ErrorBoundaryProps } from './ErrorBoundary';
10
+
11
+ // Export types and provider
6
12
  export type { ErrorBoundaryProps, ErrorBoundaryState } from './ErrorBoundary';
7
- import { ErrorBoundary } from './ErrorBoundary';
13
+ export { ErrorBoundaryProvider } from './ErrorBoundaryContext';
14
+ export type { ErrorBoundaryProviderProps, GlobalErrorHandler } from './ErrorBoundaryContext';
15
+
16
+ /**
17
+ * ErrorBoundary component with automatic context support
18
+ * This wrapper automatically uses the global error handler from ErrorBoundaryProvider
19
+ * if no onError prop is provided.
20
+ */
21
+ export const ErrorBoundary = React.forwardRef<
22
+ ErrorBoundaryClass,
23
+ Omit<ErrorBoundaryProps, '_globalErrorHandler'>
24
+ >((props, ref) => {
25
+ const context = useErrorBoundaryContext();
26
+ const globalErrorHandler = context?.defaultErrorHandler;
27
+
28
+ return React.createElement(ErrorBoundaryClass, { ...props, ref, _globalErrorHandler: globalErrorHandler });
29
+ });
30
+
31
+ ErrorBoundary.displayName = 'ErrorBoundary';
32
+
8
33
  export default ErrorBoundary;
@@ -125,7 +125,7 @@ interface FileDisplayContentProps {
125
125
  showMetadata?: boolean;
126
126
  }
127
127
 
128
- function FileDisplayContent({
128
+ const FileDisplayContent = React.memo(function FileDisplayContent({
129
129
  isLoading,
130
130
  error,
131
131
  fileUrl,
@@ -156,6 +156,29 @@ function FileDisplayContent({
156
156
  const [internalFileUrls, setInternalFileUrls] = useState<Map<string, string>>(new Map(fileUrls));
157
157
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
158
158
  const fileReferencesRef = useRef<FileReference[]>([]);
159
+ const imgRef = useRef<HTMLImageElement | null>(null);
160
+ const currentSrcRef = useRef<string | null>(null);
161
+ const isImageLoadingRef = useRef(false);
162
+
163
+ // Stabilize fileUrl to prevent unnecessary image reloads
164
+ // This prevents NS_BINDING_ABORTED errors when the component re-renders
165
+ const stableFileUrl = useMemo(() => fileUrl, [fileUrl]);
166
+
167
+ // Track when image starts loading to prevent cancellation
168
+ const handleImageLoadStart = useCallback(() => {
169
+ isImageLoadingRef.current = true;
170
+ if (stableFileUrl) {
171
+ currentSrcRef.current = stableFileUrl;
172
+ }
173
+ }, [stableFileUrl]);
174
+
175
+ // Track when image finishes loading
176
+ const handleImageLoad = useCallback(() => {
177
+ isImageLoadingRef.current = false;
178
+ if (stableFileUrl) {
179
+ currentSrcRef.current = stableFileUrl;
180
+ }
181
+ }, [stableFileUrl]);
159
182
 
160
183
  // Compute fallback text
161
184
  const computedFallbackText = useMemo(() => {
@@ -192,10 +215,15 @@ function FileDisplayContent({
192
215
 
193
216
  const handleDeleteConfirm = async () => {
194
217
  setDeleteDialogOpen(false);
195
- if (onDelete) {
196
- await onDelete();
218
+ try {
219
+ if (onDelete) {
220
+ await onDelete();
221
+ }
222
+ setImageError(false);
223
+ } catch (error) {
224
+ // Error handling is delegated to onDelete callback
225
+ setImageError(false);
197
226
  }
198
- setImageError(false);
199
227
  };
200
228
 
201
229
  const handleImageError = () => {
@@ -323,7 +351,7 @@ function FileDisplayContent({
323
351
  }
324
352
 
325
353
  // Show loading skeleton if URL is not available yet
326
- if (!fileUrl) {
354
+ if (!stableFileUrl) {
327
355
  return (
328
356
  <figure className={className || "max-w-full h-48"} title="Loading">
329
357
  <p className={fallbackClasses}>
@@ -336,10 +364,15 @@ function FileDisplayContent({
336
364
  return (
337
365
  <figure className={className || ""}>
338
366
  <img
339
- src={fileUrl}
367
+ ref={imgRef}
368
+ key={fileReference.id}
369
+ src={stableFileUrl || undefined}
340
370
  alt={fileReference.file_metadata.fileName || 'File'}
341
371
  className={imgClassName || "object-cover size-full"}
342
372
  onError={handleImageError}
373
+ onLoadStart={handleImageLoadStart}
374
+ onLoad={handleImageLoad}
375
+ loading="lazy"
343
376
  />
344
377
  </figure>
345
378
  );
@@ -347,14 +380,14 @@ function FileDisplayContent({
347
380
 
348
381
  // Document link display when displayOnly is true and file is not an image
349
382
  // Render non-image files as clickable links that open in a new tab
350
- if (displayOnly && !isImage && fileUrl && fileReference && !showDelete) {
383
+ if (displayOnly && !isImage && stableFileUrl && fileReference && !showDelete) {
351
384
  const fileName = fileReference.file_metadata?.fileName || 'Document';
352
385
  const ariaLabel = `Open ${fileName} in new tab`;
353
386
 
354
387
  return (
355
388
  <figure className={className}>
356
389
  <a
357
- href={fileUrl}
390
+ href={stableFileUrl || undefined}
358
391
  target="_blank"
359
392
  rel="noopener noreferrer"
360
393
  aria-label={ariaLabel}
@@ -380,7 +413,7 @@ function FileDisplayContent({
380
413
 
381
414
  // Standard single file display with wrapper
382
415
  // For displayOnly mode, if fallback is enabled and there's no URL or image error, show fallback instead of folder icon
383
- if (displayOnly && showFallback && (!fileUrl || imageError || !isImage)) {
416
+ if (displayOnly && showFallback && (!stableFileUrl || imageError || !isImage)) {
384
417
  return (
385
418
  <figure className={className} title={fileReference.file_metadata.fileName || 'File'}>
386
419
  <p className={fallbackClasses}>
@@ -392,13 +425,15 @@ function FileDisplayContent({
392
425
 
393
426
  return (
394
427
  <figure className={`relative ${className}`}>
395
- {isImage && fileUrl && !imageError ? (
428
+ {isImage && stableFileUrl && !imageError ? (
396
429
  <>
397
430
  <img
398
- src={fileUrl}
431
+ key={fileReference.id}
432
+ src={stableFileUrl}
399
433
  alt={fileReference.file_metadata.fileName || 'File'}
400
434
  className={imgClassName || "object-cover size-full"}
401
435
  onError={handleImageError}
436
+ loading="lazy"
402
437
  />
403
438
  {showDelete && (
404
439
  <>
@@ -536,10 +571,12 @@ function FileDisplayContent({
536
571
  <figure key={fileRef.id} className="flex items-center space-x-3 p-3 bg-sec-50 rounded-lg border border-sec-200">
537
572
  {isImage && fileUrl ? (
538
573
  <img
539
- src={fileUrl}
574
+ key={fileRef.id}
575
+ src={fileUrl || undefined}
540
576
  alt={fileRef.file_metadata.fileName || 'File'}
541
577
  className={imgClassName || "object-cover size-full"}
542
578
  onError={handleImageError}
579
+ loading="lazy"
543
580
  />
544
581
  ) : (
545
582
  <span className="text-2xl">
@@ -591,7 +628,7 @@ function FileDisplayContent({
591
628
  {children}
592
629
  </figure>
593
630
  );
594
- }
631
+ });
595
632
 
596
633
  /**
597
634
  * Internal component for public page context
@@ -773,16 +810,10 @@ function FileDisplayAuthenticated({
773
810
  }: FileDisplayProps) {
774
811
  const { supabase } = useUnifiedAuth();
775
812
 
776
- if (!supabase) {
777
- return (
778
- <figure className={className} title="Error">
779
- <p className={getFallbackClasses(fallbackSize || 'md')}>
780
- Supabase client not available in authenticated context
781
- </p>
782
- </figure>
783
- );
784
- }
813
+ // Consolidated state for displayOnly mode - must be before early return
814
+ const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState<FileReference | null>(null);
785
815
 
816
+ // Call hooks before any early returns - hooks must handle null supabase gracefully
786
817
  const {
787
818
  fileUrl,
788
819
  fileReference,
@@ -797,20 +828,17 @@ function FileDisplayAuthenticated({
797
828
  record_id,
798
829
  organisation_id,
799
830
  category,
800
- { supabase }
831
+ { supabase: supabase || null }
801
832
  );
802
-
803
- // Consolidated state for displayOnly mode
804
- const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState<FileReference | null>(null);
805
833
 
806
834
  // Use fileUrls map if available, otherwise use useFileUrl hook
807
835
  const displayOnlyFileUrlFromMap = displayOnlyFileReference ? fileUrls.get(displayOnlyFileReference.id) : null;
808
836
  const displayOnlyFileUrlHook = useFileUrl(
809
837
  displayOnlyFileReference && !displayOnlyFileUrlFromMap ? displayOnlyFileReference : null,
810
838
  {
811
- supabase,
839
+ supabase: supabase || null,
812
840
  organisation_id,
813
- autoLoad: !displayOnlyFileUrlFromMap && !!displayOnlyFileReference
841
+ autoLoad: !displayOnlyFileUrlFromMap && !!displayOnlyFileReference && !!supabase
814
842
  }
815
843
  );
816
844
  const displayOnlyFileUrl = displayOnlyFileUrlFromMap || displayOnlyFileUrlHook.url;
@@ -831,6 +859,17 @@ function FileDisplayAuthenticated({
831
859
  }
832
860
  }, [displayOnly, category, fileReferences, fileUrls]);
833
861
 
862
+ // Early return check after all hooks are called
863
+ if (!supabase) {
864
+ return (
865
+ <figure className={className} title="Error">
866
+ <p className={getFallbackClasses(fallbackSize || 'md')}>
867
+ Supabase client not available in authenticated context
868
+ </p>
869
+ </figure>
870
+ );
871
+ }
872
+
834
873
  // Delete operation - implementation pending
835
874
  const handleDelete = async () => {
836
875
  // TODO: Implement delete via FileReferenceService when delete functionality is needed
@@ -906,6 +945,13 @@ function FileDisplayAuthenticated({
906
945
  * @param props.category - Optional category filter. When specified, only displays files matching this category and uses single file display variant.
907
946
  * @returns React element with file display
908
947
  */
948
+ /**
949
+ * File display component.
950
+ * Renders files from the file reference system with support for previews, downloads, and public/private access.
951
+ *
952
+ * @param props - File display configuration
953
+ * @returns The rendered file display
954
+ */
909
955
  export function FileDisplay({
910
956
  table_name,
911
957
  record_id,
@@ -15,6 +15,10 @@ import { getCurrentAppName } from '../../utils/app/appNameResolver';
15
15
  import { getAppId } from '../../utils/app/appIdResolver';
16
16
  import { assertAppId } from '../../types/core';
17
17
 
18
+ /**
19
+ * Props for the FileUpload component.
20
+ * Configures file upload behavior including storage location, validation, and callbacks.
21
+ */
18
22
  export interface FileUploadProps {
19
23
  supabase: SupabaseClient;
20
24
  table_name: string;
@@ -71,6 +75,15 @@ export function FileUpload({
71
75
  onProgress,
72
76
  children
73
77
  }: FileUploadProps) {
78
+ // Validate pageContext early - required prop
79
+ if (!pageContext) {
80
+ const errorMsg = 'pageContext is required for FileUpload component. This is used for permission checks.';
81
+ if (import.meta.env.MODE === 'development') {
82
+ console.error('[FileUpload]', errorMsg);
83
+ }
84
+ throw new Error(errorMsg);
85
+ }
86
+
74
87
  const [isDragging, setIsDragging] = useState(false);
75
88
  const [uploadStates, setUploadStates] = useState<Map<string, FileUploadState>>(new Map());
76
89
  const [resolvedAppId, setResolvedAppId] = useState<string | null>(app_id || null);
@@ -286,6 +299,12 @@ export function FileUpload({
286
299
  throw new Error(errorMsg);
287
300
  }
288
301
 
302
+ // Validate pageContext before upload
303
+ if (!pageContext) {
304
+ const errorMsg = 'pageContext is required for file upload. This is used for permission checks.';
305
+ throw new Error(errorMsg);
306
+ }
307
+
289
308
  const result = await uploadFile({
290
309
  table_name,
291
310
  record_id,
@@ -294,7 +313,7 @@ export function FileUpload({
294
313
  app_id: resolvedAppId ? assertAppId(resolvedAppId) : assertAppId(''),
295
314
  category,
296
315
  folder,
297
- pageContext,
316
+ pageContext: pageContext,
298
317
  event_id,
299
318
  is_public: isPublic
300
319
  }, file);
@@ -393,7 +412,7 @@ export function FileUpload({
393
412
  onUploadError?.(errorMessage, file);
394
413
  }
395
414
  }
396
- }, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, folder, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError]);
415
+ }, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, folder, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError, pageContext]);
397
416
 
398
417
  const handleDragOver = useCallback((e: React.DragEvent) => {
399
418
  e.preventDefault();
@@ -475,6 +494,7 @@ export function FileUpload({
475
494
  className="hidden"
476
495
  disabled={isDisabled}
477
496
  data-testid="file-input"
497
+ aria-label={accept ? `Upload file${multiple ? 's' : ''} (${accept})` : `Upload file${multiple ? 's' : ''}`}
478
498
  />
479
499
  <div className="text-sec-600">
480
500
  {isResolvingAppId ? (
@@ -82,7 +82,7 @@ describe('Footer Component', () => {
82
82
  it('renders with children content', () => {
83
83
  renderWithProviders(
84
84
  <Footer>
85
- <div data-testid="custom-content">Custom footer content</div>
85
+ <section data-testid="custom-content">Custom footer content</section>
86
86
  </Footer>
87
87
  );
88
88
 
@@ -163,7 +163,7 @@ describe('Footer Component', () => {
163
163
  it('renders content in proper semantic structure', () => {
164
164
  renderWithProviders(
165
165
  <Footer>
166
- <div>Custom content</div>
166
+ <section>Custom content</section>
167
167
  </Footer>
168
168
  );
169
169
 
@@ -175,7 +175,7 @@ describe('Footer Component', () => {
175
175
  it('renders logo before children content', () => {
176
176
  renderWithProviders(
177
177
  <Footer logo="/logo.png">
178
- <div data-testid="children">Children content</div>
178
+ <section data-testid="children">Children content</section>
179
179
  </Footer>
180
180
  );
181
181
 
@@ -189,7 +189,7 @@ describe('Footer Component', () => {
189
189
  it('renders copyright text after children content', () => {
190
190
  renderWithProviders(
191
191
  <Footer>
192
- <div data-testid="children">Children content</div>
192
+ <section data-testid="children">Children content</section>
193
193
  </Footer>
194
194
  );
195
195
 
@@ -380,7 +380,7 @@ describe('Footer Component', () => {
380
380
  logo="/test-logo.png"
381
381
  copyright="Custom copyright text"
382
382
  >
383
- <div>Test children</div>
383
+ <section>Test children</section>
384
384
  </Footer>
385
385
  );
386
386
 
@@ -398,27 +398,27 @@ describe('Footer Component', () => {
398
398
  it('works with complex children content', () => {
399
399
  renderWithProviders(
400
400
  <Footer>
401
- <div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
402
- <div>
401
+ <section className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
402
+ <section>
403
403
  <h3 className="font-semibold mb-2">About Us</h3>
404
404
  <p className="text-sm text-muted-foreground">
405
405
  We provide innovative solutions for modern businesses.
406
406
  </p>
407
- </div>
408
- <div>
407
+ </section>
408
+ <section>
409
409
  <h3 className="font-semibold mb-2">Contact</h3>
410
410
  <p className="text-sm text-muted-foreground">
411
411
  Email: info@company.com
412
412
  </p>
413
- </div>
414
- <div>
413
+ </section>
414
+ <section>
415
415
  <h3 className="font-semibold mb-2">Follow Us</h3>
416
- <div className="flex gap-2">
416
+ <nav className="flex gap-2">
417
417
  <a href="#" className="text-muted-foreground hover:text-foreground">Twitter</a>
418
418
  <a href="#" className="text-muted-foreground hover:text-foreground">LinkedIn</a>
419
- </div>
420
- </div>
421
- </div>
419
+ </nav>
420
+ </section>
421
+ </section>
422
422
  </Footer>
423
423
  );
424
424
 
@@ -463,7 +463,7 @@ describe('Footer Component', () => {
463
463
  logo="/logo.png"
464
464
  className="custom-footer"
465
465
  >
466
- <div data-testid="custom-content">Custom footer content</div>
466
+ <section data-testid="custom-content">Custom footer content</section>
467
467
  </Footer>
468
468
  );
469
469