@jmruthers/pace-core 0.6.9 → 0.6.11

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 (1182) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/audit-tool/00-dependencies.cjs +46 -13
  3. package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
  4. package/audit-tool/audits/02-project-structure.cjs +74 -2
  5. package/audit-tool/audits/03-architecture.cjs +220 -20
  6. package/audit-tool/audits/04-code-quality.cjs +95 -3
  7. package/audit-tool/audits/05-styling.cjs +19 -7
  8. package/audit-tool/audits/06-security-rbac.cjs +214 -25
  9. package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
  10. package/audit-tool/audits/08-testing-documentation.cjs +11 -3
  11. package/audit-tool/audits/09-operations.cjs +19 -7
  12. package/audit-tool/index.cjs +22 -11
  13. package/audit-tool/utils/report-utils.cjs +4 -0
  14. package/cursor-rules/01-pace-core-compliance.mdc +1 -0
  15. package/cursor-rules/02-project-structure.mdc +3 -26
  16. package/cursor-rules/03-architecture.mdc +3 -1
  17. package/cursor-rules/04-code-quality.mdc +1 -0
  18. package/cursor-rules/05-styling.mdc +120 -8
  19. package/cursor-rules/06-security-rbac.mdc +126 -2
  20. package/cursor-rules/07-api-tech-stack.mdc +1 -0
  21. package/cursor-rules/08-testing-documentation.mdc +1 -0
  22. package/cursor-rules/09-operations.mdc +1 -0
  23. package/dist/DataTable-EFYP2QLE.js +16 -0
  24. package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
  25. package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
  26. package/dist/api-BZR2CYXL.js +5 -0
  27. package/dist/api-result-USV1Czr-.d.ts +51 -0
  28. package/dist/assets/app-icons/admin_favicon.svg +462 -0
  29. package/dist/assets/app-icons/base_favicon.svg +85 -0
  30. package/dist/assets/app-icons/cake_favicon.svg +68 -0
  31. package/dist/assets/app-icons/core_favicon.svg +256 -0
  32. package/dist/assets/app-icons/gear_favicon.svg +91 -0
  33. package/dist/assets/app-icons/medi_favicon.svg +92 -0
  34. package/dist/assets/app-icons/mint_favicon.svg +83 -0
  35. package/dist/assets/app-icons/pace_favicon.svg +49 -0
  36. package/dist/assets/app-icons/pump_favicon.svg +68 -0
  37. package/dist/assets/app-icons/seed_favicon.svg +91 -0
  38. package/dist/assets/app-icons/team_favicon.svg +67 -0
  39. package/dist/assets/app-icons/trac_favicon.svg +112 -0
  40. package/dist/assets/app-icons/trip_favicon.svg +102 -0
  41. package/dist/audit-HI2DHUVU.js +4 -0
  42. package/dist/auth-JvdRVaud.d.ts +49 -0
  43. package/dist/chunk-2DL2WSOE.js +327 -0
  44. package/dist/chunk-2OEVOGGR.js +9598 -0
  45. package/dist/chunk-44CNXN4P.js +15 -0
  46. package/dist/chunk-4R3T5ENU.js +2943 -0
  47. package/dist/chunk-7A6IMHH2.js +2321 -0
  48. package/dist/chunk-BTHN5MKC.js +121 -0
  49. package/dist/chunk-CU2BU2MQ.js +2 -0
  50. package/dist/chunk-D6BMFMQZ.js +200 -0
  51. package/dist/chunk-DDMPHZ3D.js +58 -0
  52. package/dist/chunk-ENLXB7GP.js +721 -0
  53. package/dist/chunk-J2KQK6DG.js +2159 -0
  54. package/dist/chunk-KJXRL3XE.js +6434 -0
  55. package/dist/chunk-L5LFKKLJ.js +61 -0
  56. package/dist/chunk-PCSHBLPB.js +811 -0
  57. package/dist/chunk-QRYSEPHB.js +429 -0
  58. package/dist/chunk-RMLY6KB5.js +187 -0
  59. package/dist/chunk-SACF5YSM.js +31 -0
  60. package/dist/chunk-UZNAFKGW.js +125 -0
  61. package/dist/chunk-V7FTM2LU.js +1080 -0
  62. package/dist/chunk-WY6Y7KC3.js +264 -0
  63. package/dist/chunk-XOJME5T7.js +407 -0
  64. package/dist/chunk-XPFVT3GN.js +492 -0
  65. package/dist/chunk-YFTFFJIV.js +529 -0
  66. package/dist/chunk-YYTWKVHO.js +1334 -0
  67. package/dist/components.d.ts +12 -89
  68. package/dist/components.js +23 -55
  69. package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
  70. package/dist/eslint-rules/index.cjs +3 -0
  71. package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
  72. package/dist/eslint-rules/rules/05-styling.cjs +507 -0
  73. package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
  74. package/dist/event-BfCox3N2.d.ts +265 -0
  75. package/dist/file-reference-DU1hcawx.d.ts +164 -0
  76. package/dist/functions-DH45k8ec.d.ts +208 -0
  77. package/dist/hooks.d.ts +28 -14
  78. package/dist/hooks.js +90 -56
  79. package/dist/icons/index.d.ts +1 -0
  80. package/dist/icons/index.js +1 -0
  81. package/dist/index.d.ts +392 -155
  82. package/dist/index.js +337 -347
  83. package/dist/pagination-BW1mqywp.d.ts +201 -0
  84. package/dist/papaparseLoader-WG2UXQ22.js +7 -0
  85. package/dist/providers.d.ts +29 -14
  86. package/dist/providers.js +7 -5
  87. package/dist/rbac/eslint-rules.js +2 -2
  88. package/dist/rbac/index.d.ts +180 -351
  89. package/dist/rbac/index.js +13 -11
  90. package/dist/theming/runtime.d.ts +28 -5
  91. package/dist/theming/runtime.js +2 -2
  92. package/dist/timezone-BTWWXKVY.d.ts +696 -0
  93. package/dist/types-BE2sEHKd.d.ts +55 -0
  94. package/dist/types-CvOPXWWZ.d.ts +111 -0
  95. package/dist/types-Dr8sNhER.d.ts +50 -0
  96. package/dist/types.d.ts +20 -13
  97. package/dist/types.js +1 -0
  98. package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
  99. package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
  100. package/dist/utils.d.ts +338 -156
  101. package/dist/utils.js +78 -60
  102. package/dist/validation-g5n0hDkh.d.ts +177 -0
  103. package/docs/api/modules.md +1226 -1094
  104. package/docs/api-reference/components.md +5 -5
  105. package/docs/api-reference/rpc-functions.md +12 -3
  106. package/docs/core-concepts/rbac-system.md +8 -0
  107. package/docs/getting-started/cursor-rules.md +17 -20
  108. package/docs/getting-started/dependencies.md +1 -1
  109. package/docs/getting-started/setup.md +235 -0
  110. package/docs/implementation-guides/authentication.md +27 -0
  111. package/docs/implementation-guides/data-tables.md +365 -10
  112. package/docs/migration/ApiResult-migration.md +25 -0
  113. package/docs/rbac/RBAC_CONTRACT.md +0 -12
  114. package/docs/rbac/api-reference.md +33 -31
  115. package/docs/standards/0-standards-overview.md +50 -15
  116. package/docs/standards/1-pace-core-compliance-standards.md +62 -57
  117. package/docs/standards/2-project-structure-standards.md +45 -90
  118. package/docs/standards/3-architecture-standards.md +41 -1
  119. package/docs/standards/4-code-quality-standards.md +26 -6
  120. package/docs/standards/5-styling-standards.md +35 -1
  121. package/docs/standards/6-security-rbac-standards.md +288 -7
  122. package/docs/standards/7-api-tech-stack-standards.md +116 -17
  123. package/docs/standards/8-testing-documentation-standards.md +31 -0
  124. package/docs/standards/9-operations-standards.md +19 -0
  125. package/docs/standards/README.md +20 -201
  126. package/docs/testing/README.md +10 -0
  127. package/docs/testing/test-setup-for-consumers.md +916 -0
  128. package/docs/troubleshooting/common-issues.md +17 -1
  129. package/docs/troubleshooting/organisation-context-setup.md +8 -0
  130. package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
  131. package/eslint-config-pace-core.cjs +24 -0
  132. package/package.json +14 -20
  133. package/scripts/build-docs.js +180 -0
  134. package/scripts/setup.cjs +536 -0
  135. package/scripts/validate.cjs +480 -0
  136. package/src/__mocks__/lucide-react.ts +0 -2
  137. package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
  138. package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
  139. package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
  140. package/src/__tests__/helpers/test-providers.test.tsx +99 -0
  141. package/src/__tests__/helpers/test-providers.tsx +37 -39
  142. package/src/__tests__/helpers/test-utils.test.tsx +447 -0
  143. package/src/__tests__/helpers/timer-utils.test.ts +371 -0
  144. package/src/assets/app-icons/admin_favicon.svg +462 -0
  145. package/src/assets/app-icons/base_favicon.svg +85 -0
  146. package/src/assets/app-icons/cake_favicon.svg +68 -0
  147. package/src/assets/app-icons/core_favicon.svg +256 -0
  148. package/src/assets/app-icons/gear_favicon.svg +91 -0
  149. package/src/assets/app-icons/index.test.ts +304 -0
  150. package/src/assets/app-icons/index.ts +83 -0
  151. package/src/assets/app-icons/medi_favicon.svg +92 -0
  152. package/src/assets/app-icons/mint_favicon.svg +83 -0
  153. package/src/assets/app-icons/pace_favicon.svg +49 -0
  154. package/src/assets/app-icons/pump_favicon.svg +68 -0
  155. package/src/assets/app-icons/seed_favicon.svg +91 -0
  156. package/src/assets/app-icons/team_favicon.svg +67 -0
  157. package/src/assets/app-icons/trac_favicon.svg +112 -0
  158. package/src/assets/app-icons/trip_favicon.svg +102 -0
  159. package/src/components/AddressField/AddressField.test.tsx +379 -4
  160. package/src/components/AddressField/AddressField.tsx +239 -213
  161. package/src/components/AddressField/types.ts +2 -2
  162. package/src/components/Alert/Alert.test.tsx +35 -25
  163. package/src/components/Alert/Alert.tsx +8 -8
  164. package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
  165. package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
  166. package/src/components/Avatar/Avatar.test.tsx +11 -1
  167. package/src/components/Avatar/Avatar.tsx +3 -2
  168. package/src/components/Badge/Badge.test.tsx +11 -1
  169. package/src/components/Button/Button.test.tsx +13 -3
  170. package/src/components/Button/Button.tsx +1 -1
  171. package/src/components/Calendar/Calendar.test.tsx +523 -131
  172. package/src/components/Calendar/Calendar.tsx +107 -488
  173. package/src/components/Card/Card.test.tsx +384 -258
  174. package/src/components/Card/Card.tsx +19 -10
  175. package/src/components/Checkbox/Checkbox.test.tsx +58 -174
  176. package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
  177. package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
  178. package/src/components/ContextSelector/ContextSelector.tsx +66 -280
  179. package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
  180. package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
  181. package/src/components/DataTable/AUDIT_REPORT.md +59 -44
  182. package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
  183. package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
  184. package/src/components/DataTable/DataTable.export.test.tsx +705 -0
  185. package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
  186. package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
  187. package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
  188. package/src/components/DataTable/DataTable.test.tsx +787 -416
  189. package/src/components/DataTable/DataTable.tsx +14 -14
  190. package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
  191. package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
  192. package/src/components/DataTable/DataTableCore.test.tsx +970 -0
  193. package/src/components/DataTable/README.md +155 -0
  194. package/src/components/DataTable/TESTING.md +101 -0
  195. package/src/components/DataTable/a11y.basic.test.tsx +788 -0
  196. package/src/components/DataTable/components/DataTableCore.tsx +126 -894
  197. package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
  198. package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
  199. package/src/components/DataTable/components/ImportModal.tsx +82 -408
  200. package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
  201. package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
  202. package/src/components/DataTable/context/DataTableContext.tsx +13 -13
  203. package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
  204. package/src/components/DataTable/core/ColumnFactory.ts +3 -3
  205. package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
  206. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
  207. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
  208. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
  209. package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
  210. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
  211. package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
  212. package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
  213. package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
  214. package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
  215. package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
  216. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
  217. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
  218. package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
  219. package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
  220. package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
  221. package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
  222. package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
  223. package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
  224. package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
  225. package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
  226. package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
  227. package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
  228. package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
  229. package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
  230. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
  231. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
  232. package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
  233. package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
  234. package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
  235. package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
  236. package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
  237. package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
  238. package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
  239. package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
  240. package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
  241. package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
  242. package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
  243. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
  244. package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
  245. package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
  246. package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
  247. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
  248. package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
  249. package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
  250. package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
  251. package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
  252. package/src/components/DataTable/index.ts +28 -5
  253. package/src/components/DataTable/keyboard.test.tsx +734 -0
  254. package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
  255. package/src/components/DataTable/pagination.modes.test.tsx +728 -0
  256. package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
  257. package/src/components/DataTable/styles.test.ts +379 -0
  258. package/src/components/DataTable/styles.ts +0 -1
  259. package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
  260. package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
  261. package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
  262. package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
  263. package/src/components/DataTable/test-utils.ts +94 -0
  264. package/src/components/DataTable/types/actions.ts +71 -0
  265. package/src/components/DataTable/types/base.ts +39 -0
  266. package/src/components/DataTable/types/columns.ts +125 -0
  267. package/src/components/DataTable/types/export.ts +32 -0
  268. package/src/components/DataTable/types/features.ts +81 -0
  269. package/src/components/DataTable/types/hierarchical.ts +44 -0
  270. package/src/components/DataTable/types/index.ts +43 -0
  271. package/src/components/DataTable/types/pagination.ts +85 -0
  272. package/src/components/DataTable/types/performance.ts +47 -0
  273. package/src/components/DataTable/types/props.ts +62 -0
  274. package/src/components/DataTable/types/rbac.ts +45 -0
  275. package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
  276. package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
  277. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
  278. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
  279. package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
  280. package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
  281. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
  282. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
  283. package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
  284. package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
  285. package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
  286. package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
  287. package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
  288. package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
  289. package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
  290. package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
  291. package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
  292. package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
  293. package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
  294. package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
  295. package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
  296. package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
  297. package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
  298. package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
  299. package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
  300. package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
  301. package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
  302. package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
  303. package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
  304. package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
  305. package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
  306. package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
  307. package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
  308. package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
  309. package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
  310. package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
  311. package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
  312. package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
  313. package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
  314. package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
  315. package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
  316. package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
  317. package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
  318. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
  319. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
  320. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
  321. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
  322. package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
  323. package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
  324. package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
  325. package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
  326. package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
  327. package/src/components/DataTable/utils/a11yUtils.ts +1 -1
  328. package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
  329. package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
  330. package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
  331. package/src/components/DataTable/utils/csvParse.test.ts +74 -0
  332. package/src/components/DataTable/utils/csvParse.ts +65 -0
  333. package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
  334. package/src/components/DataTable/utils/errorHandling.ts +3 -1
  335. package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
  336. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  337. package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
  338. package/src/components/DataTable/utils/flexibleImport.ts +3 -186
  339. package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
  340. package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
  341. package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
  342. package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
  343. package/src/components/DataTable/utils/importDateParser.ts +114 -0
  344. package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
  345. package/src/components/DataTable/utils/importValueParser.ts +91 -0
  346. package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
  347. package/src/components/DataTable/utils/paginationUtils.ts +7 -4
  348. package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
  349. package/src/components/DataTable/utils/performanceUtils.ts +1 -1
  350. package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
  351. package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
  352. package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
  353. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
  354. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
  355. package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
  356. package/src/components/DateTimeField/DateTimeField.tsx +1 -1
  357. package/src/components/Dialog/Dialog.test-utils.ts +49 -0
  358. package/src/components/Dialog/Dialog.test.tsx +2865 -458
  359. package/src/components/Dialog/Dialog.tsx +183 -986
  360. package/src/components/Dialog/dialogLock.test.ts +238 -0
  361. package/src/components/Dialog/dialogLock.ts +98 -0
  362. package/src/components/Dialog/index.ts +2 -0
  363. package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
  364. package/src/components/Dialog/useDialogDimensions.ts +140 -0
  365. package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
  366. package/src/components/Dialog/useDialogLifecycle.ts +135 -0
  367. package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
  368. package/src/components/Dialog/useDialogPersistence.ts +357 -0
  369. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
  370. package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
  371. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
  372. package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
  373. package/src/components/ErrorBoundary/index.ts +3 -4
  374. package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
  375. package/src/components/FileDisplay/FileDisplay.test.tsx +479 -247
  376. package/src/components/FileDisplay/FileDisplay.tsx +29 -659
  377. package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
  378. package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
  379. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
  380. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
  381. package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
  382. package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
  383. package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
  384. package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
  385. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
  386. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
  387. package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
  388. package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
  389. package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
  390. package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
  391. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
  392. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
  393. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
  394. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
  395. package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
  396. package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
  397. package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
  398. package/src/components/FileDisplay/fallbackUtils.ts +44 -0
  399. package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
  400. package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
  401. package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
  402. package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
  403. package/src/components/FileDisplay/index.tsx +1 -1
  404. package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
  405. package/src/components/FileDisplay/useFileDisplay.ts +515 -0
  406. package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
  407. package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
  408. package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
  409. package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
  410. package/src/components/FileUpload/FileUpload.test.tsx +69 -27
  411. package/src/components/FileUpload/FileUpload.tsx +112 -527
  412. package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
  413. package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
  414. package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
  415. package/src/components/FileUpload/index.tsx +1 -1
  416. package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
  417. package/src/components/FileUpload/useFileUploadManager.ts +454 -0
  418. package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
  419. package/src/components/FileUpload/useResolvedAppId.ts +77 -0
  420. package/src/components/Footer/Footer.test.tsx +15 -382
  421. package/src/components/Footer/Footer.tsx +8 -125
  422. package/src/components/Form/Form.test.tsx +425 -88
  423. package/src/components/Form/Form.tsx +91 -299
  424. package/src/components/Form/useFormPersistence.ts +257 -0
  425. package/src/components/Header/Header.test.tsx +653 -163
  426. package/src/components/Header/Header.tsx +62 -44
  427. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
  428. package/src/components/Input/Input.test.tsx +34 -120
  429. package/src/components/Input/Input.tsx +1 -1
  430. package/src/components/Label/Label.test.tsx +46 -45
  431. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
  432. package/src/components/LoginForm/LoginForm.test.tsx +0 -1
  433. package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
  434. package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
  435. package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
  436. package/src/components/NavigationMenu/index.ts +6 -1
  437. package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
  438. package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
  439. package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
  440. package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
  441. package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
  442. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
  443. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
  444. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
  445. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
  446. package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
  447. package/src/components/PaceAppLayout/README.md +0 -9
  448. package/src/components/PaceAppLayout/test-setup.tsx +15 -9
  449. package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
  450. package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
  451. package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
  452. package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
  453. package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
  454. package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
  455. package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
  456. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
  457. package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
  458. package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
  459. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
  460. package/src/components/Progress/Progress.test.tsx +127 -1
  461. package/src/components/Progress/Progress.tsx +1 -2
  462. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
  463. package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
  464. package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
  465. package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
  466. package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
  467. package/src/components/PublicLayout/PublicPageContext.ts +28 -0
  468. package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
  469. package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
  470. package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
  471. package/src/components/Select/Select.test.tsx +45 -8
  472. package/src/components/Select/Select.tsx +57 -40
  473. package/src/components/Select/context.test.tsx +56 -0
  474. package/src/components/Select/text.test.tsx +104 -0
  475. package/src/components/Select/text.ts +26 -0
  476. package/src/components/Select/types.ts +3 -0
  477. package/src/components/Select/useSelectEvents.test.ts +279 -0
  478. package/src/components/Select/useSelectEvents.ts +87 -0
  479. package/src/components/Select/useSelectSearch.test.tsx +295 -0
  480. package/src/components/Select/useSelectSearch.ts +91 -0
  481. package/src/components/Select/useSelectState.test.ts +268 -0
  482. package/src/components/Select/useSelectState.ts +104 -0
  483. package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
  484. package/src/components/Switch/Switch.test.tsx +57 -153
  485. package/src/components/Table/Table.test.tsx +395 -317
  486. package/src/components/Tabs/Tabs.test.tsx +270 -0
  487. package/src/components/Tabs/Tabs.tsx +4 -4
  488. package/src/components/Textarea/Textarea.test.tsx +11 -38
  489. package/src/components/Toast/Toast.test.tsx +425 -496
  490. package/src/components/Tooltip/Tooltip.test.tsx +4 -21
  491. package/src/components/UserMenu/UserMenu.test.tsx +1 -21
  492. package/src/components/UserMenu/UserMenu.tsx +0 -1
  493. package/src/components/index.test.ts +346 -0
  494. package/src/components/index.ts +12 -1
  495. package/src/constants/performance.test.ts +91 -0
  496. package/src/hooks/ServiceHooks.test.tsx +725 -0
  497. package/src/hooks/hooks.integration.test.tsx +608 -0
  498. package/src/hooks/index.ts +18 -3
  499. package/src/hooks/index.unit.test.ts +220 -0
  500. package/src/hooks/public/usePublicEvent.test.ts +304 -0
  501. package/src/hooks/public/usePublicEvent.ts +11 -11
  502. package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
  503. package/src/hooks/public/usePublicEventLogo.ts +2 -2
  504. package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
  505. package/src/hooks/public/usePublicRouteParams.ts +2 -2
  506. package/src/hooks/services/useAuth.ts +9 -7
  507. package/src/hooks/services/useAuthService.ts +1 -1
  508. package/src/hooks/services/useEventService.ts +1 -1
  509. package/src/hooks/useAccessibleApps.test.ts +400 -0
  510. package/src/hooks/useAccessibleApps.ts +264 -0
  511. package/src/hooks/useAddressAutocomplete.test.ts +170 -47
  512. package/src/hooks/useAddressAutocomplete.ts +109 -81
  513. package/src/hooks/useApiFetch.unit.test.ts +111 -0
  514. package/src/hooks/useAppConfig.ts +13 -3
  515. package/src/hooks/useAppConfig.unit.test.ts +712 -0
  516. package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
  517. package/src/hooks/useDataTablePerformance.ts +111 -130
  518. package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
  519. package/src/hooks/useDataTableState.test.ts +170 -0
  520. package/src/hooks/useDataTableState.ts +5 -5
  521. package/src/hooks/useDebounce.unit.test.ts +157 -0
  522. package/src/hooks/useEventTheme.test.ts +70 -18
  523. package/src/hooks/useEventTheme.ts +50 -22
  524. package/src/hooks/useEvents.ts +49 -2
  525. package/src/hooks/useEvents.unit.test.ts +227 -0
  526. package/src/hooks/useFileReference.test.ts +388 -107
  527. package/src/hooks/useFileReference.ts +184 -179
  528. package/src/hooks/useFileUrl.ts +1 -1
  529. package/src/hooks/useFileUrl.unit.test.ts +686 -0
  530. package/src/hooks/useFileUrlCache.test.ts +319 -0
  531. package/src/hooks/useFileUrlCache.ts +5 -2
  532. package/src/hooks/useFocusManagement.unit.test.ts +604 -0
  533. package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
  534. package/src/hooks/useFormDialog.test.ts +307 -0
  535. package/src/hooks/useFormDialog.ts +2 -2
  536. package/src/hooks/useInactivityTracker.ts +141 -134
  537. package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
  538. package/src/hooks/useIsMobile.unit.test.ts +317 -0
  539. package/src/hooks/useIsPrint.ts +62 -0
  540. package/src/hooks/useIsPrint.unit.test.ts +545 -0
  541. package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
  542. package/src/hooks/useOrganisationPermissions.test.ts +1 -2
  543. package/src/hooks/useOrganisationPermissions.ts +1 -4
  544. package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
  545. package/src/hooks/useOrganisationSecurity.test.ts +4 -33
  546. package/src/hooks/useOrganisationSecurity.ts +192 -203
  547. package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
  548. package/src/hooks/useOrganisations.ts +1 -1
  549. package/src/hooks/useOrganisations.unit.test.ts +369 -0
  550. package/src/hooks/usePerformanceMonitor.ts +1 -1
  551. package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
  552. package/src/hooks/usePermissionCache.test.ts +298 -329
  553. package/src/hooks/usePermissionCache.ts +277 -276
  554. package/src/hooks/usePreventTabReload.test.ts +307 -0
  555. package/src/hooks/usePublicEvent.simple.test.ts +794 -0
  556. package/src/hooks/usePublicEvent.test.ts +670 -0
  557. package/src/hooks/usePublicEvent.unit.test.ts +638 -0
  558. package/src/hooks/usePublicFileDisplay.test.ts +948 -0
  559. package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
  560. package/src/hooks/useQueryCache.test.ts +391 -0
  561. package/src/hooks/useQueryCache.ts +7 -9
  562. package/src/hooks/useRBAC.unit.test.ts +253 -0
  563. package/src/hooks/useSessionDraft.test.ts +556 -0
  564. package/src/hooks/useSessionDraft.ts +14 -11
  565. package/src/hooks/useSessionRestoration.ts +1 -1
  566. package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
  567. package/src/hooks/useStorage.ts +94 -54
  568. package/src/hooks/useStorage.unit.test.ts +684 -0
  569. package/src/hooks/useToast.test.ts +413 -0
  570. package/src/hooks/useToast.ts +2 -2
  571. package/src/hooks/useToast.unit.test.tsx +481 -0
  572. package/src/hooks/useZodForm.ts +3 -3
  573. package/src/hooks/useZodForm.unit.test.tsx +191 -0
  574. package/src/icons/index.test.ts +133 -0
  575. package/src/icons/index.ts +3 -1
  576. package/src/index.test.ts +528 -0
  577. package/src/index.ts +56 -9
  578. package/src/providers/AuthProvider.test.tsx +218 -0
  579. package/src/providers/EventProvider.test.tsx +487 -0
  580. package/src/providers/InactivityProvider.test-helper.tsx +40 -0
  581. package/src/providers/InactivityProvider.test.tsx +421 -0
  582. package/src/providers/ProviderLifecycle.test.tsx +308 -0
  583. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
  584. package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
  585. package/src/providers/index.test.ts +138 -0
  586. package/src/providers/services/AuthServiceContext.ts +27 -0
  587. package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
  588. package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
  589. package/src/providers/services/AuthServiceProvider.tsx +81 -20
  590. package/src/providers/services/EventServiceContext.ts +25 -0
  591. package/src/providers/services/EventServiceProvider.test.tsx +839 -0
  592. package/src/providers/services/EventServiceProvider.tsx +11 -20
  593. package/src/providers/services/InactivityServiceContext.ts +25 -0
  594. package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
  595. package/src/providers/services/InactivityServiceProvider.tsx +7 -17
  596. package/src/providers/services/OrganisationServiceContext.ts +25 -0
  597. package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
  598. package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
  599. package/src/providers/services/UnifiedAuthContext.ts +102 -0
  600. package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
  601. package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
  602. package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
  603. package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
  604. package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
  605. package/src/providers/services/contexts.test.tsx +281 -0
  606. package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
  607. package/src/providers/services/useUnifiedAuth.ts +29 -0
  608. package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
  609. package/src/providers/useInactivity.test-helper.ts +27 -0
  610. package/src/rbac/README.md +5 -5
  611. package/src/rbac/adapters.comprehensive.test.tsx +429 -0
  612. package/src/rbac/adapters.test.tsx +654 -0
  613. package/src/rbac/adapters.tsx +53 -38
  614. package/src/rbac/api.test.ts +986 -259
  615. package/src/rbac/api.ts +260 -216
  616. package/src/rbac/audit-batched.test.ts +550 -0
  617. package/src/rbac/audit-batched.ts +5 -4
  618. package/src/rbac/audit.test.ts +225 -28
  619. package/src/rbac/audit.ts +26 -18
  620. package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
  621. package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
  622. package/src/rbac/cache-invalidation.test.ts +715 -0
  623. package/src/rbac/cache-invalidation.ts +18 -15
  624. package/src/rbac/cache.test.ts +123 -63
  625. package/src/rbac/cache.ts +3 -4
  626. package/src/rbac/components/AccessDenied.test.tsx +324 -0
  627. package/src/rbac/components/AccessDenied.tsx +20 -18
  628. package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
  629. package/src/rbac/components/NavigationGuard.tsx +10 -8
  630. package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
  631. package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
  632. package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
  633. package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
  634. package/src/rbac/components/PagePermissionGuard.tsx +188 -381
  635. package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
  636. package/src/rbac/config.test.ts +131 -48
  637. package/src/rbac/config.ts +69 -26
  638. package/src/rbac/docs/event-based-apps.md +26 -13
  639. package/src/rbac/engine.comprehensive.test.ts +808 -0
  640. package/src/rbac/engine.test.ts +974 -130
  641. package/src/rbac/engine.ts +53 -13
  642. package/src/rbac/errors.test.ts +99 -87
  643. package/src/rbac/errors.ts +89 -55
  644. package/src/rbac/eslint-rules.js +2 -2
  645. package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
  646. package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
  647. package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
  648. package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
  649. package/src/rbac/hooks/permissions/useCan.ts +173 -253
  650. package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
  651. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
  652. package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
  653. package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
  654. package/src/rbac/hooks/useCan.test.ts +348 -32
  655. package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
  656. package/src/rbac/hooks/usePageGuardScope.ts +117 -0
  657. package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
  658. package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
  659. package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
  660. package/src/rbac/hooks/usePermissions.test.ts +459 -33
  661. package/src/rbac/hooks/usePermissions.ts +5 -7
  662. package/src/rbac/hooks/useRBAC.test.ts +1784 -21
  663. package/src/rbac/hooks/useRBAC.ts +148 -88
  664. package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
  665. package/src/rbac/hooks/useResolvedScope.ts +4 -1
  666. package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
  667. package/src/rbac/hooks/useResourcePermissions.ts +76 -140
  668. package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
  669. package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
  670. package/src/rbac/hooks/useRoleManagement.ts +158 -586
  671. package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
  672. package/src/rbac/hooks/useSecureSupabase.ts +21 -14
  673. package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
  674. package/src/rbac/index.test.ts +107 -0
  675. package/src/rbac/index.ts +32 -32
  676. package/src/rbac/performance.test.ts +451 -0
  677. package/src/rbac/permissions.test.ts +149 -68
  678. package/src/rbac/permissions.ts +0 -3
  679. package/src/rbac/rbac-core.test.tsx +276 -0
  680. package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
  681. package/src/rbac/rbac-engine-simplified.test.ts +252 -0
  682. package/src/rbac/rbac-functions.test.ts +703 -0
  683. package/src/rbac/rbac-integration.test.ts +523 -0
  684. package/src/rbac/rbac-role-isolation.test.ts +456 -0
  685. package/src/rbac/request-deduplication.test.ts +352 -0
  686. package/src/rbac/request-deduplication.ts +5 -4
  687. package/src/rbac/scenarios.user-role.test.tsx +271 -0
  688. package/src/rbac/secureClient.test.ts +499 -115
  689. package/src/rbac/secureClient.ts +54 -28
  690. package/src/rbac/security.test.ts +448 -44
  691. package/src/rbac/security.ts +7 -6
  692. package/src/rbac/types/roleManagement.ts +66 -0
  693. package/src/rbac/types.test.ts +236 -0
  694. package/src/rbac/types.ts +7 -5
  695. package/src/rbac/utils/clientSecurity.test.ts +192 -0
  696. package/src/rbac/utils/clientSecurity.ts +6 -4
  697. package/src/rbac/utils/contextValidator.test.ts +126 -0
  698. package/src/rbac/utils/contextValidator.ts +6 -3
  699. package/src/rbac/utils/deep-equal.test.ts +76 -0
  700. package/src/rbac/utils/eventContext.test.ts +401 -0
  701. package/src/rbac/utils/eventContext.ts +38 -34
  702. package/src/rbac/utils/fetchPermissionMap.ts +13 -0
  703. package/src/rbac/utils/permissionMapHelpers.ts +34 -0
  704. package/src/rbac/utils/roleManagementRpc.ts +303 -0
  705. package/src/services/AuthService.edge-cases.test.ts +746 -0
  706. package/src/services/AuthService.restoreSession.test.ts +59 -0
  707. package/src/services/AuthService.test.ts +1362 -0
  708. package/src/services/AuthService.ts +197 -216
  709. package/src/services/BaseService.edge-cases.test.ts +506 -0
  710. package/src/services/BaseService.test.ts +363 -0
  711. package/src/services/EventService.edge-cases.test.ts +636 -0
  712. package/src/services/EventService.eventColours.test.ts +64 -0
  713. package/src/services/EventService.test.ts +1250 -0
  714. package/src/services/EventService.ts +244 -315
  715. package/src/services/InactivityService.edge-cases.test.ts +492 -0
  716. package/src/services/InactivityService.lifecycle.test.ts +406 -0
  717. package/src/services/InactivityService.test.ts +829 -0
  718. package/src/services/InactivityService.ts +172 -213
  719. package/src/services/OrganisationService.edge-cases.test.ts +633 -0
  720. package/src/services/OrganisationService.pagination.test.ts +409 -0
  721. package/src/services/OrganisationService.test.ts +1579 -0
  722. package/src/services/OrganisationService.ts +186 -257
  723. package/src/services/base/BaseService.test.ts +214 -0
  724. package/src/services/interfaces/IAuthService.test.ts +184 -0
  725. package/src/services/interfaces/IAuthService.ts +10 -9
  726. package/src/services/interfaces/IEventService.test.ts +176 -0
  727. package/src/services/interfaces/IInactivityService.test.ts +183 -0
  728. package/src/services/interfaces/IOrganisationService.test.ts +207 -0
  729. package/src/services/interfaces/IOrganisationService.ts +0 -1
  730. package/src/styles/core.css +244 -12
  731. package/src/theming/parseEventColours.test.ts +321 -0
  732. package/src/theming/parseEventColours.ts +18 -9
  733. package/src/theming/runtime.test.ts +495 -0
  734. package/src/theming/runtime.ts +72 -7
  735. package/src/types/api-result.ts +53 -0
  736. package/src/types/auth.ts +0 -1
  737. package/src/types/core.test.ts +397 -0
  738. package/src/types/database-generated.test.ts +78 -0
  739. package/src/types/database.generated.ts +45 -10
  740. package/src/types/event.ts +39 -19
  741. package/src/types/file-reference.test.ts +351 -0
  742. package/src/types/file-reference.ts +37 -12
  743. package/src/types/guards.test.ts +246 -0
  744. package/src/types/index.test.ts +265 -0
  745. package/src/types/index.ts +3 -0
  746. package/src/types/organisation.roles.test.ts +55 -0
  747. package/src/types/organisation.test.ts +1105 -0
  748. package/src/types/organisation.ts +15 -15
  749. package/src/types/rpc-responses.ts +33 -0
  750. package/src/types/supabase.ts +14 -6
  751. package/src/types/theme.test.ts +830 -0
  752. package/src/types/type-validation.test.ts +526 -0
  753. package/src/types/validation.test.ts +729 -0
  754. package/src/types/vitest-globals.d.ts +1 -1
  755. package/src/utils/app/appConfig.test.ts +235 -0
  756. package/src/utils/app/appIdResolver.test.ts +252 -57
  757. package/src/utils/app/appIdResolver.ts +31 -20
  758. package/src/utils/app/appNameResolver.test.ts +18 -10
  759. package/src/utils/app/appNameResolver.ts +11 -9
  760. package/src/utils/app/appPortMap.test.ts +125 -0
  761. package/src/utils/app/appPortMap.ts +51 -0
  762. package/src/utils/app/buildAppUrl.test.ts +273 -0
  763. package/src/utils/app/buildAppUrl.ts +114 -0
  764. package/src/utils/appConfig.unit.test.ts +55 -0
  765. package/src/utils/audit/audit.test.ts +354 -39
  766. package/src/utils/audit.unit.test.ts +69 -0
  767. package/src/utils/auth-utils.unit.test.ts +69 -0
  768. package/src/utils/bundleAnalysis.unit.test.ts +326 -0
  769. package/src/utils/cn.unit.test.ts +34 -0
  770. package/src/utils/context/organisationContext.test.ts +115 -95
  771. package/src/utils/context/organisationContext.ts +32 -43
  772. package/src/utils/context/sessionTracking.test.ts +354 -0
  773. package/src/utils/core/cn.test.ts +66 -0
  774. package/src/utils/core/debugLogger.test.ts +113 -0
  775. package/src/utils/core/debugLogger.ts +15 -8
  776. package/src/utils/core/logger.test.ts +217 -0
  777. package/src/utils/core/logger.ts +20 -16
  778. package/src/utils/core/mergeRefs.ts +24 -0
  779. package/src/utils/debugLogger.test.ts +417 -0
  780. package/src/utils/device/deviceFingerprint.test.ts +8 -5
  781. package/src/utils/device/deviceFingerprint.ts +3 -3
  782. package/src/utils/deviceFingerprint.unit.test.ts +818 -0
  783. package/src/utils/dynamic/createLazyComponent.tsx +46 -0
  784. package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
  785. package/src/utils/dynamic/dynamicUtils.ts +6 -6
  786. package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
  787. package/src/utils/dynamic/lazyLoad.tsx +8 -36
  788. package/src/utils/dynamic/papaparseLoader.ts +7 -0
  789. package/src/utils/dynamicUtils.unit.test.ts +331 -0
  790. package/src/utils/file-reference/file-reference.test.ts +1238 -0
  791. package/src/utils/file-reference/index.ts +330 -348
  792. package/src/utils/formatDate.unit.test.ts +109 -0
  793. package/src/utils/formatting/formatDate.test.ts +22 -148
  794. package/src/utils/formatting/formatDateTime.test.ts +41 -119
  795. package/src/utils/formatting/formatDateTimeTimezone.test.ts +41 -85
  796. package/src/utils/formatting/formatNumber.test.ts +259 -0
  797. package/src/utils/formatting/formatTime.test.ts +36 -128
  798. package/src/utils/formatting/formatting.ts +1 -1
  799. package/src/utils/formatting.unit.test.ts +99 -0
  800. package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
  801. package/src/utils/google-places/googlePlacesUtils.ts +67 -86
  802. package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
  803. package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
  804. package/src/utils/index.ts +52 -11
  805. package/src/utils/index.unit.test.ts +251 -0
  806. package/src/utils/lazyLoad.unit.test.tsx +319 -0
  807. package/src/utils/location/location.test.ts +19 -116
  808. package/src/utils/logger.unit.test.ts +398 -0
  809. package/src/utils/organisationContext.unit.test.ts +180 -0
  810. package/src/utils/performance/bundleAnalysis.test.ts +148 -0
  811. package/src/utils/performance/bundleAnalysis.ts +16 -22
  812. package/src/utils/performance/performanceBenchmark.test.ts +251 -0
  813. package/src/utils/performance/performanceBenchmark.ts +12 -4
  814. package/src/utils/performance/performanceBudgets.test.ts +241 -0
  815. package/src/utils/performance/performanceBudgets.ts +9 -6
  816. package/src/utils/performanceBenchmark.test.ts +174 -0
  817. package/src/utils/performanceBudgets.unit.test.ts +288 -0
  818. package/src/utils/permissionTypes.unit.test.ts +250 -0
  819. package/src/utils/permissionUtils.unit.test.ts +362 -0
  820. package/src/utils/permissions/permissionTypes.test.ts +149 -0
  821. package/src/utils/permissions/permissionUtils.test.ts +20 -42
  822. package/src/utils/persistence/keyDerivation.test.ts +306 -0
  823. package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
  824. package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
  825. package/src/utils/request-deduplication.test.ts +349 -0
  826. package/src/utils/request-deduplication.ts +6 -4
  827. package/src/utils/sanitization.unit.test.ts +346 -0
  828. package/src/utils/schemaUtils.unit.test.ts +441 -0
  829. package/src/utils/secureDataAccess.unit.test.ts +334 -0
  830. package/src/utils/secureErrors.unit.test.ts +390 -0
  831. package/src/utils/secureStorage.unit.test.ts +289 -0
  832. package/src/utils/security/auth-utils.ts +38 -27
  833. package/src/utils/security/secureDataAccess.test.ts +22 -191
  834. package/src/utils/security/secureDataAccess.ts +241 -281
  835. package/src/utils/security/secureErrors.test.ts +163 -0
  836. package/src/utils/security/secureStorage.test.ts +156 -0
  837. package/src/utils/security/secureStorage.ts +1 -1
  838. package/src/utils/security/security.test.ts +212 -0
  839. package/src/utils/security/security.ts +15 -18
  840. package/src/utils/security/securityMonitor.test.ts +90 -0
  841. package/src/utils/security/securityMonitor.ts +1 -1
  842. package/src/utils/security.unit.test.ts +155 -0
  843. package/src/utils/securityMonitor.unit.test.ts +276 -0
  844. package/src/utils/sessionTracking.unit.test.ts +218 -0
  845. package/src/utils/storage/config.unit.test.ts +239 -0
  846. package/src/utils/storage/helpers.test.ts +769 -456
  847. package/src/utils/storage/helpers.ts +174 -253
  848. package/src/utils/storage/index.unit.test.ts +68 -0
  849. package/src/utils/storage/storageUtils.ts +32 -0
  850. package/src/utils/storage/types.ts +9 -2
  851. package/src/utils/supabase/createBaseClient.test.ts +201 -0
  852. package/src/utils/supabase/createBaseClient.ts +2 -1
  853. package/src/utils/timezone/timezone.test.ts +26 -44
  854. package/src/utils/timezone.test.ts +345 -0
  855. package/src/utils/validation/common.test.ts +115 -0
  856. package/src/utils/validation/csrf.test.ts +198 -0
  857. package/src/utils/validation/csrf.ts +42 -41
  858. package/src/utils/validation/htmlSanitization.ts +27 -31
  859. package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
  860. package/src/utils/validation/passwordSchema.test.ts +164 -0
  861. package/src/utils/validation/schema.test.ts +127 -0
  862. package/src/utils/validation/schema.ts +6 -3
  863. package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
  864. package/src/utils/validation/sqlInjectionProtection.ts +2 -2
  865. package/src/utils/validation/user.test.ts +173 -0
  866. package/src/utils/validation/validation.test.ts +197 -0
  867. package/src/utils/validation/validationUtils.test.ts +294 -0
  868. package/src/utils/validation.unit.test.ts +307 -0
  869. package/src/utils/validationUtils.unit.test.ts +558 -0
  870. package/src/vite-env.d.ts +6 -0
  871. package/dist/AuthService-DmfO5rGS.d.ts +0 -524
  872. package/dist/DataTable-DRUIgtUH.d.ts +0 -166
  873. package/dist/DataTable-SOAFXIWY.js +0 -15
  874. package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
  875. package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
  876. package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
  877. package/dist/api-7P7DI652.js +0 -4
  878. package/dist/audit-MYQXYZFU.js +0 -3
  879. package/dist/auth-BZOJqrdd.d.ts +0 -49
  880. package/dist/chunk-4DDCYDQ3.js +0 -544
  881. package/dist/chunk-5HNSDQWH.js +0 -5046
  882. package/dist/chunk-5W2A3DRC.js +0 -164
  883. package/dist/chunk-6GLLNA6U.js +0 -31
  884. package/dist/chunk-7ILTDCL2.js +0 -80
  885. package/dist/chunk-A3W6LW53.js +0 -70
  886. package/dist/chunk-AHU7G2R5.js +0 -423
  887. package/dist/chunk-C7ZQ5O4C.js +0 -481
  888. package/dist/chunk-EF2UGZWY.js +0 -611
  889. package/dist/chunk-FEJLJNWA.js +0 -181
  890. package/dist/chunk-FYHN4DD5.js +0 -415
  891. package/dist/chunk-GS5672WG.js +0 -2003
  892. package/dist/chunk-HF6O3O37.js +0 -187
  893. package/dist/chunk-J2U36LHD.js +0 -8517
  894. package/dist/chunk-LX6U42O3.js +0 -2177
  895. package/dist/chunk-MPBLMWVR.js +0 -2161
  896. package/dist/chunk-OJ4SKRSV.js +0 -105
  897. package/dist/chunk-S6ZQKDY6.js +0 -62
  898. package/dist/chunk-S7DKJPLT.js +0 -699
  899. package/dist/chunk-T5CVK4R3.js +0 -2816
  900. package/dist/chunk-TTRFSOKR.js +0 -121
  901. package/dist/chunk-Z2FNRKF3.js +0 -994
  902. package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
  903. package/dist/event-CW5YB_2p.d.ts +0 -239
  904. package/dist/file-reference-BavO2eQj.d.ts +0 -148
  905. package/dist/functions-lBy5L2ry.d.ts +0 -208
  906. package/dist/timezone-0AyangqX.d.ts +0 -697
  907. package/dist/types-BeoeWV5I.d.ts +0 -110
  908. package/dist/types-DXstZpNI.d.ts +0 -614
  909. package/dist/types-t9H8qKRw.d.ts +0 -55
  910. package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
  911. package/dist/useToast-AyaT-x7p.d.ts +0 -68
  912. package/dist/validation-643vUDZW.d.ts +0 -177
  913. package/scripts/build-docs-incremental.js +0 -179
  914. package/scripts/eslint-audit.cjs +0 -123
  915. package/scripts/generate-docs.js +0 -157
  916. package/scripts/install-cursor-rules.cjs +0 -255
  917. package/scripts/install-eslint-config.cjs +0 -349
  918. package/scripts/setup-build-cache.js +0 -73
  919. package/scripts/validate-pre-publish.js +0 -145
  920. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
  921. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
  922. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
  923. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
  924. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
  925. package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
  926. package/src/__tests__/hooks/usePermissions.test.ts +0 -268
  927. package/src/__tests__/integration/UserProfile.test.tsx +0 -124
  928. package/src/__tests__/public-recipe-view.test.ts +0 -228
  929. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
  930. package/src/__tests__/rls-policies.test.ts +0 -471
  931. package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
  932. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
  933. package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
  934. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
  935. package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
  936. package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
  937. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
  938. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
  939. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
  940. package/src/components/DataTable/__tests__/README.md +0 -145
  941. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
  942. package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
  943. package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
  944. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
  945. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
  946. package/src/components/DataTable/__tests__/styles.test.ts +0 -382
  947. package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
  948. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
  949. package/src/components/DataTable/__tests__/test-utils.ts +0 -94
  950. package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
  951. package/src/components/DataTable/components/ActionButtons.tsx +0 -190
  952. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
  953. package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
  954. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
  955. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
  956. package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
  957. package/src/components/DataTable/components/DataTableModals.tsx +0 -245
  958. package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
  959. package/src/components/DataTable/components/EditFields.tsx +0 -327
  960. package/src/components/DataTable/components/EditableRow.tsx +0 -462
  961. package/src/components/DataTable/components/EmptyState.tsx +0 -79
  962. package/src/components/DataTable/components/FilterRow.tsx +0 -141
  963. package/src/components/DataTable/components/LoadingState.tsx +0 -17
  964. package/src/components/DataTable/components/PaginationControls.tsx +0 -289
  965. package/src/components/DataTable/components/RowComponent.tsx +0 -403
  966. package/src/components/DataTable/components/SortIndicator.tsx +0 -50
  967. package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
  968. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
  969. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
  970. package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
  971. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
  972. package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
  973. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
  974. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
  975. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
  976. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
  977. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
  978. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
  979. package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
  980. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
  981. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
  982. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
  983. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
  984. package/src/components/DataTable/components/cellValueUtils.ts +0 -40
  985. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
  986. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
  987. package/src/components/DataTable/components/index.ts +0 -16
  988. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
  989. package/src/components/DataTable/core/ActionManager.ts +0 -235
  990. package/src/components/DataTable/core/ColumnManager.ts +0 -205
  991. package/src/components/DataTable/core/DataManager.ts +0 -188
  992. package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
  993. package/src/components/DataTable/core/PluginRegistry.ts +0 -229
  994. package/src/components/DataTable/core/StateManager.ts +0 -312
  995. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
  996. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
  997. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
  998. package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
  999. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
  1000. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
  1001. package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
  1002. package/src/components/DataTable/core/index.ts +0 -1
  1003. package/src/components/DataTable/core/interfaces.ts +0 -338
  1004. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
  1005. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
  1006. package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
  1007. package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
  1008. package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
  1009. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
  1010. package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
  1011. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
  1012. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
  1013. package/src/components/DataTable/hooks/index.ts +0 -13
  1014. package/src/components/DataTable/types.ts +0 -761
  1015. package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
  1016. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
  1017. package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
  1018. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
  1019. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
  1020. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
  1021. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
  1022. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
  1023. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
  1024. package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
  1025. package/src/components/DataTable/utils/index.ts +0 -10
  1026. package/src/components/PublicLayout/index.ts +0 -32
  1027. package/src/components/Select/hooks/useSelectEvents.ts +0 -87
  1028. package/src/components/Select/hooks/useSelectSearch.ts +0 -91
  1029. package/src/components/Select/hooks/useSelectState.ts +0 -104
  1030. package/src/components/Select/utils/text.ts +0 -26
  1031. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
  1032. package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
  1033. package/src/hooks/__tests__/index.unit.test.ts +0 -220
  1034. package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
  1035. package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
  1036. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
  1037. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
  1038. package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
  1039. package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
  1040. package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
  1041. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
  1042. package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
  1043. package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
  1044. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
  1045. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
  1046. package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
  1047. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
  1048. package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
  1049. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
  1050. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
  1051. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
  1052. package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
  1053. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
  1054. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  1055. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  1056. package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
  1057. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
  1058. package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
  1059. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
  1060. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
  1061. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
  1062. package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
  1063. package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
  1064. package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
  1065. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
  1066. package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
  1067. package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
  1068. package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
  1069. package/src/hooks/public/index.ts +0 -36
  1070. package/src/hooks/public/usePublicFileDisplay.ts +0 -504
  1071. package/src/hooks/useFileDisplay.ts +0 -715
  1072. package/src/providers/OrganisationProvider.tsx +0 -92
  1073. package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
  1074. package/src/providers/__tests__/EventProvider.test.tsx +0 -551
  1075. package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
  1076. package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
  1077. package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
  1078. package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
  1079. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
  1080. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
  1081. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
  1082. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
  1083. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
  1084. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
  1085. package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
  1086. package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
  1087. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
  1088. package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
  1089. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
  1090. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
  1091. package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
  1092. package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
  1093. package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
  1094. package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
  1095. package/src/rbac/audit-enhanced.ts +0 -384
  1096. package/src/rbac/compliance/database-validator.ts +0 -165
  1097. package/src/rbac/compliance/index.ts +0 -48
  1098. package/src/rbac/compliance/pattern-detector.ts +0 -553
  1099. package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
  1100. package/src/rbac/compliance/runtime-compliance.ts +0 -99
  1101. package/src/rbac/compliance/setup-validator.ts +0 -131
  1102. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +0 -975
  1103. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
  1104. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
  1105. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
  1106. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
  1107. package/src/rbac/components/index.ts +0 -26
  1108. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
  1109. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
  1110. package/src/rbac/hooks/index.ts +0 -34
  1111. package/src/rbac/hooks/permissions/index.ts +0 -4
  1112. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  1113. package/src/rbac/utils/__tests__/contextValidator.test.ts +0 -128
  1114. package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
  1115. package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
  1116. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
  1117. package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
  1118. package/src/services/__tests__/AuthService.test.ts +0 -1332
  1119. package/src/services/__tests__/BaseService.test.ts +0 -314
  1120. package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
  1121. package/src/services/__tests__/EventService.test.ts +0 -1025
  1122. package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
  1123. package/src/services/__tests__/InactivityService.test.ts +0 -654
  1124. package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
  1125. package/src/services/__tests__/OrganisationService.test.ts +0 -1176
  1126. package/src/theming/__tests__/parseEventColours.test.ts +0 -321
  1127. package/src/theming/__tests__/runtime.test.ts +0 -569
  1128. package/src/types/__tests__/file-reference.test.ts +0 -447
  1129. package/src/types/__tests__/guards.test.ts +0 -246
  1130. package/src/types/__tests__/organisation.roles.test.ts +0 -55
  1131. package/src/types/__tests__/organisation.test.ts +0 -1133
  1132. package/src/types/__tests__/theme.test.ts +0 -830
  1133. package/src/types/__tests__/type-validation.test.ts +0 -526
  1134. package/src/types/__tests__/validation.test.ts +0 -731
  1135. package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
  1136. package/src/utils/__tests__/audit.unit.test.ts +0 -69
  1137. package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
  1138. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
  1139. package/src/utils/__tests__/cn.unit.test.ts +0 -34
  1140. package/src/utils/__tests__/debugLogger.test.ts +0 -417
  1141. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
  1142. package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
  1143. package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
  1144. package/src/utils/__tests__/formatting.unit.test.ts +0 -99
  1145. package/src/utils/__tests__/index.unit.test.ts +0 -251
  1146. package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
  1147. package/src/utils/__tests__/logger.unit.test.ts +0 -398
  1148. package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
  1149. package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
  1150. package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
  1151. package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
  1152. package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
  1153. package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
  1154. package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
  1155. package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
  1156. package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
  1157. package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
  1158. package/src/utils/__tests__/security.unit.test.ts +0 -149
  1159. package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
  1160. package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
  1161. package/src/utils/__tests__/timezone.test.ts +0 -345
  1162. package/src/utils/__tests__/validation.unit.test.ts +0 -308
  1163. package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
  1164. package/src/utils/app/appNameResolver.simple.test.ts +0 -212
  1165. package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
  1166. package/src/utils/google-places/index.ts +0 -26
  1167. package/src/utils/location/index.ts +0 -16
  1168. package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
  1169. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
  1170. package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
  1171. package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
  1172. package/src/utils/storage/index.ts +0 -67
  1173. package/src/utils/timezone/index.ts +0 -17
  1174. package/src/utils/validation/__tests__/csrf.test.ts +0 -105
  1175. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
  1176. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
  1177. package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
  1178. package/src/utils/validation/index.ts +0 -73
  1179. /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
  1180. /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
  1181. /package/src/providers/{__tests__/README.md → README.md} +0 -0
  1182. /package/src/types/{__tests__/README.md → README.md} +0 -0
@@ -0,0 +1,2943 @@
1
+ import { setPrintAppName } from './chunk-D6BMFMQZ.js';
2
+ import { isRBACInitialized, setupRBAC } from './chunk-7A6IMHH2.js';
3
+ import { assertOrganisationId, assertUserId } from './chunk-4SXLQIZO.js';
4
+ import { secureStorage } from './chunk-RMLY6KB5.js';
5
+ import { createLogger, logger } from './chunk-BTHN5MKC.js';
6
+ import { err, ok } from './chunk-44CNXN4P.js';
7
+ import { createContext, useRef, useEffect, useState, useMemo, useContext, useReducer, useCallback } from 'react';
8
+ import { AuthError } from '@supabase/supabase-js';
9
+ import { jsx } from 'react/jsx-runtime';
10
+ import { flushSync } from 'react-dom';
11
+
12
+ // src/services/base/BaseService.ts
13
+ var BaseService = class {
14
+ constructor() {
15
+ this.subscribers = [];
16
+ this.isInitialized = false;
17
+ }
18
+ /**
19
+ * Subscribe to state changes
20
+ * @param callback Function to call when state changes
21
+ * @returns Unsubscribe function
22
+ */
23
+ subscribe(callback) {
24
+ this.subscribers.push(callback);
25
+ return () => {
26
+ const index = this.subscribers.indexOf(callback);
27
+ if (index > -1) {
28
+ this.subscribers.splice(index, 1);
29
+ }
30
+ };
31
+ }
32
+ /**
33
+ * Notify all subscribers of state changes
34
+ * This triggers React re-renders
35
+ */
36
+ notify() {
37
+ this.subscribers.forEach((callback) => {
38
+ try {
39
+ callback();
40
+ } catch (error) {
41
+ logger.error("BaseService", "Error in subscriber callback:", error);
42
+ }
43
+ });
44
+ }
45
+ /**
46
+ * Initialize the service
47
+ * Override in subclasses to implement initialization logic
48
+ */
49
+ async initialize() {
50
+ if (this.isInitialized) {
51
+ return;
52
+ }
53
+ await this.doInitialize();
54
+ this.isInitialized = true;
55
+ }
56
+ /**
57
+ * Cleanup the service
58
+ * Override in subclasses to implement cleanup logic
59
+ */
60
+ cleanup() {
61
+ this.subscribers = [];
62
+ this.doCleanup();
63
+ this.isInitialized = false;
64
+ }
65
+ /**
66
+ * Check if service is initialized
67
+ */
68
+ getInitialized() {
69
+ return this.isInitialized;
70
+ }
71
+ /**
72
+ * Reset initialization state (allows re-initialization)
73
+ * Use when dependencies change and service needs to re-initialize
74
+ */
75
+ resetInitialization() {
76
+ this.isInitialized = false;
77
+ }
78
+ };
79
+
80
+ // src/utils/security/auth-utils.ts
81
+ function toApiError(error) {
82
+ const message = error.message || "An unexpected error occurred";
83
+ const code = "status" in error && typeof error.status === "number" ? `AUTH_${error.status}` : error.name || "AUTH_ERROR";
84
+ return { code, message };
85
+ }
86
+
87
+ // src/services/AuthService.ts
88
+ var _AuthService = class _AuthService extends BaseService {
89
+ // Track previous auth state to detect transitions
90
+ constructor(supabaseClient, appName) {
91
+ super();
92
+ this.user = null;
93
+ this.session = null;
94
+ this.authLoading = false;
95
+ this.authError = null;
96
+ this.supabaseClient = null;
97
+ this.authStateSubscription = null;
98
+ this.sessionRestorationState = {
99
+ isRestoring: false,
100
+ restorationComplete: false,
101
+ restorationError: null
102
+ };
103
+ this.restorationTimeoutId = null;
104
+ // Increased timeout to handle hard refresh scenarios where localStorage access
105
+ // and session restoration may take longer, especially with organisation/event context
106
+ this.restorationTimeoutMs = 1e4;
107
+ // 10 seconds (increased from 5)
108
+ this.restorationStartTime = null;
109
+ this.appName = void 0;
110
+ this.errorHandler = null;
111
+ this.unhandledRejectionHandler = null;
112
+ this.wasAuthenticatedRef = false;
113
+ this.instanceId = ++_AuthService.instanceCount;
114
+ this.supabaseClient = supabaseClient;
115
+ this.appName = appName;
116
+ }
117
+ getInstanceId() {
118
+ return this.instanceId;
119
+ }
120
+ // Auth state getters
121
+ getUser() {
122
+ return this.user;
123
+ }
124
+ getSession() {
125
+ return this.session;
126
+ }
127
+ isAuthenticated() {
128
+ return !!(this.user && this.session);
129
+ }
130
+ isLoading() {
131
+ return this.authLoading;
132
+ }
133
+ getError() {
134
+ return this.authError;
135
+ }
136
+ getSupabaseClient() {
137
+ return this.supabaseClient;
138
+ }
139
+ getSessionRestorationState() {
140
+ return { ...this.sessionRestorationState };
141
+ }
142
+ // Auth methods
143
+ async signIn(email, password) {
144
+ if (!this.supabaseClient) {
145
+ const error = new AuthError("Supabase client not available");
146
+ this.authError = error;
147
+ this.notify();
148
+ return err(toApiError(error));
149
+ }
150
+ try {
151
+ const { data, error } = await this.supabaseClient.auth.signInWithPassword({
152
+ email,
153
+ password: password || ""
154
+ });
155
+ if (error) {
156
+ this.authError = error;
157
+ this.user = null;
158
+ this.session = null;
159
+ this.notify();
160
+ return err(toApiError(error));
161
+ }
162
+ this.authError = null;
163
+ this.user = data.user;
164
+ this.session = data.session;
165
+ if (!this.wasAuthenticatedRef && data.user) {
166
+ this.clearPersistenceOnLogin(null, true);
167
+ this.wasAuthenticatedRef = true;
168
+ }
169
+ this.notify();
170
+ return ok({ user: data.user, session: data.session });
171
+ } catch (error) {
172
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
173
+ this.authError = authError;
174
+ this.user = null;
175
+ this.session = null;
176
+ this.notify();
177
+ return err(toApiError(authError));
178
+ }
179
+ }
180
+ async signUp(email, password) {
181
+ if (!this.supabaseClient) {
182
+ const error = new AuthError("Supabase client not available");
183
+ this.authError = error;
184
+ this.notify();
185
+ return err(toApiError(error));
186
+ }
187
+ try {
188
+ const { data, error } = await this.supabaseClient.auth.signUp({
189
+ email,
190
+ password
191
+ });
192
+ if (error) {
193
+ this.authError = error;
194
+ this.user = null;
195
+ this.session = null;
196
+ this.notify();
197
+ return err(toApiError(error));
198
+ }
199
+ this.authError = null;
200
+ this.user = data.user;
201
+ this.session = data.session;
202
+ if (!this.wasAuthenticatedRef && data.user) {
203
+ this.clearPersistenceOnLogin(null, true);
204
+ this.wasAuthenticatedRef = true;
205
+ }
206
+ this.notify();
207
+ return ok({ user: data.user, session: data.session });
208
+ } catch (error) {
209
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
210
+ this.authError = authError;
211
+ this.user = null;
212
+ this.session = null;
213
+ this.notify();
214
+ return err(toApiError(authError));
215
+ }
216
+ }
217
+ async signOut() {
218
+ if (!this.supabaseClient) {
219
+ const error = new AuthError("Supabase client not available");
220
+ this.authError = error;
221
+ this.notify();
222
+ return err(toApiError(error));
223
+ }
224
+ const clearLocalState = () => {
225
+ this.authError = null;
226
+ this.user = null;
227
+ this.session = null;
228
+ try {
229
+ sessionStorage.clear();
230
+ } catch (storageError) {
231
+ logger.warn("AuthService", "Failed to clear sessionStorage", { error: storageError });
232
+ }
233
+ this.notify();
234
+ };
235
+ try {
236
+ const { error } = await this.supabaseClient.auth.signOut();
237
+ if (error) {
238
+ logger.warn("AuthService", "signOut (global) failed, clearing local state and trying local scope", {
239
+ message: error.message,
240
+ status: error.status
241
+ });
242
+ clearLocalState();
243
+ const { error: localError } = await this.supabaseClient.auth.signOut({ scope: "local" });
244
+ if (localError) {
245
+ logger.warn("AuthService", "signOut (local) also failed", { message: localError.message });
246
+ }
247
+ return err(toApiError(error));
248
+ }
249
+ clearLocalState();
250
+ return ok({ user: null, session: null });
251
+ } catch (error) {
252
+ logger.warn("AuthService", "signOut threw, clearing local state and trying local scope", {
253
+ message: error instanceof Error ? error.message : String(error)
254
+ });
255
+ clearLocalState();
256
+ try {
257
+ await this.supabaseClient.auth.signOut({ scope: "local" });
258
+ } catch (_localError) {
259
+ }
260
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
261
+ this.authError = authError;
262
+ return err(toApiError(authError));
263
+ }
264
+ }
265
+ async resetPassword(email) {
266
+ if (!this.supabaseClient) {
267
+ const error = new AuthError("Supabase client not available");
268
+ this.authError = error;
269
+ this.notify();
270
+ return err(toApiError(error));
271
+ }
272
+ try {
273
+ const { error } = await this.supabaseClient.auth.resetPasswordForEmail(email);
274
+ if (error) {
275
+ this.authError = error;
276
+ this.notify();
277
+ return err(toApiError(error));
278
+ }
279
+ this.authError = null;
280
+ this.notify();
281
+ return ok({ user: this.user, session: this.session });
282
+ } catch (error) {
283
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
284
+ this.authError = authError;
285
+ this.notify();
286
+ return err(toApiError(authError));
287
+ }
288
+ }
289
+ async updatePassword(password) {
290
+ if (!this.supabaseClient) {
291
+ const error = new AuthError("Supabase client not available");
292
+ this.authError = error;
293
+ this.notify();
294
+ return err(toApiError(error));
295
+ }
296
+ try {
297
+ const { error } = await this.supabaseClient.auth.updateUser({
298
+ password
299
+ });
300
+ if (error) {
301
+ this.authError = error;
302
+ this.notify();
303
+ return err(toApiError(error));
304
+ }
305
+ this.authError = null;
306
+ this.notify();
307
+ return ok({ user: this.user, session: this.session });
308
+ } catch (error) {
309
+ const authError = error instanceof AuthError ? error : error;
310
+ this.authError = authError;
311
+ this.notify();
312
+ return err(toApiError(authError));
313
+ }
314
+ }
315
+ async refreshSession() {
316
+ if (!this.supabaseClient) {
317
+ const error = new AuthError("Supabase client not available");
318
+ this.authError = error;
319
+ this.notify();
320
+ return err(toApiError(error));
321
+ }
322
+ try {
323
+ const { data, error } = await this.supabaseClient.auth.refreshSession();
324
+ if (error) {
325
+ this.authError = error;
326
+ this.user = null;
327
+ this.session = null;
328
+ this.notify();
329
+ return err(toApiError(error));
330
+ }
331
+ this.authError = null;
332
+ if (data?.user && data?.session) {
333
+ this.user = data.user;
334
+ this.session = data.session;
335
+ } else {
336
+ this.user = null;
337
+ this.session = null;
338
+ }
339
+ this.notify();
340
+ return ok({
341
+ user: data?.user && data?.session ? data.user : null,
342
+ session: data?.session ?? null
343
+ });
344
+ } catch (error) {
345
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
346
+ this.authError = authError;
347
+ this.user = null;
348
+ this.session = null;
349
+ this.notify();
350
+ return err(toApiError(authError));
351
+ }
352
+ }
353
+ // Lifecycle methods
354
+ async initialize() {
355
+ await super.initialize();
356
+ this.authLoading = true;
357
+ this.notify();
358
+ await this.setupAuthStateListener();
359
+ await this.restoreSession();
360
+ }
361
+ cleanup() {
362
+ if (this.authStateSubscription) {
363
+ this.authStateSubscription.unsubscribe();
364
+ this.authStateSubscription = null;
365
+ }
366
+ this.clearRestorationTimeout();
367
+ this.restorationStartTime = null;
368
+ this.sessionRestorationState = {
369
+ isRestoring: false,
370
+ restorationComplete: false,
371
+ restorationError: null
372
+ };
373
+ this.authLoading = false;
374
+ super.cleanup();
375
+ }
376
+ async doInitialize() {
377
+ this.setupErrorHandlers();
378
+ }
379
+ doCleanup() {
380
+ this.removeErrorHandlers();
381
+ }
382
+ startSessionRestoration() {
383
+ this.clearRestorationTimeout();
384
+ this.sessionRestorationState = {
385
+ isRestoring: true,
386
+ restorationComplete: false,
387
+ restorationError: null
388
+ };
389
+ this.authLoading = true;
390
+ this.restorationStartTime = Date.now();
391
+ this.notify();
392
+ this.restorationTimeoutId = setTimeout(() => {
393
+ logger.warn("AuthService", "Session restoration timed out after", this.restorationTimeoutMs, "ms");
394
+ const timeoutError = new Error(`Session restoration timed out after ${this.restorationTimeoutMs}ms`);
395
+ timeoutError.name = "SessionRestorationTimeoutError";
396
+ this.finishSessionRestoration(timeoutError);
397
+ }, this.restorationTimeoutMs);
398
+ }
399
+ finishSessionRestoration(error) {
400
+ if (!this.sessionRestorationState.isRestoring && !error) {
401
+ return;
402
+ }
403
+ this.clearRestorationTimeout();
404
+ this.restorationStartTime = null;
405
+ const restorationComplete = !error;
406
+ this.sessionRestorationState = {
407
+ isRestoring: false,
408
+ restorationComplete,
409
+ restorationError: error ?? null
410
+ };
411
+ this.authLoading = false;
412
+ if (error) {
413
+ logger.warn("AuthService", "Session restoration finished with error:", error);
414
+ }
415
+ this.notify();
416
+ }
417
+ clearRestorationTimeout() {
418
+ if (this.restorationTimeoutId) {
419
+ clearTimeout(this.restorationTimeoutId);
420
+ this.restorationTimeoutId = null;
421
+ }
422
+ }
423
+ /**
424
+ * Clear pace-core persistence entries from sessionStorage
425
+ * This includes dialog, form, and datatable persistence
426
+ *
427
+ * @param userId - Optional user ID to clear only that user's persistence.
428
+ * If not provided, clears all pace-core persistence (for user changes).
429
+ * @param clearUnscoped - If true, also clears old unscoped keys (without :user: prefix)
430
+ */
431
+ clearPersistenceOnLogin(userId, clearUnscoped = true) {
432
+ if (typeof window === "undefined" || !window.sessionStorage) {
433
+ return;
434
+ }
435
+ try {
436
+ const keysToRemove = [];
437
+ for (let i = 0; i < sessionStorage.length; i++) {
438
+ const key = sessionStorage.key(i);
439
+ if (key && (key.startsWith("pace-core:draft:") || key.startsWith("pace-core:dialog:"))) {
440
+ if (userId && !key.includes(`:user:${userId}`)) {
441
+ if (clearUnscoped && !key.includes(":user:")) {
442
+ keysToRemove.push(key);
443
+ }
444
+ continue;
445
+ }
446
+ keysToRemove.push(key);
447
+ }
448
+ }
449
+ keysToRemove.forEach((key) => {
450
+ try {
451
+ sessionStorage.removeItem(key);
452
+ } catch (error) {
453
+ logger.warn("AuthService", `Failed to remove persistence key: ${key}`, error);
454
+ }
455
+ });
456
+ } catch (error) {
457
+ logger.warn("AuthService", `Failed to clear persistence [ID:${this.instanceId}]:`, error);
458
+ }
459
+ }
460
+ handleSignedOut(session) {
461
+ this.session = null;
462
+ this.user = null;
463
+ this.authError = null;
464
+ this.wasAuthenticatedRef = false;
465
+ if (session?.user) {
466
+ this.trackSession("logout", session).catch((err2) => {
467
+ logger.warn("AuthService", `Failed to track logout session [ID:${this.instanceId}]:`, err2);
468
+ });
469
+ }
470
+ }
471
+ handleSignedInOrTokenRefreshed(event, session, wasAuthenticated, isNowAuthenticated, userChanged, previousUserId) {
472
+ this.session = session;
473
+ this.user = session?.user ?? null;
474
+ if (session?.user) {
475
+ this.authError = null;
476
+ }
477
+ if (!wasAuthenticated && isNowAuthenticated && event === "SIGNED_IN") {
478
+ this.clearPersistenceOnLogin(null, true);
479
+ } else if (userChanged && previousUserId) {
480
+ this.clearPersistenceOnLogin(previousUserId, true);
481
+ }
482
+ this.wasAuthenticatedRef = isNowAuthenticated;
483
+ if (event === "SIGNED_IN" && session?.user) {
484
+ this.trackSession("login", session).catch((err2) => {
485
+ logger.warn("AuthService", `Failed to track login session [ID:${this.instanceId}]:`, err2);
486
+ });
487
+ }
488
+ }
489
+ handleInitialSession(session, wasAuthenticated) {
490
+ if (session) {
491
+ const previousUserId = this.user?.id ?? null;
492
+ const newUserId = session.user?.id ?? null;
493
+ const userChanged = previousUserId !== null && newUserId !== null && previousUserId !== newUserId;
494
+ this.session = session;
495
+ this.user = session.user ?? null;
496
+ this.authError = null;
497
+ if (userChanged && previousUserId) {
498
+ this.clearPersistenceOnLogin(previousUserId, true);
499
+ } else if (!wasAuthenticated && !!session.user) {
500
+ this.clearPersistenceOnLogin(null, true);
501
+ }
502
+ this.wasAuthenticatedRef = !!session.user;
503
+ const hasTimeoutError = this.sessionRestorationState.restorationError?.name === "SessionRestorationTimeoutError";
504
+ if (this.sessionRestorationState.isRestoring || this.sessionRestorationState.restorationError || hasTimeoutError && session) {
505
+ this.finishSessionRestoration();
506
+ }
507
+ } else {
508
+ this.wasAuthenticatedRef = false;
509
+ if (this.sessionRestorationState.isRestoring) {
510
+ this.finishSessionRestoration();
511
+ }
512
+ }
513
+ this.authLoading = false;
514
+ this.notify();
515
+ }
516
+ async setupAuthStateListener() {
517
+ if (!this.supabaseClient) {
518
+ this.authLoading = false;
519
+ this.notify();
520
+ return;
521
+ }
522
+ try {
523
+ const subscription = this.supabaseClient.auth.onAuthStateChange(
524
+ (event, session) => {
525
+ try {
526
+ const wasAuthenticated = this.wasAuthenticatedRef;
527
+ const isNowAuthenticated = !!session?.user;
528
+ const previousUserId = this.user?.id ?? null;
529
+ const newUserId = session?.user?.id ?? null;
530
+ const userChanged = previousUserId !== null && newUserId !== null && previousUserId !== newUserId;
531
+ if (event === "SIGNED_OUT") {
532
+ this.handleSignedOut(session);
533
+ } else if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
534
+ this.handleSignedInOrTokenRefreshed(
535
+ event,
536
+ session,
537
+ wasAuthenticated,
538
+ isNowAuthenticated,
539
+ userChanged,
540
+ previousUserId
541
+ );
542
+ } else if (event === "INITIAL_SESSION") {
543
+ this.handleInitialSession(session, wasAuthenticated);
544
+ return;
545
+ }
546
+ this.authLoading = false;
547
+ this.notify();
548
+ } catch (error) {
549
+ logger.warn("AuthService", `Error in auth state change handler [ID:${this.instanceId}]:`, error);
550
+ this.authLoading = false;
551
+ this.notify();
552
+ }
553
+ }
554
+ );
555
+ this.authStateSubscription = subscription.data.subscription;
556
+ } catch (error) {
557
+ logger.error("AuthService", "Failed to setup auth state listener:", error);
558
+ throw error;
559
+ }
560
+ }
561
+ async restoreSession() {
562
+ if (!this.supabaseClient) {
563
+ const error = new Error("Supabase client not available during session restoration");
564
+ logger.error("AuthService", "Unable to restore session:", error);
565
+ this.finishSessionRestoration(error);
566
+ return;
567
+ }
568
+ this.startSessionRestoration();
569
+ try {
570
+ let currentSession = null;
571
+ let sessionError = null;
572
+ try {
573
+ const { data, error } = await this.supabaseClient.auth.getSession();
574
+ currentSession = data?.session ?? null;
575
+ sessionError = error ?? null;
576
+ } catch (_error) {
577
+ currentSession = null;
578
+ sessionError = null;
579
+ }
580
+ if (sessionError) {
581
+ this.authError = sessionError;
582
+ try {
583
+ const { data, error } = await this.supabaseClient.auth.getUser();
584
+ const currentUser = data?.user ?? null;
585
+ const userError = error ?? null;
586
+ if (currentUser) {
587
+ this.user = currentUser;
588
+ this.session = null;
589
+ }
590
+ if (userError && !this.authError) {
591
+ this.authError = userError;
592
+ }
593
+ } catch (_getUserError) {
594
+ }
595
+ }
596
+ if (currentSession) {
597
+ this.session = currentSession;
598
+ this.user = currentSession.user;
599
+ this.authError = null;
600
+ } else if (!sessionError) {
601
+ this.session = null;
602
+ this.user = null;
603
+ this.authError = null;
604
+ }
605
+ setTimeout(() => {
606
+ if (this.sessionRestorationState.isRestoring && !this.sessionRestorationState.restorationComplete) {
607
+ logger.debug("AuthService", "INITIAL_SESSION event did not fire, finishing restoration");
608
+ this.finishSessionRestoration();
609
+ }
610
+ }, 100);
611
+ } catch (error) {
612
+ const restorationError = error instanceof Error ? error : new Error("Unknown error during auth initialization");
613
+ logger.error("AuthService", "Error during auth initialization:", restorationError);
614
+ if (restorationError instanceof AuthError) {
615
+ this.authError = restorationError;
616
+ }
617
+ this.finishSessionRestoration(restorationError);
618
+ }
619
+ }
620
+ /**
621
+ * Automatically track user session using rbac_session_track
622
+ * This method is called automatically on SIGNED_IN and SIGNED_OUT events.
623
+ * It's non-blocking and failures are logged as warnings.
624
+ */
625
+ async trackSession(sessionType, session) {
626
+ if (!this.supabaseClient || !session?.user) {
627
+ return;
628
+ }
629
+ try {
630
+ let appId = void 0;
631
+ if (this.appName) {
632
+ const { data, error: error2 } = await this.supabaseClient.from("rbac_apps").select("id").eq("name", this.appName).eq("is_active", true).single();
633
+ if (!error2 && data) {
634
+ appId = data.id;
635
+ }
636
+ }
637
+ const ipAddress = void 0;
638
+ const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : void 0;
639
+ const deviceFingerprint = void 0;
640
+ const { error } = await this.supabaseClient.rpc("rbac_session_track", {
641
+ p_user_id: session.user.id,
642
+ p_session_type: sessionType,
643
+ p_event_id: null,
644
+ // Event ID should come from context, not auth service
645
+ p_app_id: appId,
646
+ p_ip_address: ipAddress,
647
+ p_user_agent: userAgent,
648
+ p_device_fingerprint: deviceFingerprint
649
+ });
650
+ if (error) {
651
+ logger.warn("AuthService", `Failed to track ${sessionType} session:`, error);
652
+ }
653
+ } catch (error) {
654
+ logger.warn("AuthService", `Error tracking ${sessionType} session:`, error);
655
+ }
656
+ }
657
+ setupErrorHandlers() {
658
+ if (typeof window === "undefined") return;
659
+ this.errorHandler = (event) => {
660
+ if (event.error?.message?.includes("AuthSessionMissingError") || event.error?.message?.includes("Auth session missing")) {
661
+ logger.warn("AuthService", "Suppressing AuthSessionMissingError during logout");
662
+ event.preventDefault();
663
+ return false;
664
+ }
665
+ };
666
+ this.unhandledRejectionHandler = (event) => {
667
+ if (event.reason?.message?.includes("AuthSessionMissingError") || event.reason?.message?.includes("Auth session missing")) {
668
+ logger.warn("AuthService", "Suppressing unhandled AuthSessionMissingError");
669
+ event.preventDefault();
670
+ return false;
671
+ }
672
+ };
673
+ window.addEventListener("error", this.errorHandler);
674
+ window.addEventListener("unhandledrejection", this.unhandledRejectionHandler);
675
+ }
676
+ removeErrorHandlers() {
677
+ if (typeof window === "undefined") return;
678
+ if (this.errorHandler) {
679
+ window.removeEventListener("error", this.errorHandler);
680
+ this.errorHandler = null;
681
+ }
682
+ if (this.unhandledRejectionHandler) {
683
+ window.removeEventListener("unhandledrejection", this.unhandledRejectionHandler);
684
+ this.unhandledRejectionHandler = null;
685
+ }
686
+ }
687
+ };
688
+ _AuthService.instanceCount = 0;
689
+ var AuthService = _AuthService;
690
+ var AuthServiceContext = createContext(null);
691
+ function AuthServiceProvider({ children, supabaseClient, appName }) {
692
+ const authServiceRef = useRef(null);
693
+ if (!authServiceRef.current) {
694
+ authServiceRef.current = new AuthService(supabaseClient, appName);
695
+ }
696
+ const authService = authServiceRef.current;
697
+ useEffect(() => {
698
+ }, [authService, supabaseClient, appName]);
699
+ const [sessionRestoration, setSessionRestoration] = useState(
700
+ () => authService.getSessionRestorationState()
701
+ );
702
+ useEffect(() => {
703
+ const unsubscribe = authService.subscribe(() => {
704
+ const restorationState = authService.getSessionRestorationState();
705
+ setSessionRestoration(restorationState);
706
+ });
707
+ return () => {
708
+ unsubscribe();
709
+ };
710
+ }, [authService]);
711
+ useEffect(() => {
712
+ const urlParams = new URLSearchParams(window.location.search);
713
+ const sessionParam = urlParams.get("session");
714
+ const sessionTokenParam = urlParams.get("session_token");
715
+ if ((sessionParam || sessionTokenParam) && supabaseClient) {
716
+ const restoreSession = async () => {
717
+ try {
718
+ if (sessionParam) {
719
+ const sessionData = JSON.parse(atob(sessionParam));
720
+ logger.info("AuthServiceProvider", "Restoring session from URL parameter");
721
+ const { data, error } = await supabaseClient.auth.setSession({
722
+ access_token: sessionData.access_token,
723
+ refresh_token: sessionData.refresh_token
724
+ });
725
+ if (error) {
726
+ logger.error("AuthServiceProvider", "Failed to restore session from URL:", error);
727
+ } else if (data.session) {
728
+ logger.info("AuthServiceProvider", "Session successfully restored from URL parameter", {
729
+ userId: data.session.user.id,
730
+ expiresAt: data.session.expires_at
731
+ });
732
+ const newUrl = new URL(window.location.href);
733
+ newUrl.searchParams.delete("session");
734
+ window.history.replaceState({}, "", newUrl.toString());
735
+ }
736
+ } else if (sessionTokenParam) {
737
+ logger.warn("AuthServiceProvider", "Using fallback session_token parameter (less reliable)");
738
+ const { data: { user }, error } = await supabaseClient.auth.getUser(sessionTokenParam);
739
+ if (!error && user) {
740
+ logger.info("AuthServiceProvider", "Session token verified, but full session restoration requires refresh_token");
741
+ } else {
742
+ logger.error("AuthServiceProvider", "Failed to verify session token:", error);
743
+ }
744
+ const newUrl = new URL(window.location.href);
745
+ newUrl.searchParams.delete("session_token");
746
+ window.history.replaceState({}, "", newUrl.toString());
747
+ }
748
+ } catch (error) {
749
+ logger.error("AuthServiceProvider", "Error restoring session from URL:", error);
750
+ const newUrl = new URL(window.location.href);
751
+ newUrl.searchParams.delete("session");
752
+ newUrl.searchParams.delete("session_token");
753
+ window.history.replaceState({}, "", newUrl.toString());
754
+ }
755
+ };
756
+ void restoreSession();
757
+ }
758
+ }, [supabaseClient]);
759
+ useEffect(() => {
760
+ const initTimer = setTimeout(() => {
761
+ authService.initialize().catch((error) => {
762
+ logger.error("AuthServiceProvider", "Failed to initialize auth service:", error);
763
+ });
764
+ }, 200);
765
+ return () => {
766
+ clearTimeout(initTimer);
767
+ authService.cleanup();
768
+ };
769
+ }, [authService]);
770
+ const contextValue = useMemo(() => ({
771
+ authService,
772
+ sessionRestoration
773
+ }), [authService, sessionRestoration]);
774
+ return /* @__PURE__ */ jsx(AuthServiceContext.Provider, { value: contextValue, children });
775
+ }
776
+ var OrganisationServiceContext = createContext(null);
777
+
778
+ // src/services/OrganisationService.ts
779
+ var _OrganisationService = class _OrganisationService extends BaseService {
780
+ constructor(supabaseClient, user, session) {
781
+ super();
782
+ this._selectedOrganisation = null;
783
+ this._organisations = [];
784
+ this._userMemberships = [];
785
+ this._roleMapState = /* @__PURE__ */ new Map();
786
+ this._isLoading = false;
787
+ this._error = null;
788
+ this._isSuperAdmin = false;
789
+ // Cache super admin status
790
+ this._isContextReady = false;
791
+ this.retryCount = 0;
792
+ // Dependencies
793
+ this.supabaseClient = null;
794
+ this.user = null;
795
+ this.session = null;
796
+ // Internal state management
797
+ this.isLoadingRef = false;
798
+ this.lastLoadTimeRef = 0;
799
+ this.hasFailedRef = false;
800
+ this.abortControllerRef = null;
801
+ this.instanceId = ++_OrganisationService.instanceCount;
802
+ this.supabaseClient = supabaseClient;
803
+ this.user = user;
804
+ this.session = session;
805
+ }
806
+ getInstanceId() {
807
+ return this.instanceId;
808
+ }
809
+ // Interface implementation
810
+ getSelectedOrganisation() {
811
+ return this._selectedOrganisation;
812
+ }
813
+ getOrganisations() {
814
+ return this._organisations;
815
+ }
816
+ getUserMemberships() {
817
+ return this._userMemberships;
818
+ }
819
+ isLoading() {
820
+ return this._isLoading;
821
+ }
822
+ getError() {
823
+ return this._error;
824
+ }
825
+ hasValidOrganisationContext() {
826
+ return !!(this._selectedOrganisation && !this._isLoading && !this._error && this._isContextReady);
827
+ }
828
+ isContextReady() {
829
+ return this._isContextReady;
830
+ }
831
+ // Additional methods for testing
832
+ setSelectedOrganisation(organisation) {
833
+ if (organisation && this._organisations.length > 0) {
834
+ if (!this._isSuperAdmin) {
835
+ const isValidOrg = this._organisations.some((org) => org.id === organisation.id);
836
+ if (!isValidOrg) {
837
+ logger.warn("OrganisationService", "Attempted to set invalid organisation - not in user's accessible organisations", {
838
+ organisationId: organisation.id,
839
+ organisationName: organisation.name,
840
+ accessibleOrgIds: this._organisations.map((o) => o.id)
841
+ });
842
+ return;
843
+ }
844
+ }
845
+ }
846
+ this._selectedOrganisation = organisation;
847
+ if (organisation) {
848
+ localStorage.setItem("pace-core-selected-organisation", JSON.stringify(organisation));
849
+ this.setDatabaseOrganisationContext(organisation);
850
+ } else {
851
+ localStorage.removeItem("pace-core-selected-organisation");
852
+ this._isContextReady = false;
853
+ }
854
+ this.notify();
855
+ }
856
+ // For testing: expose dependencies
857
+ getDependencies() {
858
+ return {
859
+ user: this.user,
860
+ session: this.session,
861
+ supabaseClient: this.supabaseClient
862
+ };
863
+ }
864
+ // For testing: manually set state
865
+ setTestState(organisations, memberships, roleMap, selectedOrg = null) {
866
+ this._organisations = organisations;
867
+ this._userMemberships = memberships;
868
+ this._roleMapState = roleMap;
869
+ if (selectedOrg) {
870
+ this._selectedOrganisation = selectedOrg;
871
+ } else if (organisations.length > 0) {
872
+ this._selectedOrganisation = organisations[0];
873
+ }
874
+ this._isLoading = false;
875
+ this._error = null;
876
+ this.notify();
877
+ }
878
+ // Update dependencies
879
+ updateDependencies(user, session) {
880
+ const previousUserId = this.user?.id || null;
881
+ const newUserId = user?.id || null;
882
+ const userChanged = previousUserId !== newUserId;
883
+ const needsRetry = this._error !== null && !this.isLoadingRef;
884
+ const isEmpty = newUserId !== null && this._organisations.length === 0 && !this.isLoadingRef;
885
+ if (userChanged || needsRetry || isEmpty) {
886
+ if (userChanged) ; else if (needsRetry) {
887
+ logger.debug("OrganisationService", `Previous error detected [ID:${this.instanceId}], retrying initialization`);
888
+ } else if (isEmpty) {
889
+ logger.debug("OrganisationService", `No organisations found [ID:${this.instanceId}], retrying initialization`);
890
+ }
891
+ this._isSuperAdmin = false;
892
+ this.resetInitialization();
893
+ if (userChanged) {
894
+ this._organisations = [];
895
+ this._userMemberships = [];
896
+ this._roleMapState = /* @__PURE__ */ new Map();
897
+ this._selectedOrganisation = null;
898
+ this._isContextReady = false;
899
+ }
900
+ this.lastLoadTimeRef = 0;
901
+ }
902
+ this.user = user;
903
+ this.session = session;
904
+ this.notify();
905
+ }
906
+ // Organisation methods
907
+ async switchOrganisation(orgId) {
908
+ if (!this.validateOrganisationAccess(orgId)) {
909
+ throw new Error(`User does not have access to organisation ${orgId}`);
910
+ }
911
+ const targetOrg = this._organisations.find((org) => org.id === orgId);
912
+ if (!targetOrg) {
913
+ throw new Error(`Organisation ${orgId} not found in user's organisations`);
914
+ }
915
+ this._selectedOrganisation = targetOrg;
916
+ localStorage.setItem("pace-core-selected-organisation", JSON.stringify(targetOrg));
917
+ await this.setDatabaseOrganisationContext(targetOrg);
918
+ this.notify();
919
+ }
920
+ getUserRole(orgId) {
921
+ const targetOrgId = orgId || this._selectedOrganisation?.id;
922
+ if (!targetOrgId) return "no_access";
923
+ return this._roleMapState.get(targetOrgId) || "no_access";
924
+ }
925
+ validateOrganisationAccess(orgId) {
926
+ return this._userMemberships.some(
927
+ (m) => m.organisation_id === orgId && m.status === "active" && m.revoked_at === null
928
+ );
929
+ }
930
+ async refreshOrganisations() {
931
+ if (!this.user || !this.session || !this.supabaseClient) return;
932
+ this._isLoading = true;
933
+ this.notify();
934
+ await this.loadUserOrganisations();
935
+ }
936
+ ensureOrganisationContext() {
937
+ if (!this._selectedOrganisation) {
938
+ throw new Error("Organisation context is required but not available");
939
+ }
940
+ return this._selectedOrganisation;
941
+ }
942
+ isOrganisationSecure() {
943
+ return !!(this._selectedOrganisation && this.user);
944
+ }
945
+ getPrimaryOrganisation() {
946
+ const rolePriority = ["org_admin", "leader", "member"];
947
+ for (const role of rolePriority) {
948
+ const membership = this._userMemberships.find((m) => m.role === role);
949
+ if (membership) {
950
+ return this._organisations.find((org) => org.id === membership.organisation_id) || null;
951
+ }
952
+ }
953
+ return null;
954
+ }
955
+ buildOrganisationHierarchy(orgs) {
956
+ const orgMap = /* @__PURE__ */ new Map();
957
+ orgs.forEach((org) => orgMap.set(org.id, org));
958
+ const roots = [];
959
+ orgs.forEach((org) => {
960
+ if (!org.parent_id) {
961
+ roots.push({
962
+ organisation: org,
963
+ children: [],
964
+ depth: 0
965
+ });
966
+ }
967
+ });
968
+ return roots;
969
+ }
970
+ // Lifecycle methods
971
+ async initialize() {
972
+ if (!this.user) {
973
+ return;
974
+ }
975
+ await super.initialize();
976
+ if (!this.isLoadingRef) {
977
+ await this.loadUserOrganisations();
978
+ }
979
+ }
980
+ cleanup() {
981
+ this.isLoadingRef = false;
982
+ this.hasFailedRef = false;
983
+ this.lastLoadTimeRef = 0;
984
+ if (this.abortControllerRef) {
985
+ this.abortControllerRef = null;
986
+ }
987
+ this._selectedOrganisation = null;
988
+ this._organisations = [];
989
+ this._userMemberships = [];
990
+ this._roleMapState = /* @__PURE__ */ new Map();
991
+ this._isLoading = false;
992
+ this._error = null;
993
+ this._isContextReady = false;
994
+ super.cleanup();
995
+ }
996
+ async doInitialize() {
997
+ }
998
+ doCleanup() {
999
+ }
1000
+ async setDatabaseOrganisationContext(_organisation) {
1001
+ this._isContextReady = true;
1002
+ this.notify();
1003
+ }
1004
+ /** Returns false if load should return early; when true, abort controller and loading state are set. */
1005
+ prepareLoad() {
1006
+ if (!this.user || !this.session || !this.supabaseClient) {
1007
+ this._selectedOrganisation = null;
1008
+ this._organisations = [];
1009
+ this._userMemberships = [];
1010
+ this._isLoading = false;
1011
+ this._error = null;
1012
+ this.notify();
1013
+ return false;
1014
+ }
1015
+ if (this.isLoadingRef) {
1016
+ this._isLoading = true;
1017
+ this.notify();
1018
+ return false;
1019
+ }
1020
+ const now = Date.now();
1021
+ if (this._organisations.length > 0 && now - this.lastLoadTimeRef < 2e3) {
1022
+ if (this._organisations.length > 0 || this._selectedOrganisation) {
1023
+ this._isLoading = false;
1024
+ } else {
1025
+ this._isLoading = true;
1026
+ }
1027
+ this.notify();
1028
+ return false;
1029
+ }
1030
+ if (this.abortControllerRef) {
1031
+ this.abortControllerRef.abort();
1032
+ }
1033
+ this.abortControllerRef = new AbortController();
1034
+ this.lastLoadTimeRef = now;
1035
+ this.isLoadingRef = true;
1036
+ this._isLoading = true;
1037
+ this._error = null;
1038
+ this.notify();
1039
+ return true;
1040
+ }
1041
+ async fetchRolesAndOrganisations(signal) {
1042
+ if (signal.aborted) {
1043
+ throw new Error("Request aborted");
1044
+ }
1045
+ if (!this.supabaseClient || !this.user) {
1046
+ throw new Error("Missing supabase client or user");
1047
+ }
1048
+ try {
1049
+ const { data: rolesData, error: rolesError } = await this.supabaseClient.from("rbac_organisation_roles").select(`
1050
+ id,
1051
+ user_id,
1052
+ organisation_id,
1053
+ role,
1054
+ status,
1055
+ granted_at,
1056
+ granted_by,
1057
+ revoked_at,
1058
+ revoked_by,
1059
+ notes,
1060
+ created_at,
1061
+ updated_at,
1062
+ core_organisations!inner(
1063
+ id,
1064
+ name,
1065
+ display_name,
1066
+ subscription_tier,
1067
+ settings,
1068
+ is_active,
1069
+ parent_id,
1070
+ created_at,
1071
+ updated_at
1072
+ )
1073
+ `).eq("user_id", this.user.id).eq("status", "active").is("revoked_at", null);
1074
+ if (rolesError) {
1075
+ logger.error("OrganisationService", "Error loading organisation roles:", rolesError);
1076
+ throw rolesError;
1077
+ }
1078
+ const memberships = rolesData?.map((m) => ({
1079
+ ...m,
1080
+ user_id: assertUserId(m.user_id),
1081
+ organisation_id: assertOrganisationId(m.organisation_id)
1082
+ })) || [];
1083
+ const organisationsMap = /* @__PURE__ */ new Map();
1084
+ rolesData?.forEach((role) => {
1085
+ const roleWithOrg = role;
1086
+ const orgData = roleWithOrg.core_organisations;
1087
+ if (orgData && roleWithOrg.organisation_id && !organisationsMap.has(roleWithOrg.organisation_id)) {
1088
+ organisationsMap.set(roleWithOrg.organisation_id, {
1089
+ id: orgData.id,
1090
+ name: orgData.name,
1091
+ display_name: orgData.display_name,
1092
+ subscription_tier: orgData.subscription_tier,
1093
+ settings: orgData.settings,
1094
+ is_active: orgData.is_active,
1095
+ parent_id: orgData.parent_id,
1096
+ created_at: orgData.created_at,
1097
+ updated_at: orgData.updated_at
1098
+ });
1099
+ }
1100
+ });
1101
+ const organisations = Array.from(organisationsMap.values());
1102
+ return { memberships, organisations };
1103
+ } catch (queryError) {
1104
+ const err2 = queryError instanceof Error ? queryError : queryError && typeof queryError === "object" && "message" in queryError ? new Error(String(queryError.message)) : new Error(String(queryError));
1105
+ logger.error("OrganisationService", "Error loading organisation roles:", err2);
1106
+ throw err2;
1107
+ }
1108
+ }
1109
+ async checkAndCacheSuperAdmin() {
1110
+ if (!this.user?.id) {
1111
+ this._isSuperAdmin = false;
1112
+ return false;
1113
+ }
1114
+ try {
1115
+ const { isSuperAdmin: checkSuperAdmin, isRBACInitialized: isRBACInitialized2, setupRBAC: setupRBAC2 } = await import('./api-BZR2CYXL.js');
1116
+ if (!isRBACInitialized2() && this.supabaseClient) {
1117
+ setupRBAC2(this.supabaseClient);
1118
+ }
1119
+ if (isRBACInitialized2()) {
1120
+ const result = await checkSuperAdmin(this.user.id);
1121
+ const isSuper = result.ok && result.data;
1122
+ this._isSuperAdmin = isSuper;
1123
+ return isSuper;
1124
+ }
1125
+ this._isSuperAdmin = false;
1126
+ return false;
1127
+ } catch (error) {
1128
+ logger.warn("OrganisationService", "Failed to check super admin status", { error });
1129
+ this._isSuperAdmin = false;
1130
+ return false;
1131
+ }
1132
+ }
1133
+ applyEmptyStateAndNotify() {
1134
+ this._organisations = [];
1135
+ this._userMemberships = [];
1136
+ this._isLoading = false;
1137
+ this._error = null;
1138
+ this._isContextReady = true;
1139
+ this.notify();
1140
+ }
1141
+ selectInitialOrganisation(activeOrgs, memberships, organisations) {
1142
+ let initialOrg = null;
1143
+ try {
1144
+ const persistedOrgString = localStorage.getItem("pace-core-selected-organisation");
1145
+ if (persistedOrgString) {
1146
+ const persistedOrg = JSON.parse(persistedOrgString);
1147
+ if (persistedOrg.id && typeof persistedOrg.id === "string" && persistedOrg.id.trim() !== "") {
1148
+ const validPersistedOrg = activeOrgs.find((org) => org.id === persistedOrg.id);
1149
+ if (validPersistedOrg) {
1150
+ initialOrg = validPersistedOrg;
1151
+ } else {
1152
+ localStorage.removeItem("pace-core-selected-organisation");
1153
+ }
1154
+ } else {
1155
+ localStorage.removeItem("pace-core-selected-organisation");
1156
+ }
1157
+ }
1158
+ } catch {
1159
+ localStorage.removeItem("pace-core-selected-organisation");
1160
+ }
1161
+ if (!initialOrg) {
1162
+ const adminMembership = memberships.find((m) => m.role === "org_admin");
1163
+ if (adminMembership) {
1164
+ const foundOrg = organisations.find((org) => org.id === adminMembership.organisation_id);
1165
+ if (foundOrg) initialOrg = foundOrg;
1166
+ }
1167
+ }
1168
+ if (!initialOrg) {
1169
+ initialOrg = activeOrgs[0];
1170
+ }
1171
+ if (!initialOrg) {
1172
+ throw new Error("No valid organisation found for user");
1173
+ }
1174
+ localStorage.setItem("pace-core-selected-organisation", JSON.stringify(initialOrg));
1175
+ return initialOrg;
1176
+ }
1177
+ async loadUserOrganisations() {
1178
+ if (!this.prepareLoad()) return;
1179
+ const abortSignal = this.abortControllerRef.signal;
1180
+ try {
1181
+ const { memberships, organisations } = await this.fetchRolesAndOrganisations(abortSignal);
1182
+ const userIsSuperAdmin = await this.checkAndCacheSuperAdmin();
1183
+ if (!memberships || memberships.length === 0) {
1184
+ if (userIsSuperAdmin) {
1185
+ this.applyEmptyStateAndNotify();
1186
+ return;
1187
+ }
1188
+ throw new Error("User has no active organisation memberships");
1189
+ }
1190
+ if (!organisations || organisations.length === 0) {
1191
+ if (userIsSuperAdmin) {
1192
+ this.applyEmptyStateAndNotify();
1193
+ return;
1194
+ }
1195
+ throw new Error("No organisations found in role data");
1196
+ }
1197
+ const roleMap = /* @__PURE__ */ new Map();
1198
+ memberships.forEach((membership) => {
1199
+ roleMap.set(membership.organisation_id, membership.role);
1200
+ });
1201
+ const activeOrgs = organisations.filter((org) => org.is_active);
1202
+ if (activeOrgs.length === 0) {
1203
+ if (userIsSuperAdmin) {
1204
+ this.applyEmptyStateAndNotify();
1205
+ return;
1206
+ }
1207
+ throw new Error("User has no access to active organisations");
1208
+ }
1209
+ const sortedOrgs = [...activeOrgs].sort((a, b) => {
1210
+ const nameA = (a.display_name || a.name || "").toLowerCase();
1211
+ const nameB = (b.display_name || b.name || "").toLowerCase();
1212
+ return nameA.localeCompare(nameB);
1213
+ });
1214
+ this._organisations = sortedOrgs;
1215
+ this._userMemberships = memberships;
1216
+ this._roleMapState = roleMap;
1217
+ const initialOrg = this.selectInitialOrganisation(activeOrgs, memberships, organisations);
1218
+ const currentSelectedOrg = this._selectedOrganisation;
1219
+ if (currentSelectedOrg && !activeOrgs.some((org) => org.id === currentSelectedOrg.id)) {
1220
+ logger.warn("OrganisationService", "Current selected organisation is no longer valid, resetting", {
1221
+ invalidOrgId: currentSelectedOrg.id,
1222
+ validOrgIds: activeOrgs.map((o) => o.id)
1223
+ });
1224
+ this._selectedOrganisation = null;
1225
+ }
1226
+ this._selectedOrganisation = initialOrg;
1227
+ await this.setDatabaseOrganisationContext(initialOrg);
1228
+ this.retryCount = 0;
1229
+ this.hasFailedRef = false;
1230
+ } catch (err2) {
1231
+ const error = err2;
1232
+ if (error.message !== "User has no access to active organisations") {
1233
+ logger.error("OrganisationService", "Failed to load organisations:", err2);
1234
+ }
1235
+ this._error = error;
1236
+ this.retryCount = this.retryCount + 1;
1237
+ this.hasFailedRef = true;
1238
+ this.clearAllCachedData();
1239
+ this._isContextReady = true;
1240
+ } finally {
1241
+ this.isLoadingRef = false;
1242
+ this._isLoading = false;
1243
+ this.abortControllerRef = null;
1244
+ this.notify();
1245
+ }
1246
+ }
1247
+ clearAllCachedData() {
1248
+ localStorage.removeItem("pace-core-selected-organisation");
1249
+ localStorage.removeItem("pace-core-organisation-context");
1250
+ this._selectedOrganisation = null;
1251
+ this._organisations = [];
1252
+ this._userMemberships = [];
1253
+ this._roleMapState = /* @__PURE__ */ new Map();
1254
+ this.retryCount = 0;
1255
+ this._isContextReady = false;
1256
+ }
1257
+ };
1258
+ _OrganisationService.instanceCount = 0;
1259
+ var OrganisationService = _OrganisationService;
1260
+ function OrganisationServiceProvider({
1261
+ children,
1262
+ supabaseClient,
1263
+ user,
1264
+ session
1265
+ }) {
1266
+ const organisationServiceRef = useRef(null);
1267
+ if (!organisationServiceRef.current) {
1268
+ organisationServiceRef.current = new OrganisationService(supabaseClient, user, session);
1269
+ }
1270
+ const organisationService = organisationServiceRef.current;
1271
+ useEffect(() => {
1272
+ organisationService.updateDependencies(user, session);
1273
+ let isMounted = true;
1274
+ organisationService.initialize().catch((error) => {
1275
+ if (isMounted) {
1276
+ const errorMessage = error instanceof Error ? error.message : String(error);
1277
+ if (errorMessage === "User has no access to active organisations") {
1278
+ logger.warn("OrganisationServiceProvider", "User has no active organisations (this is expected for users without organisation access):", error);
1279
+ } else {
1280
+ logger.error("OrganisationServiceProvider", "Failed to initialize organisation service:", error);
1281
+ }
1282
+ }
1283
+ });
1284
+ return () => {
1285
+ isMounted = false;
1286
+ };
1287
+ }, [organisationService, user, session]);
1288
+ useEffect(() => {
1289
+ return () => {
1290
+ organisationService.cleanup();
1291
+ };
1292
+ }, [organisationService]);
1293
+ const contextValue = useMemo(() => ({
1294
+ organisationService
1295
+ }), [organisationService]);
1296
+ return /* @__PURE__ */ jsx(OrganisationServiceContext.Provider, { value: contextValue, children });
1297
+ }
1298
+
1299
+ // src/services/EventService.ts
1300
+ var _EventService = class _EventService extends BaseService {
1301
+ constructor(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId) {
1302
+ super();
1303
+ this.events = [];
1304
+ this.selectedEvent = null;
1305
+ this._isLoading = false;
1306
+ // Start as false to avoid blocking UI
1307
+ this.error = null;
1308
+ // Dependencies
1309
+ this.supabaseClient = null;
1310
+ this.user = null;
1311
+ this.session = null;
1312
+ this.appName = "";
1313
+ this.selectedOrganisation = null;
1314
+ this.setSelectedEventId = null;
1315
+ this.isSuperAdmin = false;
1316
+ // Track super admin status for conditional validation
1317
+ // App config removed - scope is now page-level only (rbac_app_pages.scope_type)
1318
+ // Internal state management
1319
+ this.isInitializedRef = false;
1320
+ this.isFetchingRef = false;
1321
+ this.hasAutoSelectedRef = false;
1322
+ this.userClearedEventRef = false;
1323
+ this.instanceId = ++_EventService.instanceCount;
1324
+ this.supabaseClient = supabaseClient;
1325
+ this.user = user;
1326
+ this.session = session;
1327
+ this.appName = appName;
1328
+ this.selectedOrganisation = selectedOrganisation;
1329
+ this.setSelectedEventId = setSelectedEventId;
1330
+ }
1331
+ getInstanceId() {
1332
+ return this.instanceId;
1333
+ }
1334
+ // Helper method to get user-scoped storage key
1335
+ getStorageKey(userId) {
1336
+ if (!userId) {
1337
+ return "pace-core-selected-event-no-user";
1338
+ }
1339
+ return `pace-core-selected-event-${userId}`;
1340
+ }
1341
+ // Update dependencies
1342
+ async updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId) {
1343
+ const previousOrgId = this.selectedOrganisation?.id;
1344
+ const newOrgId = selectedOrganisation?.id;
1345
+ const previousUserId = this.user?.id || null;
1346
+ const newUserId = user?.id || null;
1347
+ await this.handleUserChange(previousUserId, newUserId);
1348
+ this.applyNewDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);
1349
+ await this.updateSuperAdminStatus(user);
1350
+ this.handleOrganisationChange(previousOrgId, newOrgId);
1351
+ this.notify();
1352
+ }
1353
+ async handleUserChange(previousUserId, newUserId) {
1354
+ if (previousUserId === newUserId) {
1355
+ return;
1356
+ }
1357
+ if (previousUserId !== null) {
1358
+ await this.clearEventSelectionForUser(previousUserId);
1359
+ }
1360
+ if (newUserId === null) {
1361
+ this.selectedEvent = null;
1362
+ this.setSelectedEventId?.(null);
1363
+ }
1364
+ this.resetInitialization();
1365
+ this.isInitializedRef = false;
1366
+ this.isFetchingRef = false;
1367
+ this.userClearedEventRef = false;
1368
+ this.hasAutoSelectedRef = false;
1369
+ }
1370
+ applyNewDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId) {
1371
+ this.supabaseClient = supabaseClient;
1372
+ this.user = user;
1373
+ this.session = session;
1374
+ this.appName = appName;
1375
+ this.selectedOrganisation = selectedOrganisation;
1376
+ this.setSelectedEventId = setSelectedEventId;
1377
+ }
1378
+ async updateSuperAdminStatus(user) {
1379
+ if (!user?.id) {
1380
+ this.isSuperAdmin = false;
1381
+ return;
1382
+ }
1383
+ try {
1384
+ const { isRBACInitialized: isRBACInitialized2, isSuperAdmin: checkSuperAdmin, setupRBAC: setupRBAC2 } = await import('./api-BZR2CYXL.js');
1385
+ if (!isRBACInitialized2() && this.supabaseClient) {
1386
+ setupRBAC2(this.supabaseClient);
1387
+ }
1388
+ if (isRBACInitialized2()) {
1389
+ const result = await checkSuperAdmin(user.id);
1390
+ this.isSuperAdmin = result.ok && result.data;
1391
+ } else {
1392
+ logger.warn("EventService", "RBAC not initialized in updateDependencies, keeping existing super admin status", {
1393
+ userId: user.id,
1394
+ existingIsSuperAdmin: this.isSuperAdmin,
1395
+ note: "RBAC should be initialized by UnifiedAuthProvider. This may indicate EventService is being used outside the provider."
1396
+ });
1397
+ }
1398
+ } catch (error) {
1399
+ logger.warn("EventService", "Failed to check super admin status in updateDependencies", {
1400
+ error,
1401
+ userId: user.id,
1402
+ existingIsSuperAdmin: this.isSuperAdmin
1403
+ });
1404
+ }
1405
+ }
1406
+ handleOrganisationChange(previousOrgId, newOrgId) {
1407
+ if (previousOrgId === newOrgId) {
1408
+ return;
1409
+ }
1410
+ this.resetInitialization();
1411
+ this.isInitializedRef = false;
1412
+ this.isFetchingRef = false;
1413
+ const shouldClearEvents = !this.isSuperAdmin;
1414
+ const isFirstOrgSet = (previousOrgId === null || previousOrgId === void 0) && newOrgId !== null && newOrgId !== void 0;
1415
+ if (isFirstOrgSet) {
1416
+ const hadAutoSelectedEvent = this.hasAutoSelectedRef && !!this.selectedEvent;
1417
+ this.userClearedEventRef = false;
1418
+ if (!hadAutoSelectedEvent) {
1419
+ this.hasAutoSelectedRef = false;
1420
+ }
1421
+ logger.debug("EventService", "Organisation first set - preserving event and resetting auto-selection flags", {
1422
+ organisationId: newOrgId,
1423
+ hasSelectedEvent: !!this.selectedEvent,
1424
+ selectedEventId: this.selectedEvent?.event_id,
1425
+ hadAutoSelectedEvent,
1426
+ preservingEvent: hadAutoSelectedEvent,
1427
+ previousOrgId,
1428
+ newOrgId
1429
+ });
1430
+ return;
1431
+ }
1432
+ const switchingOrgs = previousOrgId != null && newOrgId != null && previousOrgId !== newOrgId;
1433
+ const orgRemoved = previousOrgId != null && newOrgId === null;
1434
+ if (switchingOrgs && shouldClearEvents) {
1435
+ this.events = [];
1436
+ this.setSelectedEvent(null);
1437
+ } else if (orgRemoved && shouldClearEvents) {
1438
+ this.events = [];
1439
+ this.setSelectedEvent(null);
1440
+ }
1441
+ }
1442
+ // Event state getters
1443
+ getEvents() {
1444
+ return this.events;
1445
+ }
1446
+ getSelectedEvent() {
1447
+ return this.selectedEvent;
1448
+ }
1449
+ isLoading() {
1450
+ return this._isLoading;
1451
+ }
1452
+ getError() {
1453
+ return this.error;
1454
+ }
1455
+ // Event methods
1456
+ setSelectedEvent(event) {
1457
+ if (event) {
1458
+ this.selectedEvent = event;
1459
+ this.setSelectedEventId?.(event.event_id);
1460
+ this.persistEventSelection(event.event_id).catch((error) => {
1461
+ logger.warn("EventService", "Failed to persist event selection:", error);
1462
+ });
1463
+ this.userClearedEventRef = false;
1464
+ } else {
1465
+ this.selectedEvent?.event_id;
1466
+ this.selectedEvent = null;
1467
+ this.setSelectedEventId?.(null);
1468
+ this.clearEventSelection().catch((error) => {
1469
+ logger.warn("EventService", "Failed to clear event selection:", error);
1470
+ });
1471
+ this.hasAutoSelectedRef = false;
1472
+ this.userClearedEventRef = true;
1473
+ }
1474
+ this.notify();
1475
+ }
1476
+ async refreshEvents() {
1477
+ this.isInitializedRef = false;
1478
+ this.isFetchingRef = false;
1479
+ await this.fetchEvents();
1480
+ }
1481
+ async loadPersistedEvent(events) {
1482
+ try {
1483
+ const userId = this.user?.id || null;
1484
+ if (!userId) {
1485
+ return false;
1486
+ }
1487
+ const storageKey = this.getStorageKey(userId);
1488
+ const persistedEventId = await secureStorage.getItem(storageKey);
1489
+ if (persistedEventId && events.length > 0) {
1490
+ const persistedEvent = events.find((event) => event.event_id === persistedEventId);
1491
+ if (persistedEvent) {
1492
+ this.setSelectedEvent(persistedEvent);
1493
+ return true;
1494
+ } else {
1495
+ await secureStorage.removeItem(storageKey);
1496
+ }
1497
+ }
1498
+ } catch (error) {
1499
+ logger.warn("EventService", "Failed to load persisted event:", error);
1500
+ }
1501
+ return false;
1502
+ }
1503
+ /**
1504
+ * Restore persisted event after login screen has rendered
1505
+ * This should be called explicitly from login page component
1506
+ *
1507
+ * @returns Promise<boolean> - true if event was successfully restored, false otherwise
1508
+ */
1509
+ async restorePersistedEvent() {
1510
+ if (this.events.length === 0) {
1511
+ return false;
1512
+ }
1513
+ return await this.loadPersistedEvent(this.events);
1514
+ }
1515
+ async persistEventSelection(eventId) {
1516
+ try {
1517
+ const userId = this.user?.id || null;
1518
+ const storageKey = this.getStorageKey(userId);
1519
+ await secureStorage.setItem(storageKey, eventId, { encrypt: true });
1520
+ } catch (error) {
1521
+ logger.warn("EventService", "Failed to persist event selection:", error);
1522
+ }
1523
+ }
1524
+ async clearEventSelection() {
1525
+ try {
1526
+ const userId = this.user?.id || null;
1527
+ const storageKey = this.getStorageKey(userId);
1528
+ await secureStorage.removeItem(storageKey);
1529
+ this.selectedEvent = null;
1530
+ this.setSelectedEventId?.(null);
1531
+ } catch (error) {
1532
+ logger.warn("EventService", "Failed to clear event selection:", error);
1533
+ }
1534
+ }
1535
+ /**
1536
+ * Clear event selection for a specific user (used when user logs out or changes)
1537
+ */
1538
+ async clearEventSelectionForUser(userId) {
1539
+ try {
1540
+ if (!userId) return;
1541
+ const storageKey = this.getStorageKey(userId);
1542
+ await secureStorage.removeItem(storageKey);
1543
+ } catch (error) {
1544
+ logger.warn("EventService", "Failed to clear event selection for user:", error);
1545
+ }
1546
+ }
1547
+ autoSelectNextEvent(events) {
1548
+ const nextEvent = this.getNextEventByDate(events);
1549
+ if (nextEvent) {
1550
+ this.setSelectedEvent(nextEvent);
1551
+ }
1552
+ }
1553
+ // Lifecycle methods
1554
+ async initialize() {
1555
+ await super.initialize();
1556
+ }
1557
+ cleanup() {
1558
+ super.cleanup();
1559
+ }
1560
+ async doInitialize() {
1561
+ if (this.isInitializedRef) {
1562
+ return;
1563
+ }
1564
+ if (this.isFetchingRef) {
1565
+ return;
1566
+ }
1567
+ try {
1568
+ sessionStorage.removeItem("pace-core-selected-event");
1569
+ localStorage.removeItem("pace-core-selected-event");
1570
+ localStorage.removeItem("_sec_pace-core-selected-event");
1571
+ } catch (error) {
1572
+ logger.warn("EventService", "Failed to clean up old storage keys:", error);
1573
+ }
1574
+ if (!this.user) {
1575
+ return;
1576
+ }
1577
+ await this.fetchEvents(false);
1578
+ this.isInitializedRef = true;
1579
+ }
1580
+ doCleanup() {
1581
+ }
1582
+ async resolveOrganisationIdForRpc() {
1583
+ let userIsSuperAdmin = this.isSuperAdmin;
1584
+ try {
1585
+ const { isRBACInitialized: isRBACInitialized2, isSuperAdmin: checkSuperAdmin, setupRBAC: setupRBAC2 } = await import('./api-BZR2CYXL.js');
1586
+ if (!isRBACInitialized2() && this.supabaseClient) {
1587
+ setupRBAC2(this.supabaseClient);
1588
+ }
1589
+ if (isRBACInitialized2()) {
1590
+ const result = await checkSuperAdmin(this.user.id);
1591
+ userIsSuperAdmin = result.ok && result.data;
1592
+ this.isSuperAdmin = userIsSuperAdmin;
1593
+ } else {
1594
+ if (this.isSuperAdmin) {
1595
+ userIsSuperAdmin = true;
1596
+ logger.warn("EventService", "RBAC not initialized, using cached super admin status", {
1597
+ userId: this.user.id,
1598
+ cachedIsSuperAdmin: this.isSuperAdmin,
1599
+ note: "RBAC should be initialized by UnifiedAuthProvider. This may indicate EventService is being used outside the provider."
1600
+ });
1601
+ } else {
1602
+ logger.warn("EventService", "RBAC not initialized, using cached non-super-admin status", {
1603
+ userId: this.user.id,
1604
+ cachedIsSuperAdmin: this.isSuperAdmin,
1605
+ note: "RBAC should be initialized by UnifiedAuthProvider. This may indicate EventService is being used outside the provider."
1606
+ });
1607
+ }
1608
+ }
1609
+ if (userIsSuperAdmin) {
1610
+ return null;
1611
+ }
1612
+ if (this.selectedEvent) {
1613
+ return this.selectedEvent.organisation_id;
1614
+ }
1615
+ if (this.selectedOrganisation) {
1616
+ return this.selectedOrganisation.id;
1617
+ }
1618
+ return null;
1619
+ } catch (superAdminCheckError) {
1620
+ if (this.isSuperAdmin) {
1621
+ logger.warn("EventService", "Super admin check failed, using cached super admin status", {
1622
+ error: superAdminCheckError,
1623
+ cachedIsSuperAdmin: this.isSuperAdmin
1624
+ });
1625
+ return null;
1626
+ }
1627
+ logger.warn("EventService", "Failed to check super admin status, using organisation-scoped query", {
1628
+ error: superAdminCheckError,
1629
+ cachedIsSuperAdmin: this.isSuperAdmin
1630
+ });
1631
+ if (this.selectedEvent) {
1632
+ return this.selectedEvent.organisation_id;
1633
+ }
1634
+ if (this.selectedOrganisation) {
1635
+ return this.selectedOrganisation.id;
1636
+ }
1637
+ return null;
1638
+ }
1639
+ }
1640
+ transformRpcDataToEvents(data) {
1641
+ const eventsData = Array.isArray(data) ? data : [];
1642
+ const transformed = eventsData.map((event) => ({
1643
+ id: event.event_id,
1644
+ event_id: event.event_id,
1645
+ event_name: event.event_name,
1646
+ event_code: event.event_code,
1647
+ event_date: event.event_date ?? void 0,
1648
+ event_venue: event.event_venue ?? void 0,
1649
+ event_participants: event.event_participants ?? void 0,
1650
+ event_colours: event.event_colours,
1651
+ event_logo: "",
1652
+ organisation_id: assertOrganisationId(event.organisation_id),
1653
+ is_visible: event.is_visible,
1654
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
1655
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1656
+ }));
1657
+ return [...transformed].sort((a, b) => {
1658
+ if (a.event_date && b.event_date) {
1659
+ return new Date(b.event_date).getTime() - new Date(a.event_date).getTime();
1660
+ }
1661
+ if (a.event_date && !b.event_date) return -1;
1662
+ if (!a.event_date && b.event_date) return 1;
1663
+ return 0;
1664
+ });
1665
+ }
1666
+ validateSelectedEventInList(events) {
1667
+ if (!this.selectedEvent) {
1668
+ return;
1669
+ }
1670
+ const selectedEventId = this.selectedEvent.event_id;
1671
+ const eventStillExists = events.some((e) => e.event_id === selectedEventId);
1672
+ if (!eventStillExists) {
1673
+ const previousUserClearedRef = this.userClearedEventRef;
1674
+ this.selectedEvent = null;
1675
+ this.setSelectedEventId?.(null);
1676
+ this.userClearedEventRef = previousUserClearedRef;
1677
+ }
1678
+ }
1679
+ async runPersistedOrAutoSelection(events, skipLoadPersisted) {
1680
+ this.hasAutoSelectedRef = false;
1681
+ if (!skipLoadPersisted) {
1682
+ const persistedEventLoaded = await this.loadPersistedEvent(events);
1683
+ if (!persistedEventLoaded && !this.userClearedEventRef) {
1684
+ const nextEvent = this.getNextEventByDate(events);
1685
+ if (nextEvent) {
1686
+ this.hasAutoSelectedRef = true;
1687
+ this.setSelectedEvent(nextEvent);
1688
+ }
1689
+ }
1690
+ } else if (!this.userClearedEventRef) {
1691
+ const nextEvent = this.getNextEventByDate(events);
1692
+ if (nextEvent) {
1693
+ this.hasAutoSelectedRef = true;
1694
+ this.setSelectedEvent(nextEvent);
1695
+ }
1696
+ }
1697
+ }
1698
+ async fetchEvents(skipLoadPersisted = false) {
1699
+ if (!this.user || !this.session || !this.supabaseClient || !this.appName) {
1700
+ this.notify();
1701
+ return;
1702
+ }
1703
+ this._isLoading = true;
1704
+ this.notify();
1705
+ if (this.isFetchingRef) {
1706
+ return;
1707
+ }
1708
+ this.isFetchingRef = true;
1709
+ const isMounted = true;
1710
+ try {
1711
+ const organisationIdForRpc = await this.resolveOrganisationIdForRpc();
1712
+ const { data, error: rpcError } = await this.supabaseClient.rpc("data_user_events_get", {
1713
+ p_user_id: this.user.id,
1714
+ p_organisation_id: organisationIdForRpc,
1715
+ p_app_name: this.appName
1716
+ });
1717
+ if (rpcError) {
1718
+ logger.error("EventService", "RPC error fetching events:", rpcError);
1719
+ throw new Error(rpcError.message || "Failed to fetch events");
1720
+ }
1721
+ if (isMounted) {
1722
+ const transformedEvents = this.transformRpcDataToEvents(data ?? []);
1723
+ this.events = transformedEvents;
1724
+ this.error = null;
1725
+ this.validateSelectedEventInList(transformedEvents);
1726
+ await this.runPersistedOrAutoSelection(transformedEvents, skipLoadPersisted);
1727
+ }
1728
+ } catch (err2) {
1729
+ logger.error("EventService", "Error fetching events:", err2);
1730
+ const _error = err2 instanceof Error ? err2 : new Error("Unknown error occurred");
1731
+ {
1732
+ this.error = _error;
1733
+ this.events = [];
1734
+ }
1735
+ } finally {
1736
+ {
1737
+ this._isLoading = false;
1738
+ }
1739
+ this.isFetchingRef = false;
1740
+ this.notify();
1741
+ }
1742
+ }
1743
+ getNextEventByDate(events) {
1744
+ const eventsToUse = events || this.events;
1745
+ if (!eventsToUse || eventsToUse.length === 0) {
1746
+ return null;
1747
+ }
1748
+ const now = /* @__PURE__ */ new Date();
1749
+ const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
1750
+ const futureEvents = eventsToUse.filter((event) => {
1751
+ if (!event.event_date) return false;
1752
+ const eventDate = new Date(event.event_date);
1753
+ const startOfEventDate = new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate()).getTime();
1754
+ return startOfEventDate >= startOfToday;
1755
+ });
1756
+ if (futureEvents.length > 0) {
1757
+ const sortedFutureEvents = futureEvents.sort((a, b) => {
1758
+ const dateA = new Date(a.event_date);
1759
+ const dateB = new Date(b.event_date);
1760
+ return dateA.getTime() - dateB.getTime();
1761
+ });
1762
+ return sortedFutureEvents[0];
1763
+ }
1764
+ const pastEvents = eventsToUse.filter((event) => {
1765
+ if (!event.event_date) return false;
1766
+ const eventDate = new Date(event.event_date);
1767
+ const startOfEventDate = new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate()).getTime();
1768
+ return startOfEventDate < startOfToday;
1769
+ });
1770
+ if (pastEvents.length > 0) {
1771
+ const sortedPastEvents = pastEvents.sort((a, b) => {
1772
+ const dateA = new Date(a.event_date);
1773
+ const dateB = new Date(b.event_date);
1774
+ return dateB.getTime() - dateA.getTime();
1775
+ });
1776
+ return sortedPastEvents[0];
1777
+ }
1778
+ return null;
1779
+ }
1780
+ };
1781
+ _EventService.instanceCount = 0;
1782
+ var EventService = _EventService;
1783
+ var EventServiceContext = createContext(null);
1784
+ function EventServiceProvider({
1785
+ children,
1786
+ supabaseClient,
1787
+ user,
1788
+ session,
1789
+ appName,
1790
+ selectedOrganisation,
1791
+ setSelectedEventId
1792
+ }) {
1793
+ const eventServiceRef = useRef(null);
1794
+ const initializingRef = useRef(false);
1795
+ if (!eventServiceRef.current) {
1796
+ eventServiceRef.current = new EventService(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);
1797
+ }
1798
+ const eventService = eventServiceRef.current;
1799
+ useEffect(() => {
1800
+ let isMounted = true;
1801
+ if (initializingRef.current) {
1802
+ return;
1803
+ }
1804
+ const updateAndInitialize = async () => {
1805
+ initializingRef.current = true;
1806
+ try {
1807
+ await eventService.updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId).catch((error) => {
1808
+ if (isMounted) {
1809
+ logger.error("EventServiceProvider", "Failed to update event service dependencies:", error);
1810
+ }
1811
+ return;
1812
+ });
1813
+ if (!isMounted) return;
1814
+ if (user && session && supabaseClient && appName) {
1815
+ await eventService.initialize().catch((error) => {
1816
+ if (isMounted) {
1817
+ logger.error("EventServiceProvider", "Failed to initialize event service:", error);
1818
+ }
1819
+ });
1820
+ } else {
1821
+ }
1822
+ } finally {
1823
+ initializingRef.current = false;
1824
+ }
1825
+ };
1826
+ updateAndInitialize();
1827
+ return () => {
1828
+ isMounted = false;
1829
+ initializingRef.current = false;
1830
+ };
1831
+ }, [supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId, eventService]);
1832
+ useEffect(() => {
1833
+ return () => {
1834
+ eventService.cleanup();
1835
+ };
1836
+ }, [eventService]);
1837
+ const contextValue = useMemo(() => ({
1838
+ eventService
1839
+ }), [eventService]);
1840
+ return /* @__PURE__ */ jsx(EventServiceContext.Provider, { value: contextValue, children });
1841
+ }
1842
+ var InactivityServiceContext = createContext(null);
1843
+
1844
+ // src/services/InactivityService.ts
1845
+ var InactivityService = class extends BaseService {
1846
+ constructor(supabaseClient, user, session, idleTimeoutMs = 30 * 60 * 1e3, warnBeforeMs = 60 * 1e3, onIdleLogout) {
1847
+ super();
1848
+ this._showInactivityWarning = false;
1849
+ this._inactivityTimeRemaining = 0;
1850
+ this._isIdle = false;
1851
+ this._timeRemaining = 0;
1852
+ this._showWarning = false;
1853
+ this._isTracking = false;
1854
+ // Dependencies
1855
+ this.supabaseClient = null;
1856
+ this.user = null;
1857
+ this.session = null;
1858
+ this.idleTimeoutMs = 30 * 60 * 1e3;
1859
+ // 30 minutes
1860
+ this.warnBeforeMs = 60 * 1e3;
1861
+ // 60 seconds
1862
+ this.onIdleLogout = null;
1863
+ // Internal state management
1864
+ this.inactivityTracker = null;
1865
+ this.isInactivityEnabled = true;
1866
+ this.cleanupHandlers = null;
1867
+ this.supabaseClient = supabaseClient;
1868
+ this.user = user;
1869
+ this.session = session;
1870
+ this.idleTimeoutMs = idleTimeoutMs;
1871
+ this.warnBeforeMs = warnBeforeMs;
1872
+ this.onIdleLogout = onIdleLogout;
1873
+ this._timeRemaining = idleTimeoutMs;
1874
+ }
1875
+ // Interface implementation
1876
+ isIdle() {
1877
+ return this._isIdle;
1878
+ }
1879
+ getTimeRemaining() {
1880
+ return this._timeRemaining;
1881
+ }
1882
+ isWarningShown() {
1883
+ return this._showWarning;
1884
+ }
1885
+ isTracking() {
1886
+ return this._isTracking;
1887
+ }
1888
+ getShowInactivityWarning() {
1889
+ return this._showInactivityWarning;
1890
+ }
1891
+ getInactivityTimeRemaining() {
1892
+ return this._inactivityTimeRemaining;
1893
+ }
1894
+ // Additional getter methods that tests expect
1895
+ getIsIdle() {
1896
+ return this._isIdle;
1897
+ }
1898
+ getIsTracking() {
1899
+ return this._isTracking;
1900
+ }
1901
+ getShowWarning() {
1902
+ return this._showWarning;
1903
+ }
1904
+ // Additional methods for testing
1905
+ setShowInactivityWarning(value) {
1906
+ this._showInactivityWarning = value;
1907
+ this.notify();
1908
+ }
1909
+ setInactivityTimeRemaining(value) {
1910
+ this._inactivityTimeRemaining = value;
1911
+ this.notify();
1912
+ }
1913
+ setIsIdle(value) {
1914
+ this._isIdle = value;
1915
+ this.notify();
1916
+ }
1917
+ setTimeRemaining(value) {
1918
+ this._timeRemaining = value;
1919
+ this.notify();
1920
+ }
1921
+ setShowWarning(value) {
1922
+ this._showWarning = value;
1923
+ this.notify();
1924
+ }
1925
+ setIsTracking(value) {
1926
+ this._isTracking = value;
1927
+ this.notify();
1928
+ }
1929
+ triggerWarning(timeRemaining) {
1930
+ this._showInactivityWarning = true;
1931
+ this._inactivityTimeRemaining = Math.ceil(timeRemaining / 1e3);
1932
+ this._showWarning = true;
1933
+ this.notify();
1934
+ }
1935
+ triggerIdle() {
1936
+ this._isIdle = true;
1937
+ this.handleIdleLogout();
1938
+ this.notify();
1939
+ }
1940
+ // Update dependencies
1941
+ updateDependencies(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout) {
1942
+ this.supabaseClient = supabaseClient;
1943
+ this.user = user;
1944
+ this.session = session;
1945
+ if (idleTimeoutMs !== void 0) this.idleTimeoutMs = idleTimeoutMs;
1946
+ if (warnBeforeMs !== void 0) this.warnBeforeMs = warnBeforeMs;
1947
+ if (onIdleLogout !== void 0) this.onIdleLogout = onIdleLogout;
1948
+ this.notify();
1949
+ }
1950
+ // Inactivity methods
1951
+ resetActivity() {
1952
+ if (this.inactivityTracker) {
1953
+ this.inactivityTracker.resetActivity();
1954
+ }
1955
+ const prevIsIdle = this._isIdle;
1956
+ const prevShowWarning = this._showWarning;
1957
+ const prevShowInactivityWarning = this._showInactivityWarning;
1958
+ const prevInactivityTimeRemaining = this._inactivityTimeRemaining;
1959
+ const prevTimeRemaining = this._timeRemaining;
1960
+ this._isIdle = false;
1961
+ this._showWarning = false;
1962
+ this._showInactivityWarning = false;
1963
+ this._inactivityTimeRemaining = 0;
1964
+ this._timeRemaining = this.idleTimeoutMs;
1965
+ if (prevIsIdle !== this._isIdle || prevShowWarning !== this._showWarning || prevShowInactivityWarning !== this._showInactivityWarning || prevInactivityTimeRemaining !== this._inactivityTimeRemaining || prevTimeRemaining !== this._timeRemaining) {
1966
+ this.notify();
1967
+ }
1968
+ }
1969
+ startTracking() {
1970
+ if (this.inactivityTracker) {
1971
+ this.inactivityTracker.startTracking();
1972
+ }
1973
+ this._isTracking = true;
1974
+ this.notify();
1975
+ }
1976
+ stopTracking() {
1977
+ if (this.inactivityTracker) {
1978
+ this.inactivityTracker.stopTracking();
1979
+ }
1980
+ this._isTracking = false;
1981
+ this.notify();
1982
+ }
1983
+ async handleIdleLogout() {
1984
+ this._showInactivityWarning = false;
1985
+ this._inactivityTimeRemaining = 0;
1986
+ this.stopTracking();
1987
+ try {
1988
+ if (this.supabaseClient) {
1989
+ await this.supabaseClient.auth.signOut();
1990
+ }
1991
+ } catch (error) {
1992
+ logger.error("InactivityService", "Error during idle logout:", error);
1993
+ }
1994
+ try {
1995
+ sessionStorage.clear();
1996
+ } catch (storageError) {
1997
+ logger.warn("InactivityService", "Failed to clear sessionStorage", { error: storageError });
1998
+ }
1999
+ this.onIdleLogout?.("inactivity");
2000
+ this.notify();
2001
+ }
2002
+ handleStaySignedIn() {
2003
+ this._showInactivityWarning = false;
2004
+ this._inactivityTimeRemaining = 0;
2005
+ this.resetActivity();
2006
+ this.notify();
2007
+ }
2008
+ async handleSignOutNow() {
2009
+ this._showInactivityWarning = false;
2010
+ this._inactivityTimeRemaining = 0;
2011
+ this.stopTracking();
2012
+ try {
2013
+ if (this.supabaseClient) {
2014
+ await this.supabaseClient.auth.signOut();
2015
+ }
2016
+ } catch (error) {
2017
+ logger.error("InactivityService", "Error during manual sign out:", error);
2018
+ }
2019
+ try {
2020
+ sessionStorage.clear();
2021
+ } catch (storageError) {
2022
+ logger.warn("InactivityService", "Failed to clear sessionStorage", { error: storageError });
2023
+ }
2024
+ this.onIdleLogout?.("inactivity");
2025
+ this.notify();
2026
+ }
2027
+ // Lifecycle methods
2028
+ async initialize() {
2029
+ await super.initialize();
2030
+ await this.setupInactivityTracker();
2031
+ }
2032
+ cleanup() {
2033
+ if (this.cleanupHandlers) {
2034
+ this.cleanupHandlers();
2035
+ this.cleanupHandlers = null;
2036
+ }
2037
+ if (this.inactivityTracker) {
2038
+ this.inactivityTracker = null;
2039
+ }
2040
+ this._isTracking = false;
2041
+ this._isIdle = false;
2042
+ this._showWarning = false;
2043
+ this._showInactivityWarning = false;
2044
+ this._timeRemaining = 0;
2045
+ this._inactivityTimeRemaining = 0;
2046
+ super.cleanup();
2047
+ }
2048
+ async doInitialize() {
2049
+ if (typeof window !== "undefined") {
2050
+ const isProduction = import.meta.env.MODE === "production";
2051
+ if (isProduction) {
2052
+ logger.warn("InactivityService", "Inactivity feature enabled in production");
2053
+ }
2054
+ }
2055
+ }
2056
+ doCleanup() {
2057
+ }
2058
+ async setupInactivityTracker() {
2059
+ if (typeof window === "undefined") return;
2060
+ this.isInactivityEnabled = !!(this.user && this.session);
2061
+ if (!this.isInactivityEnabled) {
2062
+ return;
2063
+ }
2064
+ this.setupEventHandlers();
2065
+ }
2066
+ runUpdateState(ctx, forceNotify = false) {
2067
+ const now = Date.now();
2068
+ const timeSinceActivity = now - ctx.lastActivity;
2069
+ const timeUntilIdle = this.idleTimeoutMs - timeSinceActivity;
2070
+ const newIsIdle = timeSinceActivity >= this.idleTimeoutMs;
2071
+ const newShowWarning = timeSinceActivity >= this.idleTimeoutMs - this.warnBeforeMs && !newIsIdle;
2072
+ const newShowInactivityWarning = newShowWarning;
2073
+ const newInactivityTimeRemaining = newShowWarning ? Math.ceil(timeUntilIdle / 1e3) : 0;
2074
+ const newTimeRemaining = Math.max(0, timeUntilIdle);
2075
+ const stateChanged = ctx.prevIsIdle !== newIsIdle || ctx.prevShowWarning !== newShowWarning || ctx.prevShowInactivityWarning !== newShowInactivityWarning || newShowWarning && ctx.prevInactivityTimeRemaining !== newInactivityTimeRemaining || ctx.prevTimeRemaining !== newTimeRemaining;
2076
+ if (stateChanged || forceNotify) {
2077
+ this._isIdle = newIsIdle;
2078
+ this._showWarning = newShowWarning;
2079
+ this._showInactivityWarning = newShowInactivityWarning;
2080
+ this._inactivityTimeRemaining = newInactivityTimeRemaining;
2081
+ this._timeRemaining = newTimeRemaining;
2082
+ ctx.prevIsIdle = newIsIdle;
2083
+ ctx.prevShowWarning = newShowWarning;
2084
+ ctx.prevShowInactivityWarning = newShowInactivityWarning;
2085
+ ctx.prevInactivityTimeRemaining = newInactivityTimeRemaining;
2086
+ ctx.prevTimeRemaining = newTimeRemaining;
2087
+ if (stateChanged) {
2088
+ this.notify();
2089
+ }
2090
+ if (newIsIdle) {
2091
+ this.handleIdleLogout();
2092
+ }
2093
+ }
2094
+ }
2095
+ runScheduleNextCheck(ctx) {
2096
+ if (ctx.warningTimer) {
2097
+ clearTimeout(ctx.warningTimer);
2098
+ ctx.warningTimer = null;
2099
+ }
2100
+ if (ctx.logoutTimer) {
2101
+ clearTimeout(ctx.logoutTimer);
2102
+ ctx.logoutTimer = null;
2103
+ }
2104
+ if (ctx.countdownInterval) {
2105
+ clearInterval(ctx.countdownInterval);
2106
+ ctx.countdownInterval = null;
2107
+ }
2108
+ const getTimeUntilWarning = () => Math.max(0, this.idleTimeoutMs - this.warnBeforeMs - (Date.now() - ctx.lastActivity));
2109
+ const getTimeUntilLogout = () => Math.max(0, this.idleTimeoutMs - (Date.now() - ctx.lastActivity));
2110
+ const timeUntilWarning = getTimeUntilWarning();
2111
+ const timeUntilLogout = getTimeUntilLogout();
2112
+ const timeSinceActivity = Date.now() - ctx.lastActivity;
2113
+ if (timeSinceActivity >= this.idleTimeoutMs) {
2114
+ return;
2115
+ }
2116
+ if (timeSinceActivity >= this.idleTimeoutMs - this.warnBeforeMs) {
2117
+ this.runUpdateState(ctx);
2118
+ ctx.countdownInterval = setInterval(() => {
2119
+ this.runUpdateState(ctx);
2120
+ if (Date.now() - ctx.lastActivity >= this.idleTimeoutMs) {
2121
+ if (ctx.countdownInterval) {
2122
+ clearInterval(ctx.countdownInterval);
2123
+ ctx.countdownInterval = null;
2124
+ }
2125
+ this.handleIdleLogout();
2126
+ }
2127
+ }, 1e3);
2128
+ ctx.logoutTimer = setTimeout(() => {
2129
+ if (ctx.countdownInterval) {
2130
+ clearInterval(ctx.countdownInterval);
2131
+ ctx.countdownInterval = null;
2132
+ }
2133
+ this.handleIdleLogout();
2134
+ }, timeUntilLogout);
2135
+ } else {
2136
+ const timeUntilWarningMs = timeUntilWarning;
2137
+ const adaptiveInterval = timeUntilWarningMs > 5 * 60 * 1e3 ? 60 * 1e3 : timeUntilWarningMs > 60 * 1e3 ? 10 * 1e3 : 1e3;
2138
+ ctx.warningTimer = setTimeout(() => {
2139
+ this.runUpdateState(ctx);
2140
+ this.runScheduleNextCheck(ctx);
2141
+ }, Math.min(adaptiveInterval, timeUntilWarningMs));
2142
+ }
2143
+ }
2144
+ runResetTimers(ctx) {
2145
+ ctx.lastActivity = Date.now();
2146
+ if (ctx.warningTimer) {
2147
+ clearTimeout(ctx.warningTimer);
2148
+ ctx.warningTimer = null;
2149
+ }
2150
+ if (ctx.logoutTimer) {
2151
+ clearTimeout(ctx.logoutTimer);
2152
+ ctx.logoutTimer = null;
2153
+ }
2154
+ if (ctx.countdownInterval) {
2155
+ clearInterval(ctx.countdownInterval);
2156
+ ctx.countdownInterval = null;
2157
+ }
2158
+ const hadWarning = this._showWarning || this._showInactivityWarning;
2159
+ this._showInactivityWarning = false;
2160
+ this._inactivityTimeRemaining = 0;
2161
+ this._isIdle = false;
2162
+ this._showWarning = false;
2163
+ this._timeRemaining = this.idleTimeoutMs;
2164
+ ctx.prevIsIdle = false;
2165
+ ctx.prevShowWarning = false;
2166
+ ctx.prevShowInactivityWarning = false;
2167
+ ctx.prevInactivityTimeRemaining = 0;
2168
+ ctx.prevTimeRemaining = this.idleTimeoutMs;
2169
+ if (hadWarning) {
2170
+ this.notify();
2171
+ }
2172
+ this.runScheduleNextCheck(ctx);
2173
+ }
2174
+ setupEventHandlers() {
2175
+ if (typeof window === "undefined") return;
2176
+ const ctx = {
2177
+ warningTimer: null,
2178
+ logoutTimer: null,
2179
+ countdownInterval: null,
2180
+ activityThrottleTimer: null,
2181
+ lastActivity: Date.now(),
2182
+ prevIsIdle: false,
2183
+ prevShowWarning: false,
2184
+ prevShowInactivityWarning: false,
2185
+ prevInactivityTimeRemaining: 0,
2186
+ prevTimeRemaining: this.idleTimeoutMs
2187
+ };
2188
+ const activityEvents = ["mousedown", "mousemove", "keypress", "scroll", "touchstart", "click"];
2189
+ const handleActivity = () => {
2190
+ if (ctx.activityThrottleTimer) return;
2191
+ ctx.activityThrottleTimer = setTimeout(() => {
2192
+ ctx.activityThrottleTimer = null;
2193
+ this.runResetTimers(ctx);
2194
+ }, 1e3);
2195
+ };
2196
+ activityEvents.forEach((event) => {
2197
+ document.addEventListener(event, handleActivity, true);
2198
+ });
2199
+ this.runUpdateState(ctx, true);
2200
+ this.runScheduleNextCheck(ctx);
2201
+ this.cleanupHandlers = () => {
2202
+ if (ctx.warningTimer) {
2203
+ clearTimeout(ctx.warningTimer);
2204
+ ctx.warningTimer = null;
2205
+ }
2206
+ if (ctx.logoutTimer) {
2207
+ clearTimeout(ctx.logoutTimer);
2208
+ ctx.logoutTimer = null;
2209
+ }
2210
+ if (ctx.countdownInterval) {
2211
+ clearInterval(ctx.countdownInterval);
2212
+ ctx.countdownInterval = null;
2213
+ }
2214
+ if (ctx.activityThrottleTimer) {
2215
+ clearTimeout(ctx.activityThrottleTimer);
2216
+ ctx.activityThrottleTimer = null;
2217
+ }
2218
+ activityEvents.forEach((event) => {
2219
+ document.removeEventListener(event, handleActivity, true);
2220
+ });
2221
+ this._isTracking = false;
2222
+ this._isIdle = false;
2223
+ this._showWarning = false;
2224
+ this._timeRemaining = 0;
2225
+ this._showInactivityWarning = false;
2226
+ this._inactivityTimeRemaining = 0;
2227
+ };
2228
+ this._isTracking = true;
2229
+ }
2230
+ };
2231
+ function InactivityServiceProvider({
2232
+ children,
2233
+ supabaseClient,
2234
+ user,
2235
+ session,
2236
+ idleTimeoutMs,
2237
+ // REQUIRED: No default - must be explicitly provided
2238
+ warnBeforeMs,
2239
+ // REQUIRED: No default - must be explicitly provided
2240
+ onIdleLogout
2241
+ }) {
2242
+ const inactivityServiceRef = useRef(null);
2243
+ if (!inactivityServiceRef.current) {
2244
+ inactivityServiceRef.current = new InactivityService(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout);
2245
+ }
2246
+ const inactivityService = inactivityServiceRef.current;
2247
+ useEffect(() => {
2248
+ inactivityService.updateDependencies(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout);
2249
+ let isMounted = true;
2250
+ inactivityService.initialize().catch((error) => {
2251
+ if (isMounted) {
2252
+ logger.error("InactivityServiceProvider", "Failed to initialize inactivity service:", error);
2253
+ }
2254
+ });
2255
+ return () => {
2256
+ isMounted = false;
2257
+ };
2258
+ }, [inactivityService, supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout]);
2259
+ useEffect(() => {
2260
+ return () => {
2261
+ inactivityService.cleanup();
2262
+ };
2263
+ }, [inactivityService]);
2264
+ const contextValue = useMemo(() => ({
2265
+ inactivityService
2266
+ }), [inactivityService]);
2267
+ return /* @__PURE__ */ jsx(InactivityServiceContext.Provider, { value: contextValue, children });
2268
+ }
2269
+ function useAuthService() {
2270
+ const context = useContext(AuthServiceContext);
2271
+ if (!context) {
2272
+ throw new Error("useAuthService must be used within AuthServiceProvider");
2273
+ }
2274
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
2275
+ const timeoutRef = useRef(null);
2276
+ useEffect(() => {
2277
+ const debouncedUpdate = () => {
2278
+ if (timeoutRef.current) {
2279
+ clearTimeout(timeoutRef.current);
2280
+ }
2281
+ timeoutRef.current = setTimeout(() => {
2282
+ forceUpdate();
2283
+ timeoutRef.current = null;
2284
+ }, 50);
2285
+ };
2286
+ const unsubscribe = context.authService.subscribe(debouncedUpdate);
2287
+ return () => {
2288
+ unsubscribe();
2289
+ if (timeoutRef.current) {
2290
+ clearTimeout(timeoutRef.current);
2291
+ }
2292
+ };
2293
+ }, [context.authService]);
2294
+ return context.authService;
2295
+ }
2296
+ function useOrganisationService() {
2297
+ const context = useContext(OrganisationServiceContext);
2298
+ if (!context) {
2299
+ throw new Error("useOrganisationService must be used within OrganisationServiceProvider");
2300
+ }
2301
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
2302
+ const timeoutRef = useRef(null);
2303
+ useEffect(() => {
2304
+ const debouncedUpdate = () => {
2305
+ if (timeoutRef.current) {
2306
+ clearTimeout(timeoutRef.current);
2307
+ }
2308
+ timeoutRef.current = setTimeout(() => {
2309
+ forceUpdate();
2310
+ timeoutRef.current = null;
2311
+ }, 50);
2312
+ };
2313
+ const unsubscribe = context.organisationService.subscribe(debouncedUpdate);
2314
+ return () => {
2315
+ unsubscribe();
2316
+ if (timeoutRef.current) {
2317
+ clearTimeout(timeoutRef.current);
2318
+ }
2319
+ };
2320
+ }, [context.organisationService]);
2321
+ return context.organisationService;
2322
+ }
2323
+
2324
+ // src/hooks/useOrganisations.ts
2325
+ function useOrganisations() {
2326
+ const organisationService = useOrganisationService();
2327
+ const selectedOrg = organisationService.getSelectedOrganisation();
2328
+ return {
2329
+ selectedOrganisation: selectedOrg,
2330
+ organisations: organisationService.getOrganisations(),
2331
+ userMemberships: organisationService.getUserMemberships(),
2332
+ isLoading: organisationService.isLoading(),
2333
+ error: organisationService.getError(),
2334
+ hasValidOrganisationContext: organisationService.hasValidOrganisationContext(),
2335
+ isContextReady: organisationService.isContextReady(),
2336
+ setSelectedOrganisation: (org) => organisationService.setSelectedOrganisation(org),
2337
+ switchOrganisation: (orgId) => organisationService.switchOrganisation(orgId),
2338
+ getUserRole: (orgId) => organisationService.getUserRole(orgId),
2339
+ validateOrganisationAccess: (orgId) => organisationService.validateOrganisationAccess(orgId),
2340
+ refreshOrganisations: () => organisationService.refreshOrganisations(),
2341
+ ensureOrganisationContext: () => organisationService.ensureOrganisationContext(),
2342
+ isOrganisationSecure: () => organisationService.isOrganisationSecure(),
2343
+ getPrimaryOrganisation: () => organisationService.getPrimaryOrganisation()
2344
+ };
2345
+ }
2346
+ function useEventService() {
2347
+ const context = useContext(EventServiceContext);
2348
+ if (!context) {
2349
+ throw new Error("useEventService must be used within EventServiceProvider");
2350
+ }
2351
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
2352
+ const timeoutRef = useRef(null);
2353
+ useEffect(() => {
2354
+ const debouncedUpdate = () => {
2355
+ if (timeoutRef.current) {
2356
+ clearTimeout(timeoutRef.current);
2357
+ }
2358
+ timeoutRef.current = setTimeout(() => {
2359
+ forceUpdate();
2360
+ timeoutRef.current = null;
2361
+ }, 50);
2362
+ };
2363
+ const unsubscribe = context.eventService.subscribe(debouncedUpdate);
2364
+ return () => {
2365
+ unsubscribe();
2366
+ if (timeoutRef.current) {
2367
+ clearTimeout(timeoutRef.current);
2368
+ }
2369
+ };
2370
+ }, [context.eventService]);
2371
+ return context.eventService;
2372
+ }
2373
+ function useInactivityService() {
2374
+ const context = useContext(InactivityServiceContext);
2375
+ if (!context) {
2376
+ throw new Error("useInactivityService must be used within InactivityServiceProvider");
2377
+ }
2378
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
2379
+ const timeoutRef = useRef(null);
2380
+ useEffect(() => {
2381
+ const debouncedUpdate = () => {
2382
+ if (timeoutRef.current) {
2383
+ clearTimeout(timeoutRef.current);
2384
+ }
2385
+ timeoutRef.current = setTimeout(() => {
2386
+ forceUpdate();
2387
+ timeoutRef.current = null;
2388
+ }, 50);
2389
+ };
2390
+ const unsubscribe = context.inactivityService.subscribe(debouncedUpdate);
2391
+ return () => {
2392
+ unsubscribe();
2393
+ if (timeoutRef.current) {
2394
+ clearTimeout(timeoutRef.current);
2395
+ }
2396
+ };
2397
+ }, [context.inactivityService]);
2398
+ return context.inactivityService;
2399
+ }
2400
+ var log = createLogger("useSessionRestoration");
2401
+ var SESSION_RESTORATION_TIMEOUT_MS = 1e4;
2402
+ function useSessionRestoration() {
2403
+ const context = useContext(AuthServiceContext);
2404
+ if (!context) {
2405
+ throw new Error("useSessionRestoration must be used within AuthServiceProvider");
2406
+ }
2407
+ const { sessionRestoration } = context;
2408
+ const [hasTimedOut, setHasTimedOut] = useState(false);
2409
+ useEffect(() => {
2410
+ let timeoutHandle = null;
2411
+ if (sessionRestoration.isRestoring && !sessionRestoration.restorationComplete && !sessionRestoration.restorationError) {
2412
+ setHasTimedOut(false);
2413
+ timeoutHandle = setTimeout(() => {
2414
+ log.warn("Session restoration timed out");
2415
+ setHasTimedOut(true);
2416
+ }, SESSION_RESTORATION_TIMEOUT_MS);
2417
+ } else {
2418
+ setHasTimedOut(false);
2419
+ }
2420
+ return () => {
2421
+ if (timeoutHandle) {
2422
+ clearTimeout(timeoutHandle);
2423
+ }
2424
+ };
2425
+ }, [
2426
+ sessionRestoration.isRestoring,
2427
+ sessionRestoration.restorationComplete,
2428
+ sessionRestoration.restorationError
2429
+ ]);
2430
+ return useMemo(() => ({
2431
+ ...sessionRestoration,
2432
+ hasTimedOut,
2433
+ timeoutMs: SESSION_RESTORATION_TIMEOUT_MS
2434
+ }), [sessionRestoration, hasTimedOut]);
2435
+ }
2436
+ var UnifiedAuthContext = createContext(void 0);
2437
+ function useUnifiedAuth() {
2438
+ const context = useContext(UnifiedAuthContext);
2439
+ if (!context) {
2440
+ logger.error("useUnifiedAuth", "useUnifiedAuth must be used within a UnifiedAuthProvider");
2441
+ throw new Error("useUnifiedAuth must be used within a UnifiedAuthProvider");
2442
+ }
2443
+ return context;
2444
+ }
2445
+ function useAppIdResolution(supabase, appName, isAuth, currentUserId) {
2446
+ const [appId, setAppId] = useState(void 0);
2447
+ const isResolvingAppIdRef = useRef(false);
2448
+ const resolvedAppIdRef = useRef(void 0);
2449
+ const resolvedUserIdRef = useRef(void 0);
2450
+ useEffect(() => {
2451
+ if (!isAuth) {
2452
+ resolvedAppIdRef.current = void 0;
2453
+ resolvedUserIdRef.current = void 0;
2454
+ setAppId(void 0);
2455
+ return;
2456
+ }
2457
+ if (currentUserId && resolvedUserIdRef.current && resolvedUserIdRef.current !== currentUserId) {
2458
+ resolvedAppIdRef.current = void 0;
2459
+ resolvedUserIdRef.current = void 0;
2460
+ setAppId(void 0);
2461
+ }
2462
+ const shouldResolve = isAuth && currentUserId && supabase && appName && resolvedUserIdRef.current !== currentUserId && !isResolvingAppIdRef.current;
2463
+ if (!shouldResolve) return;
2464
+ isResolvingAppIdRef.current = true;
2465
+ resolvedUserIdRef.current = currentUserId;
2466
+ const userId = currentUserId;
2467
+ const appNameValue = appName;
2468
+ import('./api-BZR2CYXL.js').then(async ({ resolveAppContext, setupRBAC: setupRBAC2 }) => {
2469
+ try {
2470
+ setupRBAC2(supabase);
2471
+ const result = await resolveAppContext({ userId, appName: appNameValue });
2472
+ if (result.ok && result.data?.appId) {
2473
+ const appIdValue = result.data.appId;
2474
+ resolvedAppIdRef.current = appIdValue;
2475
+ setAppId(appIdValue);
2476
+ } else {
2477
+ resolvedUserIdRef.current = void 0;
2478
+ }
2479
+ } catch (error) {
2480
+ logger.error("UnifiedAuthProvider", "Failed to resolve appId on login", {
2481
+ error: error instanceof Error ? error.message : String(error),
2482
+ appName: appNameValue,
2483
+ userId
2484
+ });
2485
+ resolvedUserIdRef.current = void 0;
2486
+ } finally {
2487
+ isResolvingAppIdRef.current = false;
2488
+ }
2489
+ }).catch((importError) => {
2490
+ logger.error("UnifiedAuthProvider", "Failed to import RBAC API", importError);
2491
+ isResolvingAppIdRef.current = false;
2492
+ resolvedUserIdRef.current = void 0;
2493
+ });
2494
+ }, [isAuth, currentUserId, supabase, appName]);
2495
+ return appId;
2496
+ }
2497
+ function useUnifiedAuthSubscriptions(authService, organisationService, eventService, inactivityService) {
2498
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
2499
+ const forceUpdateTimeoutRef = useRef(null);
2500
+ const debouncedForceUpdate = useCallback(() => {
2501
+ if (forceUpdateTimeoutRef.current) clearTimeout(forceUpdateTimeoutRef.current);
2502
+ forceUpdateTimeoutRef.current = setTimeout(() => {
2503
+ forceUpdate();
2504
+ forceUpdateTimeoutRef.current = null;
2505
+ }, 100);
2506
+ }, [forceUpdate]);
2507
+ const authServiceRef = useRef(authService);
2508
+ const organisationServiceRef = useRef(organisationService);
2509
+ const eventServiceRef = useRef(eventService);
2510
+ const inactivityServiceRef = useRef(inactivityService);
2511
+ useEffect(() => {
2512
+ authServiceRef.current = authService;
2513
+ organisationServiceRef.current = organisationService;
2514
+ eventServiceRef.current = eventService;
2515
+ inactivityServiceRef.current = inactivityService;
2516
+ }, [authService, organisationService, eventService, inactivityService]);
2517
+ useEffect(() => {
2518
+ const unsubAuth = authServiceRef.current.subscribe(debouncedForceUpdate);
2519
+ const unsubOrg = organisationServiceRef.current.subscribe(debouncedForceUpdate);
2520
+ const unsubEvent = eventServiceRef.current.subscribe(debouncedForceUpdate);
2521
+ const unsubInactivity = inactivityServiceRef.current.subscribe(debouncedForceUpdate);
2522
+ return () => {
2523
+ unsubAuth();
2524
+ unsubOrg();
2525
+ unsubEvent();
2526
+ unsubInactivity();
2527
+ if (forceUpdateTimeoutRef.current) clearTimeout(forceUpdateTimeoutRef.current);
2528
+ };
2529
+ }, [debouncedForceUpdate]);
2530
+ }
2531
+ function useUnifiedAuthContextValue(state, callbacks, appId, appName, supabase) {
2532
+ return useMemo(
2533
+ () => {
2534
+ const selectedOrganisationId = state.selectedOrganisation?.id ?? state.selectedEvent?.organisation_id ?? null;
2535
+ return {
2536
+ user: state.currentUser,
2537
+ session: state.currentSession,
2538
+ isAuthenticated: state.isAuth,
2539
+ authLoading: state.authLoading,
2540
+ authError: state.authError,
2541
+ supabase,
2542
+ signIn: callbacks.signIn,
2543
+ signUp: callbacks.signUp,
2544
+ signOut: callbacks.signOut,
2545
+ resetPassword: callbacks.resetPassword,
2546
+ updatePassword: callbacks.updatePassword,
2547
+ refreshSession: callbacks.refreshSession,
2548
+ selectedOrganisation: state.selectedOrganisation,
2549
+ selectedOrganisationId,
2550
+ organisations: state.organisations,
2551
+ userMemberships: state.userMemberships,
2552
+ organisationLoading: state.orgLoading,
2553
+ organisationError: state.organisationError,
2554
+ hasValidOrganisationContext: state.hasValidOrganisationContext,
2555
+ isContextReady: state.isContextReady,
2556
+ switchOrganisation: callbacks.switchOrganisation,
2557
+ getUserRole: callbacks.getUserRole,
2558
+ validateOrganisationAccess: callbacks.validateOrganisationAccess,
2559
+ refreshOrganisations: callbacks.refreshOrganisations,
2560
+ ensureOrganisationContext: callbacks.ensureOrganisationContext,
2561
+ isOrganisationSecure: callbacks.isOrganisationSecure,
2562
+ getPrimaryOrganisation: callbacks.getPrimaryOrganisation,
2563
+ events: state.events,
2564
+ selectedEvent: state.selectedEvent,
2565
+ selectedEventId: state.selectedEvent?.event_id ?? null,
2566
+ eventLoading: state.eventLoading,
2567
+ eventError: state.eventError,
2568
+ setSelectedEvent: callbacks.setSelectedEvent,
2569
+ refreshEvents: callbacks.refreshEvents,
2570
+ showInactivityWarning: state.inactivityState.showInactivityWarning,
2571
+ inactivityTimeRemaining: state.inactivityState.inactivityTimeRemaining,
2572
+ isIdle: state.inactivityState.isIdle,
2573
+ timeRemaining: state.inactivityState.timeRemaining,
2574
+ showWarning: state.inactivityState.showWarning,
2575
+ isTracking: state.inactivityState.isTracking,
2576
+ resetActivity: callbacks.resetActivity,
2577
+ startTracking: callbacks.startTracking,
2578
+ stopTracking: callbacks.stopTracking,
2579
+ handleIdleLogout: callbacks.handleIdleLogout,
2580
+ handleStaySignedIn: callbacks.handleStaySignedIn,
2581
+ handleSignOutNow: callbacks.handleSignOutNow,
2582
+ appName,
2583
+ appId,
2584
+ isLoading: state.totalLoading,
2585
+ hasErrors: state.hasErrors,
2586
+ sessionRestoration: state.sessionRestoration,
2587
+ sessionRestorationTimedOut: state.sessionRestorationTimedOut,
2588
+ sessionRestorationTimeoutMs: state.sessionRestorationTimeoutMs
2589
+ };
2590
+ },
2591
+ [state, callbacks, appId, appName, supabase]
2592
+ );
2593
+ }
2594
+ function useUnifiedAuthStateAndCallbacks(authService, organisationService, eventService, inactivityService, sessionRestoration, sessionRestorationTimedOut, sessionRestorationTimeoutMs, appName, userOverride, sessionOverride) {
2595
+ const currentUser = userOverride !== void 0 ? userOverride : authService.getUser();
2596
+ const currentSession = sessionOverride !== void 0 ? sessionOverride : authService.getSession();
2597
+ const isAuth = !!(currentUser && currentSession);
2598
+ const authLoading = authService.isLoading();
2599
+ const orgLoading = organisationService.isLoading();
2600
+ const eventLoading = eventService.isLoading();
2601
+ const restorationLoading = sessionRestoration.isRestoring && !sessionRestorationTimedOut && !sessionRestoration.restorationError;
2602
+ const shouldIncludeOrgLoading = appName !== "ADMIN" && appName !== "PORTAL";
2603
+ const totalLoading = restorationLoading || authLoading || (shouldIncludeOrgLoading ? orgLoading : false) || eventLoading;
2604
+ const authError = authService.getError();
2605
+ const selectedOrganisation = organisationService.getSelectedOrganisation();
2606
+ const organisationError = organisationService.getError();
2607
+ const hasValidOrganisationContext = organisationService.hasValidOrganisationContext();
2608
+ const isContextReady = organisationService.isContextReady();
2609
+ const rawEvents = eventService.getEvents();
2610
+ const rawOrganisations = organisationService.getOrganisations();
2611
+ const rawUserMemberships = organisationService.getUserMemberships();
2612
+ const events = useMemo(() => rawEvents, [rawEvents]);
2613
+ const organisations = useMemo(() => rawOrganisations, [rawOrganisations]);
2614
+ const userMemberships = useMemo(() => rawUserMemberships, [rawUserMemberships]);
2615
+ const selectedEvent = eventService.getSelectedEvent();
2616
+ const eventError = eventService.getError();
2617
+ const showInactivityWarning = inactivityService.getShowInactivityWarning();
2618
+ const inactivityTimeRemaining = inactivityService.getInactivityTimeRemaining();
2619
+ const isIdle = inactivityService.isIdle();
2620
+ const timeRemaining = inactivityService.getTimeRemaining();
2621
+ const showWarning = inactivityService.isWarningShown();
2622
+ const isTracking = inactivityService.isTracking();
2623
+ const inactivityState = useMemo(() => ({
2624
+ showInactivityWarning,
2625
+ inactivityTimeRemaining,
2626
+ isIdle,
2627
+ timeRemaining,
2628
+ showWarning,
2629
+ isTracking
2630
+ }), [showInactivityWarning, inactivityTimeRemaining, isIdle, timeRemaining, showWarning, isTracking]);
2631
+ const hasErrors = !!(authError || organisationError || eventError || sessionRestoration.restorationError);
2632
+ const signIn = useCallback((email, password) => authService.signIn(email, password), [authService]);
2633
+ const signUp = useCallback((email, password) => authService.signUp(email, password), [authService]);
2634
+ const signOut = useCallback(() => authService.signOut(), [authService]);
2635
+ const resetPassword = useCallback((email) => authService.resetPassword(email), [authService]);
2636
+ const updatePassword = useCallback((password) => authService.updatePassword(password), [authService]);
2637
+ const refreshSession = useCallback(() => authService.refreshSession(), [authService]);
2638
+ const switchOrganisation = useCallback((orgId) => organisationService.switchOrganisation(orgId), [organisationService]);
2639
+ const getUserRole = useCallback((orgId) => organisationService.getUserRole(orgId), [organisationService]);
2640
+ const validateOrganisationAccess = useCallback((orgId) => organisationService.validateOrganisationAccess(orgId), [organisationService]);
2641
+ const refreshOrganisations = useCallback(() => organisationService.refreshOrganisations(), [organisationService]);
2642
+ const ensureOrganisationContext = useCallback(() => organisationService.ensureOrganisationContext(), [organisationService]);
2643
+ const isOrganisationSecure = useCallback(() => organisationService.isOrganisationSecure(), [organisationService]);
2644
+ const getPrimaryOrganisation = useCallback(() => organisationService.getPrimaryOrganisation(), [organisationService]);
2645
+ const setSelectedEvent = useCallback((event) => eventService.setSelectedEvent(event), [eventService]);
2646
+ const refreshEvents = useCallback(() => eventService.refreshEvents(), [eventService]);
2647
+ const resetActivity = useCallback(() => inactivityService.resetActivity(), [inactivityService]);
2648
+ const startTracking = useCallback(() => inactivityService.startTracking(), [inactivityService]);
2649
+ const stopTracking = useCallback(() => inactivityService.stopTracking(), [inactivityService]);
2650
+ const handleIdleLogout = useCallback(() => inactivityService.handleIdleLogout(), [inactivityService]);
2651
+ const handleStaySignedIn = useCallback(() => inactivityService.handleStaySignedIn(), [inactivityService]);
2652
+ const handleSignOutNow = useCallback(() => inactivityService.handleSignOutNow(), [inactivityService]);
2653
+ const state = useMemo(() => ({
2654
+ currentUser,
2655
+ currentSession,
2656
+ isAuth,
2657
+ authLoading,
2658
+ authError,
2659
+ selectedOrganisation,
2660
+ organisations,
2661
+ userMemberships,
2662
+ orgLoading,
2663
+ organisationError,
2664
+ hasValidOrganisationContext,
2665
+ isContextReady,
2666
+ events,
2667
+ selectedEvent,
2668
+ eventLoading,
2669
+ eventError,
2670
+ inactivityState,
2671
+ totalLoading,
2672
+ hasErrors,
2673
+ sessionRestoration,
2674
+ sessionRestorationTimedOut,
2675
+ sessionRestorationTimeoutMs
2676
+ }), [
2677
+ currentUser,
2678
+ currentSession,
2679
+ isAuth,
2680
+ authLoading,
2681
+ authError,
2682
+ selectedOrganisation,
2683
+ organisations,
2684
+ userMemberships,
2685
+ orgLoading,
2686
+ organisationError,
2687
+ hasValidOrganisationContext,
2688
+ isContextReady,
2689
+ events,
2690
+ selectedEvent,
2691
+ eventLoading,
2692
+ eventError,
2693
+ inactivityState,
2694
+ totalLoading,
2695
+ hasErrors,
2696
+ sessionRestoration,
2697
+ sessionRestorationTimedOut,
2698
+ sessionRestorationTimeoutMs
2699
+ ]);
2700
+ const callbacks = useMemo(() => ({
2701
+ signIn,
2702
+ signUp,
2703
+ signOut,
2704
+ resetPassword,
2705
+ updatePassword,
2706
+ refreshSession,
2707
+ switchOrganisation,
2708
+ getUserRole,
2709
+ validateOrganisationAccess,
2710
+ refreshOrganisations,
2711
+ ensureOrganisationContext,
2712
+ isOrganisationSecure,
2713
+ getPrimaryOrganisation,
2714
+ setSelectedEvent,
2715
+ refreshEvents,
2716
+ resetActivity,
2717
+ startTracking,
2718
+ stopTracking,
2719
+ handleIdleLogout,
2720
+ handleStaySignedIn,
2721
+ handleSignOutNow
2722
+ }), [
2723
+ signIn,
2724
+ signUp,
2725
+ signOut,
2726
+ resetPassword,
2727
+ updatePassword,
2728
+ refreshSession,
2729
+ switchOrganisation,
2730
+ getUserRole,
2731
+ validateOrganisationAccess,
2732
+ refreshOrganisations,
2733
+ ensureOrganisationContext,
2734
+ isOrganisationSecure,
2735
+ getPrimaryOrganisation,
2736
+ setSelectedEvent,
2737
+ refreshEvents,
2738
+ resetActivity,
2739
+ startTracking,
2740
+ stopTracking,
2741
+ handleIdleLogout,
2742
+ handleStaySignedIn,
2743
+ handleSignOutNow
2744
+ ]);
2745
+ return { state, callbacks };
2746
+ }
2747
+ function UnifiedAuthContextProvider({
2748
+ children,
2749
+ appName,
2750
+ supabaseClient: supabaseClientProp,
2751
+ user: userFromParent,
2752
+ session: sessionFromParent
2753
+ }) {
2754
+ const authService = useAuthService();
2755
+ const organisationService = useOrganisationService();
2756
+ const inactivityService = useInactivityService();
2757
+ const eventService = useEventService();
2758
+ const sessionRestorationState = useSessionRestoration();
2759
+ const sessionRestoration = useMemo(() => ({
2760
+ isRestoring: sessionRestorationState.isRestoring,
2761
+ restorationComplete: sessionRestorationState.restorationComplete,
2762
+ restorationError: sessionRestorationState.restorationError
2763
+ }), [sessionRestorationState.isRestoring, sessionRestorationState.restorationComplete, sessionRestorationState.restorationError]);
2764
+ const { hasTimedOut: sessionRestorationTimedOut, timeoutMs: sessionRestorationTimeoutMs } = sessionRestorationState;
2765
+ const currentUser = userFromParent !== void 0 ? userFromParent : authService.getUser();
2766
+ const currentSession = sessionFromParent !== void 0 ? sessionFromParent : authService.getSession();
2767
+ const isAuth = !!(currentUser && currentSession);
2768
+ const supabase = useMemo(() => supabaseClientProp, [supabaseClientProp]);
2769
+ const appId = useAppIdResolution(supabase, appName, isAuth, currentUser?.id);
2770
+ useUnifiedAuthSubscriptions(authService, organisationService, eventService, inactivityService);
2771
+ const { state, callbacks } = useUnifiedAuthStateAndCallbacks(
2772
+ authService,
2773
+ organisationService,
2774
+ eventService,
2775
+ inactivityService,
2776
+ sessionRestoration,
2777
+ sessionRestorationTimedOut,
2778
+ sessionRestorationTimeoutMs,
2779
+ appName,
2780
+ currentUser,
2781
+ currentSession
2782
+ );
2783
+ const contextValue = useUnifiedAuthContextValue(state, callbacks, appId, appName, supabase);
2784
+ useEffect(() => {
2785
+ if (appName) setPrintAppName(appName);
2786
+ }, [appName]);
2787
+ return /* @__PURE__ */ jsx(UnifiedAuthContext.Provider, { value: contextValue, children });
2788
+ }
2789
+ function EventServiceProviderWrapper({
2790
+ children,
2791
+ supabaseClient,
2792
+ user,
2793
+ session,
2794
+ appName
2795
+ }) {
2796
+ const { selectedOrganisation } = useOrganisations();
2797
+ const setSelectedEventId = useCallback(() => {
2798
+ }, []);
2799
+ useEffect(() => {
2800
+ }, [user?.id, session?.access_token, selectedOrganisation?.id]);
2801
+ return /* @__PURE__ */ jsx(
2802
+ EventServiceProvider,
2803
+ {
2804
+ supabaseClient,
2805
+ user,
2806
+ session,
2807
+ appName,
2808
+ selectedOrganisation,
2809
+ setSelectedEventId,
2810
+ children
2811
+ }
2812
+ );
2813
+ }
2814
+ function ServiceAwareProviders({
2815
+ children,
2816
+ supabaseClient,
2817
+ appName,
2818
+ persistState,
2819
+ enablePersistence,
2820
+ requireOrganisationContext,
2821
+ idleTimeoutMs,
2822
+ warnBeforeMs,
2823
+ onIdleLogout,
2824
+ renderInactivityWarning,
2825
+ dangerouslyDisableInactivity
2826
+ }) {
2827
+ const authService = useAuthService();
2828
+ const [userState, setUserState] = useState(() => authService.getUser());
2829
+ const [sessionState, setSessionState] = useState(() => authService.getSession());
2830
+ useEffect(() => {
2831
+ const unsubscribe = authService.subscribe(() => {
2832
+ const newUser = authService.getUser();
2833
+ const newSession = authService.getSession();
2834
+ flushSync(() => {
2835
+ setUserState(newUser);
2836
+ setSessionState(newSession);
2837
+ });
2838
+ });
2839
+ return unsubscribe;
2840
+ }, [authService]);
2841
+ const user = userState;
2842
+ const session = sessionState;
2843
+ return /* @__PURE__ */ jsx(
2844
+ OrganisationServiceProvider,
2845
+ {
2846
+ supabaseClient,
2847
+ user,
2848
+ session,
2849
+ children: /* @__PURE__ */ jsx(
2850
+ EventServiceProviderWrapper,
2851
+ {
2852
+ supabaseClient,
2853
+ user,
2854
+ session,
2855
+ appName,
2856
+ children: /* @__PURE__ */ jsx(
2857
+ InactivityServiceProvider,
2858
+ {
2859
+ supabaseClient,
2860
+ user,
2861
+ session,
2862
+ idleTimeoutMs,
2863
+ warnBeforeMs,
2864
+ onIdleLogout,
2865
+ children: /* @__PURE__ */ jsx(
2866
+ UnifiedAuthContextProvider,
2867
+ {
2868
+ appName,
2869
+ supabaseClient,
2870
+ user,
2871
+ session,
2872
+ persistState,
2873
+ enablePersistence,
2874
+ requireOrganisationContext,
2875
+ idleTimeoutMs,
2876
+ warnBeforeMs,
2877
+ onIdleLogout,
2878
+ renderInactivityWarning,
2879
+ dangerouslyDisableInactivity,
2880
+ children
2881
+ }
2882
+ )
2883
+ }
2884
+ )
2885
+ }
2886
+ )
2887
+ }
2888
+ );
2889
+ }
2890
+ function UnifiedAuthProvider({
2891
+ children,
2892
+ supabaseClient,
2893
+ appName,
2894
+ persistState = true,
2895
+ enablePersistence,
2896
+ requireOrganisationContext = true,
2897
+ idleTimeoutMs,
2898
+ // REQUIRED: No default - must be explicitly provided
2899
+ warnBeforeMs,
2900
+ // REQUIRED: No default - must be explicitly provided
2901
+ onIdleLogout,
2902
+ // REQUIRED: No default - must be explicitly provided
2903
+ renderInactivityWarning,
2904
+ dangerouslyDisableInactivity = false
2905
+ }) {
2906
+ const clientRef = useRef(supabaseClient);
2907
+ useEffect(() => {
2908
+ if (clientRef.current !== supabaseClient) {
2909
+ logger.warn("UnifiedAuthProvider", "Supabase client reference changed - this may indicate multiple client instances are being created", {
2910
+ previousClient: clientRef.current,
2911
+ newClient: supabaseClient,
2912
+ note: 'Ensure you create the Supabase client once and reuse it. Creating multiple clients can cause performance issues and the "Multiple GoTrueClient instances" warning.'
2913
+ });
2914
+ clientRef.current = supabaseClient;
2915
+ }
2916
+ }, [supabaseClient]);
2917
+ if (supabaseClient && !isRBACInitialized()) {
2918
+ try {
2919
+ setupRBAC(supabaseClient);
2920
+ logger.debug("UnifiedAuthProvider", "RBAC initialized synchronously");
2921
+ } catch (err2) {
2922
+ logger.error("UnifiedAuthProvider", "Failed to initialize RBAC", err2);
2923
+ }
2924
+ }
2925
+ return /* @__PURE__ */ jsx(AuthServiceProvider, { supabaseClient, appName, children: /* @__PURE__ */ jsx(
2926
+ ServiceAwareProviders,
2927
+ {
2928
+ supabaseClient,
2929
+ appName,
2930
+ persistState,
2931
+ enablePersistence,
2932
+ requireOrganisationContext,
2933
+ idleTimeoutMs,
2934
+ warnBeforeMs,
2935
+ onIdleLogout,
2936
+ renderInactivityWarning,
2937
+ dangerouslyDisableInactivity,
2938
+ children
2939
+ }
2940
+ ) });
2941
+ }
2942
+
2943
+ export { AuthServiceProvider, EventServiceContext, EventServiceProvider, InactivityServiceContext, InactivityServiceProvider, OrganisationServiceContext, OrganisationServiceProvider, UnifiedAuthContext, UnifiedAuthProvider, useAuthService, useEventService, useInactivityService, useOrganisationService, useOrganisations, useSessionRestoration, useUnifiedAuth };