@jmruthers/pace-core 0.6.5 → 0.6.7

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 (473) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/audit-tool/00-dependencies.cjs +394 -0
  4. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  5. package/audit-tool/audits/02-project-structure.cjs +255 -0
  6. package/audit-tool/audits/03-architecture.cjs +196 -0
  7. package/audit-tool/audits/04-code-quality.cjs +149 -0
  8. package/audit-tool/audits/05-styling.cjs +224 -0
  9. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  10. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  11. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  12. package/audit-tool/audits/09-operations.cjs +208 -0
  13. package/audit-tool/index.cjs +291 -0
  14. package/audit-tool/utils/code-utils.cjs +218 -0
  15. package/audit-tool/utils/file-utils.cjs +230 -0
  16. package/audit-tool/utils/report-utils.cjs +241 -0
  17. package/core-usage-manifest.json +93 -0
  18. package/cursor-rules/00-standards-overview.mdc +156 -0
  19. package/cursor-rules/01-pace-core-compliance.mdc +586 -0
  20. package/cursor-rules/02-project-structure.mdc +42 -4
  21. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
  22. package/cursor-rules/04-code-quality.mdc +419 -0
  23. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
  24. package/cursor-rules/06-security-rbac.mdc +518 -0
  25. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  26. package/cursor-rules/08-testing-documentation.mdc +324 -0
  27. package/cursor-rules/09-operations.mdc +365 -0
  28. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  29. package/dist/DataTable-7PMH7XN7.js +15 -0
  30. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  31. package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
  32. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  33. package/dist/api-Y4MQWOFW.js +4 -0
  34. package/dist/audit-MYQXYZFU.js +3 -0
  35. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  36. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  37. package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
  38. package/dist/chunk-6F3IILHI.js +62 -0
  39. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  40. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  41. package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
  42. package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
  43. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  44. package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
  45. package/dist/chunk-C7NSAPTL.js +1 -0
  46. package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
  47. package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
  48. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  49. package/dist/chunk-GHYHJTYV.js +994 -0
  50. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  51. package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
  52. package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
  53. package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
  54. package/dist/chunk-MBADTM7L.js +64 -0
  55. package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
  56. package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
  57. package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
  58. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  59. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  60. package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
  61. package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
  62. package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
  63. package/dist/components.d.ts +7 -5
  64. package/dist/components.js +46 -257
  65. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  66. package/dist/eslint-rules/index.cjs +35 -0
  67. package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
  68. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  69. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  70. package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
  71. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  72. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  73. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  74. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  75. package/dist/hooks.d.ts +6 -6
  76. package/dist/hooks.js +62 -172
  77. package/dist/icons/index.d.ts +1 -0
  78. package/dist/icons/index.js +1 -0
  79. package/dist/index.d.ts +12 -11
  80. package/dist/index.js +67 -660
  81. package/dist/providers.d.ts +2 -2
  82. package/dist/providers.js +8 -35
  83. package/dist/rbac/eslint-rules.d.ts +46 -44
  84. package/dist/rbac/eslint-rules.js +7 -4
  85. package/dist/rbac/index.d.ts +109 -586
  86. package/dist/rbac/index.js +14 -207
  87. package/dist/styles/index.js +2 -12
  88. package/dist/theming/runtime.d.ts +14 -1
  89. package/dist/theming/runtime.js +3 -19
  90. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  91. package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
  92. package/dist/types-t9H8qKRw.d.ts +55 -0
  93. package/dist/types.d.ts +1 -1
  94. package/dist/types.js +7 -94
  95. package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
  96. package/dist/utils.d.ts +24 -117
  97. package/dist/utils.js +54 -392
  98. package/docs/README.md +17 -7
  99. package/docs/api/README.md +4 -402
  100. package/docs/api/modules.md +301 -871
  101. package/docs/api-reference/components.md +21 -21
  102. package/docs/api-reference/deprecated.md +31 -6
  103. package/docs/api-reference/hooks.md +80 -80
  104. package/docs/api-reference/rpc-functions.md +78 -3
  105. package/docs/api-reference/types.md +1 -1
  106. package/docs/api-reference/utilities.md +1 -1
  107. package/docs/architecture/README.md +1 -1
  108. package/docs/core-concepts/events.md +3 -3
  109. package/docs/core-concepts/organisations.md +6 -6
  110. package/docs/core-concepts/permissions.md +6 -6
  111. package/docs/documentation-index.md +12 -18
  112. package/docs/getting-started/cursor-rules.md +3 -23
  113. package/docs/getting-started/dependencies.md +650 -0
  114. package/docs/getting-started/documentation-index.md +1 -1
  115. package/docs/getting-started/examples/README.md +4 -4
  116. package/docs/getting-started/examples/full-featured-app.md +1 -1
  117. package/docs/getting-started/faq.md +2 -2
  118. package/docs/getting-started/installation-guide.md +20 -7
  119. package/docs/getting-started/quick-reference.md +4 -4
  120. package/docs/getting-started/quick-start.md +23 -12
  121. package/docs/implementation-guides/authentication.md +15 -15
  122. package/docs/implementation-guides/component-styling.md +1 -1
  123. package/docs/implementation-guides/data-tables.md +126 -33
  124. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  125. package/docs/implementation-guides/dynamic-colors.md +3 -3
  126. package/docs/implementation-guides/file-upload-storage.md +2 -2
  127. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  128. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  129. package/docs/implementation-guides/large-datasets.md +3 -2
  130. package/docs/implementation-guides/organisation-security.md +2 -2
  131. package/docs/implementation-guides/performance.md +2 -2
  132. package/docs/implementation-guides/permission-enforcement.md +5 -1
  133. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  134. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  135. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  136. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  137. package/docs/rbac/README.md +17 -8
  138. package/docs/rbac/advanced-patterns.md +6 -6
  139. package/docs/rbac/api-reference.md +20 -20
  140. package/docs/rbac/edge-functions-guide.md +376 -0
  141. package/docs/rbac/event-based-apps.md +3 -3
  142. package/docs/rbac/examples.md +41 -41
  143. package/docs/rbac/getting-started.md +37 -37
  144. package/docs/rbac/performance.md +1 -1
  145. package/docs/rbac/quick-start.md +52 -52
  146. package/docs/rbac/secure-client-protection.md +1 -35
  147. package/docs/rbac/troubleshooting.md +1 -1
  148. package/docs/security/README.md +5 -5
  149. package/docs/standards/0-standards-overview.md +220 -0
  150. package/docs/standards/1-pace-core-compliance-standards.md +986 -0
  151. package/docs/standards/2-project-structure-standards.md +949 -0
  152. package/docs/standards/3-architecture-standards.md +606 -0
  153. package/docs/standards/4-code-quality-standards.md +728 -0
  154. package/docs/standards/5-styling-standards.md +348 -0
  155. package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
  156. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  157. package/docs/standards/8-testing-documentation-standards.md +401 -0
  158. package/docs/standards/9-operations-standards.md +1102 -0
  159. package/docs/standards/README.md +185 -57
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/common-issues.md +2 -2
  162. package/docs/troubleshooting/debugging.md +9 -9
  163. package/docs/troubleshooting/migration.md +4 -4
  164. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  165. package/eslint-config-pace-core.cjs +33 -6
  166. package/package.json +35 -23
  167. package/scripts/install-cursor-rules.cjs +25 -6
  168. package/scripts/install-eslint-config.cjs +284 -0
  169. package/src/__tests__/fixtures/supabase.ts +1 -1
  170. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
  171. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  172. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  173. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  174. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
  175. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  176. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  177. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  178. package/src/__tests__/public-recipe-view.test.ts +38 -9
  179. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  180. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  181. package/src/__tests__/templates/component.test.template.tsx +18 -15
  182. package/src/components/Button/Button.tsx +5 -1
  183. package/src/components/Calendar/Calendar.tsx +201 -47
  184. package/src/components/ContextSelector/ContextSelector.tsx +106 -119
  185. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  186. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  187. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  188. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  189. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  190. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  191. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  192. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  193. package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
  194. package/src/components/DataTable/components/EditFields.tsx +23 -3
  195. package/src/components/DataTable/components/EditableRow.tsx +12 -9
  196. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  197. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  198. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  199. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  200. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  201. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  202. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  203. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  204. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  205. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  206. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  207. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  208. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  209. package/src/components/DataTable/components/index.ts +2 -1
  210. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  211. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  212. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  213. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  214. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  215. package/src/components/DataTable/types.ts +5 -18
  216. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  217. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  218. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  219. package/src/components/DateTimeField/DateTimeField.tsx +10 -9
  220. package/src/components/Dialog/Dialog.test.tsx +128 -104
  221. package/src/components/Dialog/Dialog.tsx +742 -24
  222. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  223. package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
  224. package/src/components/FileDisplay/FileDisplay.tsx +23 -17
  225. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  226. package/src/components/FileUpload/FileUpload.tsx +112 -130
  227. package/src/components/Form/Form.test.tsx +6 -8
  228. package/src/components/Form/Form.tsx +365 -4
  229. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  230. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  231. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  232. package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
  233. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  234. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  235. package/src/components/Progress/Progress.tsx +2 -4
  236. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  237. package/src/components/Select/Select.tsx +109 -98
  238. package/src/components/Select/types.ts +4 -1
  239. package/src/components/UserMenu/UserMenu.tsx +9 -6
  240. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  241. package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
  242. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  243. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  244. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  245. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  246. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  247. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  248. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  249. package/src/hooks/public/usePublicEvent.ts +67 -195
  250. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  251. package/src/hooks/public/usePublicEventLogo.ts +24 -14
  252. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  253. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  254. package/src/hooks/useAppConfig.ts +28 -26
  255. package/src/hooks/useEventTheme.test.ts +217 -239
  256. package/src/hooks/useEventTheme.ts +16 -28
  257. package/src/hooks/useFileDisplay.ts +2 -2
  258. package/src/hooks/useOrganisationPermissions.ts +5 -7
  259. package/src/hooks/useQueryCache.ts +0 -1
  260. package/src/hooks/useSessionDraft.ts +380 -0
  261. package/src/hooks/useSessionRestoration.ts +3 -1
  262. package/src/icons/index.ts +27 -0
  263. package/src/index.ts +5 -0
  264. package/src/providers/OrganisationProvider.tsx +23 -14
  265. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  266. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  267. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  268. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  269. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  270. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  271. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  272. package/src/providers/services/EventServiceProvider.tsx +1 -24
  273. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  274. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  275. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
  276. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  277. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  278. package/src/rbac/adapters.tsx +7 -295
  279. package/src/rbac/api.test.ts +44 -56
  280. package/src/rbac/api.ts +10 -17
  281. package/src/rbac/cache-invalidation.ts +0 -1
  282. package/src/rbac/compliance/index.ts +10 -0
  283. package/src/rbac/compliance/pattern-detector.ts +553 -0
  284. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  285. package/src/rbac/components/AccessDenied.tsx +150 -0
  286. package/src/rbac/components/NavigationGuard.tsx +12 -20
  287. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  288. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  289. package/src/rbac/components/index.ts +3 -41
  290. package/src/rbac/eslint-rules.js +1 -1
  291. package/src/rbac/hooks/index.ts +0 -3
  292. package/src/rbac/hooks/permissions/index.ts +0 -3
  293. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  294. package/src/rbac/hooks/usePermissions.ts +0 -3
  295. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  296. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  297. package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
  298. package/src/rbac/hooks/useResourcePermissions.ts +139 -48
  299. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  300. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  301. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  302. package/src/rbac/index.ts +7 -9
  303. package/src/rbac/utils/contextValidator.ts +9 -7
  304. package/src/services/AuthService.ts +130 -18
  305. package/src/services/EventService.ts +4 -97
  306. package/src/services/InactivityService.ts +16 -0
  307. package/src/services/OrganisationService.ts +7 -44
  308. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  309. package/src/services/base/BaseService.ts +0 -3
  310. package/src/styles/core.css +7 -0
  311. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  312. package/src/theming/parseEventColours.ts +22 -10
  313. package/src/types/database.generated.ts +4733 -3809
  314. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  315. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  316. package/src/utils/context/organisationContext.test.ts +13 -28
  317. package/src/utils/context/organisationContext.ts +21 -52
  318. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  319. package/src/utils/file-reference/index.ts +39 -15
  320. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  321. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  322. package/src/utils/index.ts +4 -1
  323. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  324. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  325. package/src/utils/persistence/keyDerivation.ts +304 -0
  326. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  327. package/src/utils/security/secureStorage.ts +5 -5
  328. package/src/utils/storage/README.md +1 -1
  329. package/src/utils/storage/helpers.ts +3 -3
  330. package/src/utils/supabase/createBaseClient.ts +147 -0
  331. package/src/utils/timezone/timezone.test.ts +1 -2
  332. package/src/utils/timezone/timezone.ts +1 -1
  333. package/src/utils/validation/csrf.ts +4 -4
  334. package/cursor-rules/00-pace-core-compliance.mdc +0 -331
  335. package/cursor-rules/01-standards-compliance.mdc +0 -244
  336. package/cursor-rules/04-testing-standards.mdc +0 -268
  337. package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
  338. package/cursor-rules/06-code-quality.mdc +0 -309
  339. package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
  340. package/cursor-rules/CHANGELOG.md +0 -119
  341. package/cursor-rules/README.md +0 -192
  342. package/dist/DataTable-AOVNCPTX.js +0 -175
  343. package/dist/DataTable-AOVNCPTX.js.map +0 -1
  344. package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
  345. package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
  346. package/dist/api-O6HTBX5Y.js +0 -52
  347. package/dist/api-O6HTBX5Y.js.map +0 -1
  348. package/dist/audit-V53FV5AG.js +0 -17
  349. package/dist/audit-V53FV5AG.js.map +0 -1
  350. package/dist/chunk-5DRSZLL2.js.map +0 -1
  351. package/dist/chunk-63FOKYGO.js.map +0 -1
  352. package/dist/chunk-6COVEUS7.js.map +0 -1
  353. package/dist/chunk-AFVQODI2.js +0 -263
  354. package/dist/chunk-AFVQODI2.js.map +0 -1
  355. package/dist/chunk-DGUM43GV.js.map +0 -1
  356. package/dist/chunk-E66EQZE6.js.map +0 -1
  357. package/dist/chunk-EFN2EIMK.js.map +0 -1
  358. package/dist/chunk-FFQEQTNW.js.map +0 -1
  359. package/dist/chunk-FMUCXFII.js.map +0 -1
  360. package/dist/chunk-G37KK66H.js.map +0 -1
  361. package/dist/chunk-G7QEZTYQ.js +0 -2053
  362. package/dist/chunk-G7QEZTYQ.js.map +0 -1
  363. package/dist/chunk-HU2C6SSC.js.map +0 -1
  364. package/dist/chunk-IHB5DR3H.js.map +0 -1
  365. package/dist/chunk-IVOFDYWT.js.map +0 -1
  366. package/dist/chunk-J36DSWQK.js.map +0 -1
  367. package/dist/chunk-JGRYX5UX.js.map +0 -1
  368. package/dist/chunk-KQCRWDSA.js +0 -1
  369. package/dist/chunk-KQCRWDSA.js.map +0 -1
  370. package/dist/chunk-L4OXEN46.js.map +0 -1
  371. package/dist/chunk-LMC26NLJ.js +0 -84
  372. package/dist/chunk-LMC26NLJ.js.map +0 -1
  373. package/dist/chunk-M43Y4SSO.js.map +0 -1
  374. package/dist/chunk-M7MPQISP.js.map +0 -1
  375. package/dist/chunk-NTM7ZSB6.js.map +0 -1
  376. package/dist/chunk-PWLANIRT.js.map +0 -1
  377. package/dist/chunk-QXHPKYJV.js.map +0 -1
  378. package/dist/chunk-RGAWHO7N.js.map +0 -1
  379. package/dist/chunk-UPPMRMYG.js.map +0 -1
  380. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  381. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  382. package/dist/components.js.map +0 -1
  383. package/dist/contextValidator-5OGXSPKS.js +0 -9
  384. package/dist/contextValidator-5OGXSPKS.js.map +0 -1
  385. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  386. package/dist/hooks.js.map +0 -1
  387. package/dist/index.js.map +0 -1
  388. package/dist/providers.js.map +0 -1
  389. package/dist/rbac/eslint-rules.js.map +0 -1
  390. package/dist/rbac/index.js.map +0 -1
  391. package/dist/styles/index.js.map +0 -1
  392. package/dist/theming/runtime.js.map +0 -1
  393. package/dist/types.js.map +0 -1
  394. package/dist/utils.js.map +0 -1
  395. package/docs/best-practices/README.md +0 -472
  396. package/docs/best-practices/accessibility.md +0 -601
  397. package/docs/best-practices/common-patterns.md +0 -516
  398. package/docs/best-practices/deployment.md +0 -1103
  399. package/docs/best-practices/performance.md +0 -1328
  400. package/docs/best-practices/security.md +0 -940
  401. package/docs/best-practices/testing.md +0 -1034
  402. package/docs/rbac/compliance/compliance-guide.md +0 -544
  403. package/docs/standards/01-architecture-standard.md +0 -44
  404. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  405. package/docs/standards/03-component-standard.md +0 -32
  406. package/docs/standards/04-code-style-standard.md +0 -32
  407. package/docs/standards/05-security-standard.md +0 -44
  408. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  409. package/docs/standards/pace-core-compliance.md +0 -432
  410. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  411. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  412. package/scripts/audit/core/checks/bundle.cjs +0 -142
  413. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  414. package/scripts/audit/core/checks/config.cjs +0 -54
  415. package/scripts/audit/core/checks/coverage.cjs +0 -84
  416. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  417. package/scripts/audit/core/checks/documentation.cjs +0 -268
  418. package/scripts/audit/core/checks/environment.cjs +0 -116
  419. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  420. package/scripts/audit/core/checks/forms.cjs +0 -172
  421. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  422. package/scripts/audit/core/checks/hooks.cjs +0 -334
  423. package/scripts/audit/core/checks/imports.cjs +0 -244
  424. package/scripts/audit/core/checks/performance.cjs +0 -325
  425. package/scripts/audit/core/checks/routes.cjs +0 -117
  426. package/scripts/audit/core/checks/state.cjs +0 -130
  427. package/scripts/audit/core/checks/structure.cjs +0 -65
  428. package/scripts/audit/core/checks/style.cjs +0 -584
  429. package/scripts/audit/core/checks/testing.cjs +0 -122
  430. package/scripts/audit/core/checks/typescript.cjs +0 -61
  431. package/scripts/audit/core/scanner.cjs +0 -199
  432. package/scripts/audit/core/utils.cjs +0 -137
  433. package/scripts/audit/index.cjs +0 -223
  434. package/scripts/audit/reporters/console.cjs +0 -151
  435. package/scripts/audit/reporters/json.cjs +0 -54
  436. package/scripts/audit/reporters/markdown.cjs +0 -124
  437. package/scripts/audit-consuming-app.cjs +0 -86
  438. package/src/components/DataTable/components/DataTableBody.tsx +0 -454
  439. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  440. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  441. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  442. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  443. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  444. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  445. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  446. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  447. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  448. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  449. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  450. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  451. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  452. package/src/components/DataTable/utils/debugTools.ts +0 -514
  453. package/src/eslint-rules/pace-core-compliance.js +0 -638
  454. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  455. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  456. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  457. package/src/rbac/components/NavigationProvider.tsx +0 -345
  458. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  459. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  460. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  461. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  462. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  463. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  464. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  465. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  466. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  467. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  468. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  469. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  470. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  471. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  472. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  473. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -127,9 +127,9 @@ export function useRoleManagement() {
127
127
  }
128
128
 
129
129
  /**
130
- * Revoke an event app role using the secure RPC function
130
+ * Revoke an event app role using the unified RPC function
131
131
  *
132
- * This function uses the `revoke_event_app_role` RPC which:
132
+ * This function uses the `rbac_role_revoke` RPC which:
133
133
  * - Runs with SECURITY DEFINER privileges
134
134
  * - Includes proper permission checks
135
135
  * - Automatically populates audit fields (revoked_by, timestamps)
@@ -145,26 +145,114 @@ export function useRoleManagement() {
145
145
  setError(null);
146
146
 
147
147
  try {
148
- const { data, error: rpcError } = await supabase.rpc('revoke_event_app_role', {
148
+ // Build context ID from event_id and app_id
149
+ const contextId = `${params.event_id}:${params.app_id}`;
150
+
151
+ const rpcParams = {
149
152
  p_user_id: params.user_id,
150
- p_organisation_id: params.organisation_id,
151
- p_event_id: params.event_id,
152
- p_app_id: params.app_id,
153
- p_role: params.role,
153
+ p_role_type: 'event_app' as const,
154
+ p_role_name: params.role,
155
+ p_context_id: contextId,
154
156
  p_revoked_by: params.revoked_by || user?.id || undefined
155
- });
157
+ };
158
+
159
+ // Log in development for debugging
160
+ if (import.meta.env.MODE === 'development') {
161
+ console.log('[useRoleManagement] revokeEventAppRole called with:', rpcParams);
162
+ }
163
+
164
+ const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', rpcParams);
156
165
 
157
166
  if (rpcError) {
158
- throw new Error(rpcError.message || 'Failed to revoke role');
167
+ // Log full error details in development
168
+ if (import.meta.env.MODE === 'development') {
169
+ console.error('[useRoleManagement] RPC error:', {
170
+ message: rpcError.message,
171
+ details: rpcError.details,
172
+ hint: rpcError.hint,
173
+ code: rpcError.code,
174
+ fullError: rpcError
175
+ });
176
+ }
177
+ // Use just the message for the error (tests expect plain message)
178
+ const errorMessage = rpcError.message || 'Failed to revoke role - unknown RPC error';
179
+ throw new Error(errorMessage);
180
+ }
181
+
182
+ // Log response in development - ALWAYS log, even on failure
183
+ if (import.meta.env.MODE === 'development') {
184
+ console.log('[useRoleManagement] RPC response:', {
185
+ data,
186
+ error: rpcError,
187
+ dataType: Array.isArray(data) ? 'array' : typeof data,
188
+ dataLength: Array.isArray(data) ? data.length : 'N/A'
189
+ });
190
+ }
191
+
192
+ // rbac_role_revoke returns a table with success, message, revoked_count, error_code
193
+ // It should always return at least one row
194
+ if (!data || !Array.isArray(data) || data.length === 0) {
195
+ const errorMsg = 'No response from database - role revocation may have failed';
196
+ if (import.meta.env.MODE === 'development') {
197
+ console.error('[useRoleManagement] Empty or null data response:', {
198
+ data,
199
+ dataType: typeof data,
200
+ isArray: Array.isArray(data),
201
+ length: Array.isArray(data) ? data.length : 'N/A'
202
+ });
203
+ }
204
+ throw new Error(errorMsg);
205
+ }
206
+
207
+ const result = data[0];
208
+
209
+ // ALWAYS log the result in development, even on failure
210
+ if (import.meta.env.MODE === 'development') {
211
+ console.log('[useRoleManagement] RPC result:', {
212
+ success: result?.success,
213
+ message: result?.message,
214
+ error_code: result?.error_code,
215
+ revoked_count: result?.revoked_count,
216
+ fullResult: result
217
+ });
218
+ }
219
+
220
+ if (!result || result.success !== true) {
221
+ // Use message if available, otherwise fall back to error_code, otherwise default message
222
+ const errorMessage = result?.message || result?.error_code || 'Role revocation failed';
223
+
224
+ if (import.meta.env.MODE === 'development') {
225
+ console.error('[useRoleManagement] Role revocation failed:', {
226
+ result,
227
+ errorMessage,
228
+ fullData: data,
229
+ rpcParams
230
+ });
231
+ }
232
+
233
+ return {
234
+ success: false,
235
+ message: result?.message || undefined,
236
+ error: errorMessage
237
+ };
159
238
  }
160
239
 
161
240
  return {
162
- success: data === true,
163
- message: data === true ? 'Role revoked successfully' : 'No role found to revoke',
164
- error: data === false ? 'No matching role found' : undefined
241
+ success: true,
242
+ message: result.message || 'Role revoked successfully',
243
+ error: undefined
165
244
  };
166
245
  } catch (err) {
167
246
  const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
247
+
248
+ if (import.meta.env.MODE === 'development') {
249
+ console.error('[useRoleManagement] Exception in revokeEventAppRole:', {
250
+ error: err,
251
+ errorMessage,
252
+ params
253
+ });
254
+ }
255
+
168
256
  setError(err instanceof Error ? err : new Error(errorMessage));
169
257
  return {
170
258
  success: false,
@@ -173,7 +261,7 @@ export function useRoleManagement() {
173
261
  } finally {
174
262
  setIsLoading(false);
175
263
  }
176
- }, [user?.id]);
264
+ }, [user?.id, supabase]);
177
265
 
178
266
  /**
179
267
  * Grant an event app role using the secure RPC function
@@ -273,16 +361,36 @@ export function useRoleManagement() {
273
361
  });
274
362
 
275
363
  if (rpcError) {
276
- throw new Error(rpcError.message || 'Failed to revoke role');
364
+ // Use just the message for the error (tests expect plain message)
365
+ const errorMessage = rpcError.message || 'Failed to revoke role - unknown RPC error';
366
+ throw new Error(errorMessage);
277
367
  }
278
368
 
279
369
  // rbac_role_revoke returns a table with success, message, revoked_count, error_code
280
370
  const result = Array.isArray(data) && data.length > 0 ? data[0] : null;
281
371
 
372
+ // When data is empty array or null, return error as undefined (tests expect this)
373
+ if (!result) {
374
+ return {
375
+ success: false,
376
+ error: undefined
377
+ };
378
+ }
379
+
380
+ if (result.success === false) {
381
+ // Use message if available, otherwise fall back to error_code, otherwise default message
382
+ const errorMessage = result.message || result.error_code || 'Role revocation failed';
383
+ return {
384
+ success: false,
385
+ message: result.message || undefined,
386
+ error: errorMessage
387
+ };
388
+ }
389
+
282
390
  return {
283
- success: result?.success === true,
284
- message: result?.message || undefined,
285
- error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined
391
+ success: true,
392
+ message: result.message || 'Role revoked successfully',
393
+ error: undefined
286
394
  };
287
395
  } catch (err) {
288
396
  const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
@@ -383,7 +491,17 @@ export function useRoleManagement() {
383
491
  });
384
492
 
385
493
  if (rpcError) {
386
- throw new Error(rpcError.message || 'Failed to revoke role');
494
+ // Include all available error information in the message
495
+ const errorParts = [
496
+ rpcError.message,
497
+ rpcError.details,
498
+ rpcError.hint,
499
+ rpcError.code ? `Error code: ${rpcError.code}` : null
500
+ ].filter(Boolean);
501
+ const errorMessage = errorParts.length > 0
502
+ ? errorParts.join(' | ')
503
+ : 'Failed to revoke role - unknown RPC error';
504
+ throw new Error(errorMessage);
387
505
  }
388
506
 
389
507
  // rbac_role_revoke returns a table with success, message, revoked_count, error_code
@@ -493,7 +611,17 @@ export function useRoleManagement() {
493
611
  });
494
612
 
495
613
  if (rpcError) {
496
- throw new Error(rpcError.message || 'Failed to revoke role');
614
+ // Include all available error information in the message
615
+ const errorParts = [
616
+ rpcError.message,
617
+ rpcError.details,
618
+ rpcError.hint,
619
+ rpcError.code ? `Error code: ${rpcError.code}` : null
620
+ ].filter(Boolean);
621
+ const errorMessage = errorParts.length > 0
622
+ ? errorParts.join(' | ')
623
+ : 'Failed to revoke role - unknown RPC error';
624
+ throw new Error(errorMessage);
497
625
  }
498
626
 
499
627
  // rbac_role_revoke returns a table with success, message, revoked_count, error_code
@@ -223,7 +223,8 @@ export function useSecureSupabase(
223
223
  const { resolvedScope } = useResolvedScope({
224
224
  supabase: authSupabase || null,
225
225
  selectedOrganisationId: selectedOrganisation?.id || null,
226
- selectedEventId: selectedEvent?.event_id || null
226
+ selectedEventId: selectedEvent?.event_id || null,
227
+ selectedEventOrganisationId: selectedEvent?.organisation_id || null
227
228
  });
228
229
 
229
230
  // Track previous context to detect changes
@@ -238,13 +239,8 @@ export function useSecureSupabase(
238
239
  });
239
240
 
240
241
  return useMemo(() => {
241
- // If event is loading, return base client or null to avoid recreating client unnecessarily
242
- if (eventLoading) {
243
- return baseClient || authSupabase || null;
244
- }
245
-
246
- // Use resolved scope to get organisationId (derived from event if needed)
247
- // For event-required apps, resolvedScope.organisationId is derived from event
242
+ // Use resolved scope to get organisationId (now available immediately)
243
+ // For event-required apps, resolvedScope.organisationId comes from selectedEvent.organisation_id
248
244
  // For org-required apps, resolvedScope.organisationId comes from selectedOrganisation
249
245
  const organisationId = resolvedScope?.organisationId;
250
246
  const eventId = resolvedScope?.eventId || selectedEvent?.event_id;
package/src/rbac/index.ts CHANGED
@@ -45,7 +45,8 @@ export {
45
45
  isDevelopmentMode,
46
46
  } from './config';
47
47
 
48
- // Secure client
48
+ // Secure client (internal - use useSecureSupabase hook instead)
49
+ // @internal - These are implementation details. Use useSecureSupabase hook for secure client access.
49
50
  export {
50
51
  SecureSupabaseClient,
51
52
  createSecureClient,
@@ -59,7 +60,8 @@ export {
59
60
  SECURE_CLIENT_SYMBOL,
60
61
  } from './utils/clientSecurity';
61
62
 
62
- // Cache
63
+ // Cache (internal - caching is automatic in hooks and API functions)
64
+ // @internal - These are implementation details. Caching is handled automatically.
63
65
  export {
64
66
  RBACCache,
65
67
  rbacCache,
@@ -94,7 +96,8 @@ export {
94
96
  emitAuditEvent,
95
97
  } from './audit';
96
98
 
97
- // Engine
99
+ // Engine (internal - use isPermitted API instead)
100
+ // @internal - These are implementation details. Use isPermitted API for permission checks.
98
101
  export {
99
102
  RBACEngine,
100
103
  createRBACEngine,
@@ -110,17 +113,13 @@ export * from './hooks';
110
113
  // Users should now use useRBAC() hook directly without a provider wrapper
111
114
  // export * from './providers';
112
115
 
113
- // Adapters
116
+ // Adapters (Server-side only - React components removed)
114
117
  export {
115
118
  withPermissionGuard,
116
119
  withAccessLevelGuard,
117
120
  withRoleGuard,
118
- PermissionGuard,
119
- AccessLevelGuard,
120
121
  createRBACMiddleware,
121
122
  createRBACExpressMiddleware,
122
- hasPermissionCached,
123
- hasAnyPermissionCached,
124
123
  } from './adapters';
125
124
 
126
125
  // Main API functions
@@ -131,7 +130,6 @@ export {
131
130
  getRoleContext,
132
131
  isPermitted,
133
132
  isPermittedCached,
134
- hasPermission,
135
133
  hasAnyPermission,
136
134
  hasAllPermissions,
137
135
  setupRBAC,
@@ -68,19 +68,21 @@ export class ContextValidator {
68
68
  * Resolve scope based on page-level scope_type
69
69
  *
70
70
  * This method handles page-level scoping. All pages have explicit scope_type set.
71
- * Used for hybrid apps like pace-mint that have both event and organisation pages.
71
+ * Used for hybrid apps that have both event and organisation pages.
72
72
  *
73
73
  * @param scope - Current scope
74
74
  * @param pageScopeType - Page scope type ('event', 'organisation', or 'both')
75
75
  * @param appName - App name (for PORTAL/ADMIN special case)
76
- * @param supabase - Supabase client (for deriving org from event)
76
+ * @param supabase - Supabase client (for deriving org from event, only if not already provided)
77
+ * @param immediateOrganisationId - Optional immediate organisation ID (from selectedEvent.organisation_id) - avoids querying
77
78
  * @returns Resolved scope with all required context
78
79
  */
79
80
  static async resolveScopeForPage(
80
81
  scope: Scope,
81
82
  pageScopeType: PageScopeType,
82
83
  appName?: string,
83
- supabase?: SupabaseClient<Database> | null
84
+ supabase?: SupabaseClient<Database> | null,
85
+ immediateOrganisationId?: string | null
84
86
  ): Promise<ContextValidationResult> {
85
87
  // Use page-level scope (single source of truth)
86
88
  const effectiveScopeType = pageScopeType;
@@ -109,8 +111,8 @@ export class ContextValidator {
109
111
  };
110
112
  }
111
113
 
112
- // Derive org from event if event is provided but org is not
113
- let organisationId = scope.organisationId;
114
+ // Use immediate orgId if provided, otherwise derive from event
115
+ let organisationId = scope.organisationId || immediateOrganisationId || undefined;
114
116
  if (!organisationId && scope.eventId && supabase) {
115
117
  try {
116
118
  const derivedOrgId = await this.deriveOrgFromEvent(supabase, scope.eventId);
@@ -154,8 +156,8 @@ export class ContextValidator {
154
156
  };
155
157
  }
156
158
 
157
- // Derive organisationId from event if not provided
158
- let organisationId: UUID | undefined = scope.organisationId;
159
+ // Use immediate orgId if provided, otherwise derive from event
160
+ let organisationId: UUID | undefined = scope.organisationId || immediateOrganisationId || undefined;
159
161
  if (!organisationId && supabase && scope.eventId) {
160
162
  try {
161
163
  const derivedOrgId = await this.deriveOrgFromEvent(supabase, scope.eventId);
@@ -37,18 +37,20 @@ export class AuthService extends BaseService implements IAuthService {
37
37
  restorationError: null,
38
38
  };
39
39
  private restorationTimeoutId: ReturnType<typeof setTimeout> | null = null;
40
- private readonly restorationTimeoutMs = 5000;
40
+ // Increased timeout to handle hard refresh scenarios where localStorage access
41
+ // and session restoration may take longer, especially with organisation/event context
42
+ private readonly restorationTimeoutMs = 10000; // 10 seconds (increased from 5)
41
43
  private restorationStartTime: number | null = null;
42
44
  private appName: string | undefined = undefined;
43
45
  private errorHandler: ((event: ErrorEvent) => void) | null = null;
44
46
  private unhandledRejectionHandler: ((event: PromiseRejectionEvent) => void) | null = null;
47
+ private wasAuthenticatedRef: boolean = false; // Track previous auth state to detect transitions
45
48
 
46
49
  constructor(supabaseClient: SupabaseClient, appName?: string) {
47
50
  super();
48
51
  this.instanceId = ++AuthService.instanceCount;
49
52
  this.supabaseClient = supabaseClient;
50
53
  this.appName = appName;
51
- logger.debug('AuthService', `Instance created [ID:${this.instanceId}]`, { appName });
52
54
  }
53
55
 
54
56
  getInstanceId(): number {
@@ -109,6 +111,15 @@ export class AuthService extends BaseService implements IAuthService {
109
111
  this.authError = null;
110
112
  this.user = data.user;
111
113
  this.session = data.session;
114
+
115
+ // Clear persistence immediately on successful login
116
+ // This prevents dialogs/forms/datatables from auto-opening with stale state
117
+ // We clear here synchronously before components can mount and check persisted state
118
+ // Also clear old unscoped keys to prevent data leakage
119
+ if (!this.wasAuthenticatedRef && data.user) {
120
+ this.clearPersistenceOnLogin(null, true);
121
+ this.wasAuthenticatedRef = true;
122
+ }
112
123
  }
113
124
 
114
125
  this.notify();
@@ -148,6 +159,14 @@ export class AuthService extends BaseService implements IAuthService {
148
159
  this.authError = null;
149
160
  this.user = data.user;
150
161
  this.session = data.session;
162
+
163
+ // Clear persistence immediately on successful signup/login
164
+ // This prevents dialogs/forms/datatables from auto-opening with stale state
165
+ // Also clear old unscoped keys to prevent data leakage
166
+ if (!this.wasAuthenticatedRef && data.user) {
167
+ this.clearPersistenceOnLogin(null, true);
168
+ this.wasAuthenticatedRef = true;
169
+ }
151
170
  }
152
171
 
153
172
  this.notify();
@@ -176,6 +195,14 @@ export class AuthService extends BaseService implements IAuthService {
176
195
  try {
177
196
  const { error } = await this.supabaseClient.auth.signOut();
178
197
 
198
+ // Clear sessionStorage on logout
199
+ try {
200
+ sessionStorage.clear();
201
+ } catch (storageError) {
202
+ // Ignore storage errors (e.g., in private browsing mode)
203
+ logger.warn('AuthService', 'Failed to clear sessionStorage', { error: storageError });
204
+ }
205
+
179
206
  if (error) {
180
207
  this.authError = error;
181
208
  } else {
@@ -187,6 +214,14 @@ export class AuthService extends BaseService implements IAuthService {
187
214
  this.notify();
188
215
  return { user: null, session: null, error };
189
216
  } catch (error) {
217
+ // Clear sessionStorage even on error
218
+ try {
219
+ sessionStorage.clear();
220
+ } catch (storageError) {
221
+ // Ignore storage errors (e.g., in private browsing mode)
222
+ logger.warn('AuthService', 'Failed to clear sessionStorage', { error: storageError });
223
+ }
224
+
190
225
  // Convert regular Error to AuthError if needed
191
226
  const authError = error instanceof AuthError
192
227
  ? error
@@ -397,6 +432,56 @@ export class AuthService extends BaseService implements IAuthService {
397
432
  }
398
433
  }
399
434
 
435
+ /**
436
+ * Clear pace-core persistence entries from sessionStorage
437
+ * This includes dialog, form, and datatable persistence
438
+ *
439
+ * @param userId - Optional user ID to clear only that user's persistence.
440
+ * If not provided, clears all pace-core persistence (for user changes).
441
+ * @param clearUnscoped - If true, also clears old unscoped keys (without :user: prefix)
442
+ */
443
+ private clearPersistenceOnLogin(userId?: string | null, clearUnscoped: boolean = true): void {
444
+ if (typeof window === 'undefined' || !window.sessionStorage) {
445
+ return;
446
+ }
447
+
448
+ try {
449
+ const keysToRemove: string[] = [];
450
+
451
+ // Collect all pace-core persistence keys
452
+ for (let i = 0; i < sessionStorage.length; i++) {
453
+ const key = sessionStorage.key(i);
454
+ if (key && (
455
+ key.startsWith('pace-core:draft:') ||
456
+ key.startsWith('pace-core:dialog:')
457
+ )) {
458
+ // If userId is provided, only clear keys for that user
459
+ // Otherwise, clear all (for user changes or fresh login)
460
+ if (userId && !key.includes(`:user:${userId}`)) {
461
+ // Also clear old unscoped keys (without :user: prefix) if requested
462
+ if (clearUnscoped && !key.includes(':user:')) {
463
+ keysToRemove.push(key);
464
+ }
465
+ continue; // Skip keys that don't match this user
466
+ }
467
+ keysToRemove.push(key);
468
+ }
469
+ }
470
+
471
+ // Remove all collected keys
472
+ keysToRemove.forEach(key => {
473
+ try {
474
+ sessionStorage.removeItem(key);
475
+ } catch (error) {
476
+ logger.warn('AuthService', `Failed to remove persistence key: ${key}`, error);
477
+ }
478
+ });
479
+
480
+ } catch (error) {
481
+ logger.warn('AuthService', `Failed to clear persistence [ID:${this.instanceId}]:`, error);
482
+ }
483
+ }
484
+
400
485
  private async setupAuthStateListener(): Promise<void> {
401
486
  if (!this.supabaseClient) {
402
487
  this.authLoading = false;
@@ -408,16 +493,20 @@ export class AuthService extends BaseService implements IAuthService {
408
493
  const subscription = this.supabaseClient.auth.onAuthStateChange(
409
494
  (event: AuthChangeEvent, session: SupabaseSession | null) => {
410
495
  try {
411
- logger.debug('AuthService', `Auth state change [ID:${this.instanceId}]`, {
412
- event,
413
- hasSession: !!session,
414
- userId: session?.user?.id
415
- });
496
+
497
+ // Track authentication state transition
498
+ const wasAuthenticated = this.wasAuthenticatedRef;
499
+ const isNowAuthenticated = !!session?.user;
500
+ const previousUserId = this.user?.id || null;
501
+ const newUserId = session?.user?.id || null;
502
+ const userChanged = previousUserId !== null && newUserId !== null && previousUserId !== newUserId;
503
+
416
504
  // Handle different auth events
417
505
  if (event === 'SIGNED_OUT') {
418
506
  this.session = null;
419
507
  this.user = null;
420
508
  this.authError = null;
509
+ this.wasAuthenticatedRef = false;
421
510
 
422
511
  // Automatic session tracking (non-blocking)
423
512
  if (session?.user) {
@@ -434,6 +523,19 @@ export class AuthService extends BaseService implements IAuthService {
434
523
  this.authError = null;
435
524
  }
436
525
 
526
+ // Clear persistence when:
527
+ // 1. Transitioning from unauthenticated to authenticated (fresh login) - clear all + old unscoped keys
528
+ // 2. User changes (different user logs in) - clear previous user's persistence + old unscoped keys
529
+ if (!wasAuthenticated && isNowAuthenticated && event === 'SIGNED_IN') {
530
+ // Fresh login - clear all persistence including old unscoped keys
531
+ this.clearPersistenceOnLogin(null, true);
532
+ } else if (userChanged && previousUserId) {
533
+ // User changed - clear previous user's persistence + old unscoped keys
534
+ this.clearPersistenceOnLogin(previousUserId, true);
535
+ }
536
+
537
+ this.wasAuthenticatedRef = isNowAuthenticated;
538
+
437
539
  // Automatic session tracking for login (non-blocking)
438
540
  // Only track on SIGNED_IN, not TOKEN_REFRESHED (to avoid duplicate login records)
439
541
  if (event === 'SIGNED_IN' && session?.user) {
@@ -443,10 +545,30 @@ export class AuthService extends BaseService implements IAuthService {
443
545
  }
444
546
  } else if (event === 'INITIAL_SESSION') {
445
547
  if (session) {
548
+ const previousUserId = this.user?.id || null;
549
+ const newUserId = session.user?.id || null;
550
+ const userChanged = previousUserId !== null && newUserId !== null && previousUserId !== newUserId;
551
+
446
552
  this.session = session;
447
553
  this.user = session.user ?? null;
448
554
  this.authError = null;
449
555
 
556
+ // Clear persistence if user changed (e.g., different user's session restored)
557
+ // This prevents data leakage when a different user's session is restored
558
+ if (userChanged && previousUserId) {
559
+ // Clear previous user's persistence + old unscoped keys
560
+ this.clearPersistenceOnLogin(previousUserId, true);
561
+ } else if (!wasAuthenticated && !!session.user) {
562
+ // Fresh login on INITIAL_SESSION - clear all persistence including old unscoped keys
563
+ this.clearPersistenceOnLogin(null, true);
564
+ }
565
+ // Don't clear persistence on INITIAL_SESSION for same user - this is just session restoration
566
+ // Persistence should only be cleared on actual SIGNED_IN events or user changes
567
+ // INITIAL_SESSION happens on page load/refresh, and we want to preserve
568
+ // dialog/form/datatable state across page refreshes for the same user
569
+
570
+ this.wasAuthenticatedRef = !!session.user;
571
+
450
572
  // Reset restoration state if valid session arrives after earlier failure
451
573
  // This clears stale errors when session eventually succeeds
452
574
  const hasTimeoutError = this.sessionRestorationState.restorationError?.name === 'SessionRestorationTimeoutError';
@@ -457,6 +579,7 @@ export class AuthService extends BaseService implements IAuthService {
457
579
  }
458
580
  } else {
459
581
  // No session in INITIAL_SESSION event - user is not authenticated
582
+ this.wasAuthenticatedRef = false;
460
583
  // Finish restoration to clear loading state
461
584
  if (this.sessionRestorationState.isRestoring) {
462
585
  this.finishSessionRestoration();
@@ -468,11 +591,6 @@ export class AuthService extends BaseService implements IAuthService {
468
591
  // before checking authentication state
469
592
  this.authLoading = false;
470
593
 
471
- // Final check: Ensure state matches what we just logged
472
- logger.debug('AuthService', `State synchronized after INITIAL_SESSION [ID:${this.instanceId}]`, {
473
- hasUser: !!this.user,
474
- userId: this.user?.id
475
- });
476
594
 
477
595
  this.notify();
478
596
  return; // Return early to avoid setting loading to false again below
@@ -481,12 +599,6 @@ export class AuthService extends BaseService implements IAuthService {
481
599
  // For other events (SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED), set loading to false and notify
482
600
  this.authLoading = false;
483
601
 
484
- // Final check: Ensure state matches what we just logged
485
- logger.debug('AuthService', `State synchronized after event [ID:${this.instanceId}]`, {
486
- event,
487
- hasUser: !!this.user,
488
- userId: this.user?.id
489
- });
490
602
 
491
603
  this.notify();
492
604
  } catch (error) {