@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
@@ -38,15 +38,12 @@
38
38
  * ```
39
39
  */
40
40
 
41
- import { useEffect } from 'react';
41
+ import { useEffect, useContext } from 'react';
42
42
  import { useLocation } from 'react-router-dom';
43
- import { useEvents } from './useEvents';
43
+ import { EventServiceContext } from '../providers/services/EventServiceProvider';
44
44
  import { applyPalette, clearPalette } from '../theming/runtime';
45
45
  import { parseAndNormalizeEventColours } from '../theming/parseEventColours';
46
46
  import type { Event } from '../types/event';
47
- import { createLogger } from '../utils/core/logger';
48
-
49
- const log = createLogger('useEventTheme');
50
47
 
51
48
  /**
52
49
  * Hook that automatically applies event-specific theming
@@ -99,30 +96,21 @@ const log = createLogger('useEventTheme');
99
96
  * @param event - Optional event object for public page mode. If not provided, uses EventProvider context.
100
97
  */
101
98
  export function useEventTheme(event?: Event | null): void {
99
+ // Call all hooks unconditionally at the top level
100
+ // Hooks must be called in the same order on every render
102
101
  const location = useLocation();
103
102
 
104
- // Try to get event from EventProvider context (authenticated mode)
105
- // Only use useEvents() if event prop is not provided
106
- let selectedEvent: Event | null | undefined;
107
- try {
108
- if (event === undefined) {
109
- // No event prop provided, try to use EventProvider context
110
- const eventsContext = useEvents();
111
- selectedEvent = eventsContext.selectedEvent;
112
- } else {
113
- // Event prop provided, use it directly (public page mode)
114
- selectedEvent = event;
115
- }
116
- } catch (error) {
117
- // useEvents() throws if EventProvider is not available
118
- // This is expected for public pages - use the event prop if provided
119
- if (event !== undefined) {
120
- selectedEvent = event;
121
- } else {
122
- // No event prop and no EventProvider - can't apply theme
123
- selectedEvent = null;
124
- }
125
- }
103
+ // Use EventServiceContext directly to handle missing provider gracefully
104
+ // This allows the hook to work in public pages without EventProvider
105
+ const eventServiceContext = useContext(EventServiceContext);
106
+ const eventsContextSelectedEvent = eventServiceContext?.eventService?.getSelectedEvent() ?? null;
107
+
108
+ // Determine which event to use: event prop (public mode) or EventProvider context (authenticated mode)
109
+ // If event prop is provided, use it (public page mode)
110
+ // Otherwise, use EventProvider context (authenticated mode)
111
+ const selectedEvent = event !== undefined
112
+ ? event // Public page mode - use event prop
113
+ : eventsContextSelectedEvent; // Authenticated mode - use EventProvider context
126
114
 
127
115
  useEffect(() => {
128
116
  // Skip theme application when on login route
@@ -156,7 +144,7 @@ export function useEventTheme(event?: Event | null): void {
156
144
  try {
157
145
  applyPalette(normalized);
158
146
  } catch (error) {
159
- log.error('Failed to apply event palette:', error);
147
+ // Silently fail - theming is not critical
160
148
  }
161
149
 
162
150
  // Cleanup function to clear palette when component unmounts or event changes
@@ -26,8 +26,8 @@
26
26
  * FileCategory.EVENT_LOGOS
27
27
  * );
28
28
  *
29
- * if (isLoading) return <div>Loading...</div>;
30
- * if (error) return <div>Error: {error.message}</div>;
29
+ * if (isLoading) return <p>Loading...</p>;
30
+ * if (error) return <p>Error: {error.message}</p>;
31
31
  *
32
32
  * return fileUrl ? <img src={fileUrl} alt="File" /> : null;
33
33
  * }
@@ -106,13 +106,11 @@ export function useOrganisationPermissions(orgId?: string): UseOrganisationPermi
106
106
  ensureOrganisationContext
107
107
  } = useOrganisations();
108
108
 
109
- // Get super admin context if available (may not be available in all contexts)
110
- let superAdminContext: { isSuperAdmin: boolean } = { isSuperAdmin: false };
111
- try {
112
- superAdminContext = useOrganisationSecurity().superAdminContext;
113
- } catch {
114
- // Not available in this context, default to false
115
- }
109
+ // Call hook unconditionally - if provider is missing, it will throw
110
+ // Errors should be handled by error boundaries at a higher level
111
+ // We cannot conditionally call hooks
112
+ const organisationSecurity = useOrganisationSecurity();
113
+ const superAdminContext = organisationSecurity.superAdminContext;
116
114
 
117
115
  const organisationId = useMemo(() => {
118
116
  if (orgId) {
@@ -50,7 +50,6 @@ function runCacheCleanup() {
50
50
  // Initialize cleanup timer once
51
51
  if (typeof window !== 'undefined' && !cleanupTimer) {
52
52
  cleanupTimer = setInterval(runCacheCleanup, CLEANUP_INTERVAL_MS);
53
- log.debug('Query cache cleanup initialized.');
54
53
 
55
54
  // Cleanup on page unload to prevent memory leaks
56
55
  window.addEventListener('beforeunload', () => {
@@ -0,0 +1,380 @@
1
+ /**
2
+ * @file Session Draft Persistence Hook
3
+ * @package @jmruthers/pace-core
4
+ * @module Hooks/Persistence
5
+ * @since 1.0.0
6
+ *
7
+ * A React hook for persisting component state to sessionStorage.
8
+ * Provides automatic persistence that survives tab switching but clears on tab close.
9
+ *
10
+ * Features:
11
+ * - Debounced writes to prevent excessive storage operations
12
+ * - Automatic restoration on mount
13
+ * - Versioning support for schema migrations
14
+ * - Safe JSON serialization with error handling
15
+ * - Type-safe with generics
16
+ * - Handles storage quota errors gracefully
17
+ * - Optional cleanup on unmount
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const { state, setState, clearDraft } = useSessionDraft('my-component', {
22
+ * filters: [],
23
+ * sortBy: 'name'
24
+ * });
25
+ *
26
+ * // State is automatically persisted on changes
27
+ * setState({ filters: ['active'], sortBy: 'email' });
28
+ *
29
+ * // Clear draft when done
30
+ * clearDraft();
31
+ * ```
32
+ *
33
+ * @dependencies
34
+ * - React 19+ - Hooks and effects
35
+ */
36
+
37
+ import { useState, useEffect, useCallback, useRef } from 'react';
38
+
39
+ /**
40
+ * Schema version for stored drafts
41
+ * Increment when the data structure changes
42
+ */
43
+ const DRAFT_SCHEMA_VERSION = 1;
44
+
45
+ /**
46
+ * Default debounce delay in milliseconds
47
+ */
48
+ const DEFAULT_DEBOUNCE_MS = 300;
49
+
50
+ /**
51
+ * Stored draft payload structure
52
+ */
53
+ interface StoredDraft<T> {
54
+ version: number;
55
+ data: T;
56
+ timestamp: number;
57
+ }
58
+
59
+ /**
60
+ * Options for useSessionDraft hook
61
+ */
62
+ export interface UseSessionDraftOptions {
63
+ /**
64
+ * Debounce delay in milliseconds
65
+ * @default 300
66
+ */
67
+ debounceMs?: number;
68
+
69
+ /**
70
+ * Whether to clear draft on component unmount
71
+ * @default false
72
+ */
73
+ clearOnUnmount?: boolean;
74
+
75
+ /**
76
+ * Whether to enable persistence (useful for conditional persistence)
77
+ * @default true
78
+ */
79
+ enabled?: boolean;
80
+
81
+ /**
82
+ * Custom storage key prefix
83
+ * @default 'pace-core:draft'
84
+ */
85
+ keyPrefix?: string;
86
+ }
87
+
88
+ /**
89
+ * Return value of useSessionDraft hook
90
+ */
91
+ export interface UseSessionDraftReturn<T> {
92
+ /**
93
+ * Current draft state
94
+ */
95
+ state: T;
96
+
97
+ /**
98
+ * Update draft state (triggers debounced save)
99
+ */
100
+ setState: (newState: T | ((prev: T) => T)) => void;
101
+
102
+ /**
103
+ * Clear the draft from storage
104
+ */
105
+ clearDraft: () => void;
106
+
107
+ /**
108
+ * Force immediate save (bypasses debounce)
109
+ */
110
+ saveImmediately: () => void;
111
+
112
+ /**
113
+ * Whether the draft was restored from storage
114
+ */
115
+ wasRestored: boolean;
116
+ }
117
+
118
+ /**
119
+ * Debounce function that matches lodash debounce API
120
+ */
121
+ function debounce<T extends (...args: any[]) => void>(
122
+ func: T,
123
+ wait: number
124
+ ): T & { cancel: () => void } {
125
+ let timeoutId: ReturnType<typeof setTimeout> | null = null;
126
+
127
+ const debounced = ((...args: Parameters<T>) => {
128
+ if (timeoutId !== null) {
129
+ clearTimeout(timeoutId);
130
+ }
131
+ timeoutId = setTimeout(() => {
132
+ func(...args);
133
+ }, wait);
134
+ }) as T & { cancel: () => void };
135
+
136
+ debounced.cancel = () => {
137
+ if (timeoutId !== null) {
138
+ clearTimeout(timeoutId);
139
+ timeoutId = null;
140
+ }
141
+ };
142
+
143
+ return debounced;
144
+ }
145
+
146
+ /**
147
+ * Get the full storage key with namespace
148
+ */
149
+ function getStorageKey(key: string, prefix: string): string {
150
+ return `${prefix}:${key}`;
151
+ }
152
+
153
+ /**
154
+ * Save draft to sessionStorage
155
+ */
156
+ function saveDraft<T>(storageKey: string, data: T): boolean {
157
+ if (typeof window === 'undefined' || !window.sessionStorage) {
158
+ return false;
159
+ }
160
+
161
+ try {
162
+ const payload: StoredDraft<T> = {
163
+ version: DRAFT_SCHEMA_VERSION,
164
+ data,
165
+ timestamp: Date.now(),
166
+ };
167
+
168
+ const serialized = JSON.stringify(payload);
169
+ window.sessionStorage.setItem(storageKey, serialized);
170
+ return true;
171
+ } catch (error) {
172
+ // Handle quota exceeded or other storage errors
173
+ if (error instanceof DOMException && error.name === 'QuotaExceededError') {
174
+ console.warn(`[useSessionDraft] Storage quota exceeded for key: ${storageKey}`);
175
+ } else {
176
+ console.warn(`[useSessionDraft] Failed to save draft for key: ${storageKey}`, error);
177
+ }
178
+ return false;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Load draft from sessionStorage
184
+ */
185
+ function loadDraft<T>(storageKey: string): T | null {
186
+ if (typeof window === 'undefined' || !window.sessionStorage) {
187
+ return null;
188
+ }
189
+
190
+ try {
191
+ const stored = window.sessionStorage.getItem(storageKey);
192
+ if (!stored) {
193
+ return null;
194
+ }
195
+
196
+ const parsed = JSON.parse(stored) as StoredDraft<T>;
197
+
198
+ // Validate version
199
+ if (parsed.version !== DRAFT_SCHEMA_VERSION) {
200
+ console.warn(
201
+ `[useSessionDraft] Schema version mismatch for key: ${storageKey}. Expected ${DRAFT_SCHEMA_VERSION}, got ${parsed.version}`
202
+ );
203
+ // Clear outdated draft
204
+ window.sessionStorage.removeItem(storageKey);
205
+ return null;
206
+ }
207
+
208
+ // Validate structure
209
+ if (!parsed.data) {
210
+ console.warn(`[useSessionDraft] Invalid draft structure for key: ${storageKey}`);
211
+ window.sessionStorage.removeItem(storageKey);
212
+ return null;
213
+ }
214
+
215
+ return parsed.data;
216
+ } catch (error) {
217
+ console.warn(`[useSessionDraft] Failed to load draft for key: ${storageKey}`, error);
218
+ // Clear corrupted draft
219
+ try {
220
+ if (typeof window !== 'undefined' && window.sessionStorage) {
221
+ window.sessionStorage.removeItem(storageKey);
222
+ }
223
+ } catch {
224
+ // Ignore cleanup errors
225
+ }
226
+ return null;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Remove draft from sessionStorage
232
+ */
233
+ function removeDraft(storageKey: string): void {
234
+ if (typeof window === 'undefined' || !window.sessionStorage) {
235
+ return;
236
+ }
237
+
238
+ try {
239
+ window.sessionStorage.removeItem(storageKey);
240
+ } catch (error) {
241
+ console.warn(`[useSessionDraft] Failed to remove draft for key: ${storageKey}`, error);
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Hook for persisting component state to sessionStorage
247
+ *
248
+ * Automatically saves state changes with debouncing and restores state on mount.
249
+ * Data persists across tab switches but clears when the tab closes.
250
+ *
251
+ * @template T - The type of state to persist
252
+ * @param key - Unique key for this draft (will be namespaced automatically)
253
+ * @param initialState - Initial state if no draft exists
254
+ * @param options - Configuration options
255
+ * @returns Object containing state, setState, clearDraft, and utility functions
256
+ *
257
+ * @example
258
+ * ```tsx
259
+ * const { state, setState } = useSessionDraft('user-filters', {
260
+ * search: '',
261
+ * status: 'all'
262
+ * });
263
+ * ```
264
+ */
265
+ export function useSessionDraft<T>(
266
+ key: string,
267
+ initialState: T,
268
+ options: UseSessionDraftOptions = {}
269
+ ): UseSessionDraftReturn<T> {
270
+ const {
271
+ debounceMs = DEFAULT_DEBOUNCE_MS,
272
+ clearOnUnmount = false,
273
+ enabled = true,
274
+ keyPrefix = 'pace-core:draft',
275
+ } = options;
276
+
277
+ const storageKey = getStorageKey(key, keyPrefix);
278
+ const wasRestoredRef = useRef(false);
279
+
280
+ // Load draft on mount
281
+ const [state, setStateInternal] = useState<T>(() => {
282
+ if (!enabled) {
283
+ return initialState;
284
+ }
285
+
286
+ const restored = loadDraft<T>(storageKey);
287
+ if (restored !== null) {
288
+ wasRestoredRef.current = true;
289
+ return restored;
290
+ }
291
+
292
+ return initialState;
293
+ });
294
+
295
+ // Create debounced save function
296
+ const debouncedSaveRef = useRef<ReturnType<typeof debounce> | null>(null);
297
+
298
+ useEffect(() => {
299
+ if (!enabled) {
300
+ return;
301
+ }
302
+
303
+ // Create debounced save function
304
+ const debouncedSave = debounce((data: T) => {
305
+ saveDraft(storageKey, data);
306
+ }, debounceMs);
307
+
308
+ debouncedSaveRef.current = debouncedSave;
309
+
310
+ return () => {
311
+ // Cancel pending saves on unmount
312
+ debouncedSave.cancel();
313
+ };
314
+ }, [storageKey, debounceMs, enabled]);
315
+
316
+ // Save state changes
317
+ const setState = useCallback(
318
+ (newState: T | ((prev: T) => T)) => {
319
+ setStateInternal((prev) => {
320
+ const updated = typeof newState === 'function' ? (newState as (prev: T) => T)(prev) : newState;
321
+
322
+ // Save to storage (debounced)
323
+ if (enabled && debouncedSaveRef.current) {
324
+ debouncedSaveRef.current(updated);
325
+ }
326
+
327
+ return updated;
328
+ });
329
+ },
330
+ [enabled]
331
+ );
332
+
333
+ // Force immediate save
334
+ const saveImmediately = useCallback(() => {
335
+ if (!enabled) {
336
+ return;
337
+ }
338
+
339
+ // Cancel any pending debounced save
340
+ if (debouncedSaveRef.current) {
341
+ debouncedSaveRef.current.cancel();
342
+ }
343
+
344
+ // Save immediately
345
+ saveDraft(storageKey, state);
346
+ }, [storageKey, state, enabled]);
347
+
348
+ // Clear draft
349
+ const clearDraft = useCallback(() => {
350
+ removeDraft(storageKey);
351
+ setStateInternal(initialState);
352
+ wasRestoredRef.current = false;
353
+ }, [storageKey, initialState]);
354
+
355
+ // Cleanup on unmount if configured
356
+ useEffect(() => {
357
+ if (!enabled || !clearOnUnmount) {
358
+ return;
359
+ }
360
+
361
+ return () => {
362
+ // Cancel pending saves
363
+ if (debouncedSaveRef.current) {
364
+ debouncedSaveRef.current.cancel();
365
+ }
366
+
367
+ // Clear draft
368
+ removeDraft(storageKey);
369
+ };
370
+ }, [storageKey, clearOnUnmount, enabled]);
371
+
372
+ return {
373
+ state,
374
+ setState,
375
+ clearDraft,
376
+ saveImmediately,
377
+ wasRestored: wasRestoredRef.current,
378
+ };
379
+ }
380
+
@@ -16,7 +16,9 @@ import { createLogger } from '../utils/core/logger';
16
16
 
17
17
  const log = createLogger('useSessionRestoration');
18
18
 
19
- const SESSION_RESTORATION_TIMEOUT_MS = 5000;
19
+ // Increased timeout to handle hard refresh scenarios where localStorage access
20
+ // and session restoration may take longer, especially with organisation/event context
21
+ const SESSION_RESTORATION_TIMEOUT_MS = 10000; // 10 seconds (increased from 5)
20
22
 
21
23
  /**
22
24
  * Return value of the useSessionRestoration hook.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @file Icon Exports
3
+ * @package @jmruthers/pace-core
4
+ * @module Icons
5
+ * @since 0.6.6
6
+ *
7
+ * Icon exports from lucide-react.
8
+ * Import icons from '@jmruthers/pace-core/icons' instead of 'lucide-react'.
9
+ *
10
+ * All lucide-react icons are exported to ensure consuming apps have access
11
+ * to all icons while enforcing consistent usage through pace-core.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * // ✅ CORRECT: Import icons from pace-core
16
+ * import { ChevronDown, Edit, Trash } from '@jmruthers/pace-core/icons';
17
+ *
18
+ * // ❌ WRONG: Direct import from lucide-react (will fail - lucide-react is not a peer dependency)
19
+ * import { ChevronDown } from 'lucide-react';
20
+ * ```
21
+ */
22
+
23
+ // Export all icons from lucide-react
24
+ // This ensures consuming apps have access to all icons while enforcing
25
+ // consistent usage through pace-core's icon export system
26
+ export * from 'lucide-react';
27
+
package/src/index.ts CHANGED
@@ -212,6 +212,8 @@ export { z } from 'zod';
212
212
  // Common validation schemas
213
213
  export { emailSchema, nameSchema, phoneSchema, urlSchema } from './utils/validation/common';
214
214
  export { passwordSchema } from './utils/validation/passwordSchema';
215
+ // Sanitization utilities
216
+ export { sanitizeUserInput, sanitizeFormData, sanitizeHtml } from './utils/validation/sanitization';
215
217
 
216
218
  // LAYOUT COMPONENTS
217
219
  export { Header } from './components/Header/Header';
@@ -260,6 +262,9 @@ export { useEventTheme } from './hooks/useEventTheme';
260
262
  export { cn } from './utils/core/cn';
261
263
  export { setAppConfig, getAppConfig, getCurrentAppName, getCurrentAppId } from './utils/app/appConfig';
262
264
 
265
+ // Supabase client creation (restricted wrapper - ONLY for base client creation)
266
+ export { createBaseClient } from './utils/supabase/createBaseClient';
267
+
263
268
  // LOGGING UTILITIES
264
269
  export { Logger, logger, createLogger, LogLevel } from './utils/core/logger';
265
270
  export type { LoggerConfig } from './utils/core/logger';
@@ -13,9 +13,9 @@
13
13
  * for backward compatibility or standalone usage.
14
14
  */
15
15
 
16
- import React from 'react';
17
- import { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';
18
- import { OrganisationServiceProvider } from './services/OrganisationServiceProvider';
16
+ import React, { useContext } from 'react';
17
+ import { UnifiedAuthContext } from '../providers/services/UnifiedAuthProvider';
18
+ import { OrganisationServiceProvider, OrganisationServiceContext } from './services/OrganisationServiceProvider';
19
19
  import type { OrganisationProviderProps as BaseOrganisationProviderProps } from '../types/organisation';
20
20
 
21
21
  export interface OrganisationProviderProps extends BaseOrganisationProviderProps {
@@ -45,19 +45,29 @@ export function OrganisationProvider({
45
45
  autoSelectPrimaryOrganisation,
46
46
  onOrganisationChange
47
47
  }: OrganisationProviderProps) {
48
+ // Call all hooks unconditionally at the top level
49
+ // Hooks must be called in the same order on every render
50
+ // Check if we're already inside OrganisationServiceProvider (e.g., from UnifiedAuthProvider)
51
+ // If so, just pass through children to avoid double-wrapping
52
+ const existingOrganisationService = useContext(OrganisationServiceContext);
53
+
48
54
  // Get auth context from UnifiedAuthProvider
49
- const authContext = useUnifiedAuth();
55
+ // Use useContext directly instead of useUnifiedAuth() to avoid throwing
56
+ // if context isn't available yet (e.g., during initial render)
57
+ const authContext = useContext(UnifiedAuthContext);
50
58
 
51
- // If we're inside UnifiedAuthProvider, it already includes OrganisationServiceProvider
52
- // So we can just pass through the children
53
- // However, if the auth context is not available, we need to handle that
54
- if (!authContext) {
55
- // If no auth context, we can't provide organisation service
56
- // This might happen if used outside UnifiedAuthProvider
57
- // In that case, we should probably throw an error or show a message
58
- console.warn('OrganisationProvider: No auth context available. Make sure OrganisationProvider is used inside UnifiedAuthProvider.');
59
+ // Early return after all hooks have been called
60
+ if (existingOrganisationService) {
61
+ // We're already inside UnifiedAuthProvider which includes OrganisationServiceProvider
62
+ // Just pass through children - no need to wrap again
59
63
  return <>{children}</>;
60
64
  }
65
+
66
+ // If no auth context, we can't provide organisation service
67
+ // This might happen if used outside UnifiedAuthProvider
68
+ if (!authContext) {
69
+ throw new Error('OrganisationProvider must be used inside UnifiedAuthProvider, or OrganisationServiceProvider must be provided separately');
70
+ }
61
71
 
62
72
  const { supabase, user, session } = authContext;
63
73
 
@@ -66,8 +76,7 @@ export function OrganisationProvider({
66
76
  // are handled by the OrganisationService internally, not by the provider
67
77
  // Note: supabase is mapped to supabaseClient for OrganisationServiceProvider
68
78
  if (!supabase) {
69
- console.warn('OrganisationProvider: No supabase client available.');
70
- return <>{children}</>;
79
+ throw new Error('OrganisationProvider: No supabase client available from UnifiedAuthProvider');
71
80
  }
72
81
 
73
82
  return (