@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,2159 @@
1
+ import { EventServiceContext } from './chunk-4R3T5ENU.js';
2
+ import { clearPalette, parseAndNormalizeEventColours, applyPalette } from './chunk-D6BMFMQZ.js';
3
+ import { assertAppId } from './chunk-4SXLQIZO.js';
4
+ import { fetchPlaceDetails, createAddressFromPlaceResult, getAddressByPlaceId, fetchPlaceAutocomplete, setOrganisationContext } from './chunk-XPFVT3GN.js';
5
+ import { createLogger, logger } from './chunk-BTHN5MKC.js';
6
+ import { ok, err } from './chunk-44CNXN4P.js';
7
+ import { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
8
+ import { useLocation } from 'react-router-dom';
9
+
10
+ function useDebounce(value, delay) {
11
+ const [debouncedValue, setDebouncedValue] = useState(value);
12
+ useEffect(() => {
13
+ const handler = setTimeout(() => {
14
+ setDebouncedValue(value);
15
+ }, delay);
16
+ return () => {
17
+ clearTimeout(handler);
18
+ };
19
+ }, [value, delay]);
20
+ return debouncedValue;
21
+ }
22
+ var log = createLogger("useQueryCache");
23
+ var queryCache = /* @__PURE__ */ new Map();
24
+ var CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
25
+ var cleanupTimer = null;
26
+ function runCacheCleanup() {
27
+ const now = Date.now();
28
+ const expiredKeys = [];
29
+ queryCache.forEach((entry, key) => {
30
+ if (entry.expiresAt <= now) {
31
+ expiredKeys.push(key);
32
+ }
33
+ });
34
+ expiredKeys.forEach((key) => {
35
+ queryCache.delete(key);
36
+ log.debug(`Removed expired query from cache: ${key}`);
37
+ });
38
+ }
39
+ if (typeof window !== "undefined" && !cleanupTimer) {
40
+ cleanupTimer = setInterval(runCacheCleanup, CLEANUP_INTERVAL_MS);
41
+ window.addEventListener("beforeunload", () => {
42
+ if (cleanupTimer) {
43
+ clearInterval(cleanupTimer);
44
+ cleanupTimer = null;
45
+ }
46
+ });
47
+ }
48
+ function cleanupQueryCache() {
49
+ if (cleanupTimer) {
50
+ clearInterval(cleanupTimer);
51
+ cleanupTimer = null;
52
+ }
53
+ }
54
+ function useQueryCache(_supabase) {
55
+ const getCachedQuery = useCallback(async (table, filterKey, filterValue, fetchFn, options = {}) => {
56
+ const { ttl = 300, enabled = true } = options;
57
+ const cacheKey = `${table}:${filterKey}:${filterValue}`;
58
+ const now = Date.now();
59
+ if (!enabled) {
60
+ return fetchFn();
61
+ }
62
+ const cached = queryCache.get(cacheKey);
63
+ if (cached) {
64
+ if (cached.expiresAt > now && cached.data !== void 0) {
65
+ log.debug(`Cache hit for query: ${cacheKey}`);
66
+ return cached.data;
67
+ }
68
+ if (cached.promise) {
69
+ log.debug(`Waiting for in-flight request: ${cacheKey}`);
70
+ return cached.promise;
71
+ }
72
+ }
73
+ log.debug(`Cache miss for query: ${cacheKey}, fetching...`);
74
+ const fetchPromise = fetchFn();
75
+ queryCache.set(cacheKey, {
76
+ data: void 0,
77
+ expiresAt: now + ttl * 1e3,
78
+ promise: fetchPromise
79
+ });
80
+ try {
81
+ const data = await fetchPromise;
82
+ queryCache.set(cacheKey, {
83
+ data,
84
+ expiresAt: now + ttl * 1e3
85
+ });
86
+ log.debug(`Cached query result: ${cacheKey}, expires in ${ttl}s`);
87
+ return data;
88
+ } catch (error) {
89
+ queryCache.delete(cacheKey);
90
+ log.error(`Query failed for ${cacheKey}:`, error);
91
+ throw error;
92
+ }
93
+ }, []);
94
+ const invalidateQuery = useCallback((table, filterKey, filterValue) => {
95
+ const cacheKey = `${table}:${filterKey}:${filterValue}`;
96
+ queryCache.delete(cacheKey);
97
+ log.debug(`Invalidated query cache: ${cacheKey}`);
98
+ }, []);
99
+ const clearCache = useCallback(() => {
100
+ queryCache.clear();
101
+ log.debug("Cleared all query cache entries.");
102
+ }, []);
103
+ const getCacheStats = useCallback(() => {
104
+ return {
105
+ size: queryCache.size,
106
+ keys: Array.from(queryCache.keys())
107
+ };
108
+ }, []);
109
+ return {
110
+ getCachedQuery,
111
+ invalidateQuery,
112
+ clearCache,
113
+ getCacheStats
114
+ };
115
+ }
116
+ var queryCacheHelpers = {
117
+ /**
118
+ * Cache core_person queries by user_id
119
+ * TTL: 5 minutes
120
+ */
121
+ pacePersonByUserId: (supabase, userId, fetchFn) => {
122
+ const cacheKey = `core_person:user_id:${userId}`;
123
+ const now = Date.now();
124
+ const ttl = 300 * 1e3;
125
+ const cached = queryCache.get(cacheKey);
126
+ if (cached && cached.expiresAt > now && cached.data !== void 0) {
127
+ return Promise.resolve(cached.data);
128
+ }
129
+ if (cached?.promise) {
130
+ return cached.promise;
131
+ }
132
+ const promise = fetchFn();
133
+ queryCache.set(cacheKey, {
134
+ data: void 0,
135
+ expiresAt: now + ttl,
136
+ promise
137
+ });
138
+ promise.then((data) => {
139
+ queryCache.set(cacheKey, { data, expiresAt: now + ttl });
140
+ }).catch(() => {
141
+ queryCache.delete(cacheKey);
142
+ });
143
+ return promise;
144
+ },
145
+ /**
146
+ * Cache core_member queries by person_id
147
+ * TTL: 5 minutes
148
+ */
149
+ paceMemberByPersonId: (supabase, personId, fetchFn) => {
150
+ const cacheKey = `core_member:person_id:${personId}`;
151
+ const now = Date.now();
152
+ const ttl = 300 * 1e3;
153
+ const cached = queryCache.get(cacheKey);
154
+ if (cached && cached.expiresAt > now && cached.data !== void 0) {
155
+ return Promise.resolve(cached.data);
156
+ }
157
+ if (cached?.promise) {
158
+ return cached.promise;
159
+ }
160
+ const promise = fetchFn();
161
+ queryCache.set(cacheKey, {
162
+ data: void 0,
163
+ expiresAt: now + ttl,
164
+ promise
165
+ });
166
+ promise.then((data) => {
167
+ queryCache.set(cacheKey, { data, expiresAt: now + ttl });
168
+ }).catch(() => {
169
+ queryCache.delete(cacheKey);
170
+ });
171
+ return promise;
172
+ },
173
+ /**
174
+ * Cache rbac_app_pages queries by app_id
175
+ * TTL: 15 minutes (app pages are relatively static)
176
+ */
177
+ rbacAppPagesByAppId: (supabase, appId, fetchFn) => {
178
+ const cacheKey = `rbac_app_pages:app_id:${appId}`;
179
+ const now = Date.now();
180
+ const ttl = 15 * 60 * 1e3;
181
+ const cached = queryCache.get(cacheKey);
182
+ if (cached && cached.expiresAt > now && cached.data !== void 0) {
183
+ return Promise.resolve(cached.data);
184
+ }
185
+ if (cached?.promise) {
186
+ return cached.promise;
187
+ }
188
+ const promise = fetchFn();
189
+ queryCache.set(cacheKey, {
190
+ data: void 0,
191
+ expiresAt: now + ttl,
192
+ promise
193
+ });
194
+ promise.then((data) => {
195
+ queryCache.set(cacheKey, { data, expiresAt: now + ttl });
196
+ }).catch(() => {
197
+ queryCache.delete(cacheKey);
198
+ });
199
+ return promise;
200
+ }
201
+ };
202
+ async function fetchSuggestionsForInput(debouncedInput, apiKey, memoizedAutocompleteOptions, cacheEnabled, cacheTTLAutocomplete, getCachedQuery, isCancelledRef, setters) {
203
+ try {
204
+ let result;
205
+ if (cacheEnabled) {
206
+ result = await getCachedQuery(
207
+ "google-places-autocomplete",
208
+ "query",
209
+ debouncedInput,
210
+ async () => {
211
+ if (isCancelledRef.current) {
212
+ throw new Error("Request cancelled");
213
+ }
214
+ return fetchPlaceAutocomplete(debouncedInput, apiKey, memoizedAutocompleteOptions);
215
+ },
216
+ { ttl: cacheTTLAutocomplete, enabled: true }
217
+ );
218
+ } else {
219
+ result = await fetchPlaceAutocomplete(debouncedInput, apiKey, memoizedAutocompleteOptions);
220
+ }
221
+ if (isCancelledRef.current) return;
222
+ if (!result.ok) {
223
+ setters.setError(new Error(result.error.message));
224
+ setters.setSuggestions([]);
225
+ setters.setIsLoading(false);
226
+ return;
227
+ }
228
+ setters.setSuggestions(result.data);
229
+ setters.setIsLoading(false);
230
+ } catch (err2) {
231
+ if (isCancelledRef.current || err2 instanceof Error && err2.message === "Request cancelled") {
232
+ return;
233
+ }
234
+ const error = err2 instanceof Error ? err2 : new Error("Failed to fetch autocomplete suggestions");
235
+ setters.setError(error);
236
+ setters.setSuggestions([]);
237
+ setters.setIsLoading(false);
238
+ }
239
+ }
240
+ function useAddressAutocomplete(apiKey, inputValue, options = {}) {
241
+ const {
242
+ debounceDelay = 300,
243
+ cacheEnabled = true,
244
+ cacheTTL = {
245
+ autocomplete: 3600,
246
+ // 1 hour
247
+ placeDetails: 86400
248
+ // 24 hours
249
+ },
250
+ autocompleteOptions
251
+ } = options;
252
+ const [suggestions, setSuggestions] = useState([]);
253
+ const [isLoading, setIsLoading] = useState(false);
254
+ const [error, setError] = useState(null);
255
+ const debouncedInput = useDebounce(inputValue, debounceDelay);
256
+ const { getCachedQuery } = useQueryCache();
257
+ const memoizedAutocompleteOptions = useMemo(() => {
258
+ if (!autocompleteOptions) {
259
+ return void 0;
260
+ }
261
+ return {
262
+ components: autocompleteOptions.components,
263
+ location: autocompleteOptions.location,
264
+ radius: autocompleteOptions.radius,
265
+ language: autocompleteOptions.language,
266
+ types: autocompleteOptions.types
267
+ };
268
+ }, [
269
+ autocompleteOptions
270
+ ]);
271
+ useEffect(() => {
272
+ if (!debouncedInput.trim()) {
273
+ setSuggestions([]);
274
+ setIsLoading(false);
275
+ setError(null);
276
+ return;
277
+ }
278
+ if (!apiKey) {
279
+ setError(new Error("Google Places API key is required"));
280
+ return;
281
+ }
282
+ setIsLoading(true);
283
+ setError(null);
284
+ const isCancelledRef = { current: false };
285
+ fetchSuggestionsForInput(
286
+ debouncedInput,
287
+ apiKey,
288
+ memoizedAutocompleteOptions,
289
+ cacheEnabled,
290
+ cacheTTL.autocomplete ?? 3600,
291
+ getCachedQuery,
292
+ isCancelledRef,
293
+ { setSuggestions, setError, setIsLoading }
294
+ );
295
+ return () => {
296
+ isCancelledRef.current = true;
297
+ };
298
+ }, [debouncedInput, apiKey, cacheEnabled, cacheTTL.autocomplete, memoizedAutocompleteOptions, getCachedQuery]);
299
+ const selectAddress = useCallback(
300
+ async (placeId) => {
301
+ if (!placeId || !apiKey) {
302
+ return null;
303
+ }
304
+ setIsLoading(true);
305
+ setError(null);
306
+ try {
307
+ let placeResult;
308
+ if (cacheEnabled) {
309
+ placeResult = await getCachedQuery(
310
+ "google-places-details",
311
+ "place_id",
312
+ placeId,
313
+ async () => fetchPlaceDetails(placeId, apiKey),
314
+ { ttl: cacheTTL.placeDetails, enabled: true }
315
+ );
316
+ } else {
317
+ placeResult = await fetchPlaceDetails(placeId, apiKey);
318
+ }
319
+ if (!placeResult.ok) {
320
+ setError(new Error(placeResult.error.message));
321
+ setIsLoading(false);
322
+ return null;
323
+ }
324
+ const parsedAddress = createAddressFromPlaceResult(placeResult.data);
325
+ setIsLoading(false);
326
+ return parsedAddress;
327
+ } catch (err2) {
328
+ const error2 = err2 instanceof Error ? err2 : new Error("Failed to fetch place details");
329
+ setError(error2);
330
+ setIsLoading(false);
331
+ return null;
332
+ }
333
+ },
334
+ // eslint-disable-next-line react-hooks/exhaustive-deps
335
+ [apiKey, cacheEnabled, cacheTTL.placeDetails]
336
+ );
337
+ const getAddressByPlaceIdFn = useCallback(
338
+ async (placeId) => {
339
+ if (!placeId || !apiKey) {
340
+ return null;
341
+ }
342
+ try {
343
+ let result;
344
+ if (cacheEnabled) {
345
+ result = await getCachedQuery(
346
+ "google-places-details",
347
+ "place_id",
348
+ placeId,
349
+ async () => getAddressByPlaceId(placeId, apiKey),
350
+ { ttl: cacheTTL.placeDetails, enabled: true }
351
+ );
352
+ } else {
353
+ result = await getAddressByPlaceId(placeId, apiKey);
354
+ }
355
+ if (!result.ok) {
356
+ setError(new Error(result.error.message));
357
+ return null;
358
+ }
359
+ return result.data;
360
+ } catch (err2) {
361
+ const error2 = err2 instanceof Error ? err2 : new Error("Failed to get address by place_id");
362
+ setError(error2);
363
+ return null;
364
+ }
365
+ },
366
+ // eslint-disable-next-line react-hooks/exhaustive-deps
367
+ [apiKey, cacheEnabled, cacheTTL.placeDetails]
368
+ );
369
+ const clearSuggestions = useCallback(() => {
370
+ setSuggestions([]);
371
+ setError(null);
372
+ }, []);
373
+ return {
374
+ suggestions,
375
+ isLoading,
376
+ error,
377
+ selectAddress,
378
+ getAddressByPlaceId: getAddressByPlaceIdFn,
379
+ clearSuggestions
380
+ };
381
+ }
382
+
383
+ // src/utils/storage/config.ts
384
+ var FILE_SIZE_LIMITS = {
385
+ // Images
386
+ "image/jpeg": 5 * 1024 * 1024,
387
+ // 5MB
388
+ "image/png": 5 * 1024 * 1024,
389
+ // 5MB
390
+ "image/gif": 10 * 1024 * 1024,
391
+ // 10MB (for animations)
392
+ "image/webp": 5 * 1024 * 1024,
393
+ // 5MB
394
+ "image/svg+xml": 1 * 1024 * 1024,
395
+ // 1MB (vector graphics)
396
+ // Documents
397
+ "application/pdf": 50 * 1024 * 1024,
398
+ // 50MB
399
+ "application/msword": 25 * 1024 * 1024,
400
+ // 25MB
401
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": 25 * 1024 * 1024,
402
+ // 25MB
403
+ "application/vnd.ms-excel": 25 * 1024 * 1024,
404
+ // 25MB
405
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": 25 * 1024 * 1024,
406
+ // 25MB
407
+ // Archives
408
+ "application/zip": 100 * 1024 * 1024,
409
+ // 100MB
410
+ "application/x-rar-compressed": 100 * 1024 * 1024,
411
+ // 100MB
412
+ // Text files
413
+ "text/plain": 1 * 1024 * 1024,
414
+ // 1MB
415
+ "text/csv": 10 * 1024 * 1024,
416
+ // 10MB
417
+ "application/json": 10 * 1024 * 1024
418
+ // 10MB
419
+ };
420
+ var DEFAULT_FILE_SIZE_LIMIT = 10 * 1024 * 1024;
421
+ var APP_PATH_MAPPING = {
422
+ "PACE": "event_logos",
423
+ "TRAC": "trac_accommodation",
424
+ // Default category for TRAC files
425
+ "MEDI": "documents",
426
+ "CAKE": "documents"
427
+ };
428
+ var STORAGE_CONFIG = {
429
+ bucketName: "files",
430
+ fileSizeLimits: FILE_SIZE_LIMITS,
431
+ defaultFileSizeLimit: DEFAULT_FILE_SIZE_LIMIT
432
+ };
433
+ function getFileSizeLimit(mimeType) {
434
+ return STORAGE_CONFIG.fileSizeLimits[mimeType] || STORAGE_CONFIG.defaultFileSizeLimit;
435
+ }
436
+ function getBucketName(isPublic) {
437
+ return isPublic ? "public-files" : "files";
438
+ }
439
+ function validateFileSize(file) {
440
+ const limit = getFileSizeLimit(file.type);
441
+ if (file.size > limit) {
442
+ const limitMB = Math.round(limit / (1024 * 1024));
443
+ const fileMB = Math.round(file.size / (1024 * 1024));
444
+ return {
445
+ isValid: false,
446
+ error: `File size (${fileMB}MB) exceeds limit (${limitMB}MB) for ${file.type}`
447
+ };
448
+ }
449
+ return { isValid: true };
450
+ }
451
+
452
+ // src/utils/storage/helpers.ts
453
+ var log2 = createLogger("StorageHelpers");
454
+ function generateFilePath(options, fileName) {
455
+ const { orgId, userId, isPublic = false, customPath } = options;
456
+ if (!orgId && !userId) {
457
+ throw new Error("Either orgId or userId is required for file path generation");
458
+ }
459
+ const basePath = orgId ? orgId : `users/${userId}`;
460
+ if (isPublic) {
461
+ if (customPath) {
462
+ return `${basePath}/${customPath}/${fileName}`;
463
+ }
464
+ return `${basePath}/public/${fileName}`;
465
+ }
466
+ if (customPath) {
467
+ return `${basePath}/${customPath}/${fileName}`;
468
+ }
469
+ const pathFolder = customPath || "files";
470
+ return `${basePath}/${pathFolder}/${fileName}`;
471
+ }
472
+ function generateUniqueFileName(originalName) {
473
+ const timestamp = Date.now();
474
+ const uuid = crypto.randomUUID();
475
+ const extension = originalName.split(".").pop() || "";
476
+ let baseName = originalName.replace(/\.[^/.]+$/, "");
477
+ baseName = baseName.trim().replace(/\s+/g, "_").replace(/[<>:"/\\|?*]/g, "").replace(/\.\./g, "").replace(/^\.+|\.+$/g, "").substring(0, 200);
478
+ if (!extension || extension === originalName) {
479
+ return `${timestamp}-${uuid}-${baseName}`;
480
+ }
481
+ return `${timestamp}-${uuid}-${baseName}.${extension}`;
482
+ }
483
+ async function extractFileMetadata(file, options, uploadedBy) {
484
+ try {
485
+ const metadata = {
486
+ mimeType: file.type,
487
+ size: file.size,
488
+ ...options.orgId && { orgId: options.orgId },
489
+ ...options.userId && { userId: options.userId },
490
+ appName: options.appName || "pace-core",
491
+ uploadedBy,
492
+ uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
493
+ tags: options.tags || [],
494
+ isPublic: options.isPublic || false,
495
+ customMetadata: options.metadata || {}
496
+ };
497
+ if (file.type.startsWith("image/")) {
498
+ try {
499
+ const dimensions = await getImageDimensions(file);
500
+ metadata.width = dimensions.width;
501
+ metadata.height = dimensions.height;
502
+ } catch (_error) {
503
+ }
504
+ }
505
+ try {
506
+ metadata.hash = await generateFileHash(file);
507
+ } catch (_error) {
508
+ }
509
+ return ok(metadata);
510
+ } catch (error) {
511
+ return err({
512
+ code: "EXTRACT_METADATA_FAILED",
513
+ message: error instanceof Error ? error.message : "Unknown error"
514
+ });
515
+ }
516
+ }
517
+ async function getImageDimensions(file) {
518
+ return new Promise((resolve, reject) => {
519
+ const img = new Image();
520
+ const url = URL.createObjectURL(file);
521
+ img.onload = () => {
522
+ URL.revokeObjectURL(url);
523
+ resolve({ width: img.width, height: img.height });
524
+ };
525
+ img.onerror = () => {
526
+ URL.revokeObjectURL(url);
527
+ reject(new Error("Could not load image"));
528
+ };
529
+ img.src = url;
530
+ });
531
+ }
532
+ async function generateFileHash(file) {
533
+ const buffer = await file.arrayBuffer();
534
+ const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
535
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
536
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
537
+ return `sha256:${hashHex}`;
538
+ }
539
+ async function ensureFolderExists(supabase, folderPath, bucketName) {
540
+ try {
541
+ const { data, error } = await supabase.storage.from(bucketName).list(folderPath, {
542
+ limit: 1
543
+ });
544
+ if (!error) {
545
+ return true;
546
+ }
547
+ const placeholderPath = `${folderPath}/.keep`;
548
+ const placeholderBlob = new Blob([""], { type: "text/plain" });
549
+ const placeholderFile = new File([placeholderBlob], ".keep", { type: "text/plain" });
550
+ const { error: uploadError } = await supabase.storage.from(bucketName).upload(placeholderPath, placeholderFile, {
551
+ cacheControl: "3600",
552
+ upsert: true,
553
+ // Use upsert to avoid errors if file already exists
554
+ contentType: "text/plain"
555
+ });
556
+ if (uploadError) {
557
+ log2.debug(`Could not create folder placeholder (will be created on upload): ${uploadError.message}`);
558
+ return true;
559
+ }
560
+ return true;
561
+ } catch (error) {
562
+ log2.debug(`Folder creation exception (will be created on upload): ${error instanceof Error ? error.message : "Unknown error"}`);
563
+ return true;
564
+ }
565
+ }
566
+ async function uploadFile(supabase, file, options) {
567
+ try {
568
+ const sizeValidation = validateFileSize(file);
569
+ if (!sizeValidation.isValid) {
570
+ return err({ code: "FILE_SIZE_INVALID", message: sizeValidation.error ?? "File size validation failed" });
571
+ }
572
+ const uniqueFileName = generateUniqueFileName(file.name);
573
+ const filePath = generateFilePath(options, uniqueFileName);
574
+ const folderPath = filePath.substring(0, filePath.lastIndexOf("/"));
575
+ const metadataResult = await extractFileMetadata(file, options, "current-user");
576
+ if (!metadataResult.ok) {
577
+ return err(metadataResult.error);
578
+ }
579
+ const metadata = metadataResult.data;
580
+ const bucketName = getBucketName(options.isPublic || false);
581
+ await ensureFolderExists(supabase, folderPath, bucketName);
582
+ const { error } = await supabase.storage.from(bucketName).upload(filePath, file, {
583
+ cacheControl: "3600",
584
+ upsert: false,
585
+ contentType: file.type
586
+ });
587
+ if (error) {
588
+ return err({ code: "UPLOAD_FAILED", message: error.message });
589
+ }
590
+ let publicUrl;
591
+ if (options.isPublic) {
592
+ const { data: urlData } = supabase.storage.from(bucketName).getPublicUrl(filePath);
593
+ publicUrl = urlData.publicUrl;
594
+ }
595
+ return ok({ path: filePath, publicUrl, metadata });
596
+ } catch (error) {
597
+ return err({
598
+ code: "UPLOAD_FAILED",
599
+ message: error instanceof Error ? error.message : "Unknown error"
600
+ });
601
+ }
602
+ }
603
+ function getPublicUrl(supabase, path, isPublic = true) {
604
+ if (!supabase) {
605
+ throw new Error("Supabase client is required to generate a public URL");
606
+ }
607
+ if (!path || typeof path !== "string") {
608
+ throw new Error("A valid storage path is required to generate a public URL");
609
+ }
610
+ if (/^https?:\/\//i.test(path)) {
611
+ return path;
612
+ }
613
+ const normalisedPath = path.trim().replace(/^\/+/, "");
614
+ if (!normalisedPath) {
615
+ throw new Error("Storage path cannot be empty after normalisation");
616
+ }
617
+ const { bucketNameFromPath, storagePath } = resolveBucketHint(normalisedPath);
618
+ const bucketName = bucketNameFromPath || getBucketName(isPublic);
619
+ const { data } = supabase.storage.from(bucketName).getPublicUrl(storagePath);
620
+ return data.publicUrl;
621
+ }
622
+ function resolveBucketHint(pathWithPotentialBucket) {
623
+ const BUCKET_NAME_PATTERN = /^[a-z0-9][a-z0-9-_\.]{1,62}$/i;
624
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
625
+ const KNOWN_BUCKET_NAMES = /* @__PURE__ */ new Set(["files", "public-files"]);
626
+ const trimmedPath = pathWithPotentialBucket.trim();
627
+ if (!trimmedPath) {
628
+ throw new Error("Storage path cannot be empty after normalisation");
629
+ }
630
+ const doubleColonIndex = trimmedPath.indexOf("::");
631
+ if (doubleColonIndex > 0) {
632
+ const potentialBucket = trimmedPath.slice(0, doubleColonIndex).trim();
633
+ const remainingPath = trimmedPath.slice(doubleColonIndex + 2).replace(/^\/+/, "");
634
+ if (potentialBucket && remainingPath && BUCKET_NAME_PATTERN.test(potentialBucket)) {
635
+ return {
636
+ bucketNameFromPath: potentialBucket,
637
+ storagePath: remainingPath
638
+ };
639
+ }
640
+ }
641
+ const firstSlashIndex = trimmedPath.indexOf("/");
642
+ if (firstSlashIndex > 0) {
643
+ const potentialBucket = trimmedPath.slice(0, firstSlashIndex).trim();
644
+ const remainingPath = trimmedPath.slice(firstSlashIndex + 1).replace(/^\/+/, "");
645
+ if (potentialBucket && remainingPath && BUCKET_NAME_PATTERN.test(potentialBucket) && !UUID_PATTERN.test(potentialBucket) && (KNOWN_BUCKET_NAMES.has(potentialBucket) || !potentialBucket.includes("-")) && !/^\d+$/.test(potentialBucket)) {
646
+ return {
647
+ bucketNameFromPath: potentialBucket,
648
+ storagePath: remainingPath
649
+ };
650
+ }
651
+ }
652
+ return {
653
+ bucketNameFromPath: null,
654
+ storagePath: trimmedPath
655
+ };
656
+ }
657
+ async function getSignedUrl(supabase, path, options) {
658
+ try {
659
+ const bucketName = getBucketName(false);
660
+ const { data, error } = await supabase.storage.from(bucketName).createSignedUrl(path, options.expiresIn || 3600);
661
+ if (error) {
662
+ log2.error("Failed to create signed URL:", error);
663
+ return err({ code: "SIGNED_URL_FAILED", message: error.message });
664
+ }
665
+ return ok({
666
+ url: data.signedUrl,
667
+ expiresAt: new Date(Date.now() + (options.expiresIn || 3600) * 1e3).toISOString()
668
+ });
669
+ } catch (error) {
670
+ log2.error("Failed to create signed URL:", error);
671
+ return err({
672
+ code: "SIGNED_URL_FAILED",
673
+ message: error instanceof Error ? error.message : "Unknown error"
674
+ });
675
+ }
676
+ }
677
+ var globalUrlCache = /* @__PURE__ */ new Map();
678
+ var MAX_CACHE_SIZE = 500;
679
+ function getCacheKey(fileId, filePath, isPublic) {
680
+ return `file-url:${fileId}:${isPublic ? "public" : "private"}`;
681
+ }
682
+ function cleanupUrlCache() {
683
+ const now = Date.now();
684
+ for (const [key, value] of globalUrlCache.entries()) {
685
+ if (value.expiresAt < now) {
686
+ globalUrlCache.delete(key);
687
+ }
688
+ }
689
+ if (globalUrlCache.size > MAX_CACHE_SIZE) {
690
+ const entries = Array.from(globalUrlCache.entries());
691
+ entries.sort((a, b) => a[1].expiresAt - b[1].expiresAt);
692
+ const toRemove = Math.floor(MAX_CACHE_SIZE * 0.2);
693
+ for (let i = 0; i < toRemove && i < entries.length; i++) {
694
+ globalUrlCache.delete(entries[i][0]);
695
+ }
696
+ }
697
+ }
698
+ async function generateFileUrlsBatch(supabase, fileReferences, options) {
699
+ try {
700
+ const urlMap = /* @__PURE__ */ new Map();
701
+ if (fileReferences.length === 0) {
702
+ return ok(urlMap);
703
+ }
704
+ const now = Date.now();
705
+ const ttl = (options.expiresIn || 3600) * 1e3;
706
+ const publicFiles = [];
707
+ const privateFiles = [];
708
+ const uncachedFiles = [];
709
+ for (const fileRef of fileReferences) {
710
+ const cacheKey = getCacheKey(fileRef.id, fileRef.file_path, fileRef.is_public);
711
+ const cached = globalUrlCache.get(cacheKey);
712
+ if (cached && cached.expiresAt > now) {
713
+ urlMap.set(fileRef.id, cached.url);
714
+ continue;
715
+ }
716
+ if (fileRef.is_public) {
717
+ publicFiles.push({ id: fileRef.id, file_path: fileRef.file_path });
718
+ } else {
719
+ privateFiles.push({ id: fileRef.id, file_path: fileRef.file_path });
720
+ }
721
+ uncachedFiles.push(fileRef);
722
+ }
723
+ for (const file of publicFiles) {
724
+ try {
725
+ const url = getPublicUrl(supabase, file.file_path, true);
726
+ if (url) {
727
+ urlMap.set(file.id, url);
728
+ const cacheKey = getCacheKey(file.id, file.file_path, true);
729
+ globalUrlCache.set(cacheKey, { url, expiresAt: now + ttl });
730
+ }
731
+ } catch (e) {
732
+ log2.error(`Failed to generate public URL for file ${file.id}:`, e);
733
+ }
734
+ }
735
+ if (privateFiles.length > 0) {
736
+ const signedUrlPromises = privateFiles.map(async (file) => {
737
+ const signedUrlResult = await getSignedUrl(supabase, file.file_path, {
738
+ appName: options.appName || "pace-core",
739
+ orgId: options.orgId,
740
+ expiresIn: options.expiresIn || 3600
741
+ });
742
+ const url = signedUrlResult.ok ? signedUrlResult.data.url : null;
743
+ if (url) {
744
+ const cacheKey = getCacheKey(file.id, file.file_path, false);
745
+ globalUrlCache.set(cacheKey, { url, expiresAt: now + ttl });
746
+ }
747
+ return { id: file.id, url };
748
+ });
749
+ const signedUrlResults = await Promise.all(signedUrlPromises);
750
+ for (const result of signedUrlResults) {
751
+ if (result.url) {
752
+ urlMap.set(result.id, result.url);
753
+ }
754
+ }
755
+ }
756
+ if (uncachedFiles.length > 0) {
757
+ cleanupUrlCache();
758
+ }
759
+ return ok(urlMap);
760
+ } catch (error) {
761
+ return err({
762
+ code: "BATCH_URLS_FAILED",
763
+ message: error instanceof Error ? error.message : "Unknown error"
764
+ });
765
+ }
766
+ }
767
+ async function deleteFile(supabase, path, isPublic = false) {
768
+ try {
769
+ const bucketName = getBucketName(isPublic);
770
+ const { error } = await supabase.storage.from(bucketName).remove([path]);
771
+ if (error) {
772
+ return err({ code: "DELETE_FAILED", message: error.message });
773
+ }
774
+ return ok(void 0);
775
+ } catch (error) {
776
+ return err({
777
+ code: "DELETE_FAILED",
778
+ message: error instanceof Error ? error.message : "Unknown error"
779
+ });
780
+ }
781
+ }
782
+ async function listFiles(supabase, options) {
783
+ try {
784
+ const bucketName = getBucketName(options.isPublic || false);
785
+ if (!options.orgId && !options.userId) {
786
+ return err({ code: "LIST_FILES_INVALID_OPTIONS", message: "Either orgId or userId is required for listing files" });
787
+ }
788
+ const basePath = options.orgId ? options.orgId : `users/${options.userId}`;
789
+ const pathPrefix = `${basePath}/`;
790
+ const searchPath = options.pathPrefix ? `${pathPrefix}${options.pathPrefix}` : pathPrefix;
791
+ const { data, error } = await supabase.storage.from(bucketName).list(searchPath, {
792
+ limit: options.limit || 100,
793
+ offset: options.offset || 0,
794
+ sortBy: { column: "created_at", order: "desc" }
795
+ });
796
+ if (error) {
797
+ log2.error("Failed to list files:", error);
798
+ return err({ code: "LIST_FILES_FAILED", message: error.message });
799
+ }
800
+ const files = (data || []).map((item) => ({
801
+ name: item.name,
802
+ path: `${searchPath}${item.name}`,
803
+ size: item.metadata?.size || 0,
804
+ mimeType: item.metadata?.mimetype || "application/octet-stream",
805
+ lastModified: item.updated_at || item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
806
+ metadata: {
807
+ mimeType: item.metadata?.mimetype || "application/octet-stream",
808
+ size: item.metadata?.size || 0,
809
+ ...options.orgId && { orgId: options.orgId },
810
+ ...options.userId && { userId: options.userId },
811
+ appName: options.appName,
812
+ uploadedBy: "unknown",
813
+ uploadedAt: item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
814
+ isPublic: options.isPublic || false
815
+ }
816
+ }));
817
+ return ok({
818
+ files,
819
+ totalCount: files.length,
820
+ hasMore: files.length >= (options.limit || 100)
821
+ });
822
+ } catch (error) {
823
+ log2.error("Failed to list files:", error);
824
+ return err({
825
+ code: "LIST_FILES_FAILED",
826
+ message: error instanceof Error ? error.message : "Unknown error"
827
+ });
828
+ }
829
+ }
830
+ async function downloadFile(supabase, path, isPublic = false) {
831
+ try {
832
+ const bucketName = getBucketName(isPublic);
833
+ const { data, error } = await supabase.storage.from(bucketName).download(path);
834
+ if (error) {
835
+ log2.error("Failed to download file:", error);
836
+ return err({ code: "DOWNLOAD_FAILED", message: error.message });
837
+ }
838
+ if (!data) {
839
+ return err({ code: "DOWNLOAD_FAILED", message: "No data returned" });
840
+ }
841
+ const fileName = path.split("/").pop() || "download";
842
+ const { data: fileInfo } = await supabase.storage.from(bucketName).list(path.split("/").slice(0, -1).join("/"), { search: fileName });
843
+ const metadata = fileInfo?.[0]?.metadata || {};
844
+ return ok({
845
+ blob: data,
846
+ metadata: {
847
+ name: fileName,
848
+ size: metadata.size || data.size,
849
+ type: metadata.mimetype || "application/octet-stream"
850
+ }
851
+ });
852
+ } catch (error) {
853
+ log2.error("Failed to download file:", error);
854
+ return err({
855
+ code: "DOWNLOAD_FAILED",
856
+ message: error instanceof Error ? error.message : "Unknown error"
857
+ });
858
+ }
859
+ }
860
+ async function archiveFile(supabase, path, options) {
861
+ try {
862
+ const bucketName = getBucketName(options.isPublic || false);
863
+ let archivedPath;
864
+ if (options.orgId) {
865
+ archivedPath = path.replace(`${options.orgId}/`, `archived/${options.orgId}/`);
866
+ } else if (options.userId) {
867
+ archivedPath = path.replace(`users/${options.userId}/`, `archived/users/${options.userId}/`);
868
+ } else {
869
+ return err({ code: "ARCHIVE_FAILED", message: "Either orgId or userId is required for archiving files" });
870
+ }
871
+ const { error: copyError } = await supabase.storage.from(bucketName).copy(path, archivedPath);
872
+ if (copyError) {
873
+ return err({ code: "ARCHIVE_FAILED", message: copyError.message });
874
+ }
875
+ const deleteResult = await deleteFile(supabase, path, options.isPublic || false);
876
+ if (!deleteResult.ok) {
877
+ return err(deleteResult.error);
878
+ }
879
+ return ok(void 0);
880
+ } catch (error) {
881
+ return err({
882
+ code: "ARCHIVE_FAILED",
883
+ message: error instanceof Error ? error.message : "Unknown error"
884
+ });
885
+ }
886
+ }
887
+ var publicFileCache = /* @__PURE__ */ new Map();
888
+ function usePublicFileDisplay(table_name, record_id, organisation_id, category, options) {
889
+ const {
890
+ cacheTtl = 30 * 60 * 1e3,
891
+ // 30 minutes
892
+ enableCache = true,
893
+ supabase
894
+ } = options;
895
+ const [fileUrl, setFileUrl] = useState(null);
896
+ const [fileReference, setFileReference] = useState(null);
897
+ const [fileReferences, setFileReferences] = useState([]);
898
+ const [fileUrls, setFileUrls] = useState(/* @__PURE__ */ new Map());
899
+ const [fileCount, setFileCount] = useState(0);
900
+ const [isLoading, setIsLoading] = useState(false);
901
+ const [error, setError] = useState(null);
902
+ const fetchFiles = useCallback(async () => {
903
+ const emptySetters = () => {
904
+ applyEmptyPublicFileState(
905
+ setFileUrl,
906
+ setFileReference,
907
+ setFileReferences,
908
+ setFileUrls,
909
+ setFileCount,
910
+ setIsLoading,
911
+ setError
912
+ );
913
+ };
914
+ if (!table_name || !record_id || !supabase) {
915
+ emptySetters();
916
+ return;
917
+ }
918
+ warnInvalidOrganisationIdUuid(organisation_id ?? void 0);
919
+ const cacheKey = buildPublicFileCacheKey(table_name, record_id, organisation_id, category);
920
+ const cached = getCachedPublicFileData(cacheKey, enableCache);
921
+ if (cached) {
922
+ applyCachedPublicFileState(
923
+ cached,
924
+ setFileUrl,
925
+ setFileReference,
926
+ setFileReferences,
927
+ setFileUrls,
928
+ setFileCount,
929
+ setIsLoading,
930
+ setError
931
+ );
932
+ return;
933
+ }
934
+ try {
935
+ setIsLoading(true);
936
+ setError(null);
937
+ const rows = await fetchPublicFileRows(supabase, table_name, record_id, organisation_id, category);
938
+ const publicRows = rows.filter((f) => f.is_public === true);
939
+ if (publicRows.length === 0) {
940
+ emptySetters();
941
+ setCachedPublicFileData(cacheKey, emptyCacheData, enableCache, cacheTtl);
942
+ return;
943
+ }
944
+ const fileRefs = mapDbRowsToFileReferences(publicRows);
945
+ setFileReferences(fileRefs);
946
+ setFileCount(fileRefs.length);
947
+ if (category && fileRefs.length > 0) {
948
+ const firstFile = fileRefs[0];
949
+ setFileReference(firstFile);
950
+ setFileUrl(getPublicUrl(supabase, firstFile.file_path, true));
951
+ } else {
952
+ const urlResult = await generateFileUrlsBatch(supabase, fileRefs, {
953
+ appName: "pace-core",
954
+ orgId: organisation_id,
955
+ expiresIn: 3600
956
+ });
957
+ setFileUrls(urlResult.ok ? urlResult.data : /* @__PURE__ */ new Map());
958
+ setFileReference(null);
959
+ setFileUrl(null);
960
+ }
961
+ setCachedPublicFileData(
962
+ cacheKey,
963
+ buildCacheDataFromRefs(fileRefs, category, supabase),
964
+ enableCache,
965
+ cacheTtl
966
+ );
967
+ } catch (err2) {
968
+ logger.error("usePublicFileDisplay", "Error fetching files", {
969
+ table_name,
970
+ record_id,
971
+ organisation_id,
972
+ category,
973
+ error: err2 instanceof Error ? err2.message : "Unknown error",
974
+ errorDetails: err2 instanceof Error ? err2.stack : String(err2)
975
+ });
976
+ const error2 = err2 instanceof Error ? err2 : new Error("Unknown error occurred");
977
+ setError(error2);
978
+ setFileUrl(null);
979
+ setFileReference(null);
980
+ setFileReferences([]);
981
+ setFileUrls(/* @__PURE__ */ new Map());
982
+ setFileCount(0);
983
+ } finally {
984
+ setIsLoading(false);
985
+ }
986
+ }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
987
+ useEffect(() => {
988
+ if (table_name && record_id) {
989
+ fetchFiles();
990
+ } else {
991
+ setFileUrl(null);
992
+ setFileReference(null);
993
+ setFileReferences([]);
994
+ setFileUrls(/* @__PURE__ */ new Map());
995
+ setFileCount(0);
996
+ setIsLoading(false);
997
+ setError(null);
998
+ }
999
+ }, [fetchFiles, table_name, record_id, organisation_id]);
1000
+ const refetch = useCallback(async () => {
1001
+ if (!table_name || !record_id) return;
1002
+ if (enableCache) {
1003
+ const cacheKey = buildPublicFileCacheKey(table_name, record_id, organisation_id, category);
1004
+ publicFileCache.delete(cacheKey);
1005
+ }
1006
+ await fetchFiles();
1007
+ }, [fetchFiles, table_name, record_id, organisation_id, category, enableCache]);
1008
+ return {
1009
+ fileUrl,
1010
+ fileReference,
1011
+ fileReferences,
1012
+ fileUrls,
1013
+ fileCount,
1014
+ isLoading,
1015
+ error,
1016
+ refetch
1017
+ };
1018
+ }
1019
+ function buildPublicFileCacheKey(table_name, record_id, organisation_id, category) {
1020
+ return `public_file_${table_name}_${record_id}_${organisation_id === void 0 ? "undefined" : organisation_id ?? "null"}_${category || "all"}`;
1021
+ }
1022
+ function getCachedPublicFileData(cacheKey, enableCache) {
1023
+ if (!enableCache) return null;
1024
+ const cached = publicFileCache.get(cacheKey);
1025
+ if (!cached || Date.now() - cached.timestamp >= cached.ttl) return null;
1026
+ return cached.data;
1027
+ }
1028
+ function setCachedPublicFileData(cacheKey, data, enableCache, cacheTtl) {
1029
+ if (!enableCache) return;
1030
+ publicFileCache.set(cacheKey, {
1031
+ data,
1032
+ timestamp: Date.now(),
1033
+ ttl: cacheTtl
1034
+ });
1035
+ }
1036
+ function mapRpcItemToRow(item, table_name, record_id, organisation_id) {
1037
+ const metadata = item.file_metadata;
1038
+ return {
1039
+ id: item.id,
1040
+ table_name,
1041
+ record_id,
1042
+ file_path: item.file_path,
1043
+ file_metadata: item.file_metadata || null,
1044
+ organisation_id,
1045
+ app_id: metadata?.app_id || "",
1046
+ is_public: true,
1047
+ created_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
1048
+ updated_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
1049
+ created_by: null
1050
+ };
1051
+ }
1052
+ async function fetchPublicFileRowsWhenOrgUndefined(supabase, table_name, record_id, category) {
1053
+ let userScopedFiles = [];
1054
+ const orgScopedFiles = [];
1055
+ logger.debug("usePublicFileDisplay", "organisation_id is undefined, searching both user-scoped and organisation-scoped files:", {
1056
+ table_name,
1057
+ record_id,
1058
+ category
1059
+ });
1060
+ if (category) {
1061
+ const { data: userData, error: userRpcError } = await supabase.rpc("data_file_reference_by_category_list", {
1062
+ p_table_name: table_name,
1063
+ p_record_id: record_id,
1064
+ p_category: category,
1065
+ p_organisation_id: void 0
1066
+ });
1067
+ if (!userRpcError && userData) {
1068
+ userScopedFiles = userData.filter((item) => item.is_public === true && item.id && item.file_path && item.file_metadata).map((item) => mapRpcItemToRow(item, table_name, record_id, null));
1069
+ }
1070
+ } else {
1071
+ const { data: userData, error: userRpcError } = await supabase.rpc("data_file_reference_list", {
1072
+ p_table_name: table_name,
1073
+ p_record_id: record_id,
1074
+ p_organisation_id: void 0
1075
+ });
1076
+ if (!userRpcError && userData) {
1077
+ const ids = userData.map((item) => item.id);
1078
+ if (ids.length > 0) {
1079
+ const { data: fullData } = await supabase.from("core_file_references").select("id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at, created_by").in("id", ids).eq("is_public", true);
1080
+ userScopedFiles = fullData || [];
1081
+ }
1082
+ }
1083
+ }
1084
+ const allFiles = [...userScopedFiles, ...orgScopedFiles];
1085
+ allFiles.sort((a, b) => {
1086
+ const aTime = new Date(a.created_at || 0).getTime();
1087
+ const bTime = new Date(b.created_at || 0).getTime();
1088
+ return bTime - aTime;
1089
+ });
1090
+ logger.debug("usePublicFileDisplay", "Found files with undefined organisation_id:", {
1091
+ userScopedCount: userScopedFiles.length,
1092
+ orgScopedCount: orgScopedFiles.length,
1093
+ totalCount: allFiles.length
1094
+ });
1095
+ return allFiles;
1096
+ }
1097
+ async function fetchPublicFileRowsWhenOrgDefined(supabase, table_name, record_id, organisation_id, category) {
1098
+ if (category) {
1099
+ const rpcParams = {
1100
+ p_table_name: table_name,
1101
+ p_record_id: record_id,
1102
+ p_category: category,
1103
+ p_organisation_id: organisation_id ?? void 0
1104
+ };
1105
+ const { data, error: rpcError2 } = await supabase.rpc("data_file_reference_by_category_list", rpcParams);
1106
+ if (rpcError2) {
1107
+ logger.error("usePublicFileDisplay", "RPC function error", {
1108
+ function: "data_file_reference_by_category_list",
1109
+ table_name,
1110
+ record_id,
1111
+ category,
1112
+ organisation_id,
1113
+ error: rpcError2.message,
1114
+ errorCode: rpcError2.code,
1115
+ errorDetails: rpcError2.details,
1116
+ errorHint: rpcError2.hint
1117
+ });
1118
+ throw new Error(rpcError2.message || "Failed to fetch file reference");
1119
+ }
1120
+ if (!data || data.length === 0) return [];
1121
+ return data.filter((item) => item.is_public === true && item.id && item.file_path && item.file_metadata).map((item) => mapRpcItemToRow(item, table_name, record_id, organisation_id ?? null));
1122
+ }
1123
+ const { data: fileIds, error: rpcError } = await supabase.rpc("data_file_reference_list", {
1124
+ p_table_name: table_name,
1125
+ p_record_id: record_id,
1126
+ p_organisation_id: organisation_id ?? void 0
1127
+ });
1128
+ if (rpcError) {
1129
+ logger.error("usePublicFileDisplay", "RPC function error", {
1130
+ function: "data_file_reference_list",
1131
+ table_name,
1132
+ record_id,
1133
+ organisation_id,
1134
+ error: rpcError.message,
1135
+ errorCode: rpcError.code,
1136
+ errorDetails: rpcError.details,
1137
+ errorHint: rpcError.hint
1138
+ });
1139
+ throw new Error(rpcError.message || "Failed to fetch file references");
1140
+ }
1141
+ if (!fileIds || fileIds.length === 0) return [];
1142
+ const ids = fileIds.map((item) => item.id);
1143
+ const { data: fullData, error: fetchError } = await supabase.from("core_file_references").select("id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at, created_by").in("id", ids).eq("is_public", true);
1144
+ if (fetchError) throw new Error(fetchError.message || "Failed to fetch file references");
1145
+ return fullData || [];
1146
+ }
1147
+ async function fetchPublicFileRows(supabase, table_name, record_id, organisation_id, category) {
1148
+ if (organisation_id === void 0) {
1149
+ return fetchPublicFileRowsWhenOrgUndefined(supabase, table_name, record_id, category);
1150
+ }
1151
+ return fetchPublicFileRowsWhenOrgDefined(supabase, table_name, record_id, organisation_id, category);
1152
+ }
1153
+ function mapDbRowsToFileReferences(rows) {
1154
+ return rows.map((f) => {
1155
+ const fileName = f.file_path.split("/").pop() || "unknown";
1156
+ const fileType = fileName.split(".").pop() || "unknown";
1157
+ const metadata = f.file_metadata;
1158
+ return {
1159
+ id: f.id,
1160
+ table_name: f.table_name,
1161
+ record_id: f.record_id,
1162
+ file_path: f.file_path,
1163
+ file_metadata: {
1164
+ fileName: metadata?.fileName || fileName,
1165
+ fileType: metadata?.fileType || fileType,
1166
+ category: metadata?.category || "general_documents" /* GENERAL_DOCUMENTS */,
1167
+ ...metadata || {}
1168
+ },
1169
+ organisation_id: f.organisation_id,
1170
+ app_id: f.app_id,
1171
+ is_public: f.is_public ?? true,
1172
+ created_at: f.created_at || (/* @__PURE__ */ new Date()).toISOString(),
1173
+ updated_at: f.updated_at || (/* @__PURE__ */ new Date()).toISOString()
1174
+ };
1175
+ });
1176
+ }
1177
+ function buildCacheDataFromRefs(fileRefs, category, supabase) {
1178
+ const fileUrl = category && fileRefs.length > 0 ? getPublicUrl(supabase, fileRefs[0].file_path, true) : null;
1179
+ const fileReference = category && fileRefs.length > 0 ? fileRefs[0] : null;
1180
+ let fileUrls;
1181
+ if (category) {
1182
+ fileUrls = /* @__PURE__ */ new Map();
1183
+ } else {
1184
+ fileUrls = /* @__PURE__ */ new Map();
1185
+ for (const fileRef of fileRefs) {
1186
+ const url = getPublicUrl(supabase, fileRef.file_path, true);
1187
+ if (url) fileUrls.set(fileRef.id, url);
1188
+ }
1189
+ }
1190
+ return {
1191
+ fileUrl,
1192
+ fileReference,
1193
+ fileReferences: fileRefs,
1194
+ fileUrls,
1195
+ fileCount: fileRefs.length
1196
+ };
1197
+ }
1198
+ var emptyCacheData = {
1199
+ fileUrl: null,
1200
+ fileReference: null,
1201
+ fileReferences: [],
1202
+ fileUrls: /* @__PURE__ */ new Map(),
1203
+ fileCount: 0
1204
+ };
1205
+ function applyEmptyPublicFileState(setFileUrl, setFileReference, setFileReferences, setFileUrls, setFileCount, setIsLoading, setError) {
1206
+ setFileUrl(null);
1207
+ setFileReference(null);
1208
+ setFileReferences([]);
1209
+ setFileUrls(/* @__PURE__ */ new Map());
1210
+ setFileCount(0);
1211
+ setIsLoading(false);
1212
+ setError(null);
1213
+ }
1214
+ function applyCachedPublicFileState(cachedData, setFileUrl, setFileReference, setFileReferences, setFileUrls, setFileCount, setIsLoading, setError) {
1215
+ setFileUrl(cachedData.fileUrl || null);
1216
+ setFileReference(cachedData.fileReference || null);
1217
+ setFileReferences(cachedData.fileReferences || []);
1218
+ setFileUrls(cachedData.fileUrls || /* @__PURE__ */ new Map());
1219
+ setFileCount(cachedData.fileCount || 0);
1220
+ setIsLoading(false);
1221
+ setError(null);
1222
+ }
1223
+ function warnInvalidOrganisationIdUuid(organisation_id) {
1224
+ if (!organisation_id) return;
1225
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1226
+ if (!uuidRegex.test(organisation_id)) {
1227
+ logger.warn("usePublicFileDisplay", "Invalid organisationId format (not a valid UUID)", { organisation_id });
1228
+ }
1229
+ }
1230
+ function clearPublicFileDisplayCache() {
1231
+ for (const [key] of publicFileCache) {
1232
+ if (key.startsWith("public_file_")) {
1233
+ publicFileCache.delete(key);
1234
+ }
1235
+ }
1236
+ }
1237
+ function getPublicFileDisplayCacheStats() {
1238
+ const keys = Array.from(publicFileCache.keys()).filter((key) => key.startsWith("public_file_"));
1239
+ return {
1240
+ size: keys.length,
1241
+ keys
1242
+ };
1243
+ }
1244
+
1245
+ // src/utils/file-reference/index.ts
1246
+ var log3 = createLogger("FileReferenceService");
1247
+ function toApiError(error) {
1248
+ const message = error instanceof Error ? error.message : "Unknown error";
1249
+ const code = error instanceof Error && error.code ? error.code : "FILE_REFERENCE_ERROR";
1250
+ return { code, message };
1251
+ }
1252
+ function mapFileReferenceRowToFileReference(f) {
1253
+ const fileName = f.file_path.split("/").pop() || "unknown";
1254
+ const fileType = fileName.split(".").pop() || "unknown";
1255
+ const metadata = f.file_metadata;
1256
+ return {
1257
+ id: f.id,
1258
+ table_name: f.table_name,
1259
+ record_id: f.record_id,
1260
+ file_path: f.file_path,
1261
+ file_metadata: {
1262
+ fileName: metadata?.fileName || fileName,
1263
+ fileType: metadata?.fileType || fileType,
1264
+ category: metadata?.category || "general_documents" /* GENERAL_DOCUMENTS */,
1265
+ ...metadata || {}
1266
+ },
1267
+ organisation_id: f.organisation_id,
1268
+ app_id: f.app_id,
1269
+ is_public: f.is_public ?? false,
1270
+ created_at: f.created_at || (/* @__PURE__ */ new Date()).toISOString(),
1271
+ updated_at: f.updated_at || (/* @__PURE__ */ new Date()).toISOString()
1272
+ };
1273
+ }
1274
+ var FileReferenceServiceImpl = class {
1275
+ constructor(supabase) {
1276
+ this.supabase = supabase;
1277
+ }
1278
+ /**
1279
+ * Helper method to get app name from app ID
1280
+ */
1281
+ async getAppName(appId) {
1282
+ try {
1283
+ const { data, error } = await this.supabase.from("rbac_apps").select("name").eq("id", appId).single();
1284
+ if (error || !data) {
1285
+ return "unknown";
1286
+ }
1287
+ return data.name || "unknown";
1288
+ } catch {
1289
+ return "unknown";
1290
+ }
1291
+ }
1292
+ validateCreateOptions(options) {
1293
+ const isUserScoped = !options.organisation_id && !!options.userId;
1294
+ if (!isUserScoped && !options.organisation_id) {
1295
+ return err({ code: "VALIDATION_ERROR", message: "organisation_id is required for file upload, or userId must be provided for user-scoped files" });
1296
+ }
1297
+ if (!options.table_name) {
1298
+ return err({ code: "VALIDATION_ERROR", message: "table_name is required for file upload" });
1299
+ }
1300
+ if (!options.record_id) {
1301
+ return err({ code: "VALIDATION_ERROR", message: "record_id is required for file upload" });
1302
+ }
1303
+ if (!options.folder) {
1304
+ return err({ code: "VALIDATION_ERROR", message: "folder is required for file upload. The folder prop determines the storage path." });
1305
+ }
1306
+ return ok({ isUserScoped });
1307
+ }
1308
+ async resolveAuthenticatedUserIdForUserScoped() {
1309
+ const { data: { user: authUser }, error: authError } = await this.supabase.auth.getUser();
1310
+ if (authError || !authUser) {
1311
+ return err({ code: "UNAUTHORIZED", message: "User must be authenticated to upload user-scoped files" });
1312
+ }
1313
+ log3.debug("Using authenticated user ID for user-scoped file upload", { userId: authUser.id });
1314
+ return ok(authUser.id);
1315
+ }
1316
+ async checkSuperAdminUser(userId) {
1317
+ if (!userId) return false;
1318
+ try {
1319
+ const { isSuperAdmin } = await import('./api-BZR2CYXL.js');
1320
+ const superResult = await isSuperAdmin(userId);
1321
+ if (superResult.ok && superResult.data) {
1322
+ log3.debug("Super admin detected - bypassing permission checks", { userId });
1323
+ return true;
1324
+ }
1325
+ return false;
1326
+ } catch (superAdminCheckError) {
1327
+ log3.warn("Failed to check super-admin status, proceeding with normal permission checks", superAdminCheckError);
1328
+ return false;
1329
+ }
1330
+ }
1331
+ async uploadFileAndExtractMetadata(options, file, authenticatedUserId, isUserScoped) {
1332
+ const userId = authenticatedUserId || (isUserScoped ? void 0 : options.userId);
1333
+ const uploadResult = await uploadFile(this.supabase, file, {
1334
+ appName: "file-reference",
1335
+ orgId: options.organisation_id || void 0,
1336
+ userId,
1337
+ isPublic: options.is_public || false,
1338
+ customPath: options.folder
1339
+ });
1340
+ if (!uploadResult.ok) return err(uploadResult.error);
1341
+ const metadataResult = await extractFileMetadata(file, {
1342
+ appName: "file-reference",
1343
+ orgId: options.organisation_id || void 0,
1344
+ userId,
1345
+ isPublic: options.is_public || false
1346
+ }, "system");
1347
+ if (!metadataResult.ok) return err(metadataResult.error);
1348
+ return ok({ path: uploadResult.data.path, metadata: metadataResult.data });
1349
+ }
1350
+ async setOrgContextIfNeeded(isUserScoped, organisation_id) {
1351
+ if (isUserScoped || !organisation_id) return;
1352
+ const ctxResult = await setOrganisationContext(this.supabase, organisation_id);
1353
+ if (!ctxResult.ok) {
1354
+ log3.warn("setOrganisationContext failed (non-fatal):", ctxResult.error.message);
1355
+ }
1356
+ }
1357
+ async resolveRpcUserId(authenticatedUserId, options) {
1358
+ if (authenticatedUserId) return authenticatedUserId;
1359
+ if (options.userId) return options.userId;
1360
+ const { data: { user: authUser } } = await this.supabase.auth.getUser();
1361
+ return authUser?.id ?? null;
1362
+ }
1363
+ async buildPermissionDeniedMessage(options, isSuperAdminUser) {
1364
+ const appName = await this.getAppName(options.app_id).catch(() => "unknown");
1365
+ const pageContextDisplay = options.pageContext || "undefined";
1366
+ return isSuperAdminUser ? `File upload failed for super-admin user. Page context: '${pageContextDisplay}', App: '${appName}'.` : `File upload denied: insufficient permissions. You need 'create:page.${pageContextDisplay}' or 'update:page.${pageContextDisplay}' for the '${pageContextDisplay}' page.`;
1367
+ }
1368
+ async rollbackUploadAndErr(filePath, isPublic, code, message) {
1369
+ await deleteFile(this.supabase, filePath, isPublic);
1370
+ return err({ code, message });
1371
+ }
1372
+ async fetchCreatedFileReference(createdId) {
1373
+ const { data: fileRef, error: fetchError } = await this.supabase.from("core_file_references").select("id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at").eq("id", createdId).single();
1374
+ if (fetchError || !fileRef) {
1375
+ return err({ code: "FETCH_FAILED", message: fetchError?.message ?? "Failed to fetch created file reference" });
1376
+ }
1377
+ return ok(fileRef);
1378
+ }
1379
+ buildCreateRpcPayload(options, filePath, file, metadata, rpcUserId) {
1380
+ return {
1381
+ p_table_name: options.table_name,
1382
+ p_record_id: options.record_id,
1383
+ p_file_path: filePath,
1384
+ p_organisation_id: options.organisation_id ?? null,
1385
+ p_app_id: options.app_id,
1386
+ p_page_context: options.pageContext,
1387
+ p_event_id: options.event_id || null,
1388
+ p_file_metadata: {
1389
+ fileName: file.name,
1390
+ fileType: file.type,
1391
+ fileSize: file.size,
1392
+ category: options.category,
1393
+ ...metadata,
1394
+ ...options.custom_metadata
1395
+ },
1396
+ p_is_public: options.is_public || false,
1397
+ p_user_id: rpcUserId
1398
+ };
1399
+ }
1400
+ /**
1401
+ * Creates a file reference by uploading a file to storage and linking it in the database.
1402
+ *
1403
+ * Storage Flow:
1404
+ * 1. Upload file to storage bucket first (files or public-files based on is_public flag)
1405
+ * - Path format: {orgId}/{folder}/{timestamp-uuid-filename}
1406
+ * - Bucket selection: 'files' (private) or 'public-files' (public)
1407
+ * 2. Extract file metadata (dimensions, hash, etc.)
1408
+ * 3. Set organisation context for RLS policies
1409
+ * 4. Create database reference via RPC function
1410
+ * 5. If DB insert fails, rollback by deleting uploaded file
1411
+ *
1412
+ * This ensures atomicity: either both storage and DB succeed, or both are cleaned up.
1413
+ */
1414
+ async createFileReference(options, file) {
1415
+ try {
1416
+ const validation = this.validateCreateOptions(options);
1417
+ if (!validation.ok) return validation;
1418
+ const { isUserScoped } = validation.data;
1419
+ let authenticatedUserId;
1420
+ if (isUserScoped) {
1421
+ const authResult = await this.resolveAuthenticatedUserIdForUserScoped();
1422
+ if (!authResult.ok) return authResult;
1423
+ authenticatedUserId = authResult.data;
1424
+ }
1425
+ const isSuperAdminUser = await this.checkSuperAdminUser(authenticatedUserId || options.userId);
1426
+ const uploadResult = await this.uploadFileAndExtractMetadata(options, file, authenticatedUserId, isUserScoped);
1427
+ if (!uploadResult.ok) return uploadResult;
1428
+ const { path: filePath, metadata } = uploadResult.data;
1429
+ await this.setOrgContextIfNeeded(isUserScoped, options.organisation_id ?? void 0);
1430
+ const rpcUserId = await this.resolveRpcUserId(authenticatedUserId, options);
1431
+ const payload = this.buildCreateRpcPayload(options, filePath, file, metadata, rpcUserId);
1432
+ const { data, error } = await this.supabase.rpc("data_file_reference_create", payload);
1433
+ if (error) {
1434
+ return await this.rollbackUploadAndErr(filePath, options.is_public ?? false, "CREATE_FAILED", error.message);
1435
+ }
1436
+ if (!data || data === null) {
1437
+ const message = await this.buildPermissionDeniedMessage(options, isSuperAdminUser);
1438
+ return await this.rollbackUploadAndErr(filePath, options.is_public ?? false, "PERMISSION_DENIED", message);
1439
+ }
1440
+ const fetchResult = await this.fetchCreatedFileReference(data);
1441
+ if (!fetchResult.ok) {
1442
+ return await this.rollbackUploadAndErr(
1443
+ filePath,
1444
+ options.is_public ?? false,
1445
+ "FETCH_FAILED",
1446
+ fetchResult.error.message
1447
+ );
1448
+ }
1449
+ invalidateFileDisplayCache(
1450
+ options.table_name,
1451
+ options.record_id,
1452
+ options.organisation_id || null,
1453
+ options.category
1454
+ );
1455
+ return ok(fetchResult.data);
1456
+ } catch (error) {
1457
+ log3.error("Error creating file reference:", error);
1458
+ return err(toApiError(error));
1459
+ }
1460
+ }
1461
+ async getFileReference(table_name, record_id, organisation_id) {
1462
+ try {
1463
+ let query = this.supabase.from("core_file_references").select("id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at").eq("table_name", table_name).eq("record_id", record_id);
1464
+ if (organisation_id === null || organisation_id === void 0) {
1465
+ query = query.is("organisation_id", null);
1466
+ } else {
1467
+ query = query.eq("organisation_id", organisation_id);
1468
+ }
1469
+ const { data, error } = await query.single();
1470
+ if (error) {
1471
+ if (error.code === "PGRST116") {
1472
+ return ok(null);
1473
+ }
1474
+ return err({ code: "FETCH_FAILED", message: error.message });
1475
+ }
1476
+ return ok(data);
1477
+ } catch (error) {
1478
+ log3.error("Error getting file reference:", error);
1479
+ return err(toApiError(error));
1480
+ }
1481
+ }
1482
+ async getFileUrl(table_name, record_id, organisation_id) {
1483
+ try {
1484
+ const refResult = await this.getFileReference(table_name, record_id, organisation_id);
1485
+ if (!refResult.ok) {
1486
+ return refResult;
1487
+ }
1488
+ const fileRef = refResult.data;
1489
+ if (!fileRef) {
1490
+ return ok(null);
1491
+ }
1492
+ if (fileRef.is_public) {
1493
+ const { data: pathData } = await this.supabase.rpc("data_file_reference_url_get", {
1494
+ p_table_name: table_name,
1495
+ p_record_id: record_id,
1496
+ p_organisation_id: organisation_id
1497
+ });
1498
+ if (!pathData) {
1499
+ return ok(null);
1500
+ }
1501
+ return ok(getPublicUrl(this.supabase, pathData, true));
1502
+ }
1503
+ return this.getSignedUrl(table_name, record_id, organisation_id);
1504
+ } catch (error) {
1505
+ log3.error("Error getting file URL:", error);
1506
+ return err(toApiError(error));
1507
+ }
1508
+ }
1509
+ async getSignedUrl(table_name, record_id, organisation_id, expires_in = 3600) {
1510
+ try {
1511
+ const { data: filePath, error } = await this.supabase.rpc("data_file_reference_signed_url_get", {
1512
+ p_table_name: table_name,
1513
+ p_record_id: record_id,
1514
+ p_organisation_id: organisation_id ?? null,
1515
+ p_expires_in: expires_in
1516
+ });
1517
+ if (error) {
1518
+ return err({ code: "SIGNED_URL_FAILED", message: error.message });
1519
+ }
1520
+ if (!filePath) {
1521
+ return ok(null);
1522
+ }
1523
+ const signedUrlResult = await getSignedUrl(this.supabase, filePath, {
1524
+ appName: "file-reference",
1525
+ orgId: organisation_id,
1526
+ userId: organisation_id ? void 0 : record_id,
1527
+ expiresIn: expires_in
1528
+ });
1529
+ if (!signedUrlResult.ok) {
1530
+ return err(signedUrlResult.error);
1531
+ }
1532
+ return ok(signedUrlResult.data.url ?? null);
1533
+ } catch (error) {
1534
+ log3.error("Error getting signed URL:", error);
1535
+ return err(toApiError(error));
1536
+ }
1537
+ }
1538
+ async updateFileReference(id, updates) {
1539
+ try {
1540
+ const { data, error } = await this.supabase.from("core_file_references").update(updates).eq("id", id).select().single();
1541
+ if (error) {
1542
+ return err({ code: "UPDATE_FAILED", message: error.message });
1543
+ }
1544
+ return ok(data);
1545
+ } catch (error) {
1546
+ log3.error("Error updating file reference:", error);
1547
+ return err(toApiError(error));
1548
+ }
1549
+ }
1550
+ async deleteFileReference(table_name, record_id, organisation_id, delete_file = false) {
1551
+ try {
1552
+ const refResult = await this.getFileReference(table_name, record_id, organisation_id);
1553
+ const fileRef = refResult.ok ? refResult.data : null;
1554
+ const { error } = await this.supabase.rpc("data_file_reference_delete", {
1555
+ p_table_name: table_name,
1556
+ p_record_id: record_id,
1557
+ p_organisation_id: organisation_id ?? null,
1558
+ p_delete_file: delete_file
1559
+ });
1560
+ if (error) {
1561
+ return err({ code: "DELETE_FAILED", message: error.message });
1562
+ }
1563
+ if (delete_file && fileRef) {
1564
+ await deleteFile(this.supabase, fileRef.file_path, fileRef.is_public || false);
1565
+ }
1566
+ return ok(true);
1567
+ } catch (error) {
1568
+ log3.error("Error deleting file reference:", error);
1569
+ return err(toApiError(error));
1570
+ }
1571
+ }
1572
+ async listFileReferences(table_name, record_id, organisation_id) {
1573
+ try {
1574
+ const { data, error } = await this.supabase.rpc("data_file_reference_list", {
1575
+ p_table_name: table_name,
1576
+ p_record_id: record_id,
1577
+ p_organisation_id: organisation_id ?? null
1578
+ });
1579
+ if (error) {
1580
+ return err({ code: "LIST_FAILED", message: error.message });
1581
+ }
1582
+ if (!data || data.length === 0) {
1583
+ return ok([]);
1584
+ }
1585
+ const fileReferences = data.filter((item) => item.id && item.file_path && item.file_metadata).map((item) => {
1586
+ const fileName = item.file_path.split("/").pop() || "unknown";
1587
+ const fileType = fileName.split(".").pop() || "unknown";
1588
+ return {
1589
+ id: item.id,
1590
+ table_name,
1591
+ record_id,
1592
+ file_path: item.file_path,
1593
+ file_metadata: { fileName, fileType, ...item.file_metadata || {} },
1594
+ organisation_id: organisation_id ?? null,
1595
+ app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(""),
1596
+ is_public: item.is_public ?? false,
1597
+ created_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
1598
+ updated_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString()
1599
+ };
1600
+ });
1601
+ return ok(fileReferences);
1602
+ } catch (error) {
1603
+ log3.error("Error listing file references:", error);
1604
+ return err(toApiError(error));
1605
+ }
1606
+ }
1607
+ async getFileCount(table_name, record_id, organisation_id) {
1608
+ try {
1609
+ const { data, error } = await this.supabase.rpc("data_file_reference_count_get", {
1610
+ p_table_name: table_name,
1611
+ p_record_id: record_id,
1612
+ p_organisation_id: organisation_id ?? null
1613
+ });
1614
+ if (error) {
1615
+ return err({ code: "COUNT_FAILED", message: error.message });
1616
+ }
1617
+ return ok(data ?? 0);
1618
+ } catch (error) {
1619
+ log3.error("Error getting file count:", error);
1620
+ return err(toApiError(error));
1621
+ }
1622
+ }
1623
+ async getFileReferenceById(id, organisation_id) {
1624
+ try {
1625
+ const { data, error } = await this.supabase.rpc("data_file_reference_get", {
1626
+ p_file_reference_id: id,
1627
+ p_organisation_id: organisation_id ?? null
1628
+ });
1629
+ if (error) {
1630
+ return err({ code: "FETCH_FAILED", message: error.message });
1631
+ }
1632
+ if (!data || data.length === 0) {
1633
+ return ok(null);
1634
+ }
1635
+ return ok(data[0]);
1636
+ } catch (error) {
1637
+ log3.error("Error getting file reference by ID:", error);
1638
+ return err(toApiError(error));
1639
+ }
1640
+ }
1641
+ async getFilesByCategory(table_name, record_id, category, organisation_id) {
1642
+ try {
1643
+ const { data, error } = await this.supabase.rpc("data_file_reference_by_category_list", {
1644
+ p_table_name: table_name,
1645
+ p_record_id: record_id,
1646
+ p_category: category,
1647
+ p_organisation_id: organisation_id ?? null
1648
+ });
1649
+ if (error) {
1650
+ log3.error("RPC ERROR getting files by category:", { error, table_name, record_id, category });
1651
+ return err({ code: "LIST_FAILED", message: error.message });
1652
+ }
1653
+ if (!data || data.length === 0) {
1654
+ return ok([]);
1655
+ }
1656
+ const fileReferences = data.filter((item) => {
1657
+ const fileCategory = item.file_metadata?.category;
1658
+ return fileCategory === category && item.id && item.file_path && item.file_metadata;
1659
+ }).map((item) => ({
1660
+ id: item.id,
1661
+ table_name,
1662
+ record_id,
1663
+ file_path: item.file_path,
1664
+ file_metadata: {
1665
+ fileName: item.file_path.split("/").pop() || "unknown",
1666
+ fileType: item.file_path.split(".").pop() || "unknown",
1667
+ category: item.file_metadata?.category || "general_documents" /* GENERAL_DOCUMENTS */,
1668
+ ...item.file_metadata || {}
1669
+ },
1670
+ organisation_id: organisation_id ?? null,
1671
+ app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(""),
1672
+ is_public: item.is_public ?? false,
1673
+ created_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
1674
+ updated_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString()
1675
+ }));
1676
+ return ok(fileReferences);
1677
+ } catch (error) {
1678
+ log3.error("Error getting files by category:", error);
1679
+ return err(toApiError(error));
1680
+ }
1681
+ }
1682
+ async uploadMultipleFiles(options, files) {
1683
+ const success = [];
1684
+ const failed = [];
1685
+ const results = [];
1686
+ for (const file of files) {
1687
+ const result = await this.createFileReference(options, file);
1688
+ if (result.ok) {
1689
+ const fileReference = result.data;
1690
+ success.push(fileReference);
1691
+ results.push({
1692
+ file,
1693
+ result: {
1694
+ file_reference: fileReference,
1695
+ file_url: fileReference.is_public ? getPublicUrl(this.supabase, fileReference.file_path, true) : ""
1696
+ }
1697
+ });
1698
+ } else {
1699
+ failed.push({ file, error: result.error.message });
1700
+ results.push({ file, result: null, error: result.error.message });
1701
+ }
1702
+ }
1703
+ return ok({
1704
+ success,
1705
+ failed,
1706
+ total: results.length,
1707
+ successful: success.length,
1708
+ results
1709
+ });
1710
+ }
1711
+ };
1712
+ function createFileReferenceService(supabase) {
1713
+ return new FileReferenceServiceImpl(supabase);
1714
+ }
1715
+ async function uploadFileWithReference(supabase, options, file) {
1716
+ const service = createFileReferenceService(supabase);
1717
+ const result = await service.createFileReference(options, file);
1718
+ if (!result.ok) {
1719
+ return result;
1720
+ }
1721
+ const fileReference = result.data;
1722
+ let urlString;
1723
+ if (options.is_public) {
1724
+ urlString = getPublicUrl(supabase, fileReference.file_path, true);
1725
+ } else {
1726
+ const signedResult = await getSignedUrl(supabase, fileReference.file_path, {
1727
+ appName: "file-reference",
1728
+ orgId: options.organisation_id || void 0,
1729
+ userId: options.userId || void 0,
1730
+ expiresIn: 3600
1731
+ });
1732
+ urlString = signedResult.ok ? signedResult.data.url ?? "" : "";
1733
+ }
1734
+ return ok({
1735
+ file_reference: fileReference,
1736
+ file_url: urlString,
1737
+ signed_url: options.is_public ? void 0 : urlString || void 0
1738
+ });
1739
+ }
1740
+
1741
+ // src/components/FileDisplay/useFileDisplayData.ts
1742
+ async function fetchFileDisplayDataInternal(params) {
1743
+ const { table_name, record_id, organisation_id, category, supabase } = params;
1744
+ if (!supabase) {
1745
+ return [];
1746
+ }
1747
+ let query = supabase.from("core_file_references").select("id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at, created_by").eq("table_name", table_name).eq("record_id", record_id);
1748
+ if (organisation_id === void 0 || organisation_id === null) {
1749
+ query = query.is("organisation_id", null);
1750
+ } else {
1751
+ query = query.eq("organisation_id", organisation_id);
1752
+ }
1753
+ if (category !== void 0 && category !== null) {
1754
+ query = query.contains("file_metadata", { category });
1755
+ }
1756
+ const { data, error } = await query.order("created_at", { ascending: false });
1757
+ if (error) {
1758
+ throw new Error(error.message ?? "Failed to fetch file references");
1759
+ }
1760
+ const rows = data ?? [];
1761
+ return rows.map(mapFileReferenceRowToFileReference);
1762
+ }
1763
+
1764
+ // src/components/FileDisplay/useFileDisplay.ts
1765
+ var authenticatedFileCache = /* @__PURE__ */ new Map();
1766
+ var MAX_CACHE_SIZE2 = 100;
1767
+ function cleanupCache() {
1768
+ const now = Date.now();
1769
+ const entries = Array.from(authenticatedFileCache.entries());
1770
+ const expiredKeys = [];
1771
+ entries.forEach(([key, value]) => {
1772
+ if (now - value.timestamp >= value.ttl) {
1773
+ expiredKeys.push(key);
1774
+ }
1775
+ });
1776
+ expiredKeys.forEach((key) => authenticatedFileCache.delete(key));
1777
+ if (authenticatedFileCache.size > MAX_CACHE_SIZE2) {
1778
+ const sorted = entries.filter(([key]) => !expiredKeys.includes(key)).sort((a, b) => a[1].timestamp - b[1].timestamp);
1779
+ const toRemove = sorted.slice(0, authenticatedFileCache.size - MAX_CACHE_SIZE2);
1780
+ toRemove.forEach(([key]) => authenticatedFileCache.delete(key));
1781
+ }
1782
+ }
1783
+ function getFileDisplayCacheKey(table_name, record_id, organisation_id, category) {
1784
+ return `file_${table_name}_${record_id}_${organisation_id === void 0 ? "undefined" : organisation_id ?? "null"}_${category || "all"}`;
1785
+ }
1786
+ async function tryApplyCacheHit(cacheKey, enableCache, cacheTtl, supabase, organisation_id, record_id, safeSetState, setters) {
1787
+ if (!enableCache || !supabase) return false;
1788
+ const cached = authenticatedFileCache.get(cacheKey);
1789
+ if (!cached || Date.now() - cached.timestamp >= cached.ttl) return false;
1790
+ const cachedData = cached.data;
1791
+ if (cachedData.fileReference && !cachedData.fileUrl && cachedData.fileReference.is_public === false && supabase) {
1792
+ try {
1793
+ const signedUrlResult = await getSignedUrl(supabase, cachedData.fileReference.file_path, {
1794
+ appName: "pace-core",
1795
+ orgId: organisation_id,
1796
+ userId: organisation_id ? void 0 : record_id,
1797
+ expiresIn: 3600
1798
+ });
1799
+ const regeneratedUrl = signedUrlResult.ok ? signedUrlResult.data.url : null;
1800
+ safeSetState(setters.setFileUrl, regeneratedUrl);
1801
+ safeSetState(setters.setFileReference, cachedData.fileReference);
1802
+ safeSetState(setters.setFileReferences, cachedData.fileReferences || []);
1803
+ safeSetState(setters.setFileUrls, cachedData.fileUrls || /* @__PURE__ */ new Map());
1804
+ safeSetState(setters.setFileCount, cachedData.fileCount || 0);
1805
+ safeSetState(setters.setIsLoading, false);
1806
+ safeSetState(setters.setError, null);
1807
+ return true;
1808
+ } catch (err2) {
1809
+ logger.warn("useFileDisplay", "Failed to regenerate signed URL from cache, falling back to fetch:", err2);
1810
+ }
1811
+ }
1812
+ safeSetState(setters.setFileUrl, cachedData.fileUrl || null);
1813
+ safeSetState(setters.setFileReference, cachedData.fileReference || null);
1814
+ safeSetState(setters.setFileReferences, cachedData.fileReferences || []);
1815
+ safeSetState(setters.setFileUrls, cachedData.fileUrls || /* @__PURE__ */ new Map());
1816
+ safeSetState(setters.setFileCount, cachedData.fileCount || 0);
1817
+ safeSetState(setters.setIsLoading, false);
1818
+ safeSetState(setters.setError, null);
1819
+ return true;
1820
+ }
1821
+ async function applyFetchedFilesAndCache(files, category, table_name, record_id, organisation_id, supabase, cacheKey, enableCache, cacheTtl, safeSetState, setters) {
1822
+ safeSetState(setters.setFileReferences, files);
1823
+ safeSetState(setters.setFileCount, files.length);
1824
+ if (category && files.length > 0) {
1825
+ const firstFile = files[0];
1826
+ safeSetState(setters.setFileReference, firstFile);
1827
+ let url = null;
1828
+ if (firstFile.is_public) {
1829
+ url = getPublicUrl(supabase, firstFile.file_path, true);
1830
+ } else {
1831
+ const signedUrlResult = await getSignedUrl(supabase, firstFile.file_path, {
1832
+ appName: "pace-core",
1833
+ orgId: organisation_id,
1834
+ userId: organisation_id ? void 0 : record_id,
1835
+ expiresIn: 3600
1836
+ });
1837
+ url = signedUrlResult.ok ? signedUrlResult.data.url : null;
1838
+ if (!url) {
1839
+ logger.warn("useFileDisplay", "Failed to generate signed URL for file:", {
1840
+ file_path: firstFile.file_path,
1841
+ record_id,
1842
+ table_name
1843
+ });
1844
+ }
1845
+ }
1846
+ safeSetState(setters.setFileUrl, url);
1847
+ } else {
1848
+ const urlResult = await generateFileUrlsBatch(supabase, files, {
1849
+ appName: "pace-core",
1850
+ orgId: organisation_id,
1851
+ userId: organisation_id ? void 0 : record_id,
1852
+ expiresIn: 3600
1853
+ });
1854
+ const urlMap = urlResult.ok ? urlResult.data : /* @__PURE__ */ new Map();
1855
+ safeSetState(setters.setFileUrls, urlMap);
1856
+ safeSetState(setters.setFileReference, null);
1857
+ safeSetState(setters.setFileUrl, null);
1858
+ }
1859
+ if (enableCache) {
1860
+ const cacheData = {
1861
+ fileUrl: null,
1862
+ fileReference: category && files.length > 0 ? files[0] : null,
1863
+ fileReferences: files,
1864
+ fileUrls: /* @__PURE__ */ new Map(),
1865
+ fileCount: files.length
1866
+ };
1867
+ if (category && files.length > 0) {
1868
+ const firstFile = files[0];
1869
+ cacheData.fileUrl = firstFile.is_public ? getPublicUrl(supabase, firstFile.file_path, true) : null;
1870
+ } else {
1871
+ const urlMap = /* @__PURE__ */ new Map();
1872
+ for (const fileRef of files) {
1873
+ if (fileRef.is_public) {
1874
+ const url = getPublicUrl(supabase, fileRef.file_path, true);
1875
+ if (url) urlMap.set(fileRef.id, url);
1876
+ }
1877
+ }
1878
+ cacheData.fileUrls = urlMap;
1879
+ }
1880
+ authenticatedFileCache.set(cacheKey, {
1881
+ data: cacheData,
1882
+ timestamp: Date.now(),
1883
+ ttl: cacheTtl
1884
+ });
1885
+ cleanupCache();
1886
+ }
1887
+ }
1888
+ function useFileDisplay(table_name, record_id, organisation_id, category, options) {
1889
+ const {
1890
+ cacheTtl = 30 * 60 * 1e3,
1891
+ // 30 minutes
1892
+ enableCache = true,
1893
+ supabase
1894
+ } = options;
1895
+ const [fileUrl, setFileUrl] = useState(null);
1896
+ const [fileReference, setFileReference] = useState(null);
1897
+ const [fileReferences, setFileReferences] = useState([]);
1898
+ const [fileUrls, setFileUrls] = useState(/* @__PURE__ */ new Map());
1899
+ const [fileCount, setFileCount] = useState(0);
1900
+ const [isLoading, setIsLoading] = useState(false);
1901
+ const [error, setError] = useState(null);
1902
+ const isMountedRef = useRef(true);
1903
+ const safeSetState = (setter, value) => {
1904
+ if (isMountedRef.current) {
1905
+ setter(value);
1906
+ }
1907
+ };
1908
+ const fetchFiles = useCallback(async () => {
1909
+ if (!table_name || !record_id || !supabase) {
1910
+ safeSetState(setFileUrl, null);
1911
+ safeSetState(setFileReference, null);
1912
+ safeSetState(setFileReferences, []);
1913
+ safeSetState(setFileUrls, /* @__PURE__ */ new Map());
1914
+ safeSetState(setFileCount, 0);
1915
+ safeSetState(setIsLoading, false);
1916
+ return;
1917
+ }
1918
+ if (organisation_id) {
1919
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1920
+ if (!uuidRegex.test(organisation_id)) {
1921
+ logger.warn("useFileDisplay", "Invalid organisationId format (not a valid UUID):", organisation_id);
1922
+ }
1923
+ }
1924
+ safeSetState(setIsLoading, true);
1925
+ safeSetState(setError, null);
1926
+ const cacheKey = getFileDisplayCacheKey(table_name, record_id, organisation_id, category);
1927
+ const applied = await tryApplyCacheHit(
1928
+ cacheKey,
1929
+ enableCache,
1930
+ cacheTtl,
1931
+ supabase,
1932
+ organisation_id,
1933
+ record_id,
1934
+ safeSetState,
1935
+ {
1936
+ setFileUrl,
1937
+ setFileReference,
1938
+ setFileReferences,
1939
+ setFileUrls,
1940
+ setFileCount,
1941
+ setIsLoading,
1942
+ setError
1943
+ }
1944
+ );
1945
+ if (applied) return;
1946
+ try {
1947
+ const files = await fetchFileDisplayDataInternal({
1948
+ table_name,
1949
+ record_id,
1950
+ organisation_id,
1951
+ category,
1952
+ supabase
1953
+ });
1954
+ if (files.length === 0) {
1955
+ safeSetState(setFileUrl, null);
1956
+ safeSetState(setFileReference, null);
1957
+ safeSetState(setFileReferences, []);
1958
+ safeSetState(setFileUrls, /* @__PURE__ */ new Map());
1959
+ safeSetState(setFileCount, 0);
1960
+ if (enableCache) {
1961
+ authenticatedFileCache.set(cacheKey, {
1962
+ data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: /* @__PURE__ */ new Map(), fileCount: 0 },
1963
+ timestamp: Date.now(),
1964
+ ttl: cacheTtl
1965
+ });
1966
+ cleanupCache();
1967
+ }
1968
+ return;
1969
+ }
1970
+ await applyFetchedFilesAndCache(
1971
+ files,
1972
+ category,
1973
+ table_name,
1974
+ record_id,
1975
+ organisation_id,
1976
+ supabase,
1977
+ cacheKey,
1978
+ enableCache,
1979
+ cacheTtl,
1980
+ safeSetState,
1981
+ { setFileUrl, setFileReference, setFileReferences, setFileUrls, setFileCount }
1982
+ );
1983
+ } catch (err2) {
1984
+ logger.error("useFileDisplay", "Error fetching files:", err2);
1985
+ const error2 = err2 instanceof Error ? err2 : new Error("Unknown error occurred");
1986
+ safeSetState(setError, error2);
1987
+ safeSetState(setFileUrl, null);
1988
+ safeSetState(setFileReference, null);
1989
+ safeSetState(setFileReferences, []);
1990
+ safeSetState(setFileUrls, /* @__PURE__ */ new Map());
1991
+ safeSetState(setFileCount, 0);
1992
+ } finally {
1993
+ safeSetState(setIsLoading, false);
1994
+ }
1995
+ }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
1996
+ useEffect(() => {
1997
+ isMountedRef.current = true;
1998
+ if (table_name && record_id && supabase) {
1999
+ fetchFiles().catch((err2) => {
2000
+ if (isMountedRef.current) {
2001
+ safeSetState(setError, err2 instanceof Error ? err2 : new Error("Unknown error"));
2002
+ }
2003
+ });
2004
+ } else {
2005
+ safeSetState(setFileUrl, null);
2006
+ safeSetState(setFileReference, null);
2007
+ safeSetState(setFileReferences, []);
2008
+ safeSetState(setFileUrls, /* @__PURE__ */ new Map());
2009
+ safeSetState(setFileCount, 0);
2010
+ safeSetState(setIsLoading, false);
2011
+ safeSetState(setError, null);
2012
+ }
2013
+ return () => {
2014
+ isMountedRef.current = false;
2015
+ };
2016
+ }, [table_name, record_id, organisation_id, supabase]);
2017
+ const refetch = useCallback(async () => {
2018
+ if (!table_name || !record_id || !supabase) return;
2019
+ if (enableCache) {
2020
+ authenticatedFileCache.delete(getFileDisplayCacheKey(table_name, record_id, organisation_id, category));
2021
+ }
2022
+ await fetchFiles();
2023
+ }, [fetchFiles, table_name, record_id, organisation_id, category, supabase, enableCache]);
2024
+ return {
2025
+ fileUrl,
2026
+ fileReference,
2027
+ fileReferences,
2028
+ fileUrls,
2029
+ fileCount,
2030
+ isLoading,
2031
+ error,
2032
+ refetch
2033
+ };
2034
+ }
2035
+ function clearFileDisplayCache() {
2036
+ for (const [key] of authenticatedFileCache) {
2037
+ if (key.startsWith("file_")) {
2038
+ authenticatedFileCache.delete(key);
2039
+ }
2040
+ }
2041
+ }
2042
+ function getFileDisplayCacheStats() {
2043
+ const keys = Array.from(authenticatedFileCache.keys()).filter((key) => key.startsWith("file_"));
2044
+ return {
2045
+ size: keys.length,
2046
+ keys
2047
+ };
2048
+ }
2049
+ function invalidateFileDisplayCache(table_name, record_id, organisation_id, category) {
2050
+ const orgId = organisation_id ?? void 0;
2051
+ authenticatedFileCache.delete(getFileDisplayCacheKey(table_name, record_id, orgId, category));
2052
+ if (category) {
2053
+ authenticatedFileCache.delete(getFileDisplayCacheKey(table_name, record_id, orgId, void 0));
2054
+ }
2055
+ }
2056
+ var log4 = createLogger("useEventTheme");
2057
+ function useEventTheme(event) {
2058
+ const location = useLocation();
2059
+ const eventServiceContext = useContext(EventServiceContext);
2060
+ const eventService = eventServiceContext?.eventService ?? null;
2061
+ useEffect(() => {
2062
+ if (event !== void 0 || !eventService) return;
2063
+ const applyThemeFromService = () => {
2064
+ const isOnLoginRoute = location.pathname === "/login" || location.pathname.startsWith("/login/");
2065
+ if (isOnLoginRoute) {
2066
+ clearPalette();
2067
+ return;
2068
+ }
2069
+ const current = eventService.getSelectedEvent();
2070
+ if (!current?.event_colours) {
2071
+ clearPalette();
2072
+ return;
2073
+ }
2074
+ const normalized = parseAndNormalizeEventColours(current.event_colours);
2075
+ if (normalized) {
2076
+ try {
2077
+ applyPalette(normalized, current.event_name ?? "");
2078
+ } catch (err2) {
2079
+ log4.error("Failed to apply event palette (subscription):", err2);
2080
+ }
2081
+ } else {
2082
+ clearPalette();
2083
+ }
2084
+ };
2085
+ applyThemeFromService();
2086
+ const unsubscribe = eventService.subscribe(applyThemeFromService);
2087
+ return () => {
2088
+ unsubscribe();
2089
+ };
2090
+ }, [event, eventService, location.pathname]);
2091
+ const eventsContextSelectedEvent = eventService?.getSelectedEvent() ?? null;
2092
+ const selectedEvent = event !== void 0 ? event : eventsContextSelectedEvent;
2093
+ useEffect(() => {
2094
+ const isOnLoginRoute = location.pathname === "/login" || location.pathname.startsWith("/login");
2095
+ if (isOnLoginRoute) {
2096
+ clearPalette();
2097
+ return;
2098
+ }
2099
+ if (event === void 0) {
2100
+ return;
2101
+ }
2102
+ if (!selectedEvent) {
2103
+ clearPalette();
2104
+ return;
2105
+ }
2106
+ const eventColours = selectedEvent.event_colours;
2107
+ const normalized = parseAndNormalizeEventColours(eventColours);
2108
+ if (!normalized) {
2109
+ clearPalette();
2110
+ return;
2111
+ }
2112
+ try {
2113
+ applyPalette(normalized, selectedEvent.event_name ?? "");
2114
+ } catch (error) {
2115
+ log4.error("Failed to apply event palette:", error);
2116
+ }
2117
+ }, [selectedEvent, location.pathname, event]);
2118
+ }
2119
+ function usePreventTabReload(options = {}) {
2120
+ const { enabled = true, gracePeriodMs = 2e3 } = options;
2121
+ const isRestoringFromCacheRef = useRef(false);
2122
+ const gracePeriodTimeoutRef = useRef(null);
2123
+ useEffect(() => {
2124
+ if (!enabled || typeof window === "undefined") return;
2125
+ const handlePageShow = (event) => {
2126
+ if (event.persisted) {
2127
+ isRestoringFromCacheRef.current = true;
2128
+ if (gracePeriodTimeoutRef.current) {
2129
+ clearTimeout(gracePeriodTimeoutRef.current);
2130
+ }
2131
+ gracePeriodTimeoutRef.current = setTimeout(() => {
2132
+ isRestoringFromCacheRef.current = false;
2133
+ }, gracePeriodMs);
2134
+ }
2135
+ };
2136
+ const handleVisibilityChange = () => {
2137
+ if (!document.hidden) {
2138
+ isRestoringFromCacheRef.current = true;
2139
+ if (gracePeriodTimeoutRef.current) {
2140
+ clearTimeout(gracePeriodTimeoutRef.current);
2141
+ }
2142
+ gracePeriodTimeoutRef.current = setTimeout(() => {
2143
+ isRestoringFromCacheRef.current = false;
2144
+ }, gracePeriodMs);
2145
+ }
2146
+ };
2147
+ window.addEventListener("pageshow", handlePageShow);
2148
+ document.addEventListener("visibilitychange", handleVisibilityChange);
2149
+ return () => {
2150
+ window.removeEventListener("pageshow", handlePageShow);
2151
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
2152
+ if (gracePeriodTimeoutRef.current) {
2153
+ clearTimeout(gracePeriodTimeoutRef.current);
2154
+ }
2155
+ };
2156
+ }, [enabled, gracePeriodMs]);
2157
+ }
2158
+
2159
+ export { APP_PATH_MAPPING, DEFAULT_FILE_SIZE_LIMIT, FILE_SIZE_LIMITS, STORAGE_CONFIG, archiveFile, cleanupQueryCache, clearFileDisplayCache, clearPublicFileDisplayCache, createFileReferenceService, deleteFile, downloadFile, extractFileMetadata, generateFilePath, generateFileUrlsBatch, generateUniqueFileName, getBucketName, getFileDisplayCacheStats, getFileSizeLimit, getPublicFileDisplayCacheStats, getPublicUrl, getSignedUrl, invalidateFileDisplayCache, listFiles, queryCacheHelpers, uploadFile, uploadFileWithReference, useAddressAutocomplete, useDebounce, useEventTheme, useFileDisplay, usePreventTabReload, usePublicFileDisplay, useQueryCache, validateFileSize };