@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
@@ -8,17 +8,57 @@ import {
8
8
  FileReferenceService,
9
9
  FileUploadResult,
10
10
  FileCategory,
11
- FileMetadata
11
+ FileMetadata,
12
+ BulkUploadResult
12
13
  } from '../../types/file-reference';
14
+ import type { Database } from '../../types/database';
15
+ import { ok, err, type ApiResult, type ApiError } from '../../types/api-result';
13
16
  import { uploadFile, getPublicUrl, getSignedUrl, deleteFile, extractFileMetadata } from '../storage/helpers';
14
17
  import { setOrganisationContext } from '../context/organisationContext';
15
- import { invalidateFileDisplayCache } from '../../hooks/useFileDisplay';
18
+ import { invalidateFileDisplayCache } from '../../components/FileDisplay/useFileDisplay';
16
19
  import { createLogger } from '../core/logger';
17
20
  import { assertAppId } from '../../types/core';
18
- import { isSuperAdmin } from '../../rbac/api';
19
21
 
20
22
  const log = createLogger('FileReferenceService');
21
23
 
24
+ function toApiError(error: unknown): ApiError {
25
+ const message = error instanceof Error ? error.message : 'Unknown error';
26
+ const code = error instanceof Error && (error as Error & { code?: string }).code
27
+ ? (error as Error & { code: string }).code
28
+ : 'FILE_REFERENCE_ERROR';
29
+ return { code, message };
30
+ }
31
+
32
+ /** Row type from core_file_references table */
33
+ export type CoreFileReferencesRow = Database['public']['Tables']['core_file_references']['Row'];
34
+
35
+ /**
36
+ * Maps a core_file_references DB row to FileReference.
37
+ * Used by useFileDisplay and fetchFileDisplayData when reading from direct queries.
38
+ */
39
+ export function mapFileReferenceRowToFileReference(f: CoreFileReferencesRow): FileReference {
40
+ const fileName = f.file_path.split('/').pop() || 'unknown';
41
+ const fileType = fileName.split('.').pop() || 'unknown';
42
+ const metadata = f.file_metadata as { fileName?: string; fileType?: string; category?: FileCategory; [key: string]: unknown } | null;
43
+ return {
44
+ id: f.id,
45
+ table_name: f.table_name,
46
+ record_id: f.record_id,
47
+ file_path: f.file_path,
48
+ file_metadata: {
49
+ fileName: metadata?.fileName || fileName,
50
+ fileType: metadata?.fileType || fileType,
51
+ category: metadata?.category || FileCategory.GENERAL_DOCUMENTS,
52
+ ...(metadata || {})
53
+ } as FileReference['file_metadata'],
54
+ organisation_id: f.organisation_id,
55
+ app_id: f.app_id as FileReference['app_id'],
56
+ is_public: f.is_public ?? false,
57
+ created_at: f.created_at || new Date().toISOString(),
58
+ updated_at: f.updated_at || new Date().toISOString()
59
+ };
60
+ }
61
+
22
62
  export class FileReferenceServiceImpl implements FileReferenceService {
23
63
  constructor(private supabase: SupabaseClient) {}
24
64
 
@@ -32,17 +72,163 @@ export class FileReferenceServiceImpl implements FileReferenceService {
32
72
  .select('name')
33
73
  .eq('id', appId)
34
74
  .single();
35
-
75
+
36
76
  if (error || !data) {
37
77
  return 'unknown';
38
78
  }
39
-
79
+
40
80
  return data.name || 'unknown';
41
81
  } catch {
42
82
  return 'unknown';
43
83
  }
44
84
  }
45
85
 
86
+ private validateCreateOptions(options: FileUploadOptions): ApiResult<{ isUserScoped: boolean }> {
87
+ const isUserScoped = !options.organisation_id && !!options.userId;
88
+ if (!isUserScoped && !options.organisation_id) {
89
+ return err({ code: 'VALIDATION_ERROR', message: 'organisation_id is required for file upload, or userId must be provided for user-scoped files' });
90
+ }
91
+ if (!options.table_name) {
92
+ return err({ code: 'VALIDATION_ERROR', message: 'table_name is required for file upload' });
93
+ }
94
+ if (!options.record_id) {
95
+ return err({ code: 'VALIDATION_ERROR', message: 'record_id is required for file upload' });
96
+ }
97
+ if (!options.folder) {
98
+ return err({ code: 'VALIDATION_ERROR', message: 'folder is required for file upload. The folder prop determines the storage path.' });
99
+ }
100
+ return ok({ isUserScoped });
101
+ }
102
+
103
+ private async resolveAuthenticatedUserIdForUserScoped(): Promise<ApiResult<string>> {
104
+ const { data: { user: authUser }, error: authError } = await this.supabase.auth.getUser();
105
+ if (authError || !authUser) {
106
+ return err({ code: 'UNAUTHORIZED', message: 'User must be authenticated to upload user-scoped files' });
107
+ }
108
+ log.debug('Using authenticated user ID for user-scoped file upload', { userId: authUser.id });
109
+ return ok(authUser.id);
110
+ }
111
+
112
+ private async checkSuperAdminUser(userId: string | undefined): Promise<boolean> {
113
+ if (!userId) return false;
114
+ try {
115
+ const { isSuperAdmin } = await import('../../rbac/api');
116
+ const superResult = await isSuperAdmin(userId);
117
+ if (superResult.ok && superResult.data) {
118
+ log.debug('Super admin detected - bypassing permission checks', { userId });
119
+ return true;
120
+ }
121
+ return false;
122
+ } catch (superAdminCheckError) {
123
+ log.warn('Failed to check super-admin status, proceeding with normal permission checks', superAdminCheckError);
124
+ return false;
125
+ }
126
+ }
127
+
128
+ private async uploadFileAndExtractMetadata(
129
+ options: FileUploadOptions,
130
+ file: File,
131
+ authenticatedUserId: string | undefined,
132
+ isUserScoped: boolean
133
+ ): Promise<ApiResult<{ path: string; metadata: Record<string, unknown> }>> {
134
+ const userId = authenticatedUserId || (isUserScoped ? undefined : options.userId);
135
+ const uploadResult = await uploadFile(this.supabase, file, {
136
+ appName: 'file-reference',
137
+ orgId: options.organisation_id || undefined,
138
+ userId,
139
+ isPublic: options.is_public || false,
140
+ customPath: options.folder
141
+ });
142
+ if (!uploadResult.ok) return err(uploadResult.error);
143
+ const metadataResult = await extractFileMetadata(file, {
144
+ appName: 'file-reference',
145
+ orgId: options.organisation_id || undefined,
146
+ userId,
147
+ isPublic: options.is_public || false
148
+ }, 'system');
149
+ if (!metadataResult.ok) return err(metadataResult.error);
150
+ return ok({ path: uploadResult.data.path, metadata: metadataResult.data as unknown as Record<string, unknown> });
151
+ }
152
+
153
+ private async setOrgContextIfNeeded(isUserScoped: boolean, organisation_id: string | undefined): Promise<void> {
154
+ if (isUserScoped || !organisation_id) return;
155
+ const ctxResult = await setOrganisationContext(this.supabase, organisation_id);
156
+ if (!ctxResult.ok) {
157
+ log.warn('setOrganisationContext failed (non-fatal):', ctxResult.error.message);
158
+ }
159
+ }
160
+
161
+ private async resolveRpcUserId(
162
+ authenticatedUserId: string | undefined,
163
+ options: FileUploadOptions
164
+ ): Promise<string | null> {
165
+ if (authenticatedUserId) return authenticatedUserId;
166
+ if (options.userId) return options.userId;
167
+ const { data: { user: authUser } } = await this.supabase.auth.getUser();
168
+ return authUser?.id ?? null;
169
+ }
170
+
171
+ private async buildPermissionDeniedMessage(
172
+ options: FileUploadOptions,
173
+ isSuperAdminUser: boolean
174
+ ): Promise<string> {
175
+ const appName = await this.getAppName(options.app_id).catch(() => 'unknown');
176
+ const pageContextDisplay = options.pageContext || 'undefined';
177
+ return isSuperAdminUser
178
+ ? `File upload failed for super-admin user. Page context: '${pageContextDisplay}', App: '${appName}'.`
179
+ : `File upload denied: insufficient permissions. You need 'create:page.${pageContextDisplay}' or 'update:page.${pageContextDisplay}' for the '${pageContextDisplay}' page.`;
180
+ }
181
+
182
+ private async rollbackUploadAndErr(
183
+ filePath: string,
184
+ isPublic: boolean,
185
+ code: string,
186
+ message: string
187
+ ): Promise<ApiResult<FileReference>> {
188
+ await deleteFile(this.supabase, filePath, isPublic);
189
+ return err({ code, message });
190
+ }
191
+
192
+ private async fetchCreatedFileReference(createdId: string): Promise<ApiResult<FileReference>> {
193
+ const { data: fileRef, error: fetchError } = await this.supabase
194
+ .from('core_file_references')
195
+ .select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
196
+ .eq('id', createdId)
197
+ .single();
198
+ if (fetchError || !fileRef) {
199
+ return err({ code: 'FETCH_FAILED', message: fetchError?.message ?? 'Failed to fetch created file reference' });
200
+ }
201
+ return ok(fileRef as FileReference);
202
+ }
203
+
204
+ private buildCreateRpcPayload(
205
+ options: FileUploadOptions,
206
+ filePath: string,
207
+ file: File,
208
+ metadata: Record<string, unknown>,
209
+ rpcUserId: string | null
210
+ ): Record<string, unknown> {
211
+ return {
212
+ p_table_name: options.table_name,
213
+ p_record_id: options.record_id,
214
+ p_file_path: filePath,
215
+ p_organisation_id: options.organisation_id ?? null,
216
+ p_app_id: options.app_id,
217
+ p_page_context: options.pageContext,
218
+ p_event_id: options.event_id || null,
219
+ p_file_metadata: {
220
+ fileName: file.name,
221
+ fileType: file.type,
222
+ fileSize: file.size,
223
+ category: options.category,
224
+ ...metadata,
225
+ ...options.custom_metadata
226
+ },
227
+ p_is_public: options.is_public || false,
228
+ p_user_id: rpcUserId
229
+ };
230
+ }
231
+
46
232
  /**
47
233
  * Creates a file reference by uploading a file to storage and linking it in the database.
48
234
  *
@@ -57,175 +243,50 @@ export class FileReferenceServiceImpl implements FileReferenceService {
57
243
  *
58
244
  * This ensures atomicity: either both storage and DB succeed, or both are cleaned up.
59
245
  */
60
- async createFileReference(options: FileUploadOptions, file: File): Promise<FileReference> {
246
+ async createFileReference(options: FileUploadOptions, file: File): Promise<ApiResult<FileReference>> {
61
247
  try {
248
+ const validation = this.validateCreateOptions(options);
249
+ if (!validation.ok) return validation;
250
+ const { isUserScoped } = validation.data;
62
251
 
63
- // organisation_id is optional for user-scoped files (e.g., profile photos)
64
- const isUserScoped = !options.organisation_id && options.userId;
65
- if (!isUserScoped && !options.organisation_id) {
66
- throw new Error('organisation_id is required for file upload, or userId must be provided for user-scoped files');
67
- }
68
- if (!options.table_name) {
69
- throw new Error('table_name is required for file upload');
70
- }
71
- if (!options.record_id) {
72
- throw new Error('record_id is required for file upload');
73
- }
74
- if (!options.folder) {
75
- throw new Error('folder is required for file upload. The folder prop determines the storage path.');
76
- }
77
-
78
- // For user-scoped files, we MUST use auth.uid() for the path to match RLS policies
79
- // Get the authenticated user ID from the Supabase session
80
- let authenticatedUserId: string | undefined = undefined;
252
+ let authenticatedUserId: string | undefined;
81
253
  if (isUserScoped) {
82
- const { data: { user: authUser }, error: authError } = await this.supabase.auth.getUser();
83
- if (authError || !authUser) {
84
- throw new Error('User must be authenticated to upload user-scoped files');
85
- }
86
- authenticatedUserId = authUser.id;
87
- log.debug('Using authenticated user ID for user-scoped file upload', { userId: authenticatedUserId });
254
+ const authResult = await this.resolveAuthenticatedUserIdForUserScoped();
255
+ if (!authResult.ok) return authResult;
256
+ authenticatedUserId = authResult.data;
88
257
  }
89
258
 
90
- // CRITICAL: Check super admin status in application layer (consistent with pace-core pattern)
91
- // Super admins bypass all permission checks - this is handled in the application layer,
92
- // not in RLS policies. The RPC function still validates input and handles the insert,
93
- // but permission checks are bypassed for super admins.
94
- let isSuperAdminUser = false;
95
- const userIdForCheck = authenticatedUserId || options.userId;
96
- if (userIdForCheck) {
97
- try {
98
- // Import isSuperAdmin from rbac/api - this is the standard way to check super admin
99
- const { isSuperAdmin } = await import('../../rbac/api');
100
- isSuperAdminUser = await isSuperAdmin(userIdForCheck);
101
- if (isSuperAdminUser) {
102
- log.debug('Super admin detected - bypassing permission checks', { userId: userIdForCheck });
103
- }
104
- } catch (superAdminCheckError) {
105
- // If super admin check fails, continue with normal permission flow
106
- log.warn('Failed to check super-admin status, proceeding with normal permission checks', superAdminCheckError);
107
- }
108
- }
259
+ const isSuperAdminUser = await this.checkSuperAdminUser(authenticatedUserId || options.userId);
109
260
 
110
- // Step 1: Upload file to storage bucket first
111
- // This generates a unique path: {orgId}/{folder}/{timestamp-uuid-filename} or users/{auth.uid()}/{folder}/{timestamp-uuid-filename}
112
- // Bucket is automatically selected based on is_public flag
113
- const uploadResult = await uploadFile(this.supabase, file, {
114
- appName: 'file-reference',
115
- orgId: options.organisation_id || undefined,
116
- userId: authenticatedUserId || (isUserScoped ? undefined : options.userId), // Use auth.uid() for user-scoped files
117
- isPublic: options.is_public || false,
118
- customPath: options.folder // Use folder prop as the custom path segment
119
- });
120
- if (!uploadResult.success) {
121
- throw new Error(`Failed to upload file: ${uploadResult.error}`);
122
- }
261
+ const uploadResult = await this.uploadFileAndExtractMetadata(options, file, authenticatedUserId, isUserScoped);
262
+ if (!uploadResult.ok) return uploadResult;
263
+ const { path: filePath, metadata } = uploadResult.data;
123
264
 
124
- if (!uploadResult.path) {
125
- throw new Error('File upload did not return a path');
126
- }
265
+ await this.setOrgContextIfNeeded(isUserScoped, options.organisation_id ?? undefined);
127
266
 
128
- const filePath = uploadResult.path;
267
+ const rpcUserId = await this.resolveRpcUserId(authenticatedUserId, options);
129
268
 
130
- // Step 2: Extract file metadata (dimensions, hash, etc.)
131
- const metadata = await extractFileMetadata(file, {
132
- appName: 'file-reference',
133
- orgId: options.organisation_id || undefined,
134
- userId: authenticatedUserId || (isUserScoped ? undefined : options.userId), // Use auth.uid() for user-scoped files
135
- isPublic: options.is_public || false
136
- }, 'system');
137
-
138
- // Step 3: Set organisation context in database session before creating file reference
139
- // Skip for user-scoped files (no org context needed)
140
- if (!isUserScoped && options.organisation_id) {
141
- await setOrganisationContext(this.supabase, options.organisation_id);
142
- }
269
+ const payload = this.buildCreateRpcPayload(options, filePath, file, metadata, rpcUserId);
270
+ const { data, error } = await this.supabase.rpc('data_file_reference_create', payload as Parameters<SupabaseClient['rpc']>[1] & object);
143
271
 
144
- // Step 4: Create file reference in database using RPC function
145
- // This links the storage path to the record in core_file_references table
146
- // CRITICAL: Always pass the authenticated user ID to SECURITY DEFINER functions
147
- // In SECURITY DEFINER functions, auth.uid() returns the function owner's ID,
148
- // not the caller's ID, so we must explicitly pass the user ID
149
- let rpcUserId: string | null = null;
150
- if (authenticatedUserId) {
151
- rpcUserId = authenticatedUserId;
152
- } else if (options.userId) {
153
- rpcUserId = options.userId;
154
- } else {
155
- // Get authenticated user ID from session as fallback
156
- const { data: { user: authUser } } = await this.supabase.auth.getUser();
157
- if (authUser) {
158
- rpcUserId = authUser.id;
159
- }
160
- }
161
-
162
- const { data, error } = await this.supabase
163
- .rpc('data_file_reference_create', {
164
- p_table_name: options.table_name,
165
- p_record_id: options.record_id,
166
- p_file_path: filePath, // Storage path from step 1
167
- p_organisation_id: options.organisation_id ?? null,
168
- p_app_id: options.app_id,
169
- p_page_context: options.pageContext,
170
- p_event_id: options.event_id || null, // Pass event_id for event-based apps
171
- p_file_metadata: {
172
- fileName: file.name,
173
- fileType: file.type,
174
- fileSize: file.size,
175
- category: options.category,
176
- ...metadata,
177
- ...options.custom_metadata
178
- },
179
- p_is_public: options.is_public || false,
180
- p_user_id: rpcUserId // Always pass authenticated user ID for SECURITY DEFINER functions
181
- });
182
-
183
- // Step 5: Rollback - if database insert fails, clean up uploaded file
184
272
  if (error) {
185
- await deleteFile(this.supabase, filePath, options.is_public || false);
186
- throw new Error(`Failed to create file reference: ${error.message}`);
273
+ return await this.rollbackUploadAndErr(filePath, options.is_public ?? false, 'CREATE_FAILED', error.message);
187
274
  }
188
-
189
- // Check if RPC returned null (permission denied or other failure)
190
275
  if (!data || data === null) {
191
- // Clean up the uploaded file since DB insert failed
192
- await deleteFile(this.supabase, filePath, options.is_public || false);
193
-
194
- // Provide more helpful error message
195
- const appName = await this.getAppName(options.app_id).catch(() => 'unknown');
196
- const pageContextDisplay = options.pageContext || 'undefined';
197
-
198
- if (isSuperAdminUser) {
199
- // Super-admin should have been allowed - this suggests a database or RPC function issue
200
- // Since we already checked super admin in the application layer, this is unexpected
201
- throw new Error(
202
- `File upload failed for super-admin user. This may indicate a database issue. ` +
203
- `Page context: '${pageContextDisplay}', App: '${appName}'. ` +
204
- `Please check that the page '${pageContextDisplay}' exists in rbac_app_pages table for app '${appName}'.`
205
- );
206
- } else {
207
- throw new Error(
208
- `File upload denied: insufficient permissions. You need 'create:page.${pageContextDisplay}' or 'update:page.${pageContextDisplay}' permission for the '${pageContextDisplay}' page. ` +
209
- `Make sure the page exists in rbac_app_pages table for app '${appName}'.`
210
- );
211
- }
276
+ const message = await this.buildPermissionDeniedMessage(options, isSuperAdminUser);
277
+ return await this.rollbackUploadAndErr(filePath, options.is_public ?? false, 'PERMISSION_DENIED', message);
212
278
  }
213
279
 
214
- // Get the created file reference
215
- const { data: fileRef, error: fetchError } = await this.supabase
216
- .from('core_file_references')
217
- .select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
218
- .eq('id', data)
219
- .single();
220
-
221
- if (fetchError || !fileRef) {
222
- // Clean up uploaded file if we can't fetch the reference
223
- await deleteFile(this.supabase, filePath, options.is_public || false);
224
- throw new Error(`Failed to fetch created file reference: ${fetchError?.message}`);
280
+ const fetchResult = await this.fetchCreatedFileReference(data as string);
281
+ if (!fetchResult.ok) {
282
+ return await this.rollbackUploadAndErr(
283
+ filePath,
284
+ options.is_public ?? false,
285
+ 'FETCH_FAILED',
286
+ fetchResult.error.message
287
+ );
225
288
  }
226
289
 
227
- // Invalidate cache for this file display entry so newly uploaded files appear immediately
228
- // For user-scoped files, pass null for organisation_id
229
290
  invalidateFileDisplayCache(
230
291
  options.table_name,
231
292
  options.record_id,
@@ -233,53 +294,49 @@ export class FileReferenceServiceImpl implements FileReferenceService {
233
294
  options.category
234
295
  );
235
296
 
236
- return fileRef as FileReference;
297
+ return ok(fetchResult.data);
237
298
  } catch (error) {
238
299
  log.error('Error creating file reference:', error);
239
- throw error;
300
+ return err(toApiError(error));
240
301
  }
241
302
  }
242
303
 
243
- async getFileReference(table_name: string, record_id: string, organisation_id?: string): Promise<FileReference | null> {
304
+ async getFileReference(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<FileReference | null>> {
244
305
  try {
245
306
  let query = this.supabase
246
307
  .from('core_file_references')
247
308
  .select('id, table_name, record_id, file_path, file_metadata, organisation_id, app_id, is_public, created_at, updated_at')
248
309
  .eq('table_name', table_name)
249
310
  .eq('record_id', record_id);
250
-
251
- // Handle NULL organisation_id for user-owned files
252
311
  if (organisation_id === null || organisation_id === undefined) {
253
312
  query = query.is('organisation_id', null);
254
313
  } else {
255
314
  query = query.eq('organisation_id', organisation_id);
256
315
  }
257
-
258
316
  const { data, error } = await query.single();
259
-
260
317
  if (error) {
261
318
  if (error.code === 'PGRST116') {
262
- return null; // No rows found
319
+ return ok(null);
263
320
  }
264
- throw new Error(`Failed to get file reference: ${error.message}`);
321
+ return err({ code: 'FETCH_FAILED', message: error.message });
265
322
  }
266
-
267
- return data as FileReference;
323
+ return ok(data as FileReference);
268
324
  } catch (error) {
269
325
  log.error('Error getting file reference:', error);
270
- throw error;
326
+ return err(toApiError(error));
271
327
  }
272
328
  }
273
329
 
274
- async getFileUrl(table_name: string, record_id: string, organisation_id?: string): Promise<string | null> {
330
+ async getFileUrl(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<string | null>> {
275
331
  try {
276
- // Get file reference to check if it's public
277
- const fileRef = await this.getFileReference(table_name, record_id, organisation_id);
332
+ const refResult = await this.getFileReference(table_name, record_id, organisation_id);
333
+ if (!refResult.ok) {
334
+ return refResult;
335
+ }
336
+ const fileRef = refResult.data;
278
337
  if (!fileRef) {
279
- return null;
338
+ return ok(null);
280
339
  }
281
-
282
- // For public files, RPC returns file path - generate public URL client-side
283
340
  if (fileRef.is_public) {
284
341
  const { data: pathData } = await this.supabase
285
342
  .rpc('data_file_reference_url_get', {
@@ -287,26 +344,20 @@ export class FileReferenceServiceImpl implements FileReferenceService {
287
344
  p_record_id: record_id,
288
345
  p_organisation_id: organisation_id
289
346
  });
290
-
291
347
  if (!pathData) {
292
- return null;
348
+ return ok(null);
293
349
  }
294
-
295
- // Generate public URL using bucket-aware helper
296
- return getPublicUrl(this.supabase, pathData, true);
297
- } else {
298
- // For private files, use signed URL
299
- return await this.getSignedUrl(table_name, record_id, organisation_id);
350
+ return ok(getPublicUrl(this.supabase, pathData, true));
300
351
  }
352
+ return this.getSignedUrl(table_name, record_id, organisation_id);
301
353
  } catch (error) {
302
354
  log.error('Error getting file URL:', error);
303
- throw error;
355
+ return err(toApiError(error));
304
356
  }
305
357
  }
306
358
 
307
- async getSignedUrl(table_name: string, record_id: string, organisation_id?: string, expires_in: number = 3600): Promise<string | null> {
359
+ async getSignedUrl(table_name: string, record_id: string, organisation_id?: string, expires_in: number = 3600): Promise<ApiResult<string | null>> {
308
360
  try {
309
- // Get file path from RPC function
310
361
  const { data: filePath, error } = await this.supabase
311
362
  .rpc('data_file_reference_signed_url_get', {
312
363
  p_table_name: table_name,
@@ -314,31 +365,29 @@ export class FileReferenceServiceImpl implements FileReferenceService {
314
365
  p_organisation_id: organisation_id ?? null,
315
366
  p_expires_in: expires_in
316
367
  });
317
-
318
368
  if (error) {
319
- throw new Error(`Failed to get signed URL: ${error.message}`);
369
+ return err({ code: 'SIGNED_URL_FAILED', message: error.message });
320
370
  }
321
-
322
371
  if (!filePath) {
323
- return null;
372
+ return ok(null);
324
373
  }
325
-
326
- // Generate signed URL client-side using bucket-aware helper (files bucket for private files)
327
374
  const signedUrlResult = await getSignedUrl(this.supabase, filePath, {
328
375
  appName: 'file-reference',
329
376
  orgId: organisation_id,
330
377
  userId: organisation_id ? undefined : record_id,
331
378
  expiresIn: expires_in
332
379
  });
333
-
334
- return signedUrlResult?.url || null;
380
+ if (!signedUrlResult.ok) {
381
+ return err(signedUrlResult.error);
382
+ }
383
+ return ok(signedUrlResult.data.url ?? null);
335
384
  } catch (error) {
336
385
  log.error('Error getting signed URL:', error);
337
- throw error;
386
+ return err(toApiError(error));
338
387
  }
339
388
  }
340
389
 
341
- async updateFileReference(id: string, updates: Partial<FileReference>): Promise<FileReference> {
390
+ async updateFileReference(id: string, updates: Partial<FileReference>): Promise<ApiResult<FileReference>> {
342
391
  try {
343
392
  const { data, error } = await this.supabase
344
393
  .from('core_file_references')
@@ -346,23 +395,21 @@ export class FileReferenceServiceImpl implements FileReferenceService {
346
395
  .eq('id', id)
347
396
  .select()
348
397
  .single();
349
-
350
398
  if (error) {
351
- throw new Error(`Failed to update file reference: ${error.message}`);
399
+ return err({ code: 'UPDATE_FAILED', message: error.message });
352
400
  }
353
-
354
- return data as FileReference;
401
+ return ok(data as FileReference);
355
402
  } catch (error) {
356
403
  log.error('Error updating file reference:', error);
357
- throw error;
404
+ return err(toApiError(error));
358
405
  }
359
406
  }
360
407
 
361
- async deleteFileReference(table_name: string, record_id: string, organisation_id?: string, delete_file: boolean = false): Promise<boolean> {
408
+ async deleteFileReference(table_name: string, record_id: string, organisation_id?: string, delete_file: boolean = false): Promise<ApiResult<boolean>> {
362
409
  try {
363
- // Get file reference first to determine bucket
364
- const fileRef = await this.getFileReference(table_name, record_id, organisation_id);
365
-
410
+ const refResult = await this.getFileReference(table_name, record_id, organisation_id);
411
+ const fileRef = refResult.ok ? refResult.data : null;
412
+
366
413
  const { error } = await this.supabase
367
414
  .rpc('data_file_reference_delete', {
368
415
  p_table_name: table_name,
@@ -370,24 +417,20 @@ export class FileReferenceServiceImpl implements FileReferenceService {
370
417
  p_organisation_id: organisation_id ?? null,
371
418
  p_delete_file: delete_file
372
419
  });
373
-
374
420
  if (error) {
375
- throw new Error(`Failed to delete file reference: ${error.message}`);
421
+ return err({ code: 'DELETE_FAILED', message: error.message });
376
422
  }
377
-
378
- // If delete_file is true and we have the file reference, delete from storage
379
423
  if (delete_file && fileRef) {
380
424
  await deleteFile(this.supabase, fileRef.file_path, fileRef.is_public || false);
381
425
  }
382
-
383
- return true;
426
+ return ok(true);
384
427
  } catch (error) {
385
428
  log.error('Error deleting file reference:', error);
386
- throw error;
429
+ return err(toApiError(error));
387
430
  }
388
431
  }
389
432
 
390
- async listFileReferences(table_name: string, record_id: string, organisation_id?: string): Promise<FileReference[]> {
433
+ async listFileReferences(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<FileReference[]>> {
391
434
  try {
392
435
  const { data, error } = await this.supabase
393
436
  .rpc('data_file_reference_list', {
@@ -395,20 +438,12 @@ export class FileReferenceServiceImpl implements FileReferenceService {
395
438
  p_record_id: record_id,
396
439
  p_organisation_id: organisation_id ?? null
397
440
  });
398
-
399
441
  if (error) {
400
- throw new Error(`Failed to list file references: ${error.message}`);
442
+ return err({ code: 'LIST_FAILED', message: error.message });
401
443
  }
402
-
403
- // RPC returns: id, file_path, file_metadata, is_public, created_at
404
- // We can construct FileReference objects directly from RPC response + function parameters
405
- // This avoids a second query and reduces network requests
406
444
  if (!data || data.length === 0) {
407
- return [];
445
+ return ok([]);
408
446
  }
409
-
410
- // Construct FileReference objects from RPC response
411
- // This avoids RLS issues with direct queries - the RPC already validated permissions
412
447
  interface RpcFileItem {
413
448
  id: string;
414
449
  file_path: string;
@@ -419,38 +454,29 @@ export class FileReferenceServiceImpl implements FileReferenceService {
419
454
  const fileReferences: FileReference[] = data
420
455
  .filter((item: RpcFileItem) => item.id && item.file_path && item.file_metadata)
421
456
  .map((item: RpcFileItem) => {
422
- // Extract file name and type from file_path
423
457
  const fileName = item.file_path.split('/').pop() || 'unknown';
424
458
  const fileType = fileName.split('.').pop() || 'unknown';
425
-
426
- // Construct complete FileReference from RPC response + function parameters
427
- const fileRef: FileReference = {
459
+ return {
428
460
  id: item.id,
429
461
  table_name: table_name,
430
462
  record_id: record_id,
431
463
  file_path: item.file_path,
432
- file_metadata: {
433
- fileName,
434
- fileType,
435
- ...(item.file_metadata || {}),
436
- } as FileMetadata,
464
+ file_metadata: { fileName, fileType, ...(item.file_metadata || {}) } as FileMetadata,
437
465
  organisation_id: organisation_id ?? null,
438
- app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''), // May not be in metadata, use empty string
466
+ app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''),
439
467
  is_public: item.is_public ?? false,
440
468
  created_at: item.created_at || new Date().toISOString(),
441
- updated_at: item.created_at || new Date().toISOString() // RPC doesn't return updated_at, use created_at
442
- };
443
- return fileRef;
469
+ updated_at: item.created_at || new Date().toISOString()
470
+ } as FileReference;
444
471
  });
445
-
446
- return fileReferences;
472
+ return ok(fileReferences);
447
473
  } catch (error) {
448
474
  log.error('Error listing file references:', error);
449
- throw error;
475
+ return err(toApiError(error));
450
476
  }
451
477
  }
452
478
 
453
- async getFileCount(table_name: string, record_id: string, organisation_id?: string): Promise<number> {
479
+ async getFileCount(table_name: string, record_id: string, organisation_id?: string): Promise<ApiResult<number>> {
454
480
  try {
455
481
  const { data, error } = await this.supabase
456
482
  .rpc('data_file_reference_count_get', {
@@ -458,51 +484,43 @@ export class FileReferenceServiceImpl implements FileReferenceService {
458
484
  p_record_id: record_id,
459
485
  p_organisation_id: organisation_id ?? null
460
486
  });
461
-
462
487
  if (error) {
463
- throw new Error(`Failed to get file count: ${error.message}`);
488
+ return err({ code: 'COUNT_FAILED', message: error.message });
464
489
  }
465
-
466
- return data || 0;
490
+ return ok(data ?? 0);
467
491
  } catch (error) {
468
492
  log.error('Error getting file count:', error);
469
- throw error;
493
+ return err(toApiError(error));
470
494
  }
471
495
  }
472
496
 
473
- async getFileReferenceById(id: string, organisation_id?: string): Promise<FileReference | null> {
497
+ async getFileReferenceById(id: string, organisation_id?: string): Promise<ApiResult<FileReference | null>> {
474
498
  try {
475
499
  const { data, error } = await this.supabase
476
500
  .rpc('data_file_reference_get', {
477
501
  p_file_reference_id: id,
478
502
  p_organisation_id: organisation_id ?? null
479
503
  });
480
-
481
504
  if (error) {
482
- throw new Error(`Failed to get file reference by ID: ${error.message}`);
505
+ return err({ code: 'FETCH_FAILED', message: error.message });
483
506
  }
484
-
485
507
  if (!data || data.length === 0) {
486
- return null;
508
+ return ok(null);
487
509
  }
488
-
489
- return data[0] as FileReference;
510
+ return ok(data[0] as FileReference);
490
511
  } catch (error) {
491
512
  log.error('Error getting file reference by ID:', error);
492
- throw error;
513
+ return err(toApiError(error));
493
514
  }
494
515
  }
495
516
 
496
517
  async getFilesByCategory(
497
- table_name: string,
498
- record_id: string,
499
- category: FileCategory,
518
+ table_name: string,
519
+ record_id: string,
520
+ category: FileCategory,
500
521
  organisation_id?: string
501
- ): Promise<FileReference[]> {
522
+ ): Promise<ApiResult<FileReference[]>> {
502
523
  try {
503
- // CRITICAL: Use RPC function to get files by category - this correctly filters on file_metadata->>'category'
504
- // NOTE: We MUST use RPC function. Direct queries with .eq('category', ...) will FAIL with HTTP 406
505
- // because there is NO 'category' column. The category is stored in the JSONB file_metadata field.
506
524
  const { data, error } = await this.supabase
507
525
  .rpc('data_file_reference_by_category_list', {
508
526
  p_table_name: table_name,
@@ -510,32 +528,13 @@ export class FileReferenceServiceImpl implements FileReferenceService {
510
528
  p_category: category,
511
529
  p_organisation_id: organisation_id ?? null
512
530
  });
513
-
514
531
  if (error) {
515
- // Provide clear error message about category filtering
516
- log.error('RPC ERROR getting files by category:', {
517
- error,
518
- errorCode: error.code,
519
- errorMessage: error.message,
520
- errorDetails: error.details,
521
- table_name,
522
- record_id,
523
- category,
524
- organisation_id,
525
- message: 'CRITICAL: Category filtering MUST use RPC function data_file_reference_by_category_list. Direct queries with .eq(\'category\', ...) will FAIL with HTTP 406 because category is stored in file_metadata JSONB field, not a direct column.'
526
- });
527
- throw new Error(`Failed to get files by category: ${error.message}. Category filtering uses file_metadata JSONB field, not a direct column. RPC function required.`);
532
+ log.error('RPC ERROR getting files by category:', { error, table_name, record_id, category });
533
+ return err({ code: 'LIST_FAILED', message: error.message });
528
534
  }
529
-
530
- // RPC returns partial data with: id, file_path, file_metadata, is_public, created_at
531
- // We have table_name, record_id, organisation_id from function parameters
532
- // We can construct FileReference objects directly without another query (avoiding RLS issues)
533
535
  if (!data || data.length === 0) {
534
- return [];
536
+ return ok([]);
535
537
  }
536
-
537
- // Construct FileReference objects from RPC response
538
- // This avoids RLS issues with direct queries - the RPC already validated permissions
539
538
  interface RpcFileItem {
540
539
  id: string;
541
540
  file_path: string;
@@ -545,64 +544,45 @@ export class FileReferenceServiceImpl implements FileReferenceService {
545
544
  }
546
545
  const fileReferences: FileReference[] = data
547
546
  .filter((item: RpcFileItem) => {
548
- // Verify category matches (defensive check)
549
547
  const fileCategory = item.file_metadata?.category;
550
- const matches = fileCategory === category;
551
- if (!matches) {
552
- log.warn('File category mismatch in RPC response:', {
553
- fileId: item.id,
554
- expectedCategory: category,
555
- actualCategory: fileCategory
556
- });
557
- }
558
- return matches && item.id && item.file_path && item.file_metadata;
548
+ return fileCategory === category && item.id && item.file_path && item.file_metadata;
559
549
  })
560
- .map((item: RpcFileItem) => {
561
- // Extract file name and type from file_path
562
- const fileName = item.file_path.split('/').pop() || 'unknown';
563
- const fileType = fileName.split('.').pop() || 'unknown';
564
-
565
- // Construct complete FileReference from RPC response + function parameters
566
- const fileRef: FileReference = {
567
- id: item.id,
568
- table_name: table_name,
569
- record_id: record_id,
570
- file_path: item.file_path,
571
- file_metadata: {
572
- fileName,
573
- fileType,
574
- category: (item.file_metadata?.category as FileCategory) || FileCategory.GENERAL_DOCUMENTS,
575
- ...(item.file_metadata || {}),
576
- } as FileMetadata,
577
- organisation_id: organisation_id ?? null,
578
- app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''), // May not be in metadata, use empty string
579
- is_public: item.is_public ?? false,
580
- created_at: item.created_at || new Date().toISOString(),
581
- updated_at: item.created_at || new Date().toISOString() // RPC doesn't return updated_at, use created_at
582
- };
583
- return fileRef;
584
- });
585
-
586
- return fileReferences;
550
+ .map((item: RpcFileItem) => ({
551
+ id: item.id,
552
+ table_name: table_name,
553
+ record_id: record_id,
554
+ file_path: item.file_path,
555
+ file_metadata: {
556
+ fileName: item.file_path.split('/').pop() || 'unknown',
557
+ fileType: (item.file_path.split('.').pop() || 'unknown'),
558
+ category: (item.file_metadata?.category as FileCategory) || FileCategory.GENERAL_DOCUMENTS,
559
+ ...(item.file_metadata || {}),
560
+ } as FileMetadata,
561
+ organisation_id: organisation_id ?? null,
562
+ app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''),
563
+ is_public: item.is_public ?? false,
564
+ created_at: item.created_at || new Date().toISOString(),
565
+ updated_at: item.created_at || new Date().toISOString(),
566
+ } as FileReference));
567
+ return ok(fileReferences);
587
568
  } catch (error) {
588
569
  log.error('Error getting files by category:', error);
589
- throw error;
570
+ return err(toApiError(error));
590
571
  }
591
572
  }
592
573
 
593
574
  async uploadMultipleFiles(
594
575
  options: FileUploadOptions,
595
576
  files: File[]
596
- ): Promise<import('../../types/file-reference').BulkUploadResult> {
577
+ ): Promise<ApiResult<BulkUploadResult>> {
597
578
  const success: FileReference[] = [];
598
579
  const failed: { file: File; error: string }[] = [];
599
580
  const results: Array<{ file: File; result: FileUploadResult | null; error?: string }> = [];
600
581
 
601
- // Upload files sequentially to avoid overwhelming the server
602
582
  for (const file of files) {
603
- try {
604
- const fileReference = await this.createFileReference(options, file);
605
-
583
+ const result = await this.createFileReference(options, file);
584
+ if (result.ok) {
585
+ const fileReference = result.data;
606
586
  success.push(fileReference);
607
587
  results.push({
608
588
  file,
@@ -613,20 +593,19 @@ export class FileReferenceServiceImpl implements FileReferenceService {
613
593
  : '',
614
594
  },
615
595
  });
616
- } catch (error) {
617
- const message = error instanceof Error ? error.message : 'Unknown error';
618
- failed.push({ file, error: message });
619
- results.push({ file, result: null, error: message });
596
+ } else {
597
+ failed.push({ file, error: result.error.message });
598
+ results.push({ file, result: null, error: result.error.message });
620
599
  }
621
600
  }
622
601
 
623
- return {
602
+ return ok({
624
603
  success,
625
604
  failed,
626
605
  total: results.length,
627
606
  successful: success.length,
628
607
  results,
629
- };
608
+ });
630
609
  }
631
610
  }
632
611
 
@@ -640,25 +619,28 @@ export async function uploadFileWithReference(
640
619
  supabase: SupabaseClient,
641
620
  options: FileUploadOptions,
642
621
  file: File
643
- ): Promise<FileUploadResult> {
644
-
622
+ ): Promise<ApiResult<FileUploadResult>> {
645
623
  const service = createFileReferenceService(supabase);
646
- const fileReference = await service.createFileReference(options, file);
647
-
648
- const fileUrl = options.is_public
649
- ? getPublicUrl(supabase, fileReference.file_path, true)
650
- : await getSignedUrl(supabase, fileReference.file_path, {
651
- appName: 'file-reference',
652
- orgId: options.organisation_id || undefined,
653
- userId: options.userId || undefined,
654
- expiresIn: 3600
655
- });
656
-
657
- const urlString = typeof fileUrl === 'string' ? fileUrl : fileUrl?.url || '';
658
-
659
- return {
624
+ const result = await service.createFileReference(options, file);
625
+ if (!result.ok) {
626
+ return result;
627
+ }
628
+ const fileReference = result.data;
629
+ let urlString: string;
630
+ if (options.is_public) {
631
+ urlString = getPublicUrl(supabase, fileReference.file_path, true);
632
+ } else {
633
+ const signedResult = await getSignedUrl(supabase, fileReference.file_path, {
634
+ appName: 'file-reference',
635
+ orgId: options.organisation_id || undefined,
636
+ userId: options.userId || undefined,
637
+ expiresIn: 3600
638
+ });
639
+ urlString = signedResult.ok ? signedResult.data.url ?? '' : '';
640
+ }
641
+ return ok({
660
642
  file_reference: fileReference,
661
643
  file_url: urlString,
662
644
  signed_url: options.is_public ? undefined : urlString || undefined
663
- };
645
+ });
664
646
  }