@jmruthers/pace-core 0.5.136 → 0.5.137

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 (289) hide show
  1. package/dist/{DataTable-CYOHOX3O.js → DataTable-6M4L6BI2.js} +10 -9
  2. package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
  3. package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
  4. package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
  5. package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
  6. package/dist/chunk-4C7EXCAR.js.map +1 -0
  7. package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
  8. package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
  9. package/dist/chunk-7QCC6MCP.js.map +1 -0
  10. package/dist/{chunk-FHWWBIHA.js → chunk-BCIBECNB.js} +5 -5
  11. package/dist/chunk-BJPBT3CU.js +21 -0
  12. package/dist/chunk-BJPBT3CU.js.map +1 -0
  13. package/dist/{chunk-L6PGMCMD.js → chunk-BLCXZEYF.js} +3 -3
  14. package/dist/{chunk-HJGGOMQ6.js → chunk-HAWZXGR2.js} +147 -103
  15. package/dist/chunk-HAWZXGR2.js.map +1 -0
  16. package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
  17. package/dist/chunk-JISYG63F.js +70 -0
  18. package/dist/chunk-JISYG63F.js.map +1 -0
  19. package/dist/{chunk-NOHEVYVX.js → chunk-KYRHUBIU.js} +417 -319
  20. package/dist/chunk-KYRHUBIU.js.map +1 -0
  21. package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
  22. package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
  23. package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
  24. package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
  25. package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
  26. package/dist/components.d.ts +1 -1
  27. package/dist/components.js +12 -11
  28. package/dist/components.js.map +1 -1
  29. package/dist/hooks.js +8 -9
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +15 -14
  33. package/dist/index.js.map +1 -1
  34. package/dist/providers.js +3 -4
  35. package/dist/rbac/index.js +8 -9
  36. package/dist/schema-DTDZQe2u.d.ts +28 -0
  37. package/dist/types.d.ts +152 -3
  38. package/dist/types.js +51 -16
  39. package/dist/types.js.map +1 -1
  40. package/dist/utils.d.ts +89 -4
  41. package/dist/utils.js +214 -96
  42. package/dist/utils.js.map +1 -1
  43. package/dist/validation.d.ts +1 -343
  44. package/dist/validation.js +3 -100
  45. package/docs/api/classes/ColumnFactory.md +1 -1
  46. package/docs/api/classes/ErrorBoundary.md +1 -1
  47. package/docs/api/classes/InvalidScopeError.md +1 -1
  48. package/docs/api/classes/MissingUserContextError.md +1 -1
  49. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  50. package/docs/api/classes/PermissionDeniedError.md +1 -1
  51. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  52. package/docs/api/classes/RBACAuditManager.md +1 -1
  53. package/docs/api/classes/RBACCache.md +1 -1
  54. package/docs/api/classes/RBACEngine.md +1 -1
  55. package/docs/api/classes/RBACError.md +1 -1
  56. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  57. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  58. package/docs/api/classes/StorageUtils.md +1 -1
  59. package/docs/api/enums/FileCategory.md +1 -1
  60. package/docs/api/interfaces/AggregateConfig.md +1 -1
  61. package/docs/api/interfaces/BadgeProps.md +27 -0
  62. package/docs/api/interfaces/ButtonProps.md +1 -1
  63. package/docs/api/interfaces/CardProps.md +1 -1
  64. package/docs/api/interfaces/ColorPalette.md +1 -1
  65. package/docs/api/interfaces/ColorShade.md +1 -1
  66. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  67. package/docs/api/interfaces/DataRecord.md +1 -1
  68. package/docs/api/interfaces/DataTableAction.md +1 -1
  69. package/docs/api/interfaces/DataTableColumn.md +1 -1
  70. package/docs/api/interfaces/DataTableProps.md +1 -1
  71. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  72. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  73. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  74. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  75. package/docs/api/interfaces/EventLogoProps.md +1 -1
  76. package/docs/api/interfaces/ExportColumn.md +1 -1
  77. package/docs/api/interfaces/ExportOptions.md +1 -1
  78. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  79. package/docs/api/interfaces/FileMetadata.md +1 -1
  80. package/docs/api/interfaces/FileReference.md +1 -1
  81. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  82. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  83. package/docs/api/interfaces/FileUploadProps.md +1 -1
  84. package/docs/api/interfaces/FooterProps.md +1 -1
  85. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  86. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  87. package/docs/api/interfaces/InputProps.md +1 -1
  88. package/docs/api/interfaces/LabelProps.md +1 -1
  89. package/docs/api/interfaces/LoginFormProps.md +1 -1
  90. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  91. package/docs/api/interfaces/NavigationContextType.md +1 -1
  92. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  93. package/docs/api/interfaces/NavigationItem.md +1 -1
  94. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  95. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  96. package/docs/api/interfaces/Organisation.md +1 -1
  97. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  98. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  99. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  100. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  101. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  102. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  103. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  104. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  105. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  106. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  107. package/docs/api/interfaces/PaletteData.md +1 -1
  108. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  109. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  110. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  111. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  112. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  113. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  114. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  115. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  116. package/docs/api/interfaces/RBACConfig.md +1 -1
  117. package/docs/api/interfaces/RBACLogger.md +1 -1
  118. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  119. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  120. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  121. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  122. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  123. package/docs/api/interfaces/RouteConfig.md +1 -1
  124. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  125. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  126. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  127. package/docs/api/interfaces/StorageConfig.md +1 -1
  128. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  129. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  130. package/docs/api/interfaces/StorageListOptions.md +1 -1
  131. package/docs/api/interfaces/StorageListResult.md +1 -1
  132. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  133. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  134. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  135. package/docs/api/interfaces/StyleImport.md +1 -1
  136. package/docs/api/interfaces/SwitchProps.md +1 -1
  137. package/docs/api/interfaces/ToastActionElement.md +1 -1
  138. package/docs/api/interfaces/ToastProps.md +1 -1
  139. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  140. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  141. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  142. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  143. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  144. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  145. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  146. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  147. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  148. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  149. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  150. package/docs/api/interfaces/UserEventAccess.md +1 -1
  151. package/docs/api/interfaces/UserMenuProps.md +1 -1
  152. package/docs/api/interfaces/UserProfile.md +1 -1
  153. package/docs/api/modules.md +79 -10
  154. package/docs/architecture/README.md +0 -1
  155. package/docs/styles/README.md +0 -2
  156. package/examples/RBAC/CompleteRBACExample.tsx +324 -0
  157. package/examples/RBAC/EventBasedApp.tsx +239 -0
  158. package/examples/RBAC/PermissionExample.tsx +151 -0
  159. package/examples/RBAC/index.ts +13 -0
  160. package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
  161. package/examples/public-pages/PublicEventPage.tsx +274 -0
  162. package/examples/public-pages/PublicPageApp.tsx +308 -0
  163. package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
  164. package/examples/public-pages/index.ts +14 -0
  165. package/package.json +1 -10
  166. package/src/__tests__/TEST_STANDARD.md +92 -0
  167. package/src/components/Badge/Badge.test.tsx +314 -0
  168. package/src/components/Badge/Badge.tsx +304 -0
  169. package/src/components/Badge/index.ts +3 -0
  170. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  171. package/src/components/DataTable/__tests__/styles.test.ts +1 -1
  172. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  173. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  174. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  176. package/src/components/DataTable/components/PaginationControls.tsx +1 -0
  177. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  178. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  179. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  180. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  181. package/src/components/DataTable/core/ActionManager.ts +235 -0
  182. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  183. package/src/components/DataTable/core/DataManager.ts +188 -0
  184. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  185. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  186. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  187. package/src/components/DataTable/core/StateManager.ts +311 -0
  188. package/src/components/DataTable/core/interfaces.ts +338 -0
  189. package/src/components/DataTable/styles.ts +27 -6
  190. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  191. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  192. package/src/components/DataTable/utils/debugTools.ts +609 -0
  193. package/src/components/DataTable/utils/index.ts +1 -0
  194. package/src/components/Dialog/README.md +804 -0
  195. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
  196. package/src/components/Dialog/utils/safeHtml.ts +185 -0
  197. package/src/components/Footer/Footer.test.tsx +1 -1
  198. package/src/components/Form/Form.test.tsx +1 -1
  199. package/src/components/Form/FormErrorSummary.tsx +113 -0
  200. package/src/components/Form/FormFieldset.tsx +127 -0
  201. package/src/components/Form/FormLiveRegion.tsx +198 -0
  202. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  203. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  204. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
  205. package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
  206. package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
  207. package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
  208. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
  209. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  210. package/src/components/Select/Select.test.tsx +1 -1
  211. package/src/components/Select/Select.tsx +20 -8
  212. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  213. package/src/components/index.ts +3 -0
  214. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  215. package/src/index.ts +4 -0
  216. package/src/styles/core.css +3 -0
  217. package/src/utils/appConfig.ts +47 -0
  218. package/src/utils/appIdResolver.test.ts +499 -0
  219. package/src/utils/appIdResolver.ts +130 -0
  220. package/src/utils/appNameResolver.simple.test.ts +212 -0
  221. package/src/utils/appNameResolver.test.ts +121 -0
  222. package/src/utils/appNameResolver.ts +191 -0
  223. package/src/utils/audit.ts +127 -0
  224. package/src/utils/auth-utils.ts +96 -0
  225. package/src/utils/bundleAnalysis.ts +129 -0
  226. package/src/utils/cn.ts +7 -0
  227. package/src/utils/debugLogger.ts +67 -0
  228. package/src/utils/deviceFingerprint.ts +215 -0
  229. package/src/utils/dynamicUtils.ts +105 -0
  230. package/src/utils/file-reference.test.ts +788 -0
  231. package/src/utils/file-reference.ts +519 -0
  232. package/src/utils/formatDate.test.ts +237 -0
  233. package/src/utils/formatting.ts +133 -0
  234. package/src/utils/index.ts +7 -0
  235. package/src/utils/lazyLoad.tsx +44 -0
  236. package/src/utils/logger.ts +179 -0
  237. package/src/utils/organisationContext.test.ts +322 -0
  238. package/src/utils/organisationContext.ts +153 -0
  239. package/src/utils/performanceBenchmark.ts +64 -0
  240. package/src/utils/performanceBudgets.ts +110 -0
  241. package/src/utils/permissionTypes.ts +37 -0
  242. package/src/utils/permissionUtils.test.ts +393 -0
  243. package/src/utils/permissionUtils.ts +34 -0
  244. package/src/utils/sanitization.ts +264 -0
  245. package/src/utils/schemaUtils.ts +37 -0
  246. package/src/utils/secureDataAccess.test.ts +711 -0
  247. package/src/utils/secureDataAccess.ts +377 -0
  248. package/src/utils/secureErrors.ts +79 -0
  249. package/src/utils/secureStorage.ts +244 -0
  250. package/src/utils/security.ts +156 -0
  251. package/src/utils/securityMonitor.ts +45 -0
  252. package/src/utils/sessionTracking.ts +126 -0
  253. package/src/utils/validation.ts +111 -0
  254. package/src/utils/validationUtils.ts +120 -0
  255. package/src/validation/index.ts +2 -2
  256. package/dist/chunk-444EZN6N.js.map +0 -1
  257. package/dist/chunk-APIBCTL2.js +0 -670
  258. package/dist/chunk-APIBCTL2.js.map +0 -1
  259. package/dist/chunk-HJGGOMQ6.js.map +0 -1
  260. package/dist/chunk-K2WWTH7O.js +0 -94
  261. package/dist/chunk-K2WWTH7O.js.map +0 -1
  262. package/dist/chunk-LMC26NLJ.js +0 -84
  263. package/dist/chunk-LMC26NLJ.js.map +0 -1
  264. package/dist/chunk-NOHEVYVX.js.map +0 -1
  265. package/dist/chunk-TVYPTYOY.js.map +0 -1
  266. package/dist/validation-8npbysjg.d.ts +0 -177
  267. /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-6M4L6BI2.js.map} +0 -0
  268. /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  269. /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
  270. /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
  271. /package/dist/{chunk-FHWWBIHA.js.map → chunk-BCIBECNB.js.map} +0 -0
  272. /package/dist/{chunk-L6PGMCMD.js.map → chunk-BLCXZEYF.js.map} +0 -0
  273. /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
  274. /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
  275. /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
  276. /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
  277. /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
  278. /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
  279. /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
  280. /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
  281. /package/examples/{components → components 2}/DataTable/index.ts +0 -0
  282. /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
  283. /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
  284. /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
  285. /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
  286. /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
  287. /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
  288. /package/examples/{components → components 2}/Dialog/index.ts +0 -0
  289. /package/examples/{components → components 2}/index.ts +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/OrganisationProvider.tsx","../src/hooks/useEventTheme.ts","../src/components/PublicLayout/PublicErrorBoundary.tsx","../src/components/PublicLayout/PublicPageProvider.tsx","../src/hooks/useAppConfig.ts","../src/utils/storage/config.ts","../src/utils/storage/helpers.ts","../src/hooks/useFileDisplay.ts","../src/utils/organisationContext.ts","../src/utils/file-reference.ts","../src/hooks/public/usePublicFileDisplay.ts"],"sourcesContent":["/**\n * @file Re-export for OrganisationProvider\n * @package @jmruthers/pace-core\n * @module Providers\n * @since 0.1.0\n * \n * Re-exports the service-based OrganisationProvider for backward compatibility.\n */\n\nexport { OrganisationServiceProvider as OrganisationProvider } from './services/OrganisationServiceProvider';\nexport type { OrganisationServiceProviderProps as OrganisationProviderProps } from './services/OrganisationServiceProvider';\n\n// Re-export context and hook\nexport { OrganisationServiceContext, useOrganisationService } from './services/OrganisationServiceProvider';\nexport type { OrganisationServiceContextType } from './services/OrganisationServiceProvider';\n\n// Re-export convenience hook for backward compatibility\nexport { useOrganisations } from '../hooks/useOrganisations';\n\n","/**\n * @file Event Theme Hook\n * @package @jmruthers/pace-core\n * @module Hooks/EventTheme\n * @since 2.0.0\n * \n * Hook that automatically applies event-specific theming when an event is selected.\n * This ensures consistent UX across all apps in the pace suite.\n * \n * Works in two modes:\n * 1. Authenticated mode: Uses EventProvider context via useEvents() hook\n * 2. Public page mode: Accepts event prop directly (no EventProvider required)\n * \n * @example\n * ```tsx\n * // Authenticated pages (with EventProvider)\n * import { useEventTheme } from '@jmruthers/pace-core/hooks';\n * \n * function MyApp() {\n * // Automatically applies event colors when event is selected via EventProvider\n * useEventTheme();\n * \n * return <div>Your app content</div>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Public pages (without EventProvider)\n * import { useEventTheme } from '@jmruthers/pace-core/hooks';\n * \n * function PublicPageLayout({ event }) {\n * // Applies event colors directly from event prop\n * useEventTheme(event);\n * \n * return <div>Public page content</div>;\n * }\n * ```\n */\n\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { useEvents } from './useEvents';\nimport { applyPalette, clearPalette } from '../theming/runtime';\nimport { parseAndNormalizeEventColours } from '../theming/parseEventColours';\nimport type { Event } from '../types/unified';\nimport { createLogger } from '../utils/core/logger';\n\nconst log = createLogger('useEventTheme');\n\n/**\n * Hook that automatically applies event-specific theming\n * \n * This hook applies event colors using the theming system. It works in two modes:\n * \n * **Authenticated Mode (default):**\n * - Uses EventProvider context via useEvents() hook\n * - Automatically watches selectedEvent changes\n * - Used in authenticated pages with EventProvider\n * \n * **Public Page Mode:**\n * - Accepts event prop directly\n * - No EventProvider required\n * - Used in public pages without authentication context\n * \n * Behavior:\n * - Applies event colors when an event with `event_colours` is provided\n * - Clears theming when no event is provided\n * - Skips theme application when on the login route to prevent login screen styling\n * - Handles cleanup and error cases gracefully\n * \n * @param event - Optional event object. If provided, uses this event directly (public page mode).\n * If not provided, uses EventProvider context via useEvents() (authenticated mode).\n * \n * @returns void - This is an effect hook with no return value\n * \n * @example\n * ```tsx\n * // Authenticated mode - uses EventProvider\n * function MyApp() {\n * useEventTheme(); // Watches selectedEvent from EventProvider\n * return <div>App content</div>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Public page mode - uses event prop\n * function PublicPageLayout({ event }) {\n * useEventTheme(event); // Uses event prop directly\n * return <div>Public content</div>;\n * }\n * ```\n */\nexport function useEventTheme(event?: Event | null): void {\n const location = useLocation();\n \n // Try to get event from EventProvider context (authenticated mode)\n // Only use useEvents() if event prop is not provided\n let selectedEvent: Event | null | undefined;\n try {\n if (event === undefined) {\n // No event prop provided, try to use EventProvider context\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } else {\n // Event prop provided, use it directly (public page mode)\n selectedEvent = event;\n }\n } catch (error) {\n // useEvents() throws if EventProvider is not available\n // This is expected for public pages - use the event prop if provided\n if (event !== undefined) {\n selectedEvent = event;\n } else {\n // No event prop and no EventProvider - can't apply theme\n selectedEvent = null;\n }\n }\n\n useEffect(() => {\n // Skip theme application when on login route\n // This prevents event colors from styling the login screen\n const isOnLoginRoute = location.pathname === '/login' || location.pathname.startsWith('/login');\n \n if (isOnLoginRoute) {\n // Clear any active theme when on login route\n clearPalette();\n return;\n }\n\n // If there's no selected event, clear any dynamic theming\n if (!selectedEvent) {\n clearPalette();\n return;\n }\n\n // Check if the event has theme colors\n const eventColours = selectedEvent.event_colours;\n\n // Parse and normalize event_colours using shared utility\n const normalized = parseAndNormalizeEventColours(eventColours);\n \n if (!normalized) {\n clearPalette();\n return;\n }\n\n // Apply the normalized palette\n try {\n applyPalette(normalized);\n } catch (error) {\n log.error('Failed to apply event palette:', error);\n }\n\n // Cleanup function to clear palette when component unmounts or event changes\n return () => {\n // Don't clear on unmount since we want the theme to persist\n // The next event selection will update it\n };\n }, [selectedEvent, location.pathname]);\n}\n","/**\n * @file Public Error Boundary Component\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * An error boundary component specifically designed for public pages.\n * Provides graceful error handling and recovery for public content.\n *\n * Features:\n * - Graceful error handling\n * - User-friendly error messages\n * - Recovery mechanisms\n * - Accessibility compliant\n * - TypeScript support\n *\n * @example\n * ```tsx\n * import { PublicErrorBoundary } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * return (\n * <PublicErrorBoundary>\n * <PublicPageContent />\n * </PublicErrorBoundary>\n * );\n * }\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Screen reader friendly error messages\n * - Keyboard accessible recovery actions\n * - High contrast support\n *\n * @dependencies\n * - React 18+ - Component framework\n * - Error boundary patterns\n * - Tailwind CSS - Styling\n */\n\nimport React, { Component, ErrorInfo, ReactNode } from 'react';\nimport { cn } from '../../utils/core/cn';\n\nexport interface PublicErrorBoundaryProps {\n /** Child components to wrap */\n children: ReactNode;\n /** Custom error fallback component */\n fallback?: React.ComponentType<PublicErrorBoundaryState>;\n /** Custom CSS classes for error display */\n className?: string;\n /** Whether to show error details in development */\n showErrorDetails?: boolean;\n /** Custom error message */\n customErrorMessage?: string;\n /** Custom recovery action */\n onRecover?: () => void;\n}\n\nexport interface PublicErrorBoundaryState {\n /** Whether an error has occurred */\n hasError: boolean;\n /** The error that occurred */\n error: Error | null;\n /** Error information */\n errorInfo: ErrorInfo | null;\n /** Function to reset the error state */\n resetError: () => void;\n}\n\n/**\n * Error boundary component for public pages\n * \n * This component catches JavaScript errors anywhere in the child component tree,\n * logs those errors, and displays a fallback UI instead of the component tree that crashed.\n * \n * @param props - Error boundary configuration\n * @returns React element with error boundary wrapper\n */\nexport class PublicErrorBoundary extends Component<PublicErrorBoundaryProps, PublicErrorBoundaryState> {\n constructor(props: PublicErrorBoundaryProps) {\n super(props);\n this.state = {\n hasError: false,\n error: null,\n errorInfo: null,\n resetError: this.resetError.bind(this)\n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<PublicErrorBoundaryState> {\n // Update state so the next render will show the fallback UI\n return {\n hasError: true,\n error\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n // Log the error to console in development\n if (import.meta.env.MODE === 'development') {\n console.error('PublicErrorBoundary caught an error:', error, errorInfo);\n }\n\n // Update state with error info\n this.setState({\n error,\n errorInfo\n });\n\n // You can also log the error to an error reporting service here\n // Example: logErrorToService(error, errorInfo);\n }\n\n resetError = () => {\n this.setState({\n hasError: false,\n error: null,\n errorInfo: null\n });\n };\n\n render() {\n if (this.state.hasError) {\n // Custom fallback component\n if (this.props.fallback) {\n return <this.props.fallback {...this.state} />;\n }\n\n // Default error UI\n return (\n <div className={cn(\n \"min-h-screen bg-background flex items-center justify-center\",\n this.props.className\n )}>\n <div className=\"max-w-md mx-auto text-center px-4\">\n <div className=\"mb-6\">\n <div className=\"mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-acc-100 mb-4\">\n <svg\n className=\"h-6 w-6 text-acc-600\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-bold text-sec-900 mb-2\">\n Something went wrong\n </h1>\n <p className=\"text-sec-600 mb-6\">\n {this.props.customErrorMessage || \n 'We encountered an error while loading this page. Please try again.'}\n </p>\n </div>\n\n {/* Error details in development */}\n {this.props.showErrorDetails && this.state.error && (\n <div className=\"mb-6 p-4 bg-acc-50 border border-acc-200 rounded-md text-left\">\n <h3 className=\"text-sm font-medium text-acc-800 mb-2\">\n Error Details (Development Only)\n </h3>\n <pre className=\"text-xs text-acc-700 whitespace-pre-wrap\">\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </div>\n )}\n\n {/* Recovery actions */}\n <div className=\"space-y-3\">\n <button\n onClick={this.resetError}\n className=\"w-full px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors focus:outline-none focus:ring-2 focus:ring-main-500 focus:ring-offset-2\"\n >\n Try Again\n </button>\n \n <button\n onClick={() => window.location.reload()}\n className=\"w-full px-4 py-2 bg-sec-600 text-main-50 rounded-md hover:bg-sec-700 transition-colors focus:outline-none focus:ring-2 focus:ring-sec-500 focus:ring-offset-2\"\n >\n Reload Page\n </button>\n\n {this.props.onRecover && (\n <button\n onClick={this.props.onRecover}\n className=\"w-full px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors focus:outline-none focus:ring-2 focus:ring-main-500 focus:ring-offset-2\"\n >\n Alternative Action\n </button>\n )}\n </div>\n\n {/* Help text */}\n <div className=\"mt-6 text-sm text-sec-500\">\n <p>\n If this problem persists, please contact support or try accessing the page later.\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Hook for accessing error boundary state\n * Useful for components that need to know if they're inside an error boundary\n */\nexport function useErrorBoundary() {\n const [error, setError] = React.useState<Error | null>(null);\n\n const resetError = React.useCallback(() => {\n setError(null);\n }, []);\n\n const captureError = React.useCallback((error: Error) => {\n setError(error);\n }, []);\n\n // Throw error synchronously during render so error boundary can catch it\n if (error) {\n throw error;\n }\n\n return { captureError, resetError };\n}\n\n/**\n * Default error fallback component\n */\nexport function DefaultPublicErrorFallback({ \n error, \n resetError \n}: PublicErrorBoundaryState) {\n return (\n <div className=\"min-h-screen bg-background flex items-center justify-center\">\n <div className=\"max-w-md mx-auto text-center px-4\">\n <div className=\"mb-6\">\n <div className=\"mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-acc-100 mb-4\">\n <svg\n className=\"h-6 w-6 text-acc-600\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-bold text-sec-900 mb-2\">\n Page Error\n </h1>\n <p className=\"text-sec-600 mb-6\">\n We encountered an error while loading this page.\n </p>\n </div>\n\n <button\n onClick={resetError}\n className=\"w-full px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors focus:outline-none focus:ring-2 focus:ring-main-500 focus:ring-offset-2\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n}\n","/**\n * @file Public Page Provider\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * A completely isolated provider for public pages that doesn't trigger\n * any authentication context. This ensures public pages work independently\n * of the main application's authentication system.\n *\n * Features:\n * - No authentication required\n * - No organisation context\n * - No event context\n * - Completely isolated from main app context\n * - Environment variable access for public data\n * - Error boundary integration\n *\n * @example\n * ```tsx\n * import { PublicPageProvider } from '@jmruthers/pace-core';\n * \n * const APP_NAME = 'CORE';\n * \n * function PublicApp() {\n * return (\n * <PublicPageProvider appName={APP_NAME}>\n * <Routes>\n * <Route path=\"/events/:eventCode/recipe-grid-report\" element={<PublicRecipePage />} />\n * </Routes>\n * </PublicPageProvider>\n * );\n * }\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { PublicErrorBoundary } from './PublicErrorBoundary';\nimport { logger } from '../../utils/core/logger';\n\ninterface PublicPageContextType {\n isPublicPage: true;\n supabase: ReturnType<typeof createClient<Database>> | null;\n appName: string | null;\n environment: {\n supabaseUrl: string | null;\n supabaseKey: string | null;\n };\n}\n\nexport const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);\n\nexport interface PublicPageProviderProps {\n children: ReactNode;\n /** Application name for logo display and branding. Should match the APP_NAME constant used in App.tsx */\n appName?: string;\n}\n\n/**\n * Provider for public pages that completely isolates them from authentication context\n * \n * This provider:\n * - Does not initialize any authentication providers\n * - Provides environment variables for public data access\n * - Includes error boundary for graceful error handling\n * - Is completely separate from the main app context\n * - Provides appName for consistent logo display\n */\nexport function PublicPageProvider({ children, appName }: PublicPageProviderProps) {\n // Get environment variables for public data access\n // Handle both Vite (import.meta.env) and Node.js (process.env) environments\n const getEnvVar = (key: string): string | undefined => {\n // Check Vite environment first (browser)\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n return (import.meta as any).env[key];\n }\n // Check Node.js environment (server-side)\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env[key];\n }\n return undefined;\n };\n\n const supabaseUrl = getEnvVar('VITE_SUPABASE_URL') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_URL') || \n null;\n \n const supabaseKey = getEnvVar('VITE_SUPABASE_ANON_KEY') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_ANON_KEY') || \n null;\n\n // Create Supabase client if environment variables are available\n const supabase = useMemo(() => {\n if (!supabaseUrl || !supabaseKey) {\n logger.warn('PublicPageProvider', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are set in your environment.');\n return null;\n }\n const client = createClient<Database>(supabaseUrl, supabaseKey);\n logger.info('PublicPageProvider', 'Supabase client created successfully for public pages');\n return client;\n }, [supabaseUrl, supabaseKey]);\n\n const contextValue: PublicPageContextType = {\n isPublicPage: true,\n supabase,\n appName: appName || null,\n environment: {\n supabaseUrl,\n supabaseKey\n }\n };\n\n return (\n <PublicPageContext.Provider value={contextValue}>\n <PublicErrorBoundary>\n {children}\n </PublicErrorBoundary>\n </PublicPageContext.Provider>\n );\n}\n\n/**\n * Hook to access public page context\n * \n * @returns Public page context with environment variables\n */\nexport function usePublicPageContext(): PublicPageContextType {\n const context = useContext(PublicPageContext);\n \n if (!context) {\n throw new Error('usePublicPageContext must be used within a PublicPageProvider');\n }\n \n return context;\n}\n\n/**\n * Hook to check if we're in a public page context\n * \n * @returns True if we're in a public page context\n */\nexport function useIsPublicPage(): boolean {\n const context = useContext(PublicPageContext);\n return context !== undefined;\n}\n","/**\n * @file useAppConfig Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useAppConfig\n * @since 0.4.0\n *\n * Hook for accessing app configuration like direct access support and event requirements.\n * This is a convenience hook that extracts app config from the UnifiedAuthProvider.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { supportsDirectAccess, requiresEvent, isLoading } = useAppConfig();\n * \n * if (isLoading) return <div>Loading...</div>;\n * \n * return (\n * <div>\n * {supportsDirectAccess && (\n * <div>This app supports direct access!</div>\n * )}\n * {requiresEvent && (\n * <EventSelector />\n * )}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useMemo, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers/UnifiedAuthProvider';\nimport { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';\n\nexport interface UseAppConfigReturn {\n supportsDirectAccess: boolean;\n requiresEvent: boolean;\n isLoading: boolean;\n appName: string;\n}\n\n/**\n * Hook to access app configuration\n * Works in both authenticated and public contexts\n * @returns App configuration and loading state\n */\nexport function useAppConfig(): UseAppConfigReturn {\n // Check if we're in a public page context first\n const isPublicPage = useIsPublicPage();\n \n // Try to get appName from PublicPageContext (access context directly to avoid hook rule violations)\n const publicPageContext = useContext(PublicPageContext);\n const contextAppName = publicPageContext?.appName || null;\n \n if (isPublicPage) {\n const getAppName = (): string => {\n // Priority 1: Use appName from PublicPageContext (passed from App.tsx)\n if (contextAppName) {\n return contextAppName;\n }\n \n // Priority 2: Check environment variables\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n return (import.meta as any).env.VITE_APP_NAME || \n (import.meta as any).env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.VITE_APP_NAME || \n import.meta.env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n \n // Priority 3: Default fallback\n return 'PACE';\n };\n \n return useMemo(() => ({\n supportsDirectAccess: false, // Public pages don't support direct access\n requiresEvent: true, // Public pages always require an event\n isLoading: false,\n appName: getAppName()\n }), [contextAppName]);\n }\n \n // For authenticated pages, use UnifiedAuthProvider\n try {\n const { appConfig, appName } = useUnifiedAuth();\n return useMemo(() => ({\n supportsDirectAccess: !(appConfig?.requires_event ?? true),\n requiresEvent: appConfig?.requires_event ?? true,\n isLoading: appConfig === null,\n appName\n }), [appConfig?.requires_event, appName]);\n } catch (error) {\n // Fallback if UnifiedAuthProvider is not available\n return useMemo(() => ({\n supportsDirectAccess: false,\n requiresEvent: true,\n isLoading: false,\n appName: 'PACE'\n }), []);\n }\n} ","/**\n * Storage configuration for pace-core\n */\n\nimport { FileSizeLimits, StorageConfig } from './types';\n\n/**\n * File size limits by MIME type (in bytes)\n */\nexport const FILE_SIZE_LIMITS: FileSizeLimits = {\n // Images\n 'image/jpeg': 5 * 1024 * 1024, // 5MB\n 'image/png': 5 * 1024 * 1024, // 5MB\n 'image/gif': 10 * 1024 * 1024, // 10MB (for animations)\n 'image/webp': 5 * 1024 * 1024, // 5MB\n 'image/svg+xml': 1 * 1024 * 1024, // 1MB (vector graphics)\n \n // Documents\n 'application/pdf': 50 * 1024 * 1024, // 50MB\n 'application/msword': 25 * 1024 * 1024, // 25MB\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 25 * 1024 * 1024, // 25MB\n 'application/vnd.ms-excel': 25 * 1024 * 1024, // 25MB\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 25 * 1024 * 1024, // 25MB\n \n // Archives\n 'application/zip': 100 * 1024 * 1024, // 100MB\n 'application/x-rar-compressed': 100 * 1024 * 1024, // 100MB\n \n // Text files\n 'text/plain': 1 * 1024 * 1024, // 1MB\n 'text/csv': 10 * 1024 * 1024, // 10MB\n 'application/json': 10 * 1024 * 1024, // 10MB\n};\n\n/**\n * Default file size limit for unknown MIME types\n */\nexport const DEFAULT_FILE_SIZE_LIMIT = 10 * 1024 * 1024; // 10MB\n\n/**\n * App-specific path mappings for file organization\n * Maps app names to their primary category in the organization-first structure\n */\nexport const APP_PATH_MAPPING: Record<string, string> = {\n 'PACE': 'event_logos',\n 'TRAC': 'trac_accommodation', // Default category for TRAC files\n 'MEDI': 'documents',\n 'CAKE': 'documents'\n};\n\n/**\n * Storage configuration\n */\nexport const STORAGE_CONFIG: StorageConfig = {\n bucketName: 'files',\n fileSizeLimits: FILE_SIZE_LIMITS,\n defaultFileSizeLimit: DEFAULT_FILE_SIZE_LIMIT,\n};\n\n/**\n * Get the file size limit for a given MIME type\n */\nexport function getFileSizeLimit(mimeType: string): number {\n return STORAGE_CONFIG.fileSizeLimits[mimeType] || STORAGE_CONFIG.defaultFileSizeLimit;\n}\n\n/**\n * Get the bucket name based on whether the file is public or private\n * @param isPublic - Whether the file should be publicly accessible\n * @returns The bucket name: 'public-files' for public files, 'files' for private files\n */\nexport function getBucketName(isPublic: boolean): 'files' | 'public-files' {\n return isPublic ? 'public-files' : 'files';\n}\n\n/**\n * Validate file size against limits\n */\nexport function validateFileSize(file: File): { isValid: boolean; error?: string } {\n const limit = getFileSizeLimit(file.type);\n \n if (file.size > limit) {\n const limitMB = Math.round(limit / (1024 * 1024));\n const fileMB = Math.round(file.size / (1024 * 1024));\n return {\n isValid: false,\n error: `File size (${fileMB}MB) exceeds limit (${limitMB}MB) for ${file.type}`\n };\n }\n \n return { isValid: true };\n}\n\n/**\n * Get human-readable file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n if (bytes < 0) return `${bytes} Bytes`; // Handle negative numbers\n \n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n // Ensure we don't exceed the available size units\n const sizeIndex = Math.min(Math.max(i, 0), sizes.length - 1);\n \n return parseFloat((bytes / Math.pow(k, sizeIndex)).toFixed(2)) + ' ' + sizes[sizeIndex];\n}\n","/**\n * Storage helper functions for pace-core\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { \n StorageUploadOptions, \n StorageUploadResult, \n StorageFileMetadata,\n StorageUrlOptions,\n StorageListOptions,\n StorageListResult,\n StorageFileInfo\n} from './types';\nimport { validateFileSize, STORAGE_CONFIG, getBucketName } from './config';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('StorageHelpers');\n\n/**\n * Generate a file path based on organization-first structure\n */\nexport function generateFilePath(options: StorageUploadOptions, fileName: string): string {\n const { orgId, isPublic = false, customPath } = options;\n \n \n // Validate required orgId\n if (!orgId) {\n throw new Error('orgId is required for file path generation');\n }\n \n if (isPublic) {\n // Public files go to {orgId}/{category}/filename\n if (customPath) {\n return `${orgId}/${customPath}/${fileName}`;\n }\n return `${orgId}/public/${fileName}`;\n }\n \n // Organization-first structure: {orgId}/{category}/filename\n if (customPath) {\n return `${orgId}/${customPath}/${fileName}`;\n }\n \n // Use customPath if available, otherwise default to files\n const pathCategory = customPath || 'files';\n return `${orgId}/${pathCategory}/${fileName}`;\n}\n\n/**\n * Generate a unique filename with timestamp and UUID\n * Sanitizes the original filename to remove spaces and invalid characters for storage compatibility\n */\nexport function generateUniqueFileName(originalName: string): string {\n const timestamp = Date.now();\n const uuid = crypto.randomUUID();\n const extension = originalName.split('.').pop() || '';\n let baseName = originalName.replace(/\\.[^/.]+$/, '');\n \n // Sanitize the base name for storage compatibility\n // Replace spaces with underscores and remove invalid characters\n baseName = baseName\n .trim()\n .replace(/\\s+/g, '_') // Replace spaces with underscores\n .replace(/[<>:\"/\\\\|?*]/g, '') // Remove invalid file name characters\n .replace(/\\.\\./g, '') // Remove directory traversal attempts\n .replace(/^\\.+|\\.+$/g, '') // Remove leading/trailing dots\n .substring(0, 200); // Limit length to leave room for timestamp and UUID\n \n // If there's no extension, don't add one\n if (!extension || extension === originalName) {\n return `${timestamp}-${uuid}-${baseName}`;\n }\n \n return `${timestamp}-${uuid}-${baseName}.${extension}`;\n}\n\n/**\n * Extract file metadata from a File object\n */\nexport async function extractFileMetadata(\n file: File, \n options: StorageUploadOptions,\n uploadedBy: string\n): Promise<StorageFileMetadata> {\n const metadata: StorageFileMetadata = {\n mimeType: file.type,\n size: file.size,\n orgId: options.orgId,\n appName: options.appName || 'pace-core',\n uploadedBy,\n uploadedAt: new Date().toISOString(),\n tags: options.tags || [],\n isPublic: options.isPublic || false,\n customMetadata: options.metadata || {}\n };\n\n // Extract image dimensions if it's an image\n if (file.type.startsWith('image/')) {\n try {\n const dimensions = await getImageDimensions(file);\n metadata.width = dimensions.width;\n metadata.height = dimensions.height;\n } catch (error) {\n // Non-critical error - image dimensions are optional metadata\n // Using Logger would be better, but this is in a utility function\n // For now, silently continue - dimensions are optional\n }\n }\n\n // Generate file hash if possible\n try {\n metadata.hash = await generateFileHash(file);\n } catch (error) {\n // Non-critical error - file hash is optional metadata\n // Using Logger would be better, but this is in a utility function\n // For now, silently continue - hash is optional\n }\n\n return metadata;\n}\n\n/**\n * Get image dimensions from a File object\n */\nasync function getImageDimensions(file: File): Promise<{ width: number; height: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(file);\n \n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve({ width: img.width, height: img.height });\n };\n \n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Could not load image'));\n };\n \n img.src = url;\n });\n}\n\n/**\n * Generate a hash for a file\n */\nasync function generateFileHash(file: File): Promise<string> {\n const buffer = await file.arrayBuffer();\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return `sha256:${hashHex}`;\n}\n\n/**\n * Upload a file to Supabase storage with app segregation\n */\nexport async function uploadFile(\n supabase: SupabaseClient,\n file: File,\n options: StorageUploadOptions\n): Promise<StorageUploadResult> {\n try {\n // Validate file size\n const sizeValidation = validateFileSize(file);\n if (!sizeValidation.isValid) {\n return {\n success: false,\n error: sizeValidation.error\n };\n }\n\n // Generate unique filename and path\n const uniqueFileName = generateUniqueFileName(file.name);\n const filePath = generateFilePath(options, uniqueFileName);\n\n // Extract metadata\n const metadata = await extractFileMetadata(file, options, 'current-user'); // TODO: Get actual user ID\n\n // Select bucket based on isPublic flag\n const bucketName = getBucketName(options.isPublic || false);\n\n // Upload file to Supabase\n const { data, error } = await supabase.storage\n .from(bucketName)\n .upload(filePath, file, {\n cacheControl: '3600',\n upsert: false,\n contentType: file.type\n });\n\n if (error) {\n return {\n success: false,\n error: `Upload failed: ${error.message}`\n };\n }\n\n // Generate public URL if file is public\n let publicUrl: string | undefined;\n if (options.isPublic) {\n const { data: urlData } = supabase.storage\n .from(bucketName)\n .getPublicUrl(filePath);\n publicUrl = urlData.publicUrl;\n }\n\n return {\n success: true,\n path: filePath,\n publicUrl,\n metadata\n };\n\n } catch (error) {\n return {\n success: false,\n error: `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n\n/**\n * Get a public URL for a file\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: true)\n * @returns Public URL for the file\n */\nexport function getPublicUrl(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = true\n): string {\n if (!supabase) {\n throw new Error('Supabase client is required to generate a public URL');\n }\n\n if (!path || typeof path !== 'string') {\n throw new Error('A valid storage path is required to generate a public URL');\n }\n\n // If the path is already an absolute URL, return it directly\n if (/^https?:\\/\\//i.test(path)) {\n return path;\n }\n\n // Normalise path by trimming whitespace and leading slashes\n let normalisedPath = path.trim().replace(/^\\/+/, '');\n\n if (!normalisedPath) {\n throw new Error('Storage path cannot be empty after normalisation');\n }\n\n const { bucketNameFromPath, storagePath } = resolveBucketHint(normalisedPath);\n\n const bucketName = bucketNameFromPath || getBucketName(isPublic);\n\n const { data } = supabase.storage\n .from(bucketName)\n .getPublicUrl(storagePath);\n\n return data.publicUrl;\n}\n\nfunction resolveBucketHint(pathWithPotentialBucket: string): { bucketNameFromPath: string | null; storagePath: string } {\n const BUCKET_NAME_PATTERN = /^[a-z0-9][a-z0-9-_\\.]{1,62}$/i;\n const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n const KNOWN_BUCKET_NAMES = new Set(['files', 'public-files']);\n\n const trimmedPath = pathWithPotentialBucket.trim();\n\n if (!trimmedPath) {\n throw new Error('Storage path cannot be empty after normalisation');\n }\n\n // Support \"bucket::path\" notation to remove ambiguity with colon usage in file names.\n const doubleColonIndex = trimmedPath.indexOf('::');\n if (doubleColonIndex > 0) {\n const potentialBucket = trimmedPath.slice(0, doubleColonIndex).trim();\n const remainingPath = trimmedPath.slice(doubleColonIndex + 2).replace(/^\\/+/, '');\n\n if (potentialBucket && remainingPath && BUCKET_NAME_PATTERN.test(potentialBucket)) {\n return {\n bucketNameFromPath: potentialBucket,\n storagePath: remainingPath\n };\n }\n }\n\n // Support \"bucket/path\" hints, provided the first segment looks like a bucket name and not a directory prefix.\n const firstSlashIndex = trimmedPath.indexOf('/');\n if (firstSlashIndex > 0) {\n const potentialBucket = trimmedPath.slice(0, firstSlashIndex).trim();\n const remainingPath = trimmedPath.slice(firstSlashIndex + 1).replace(/^\\/+/, '');\n\n if (\n potentialBucket &&\n remainingPath &&\n BUCKET_NAME_PATTERN.test(potentialBucket) &&\n !UUID_PATTERN.test(potentialBucket) &&\n (KNOWN_BUCKET_NAMES.has(potentialBucket) || !potentialBucket.includes('-')) &&\n !/^\\d+$/.test(potentialBucket)\n ) {\n return {\n bucketNameFromPath: potentialBucket,\n storagePath: remainingPath\n };\n }\n }\n\n return {\n bucketNameFromPath: null,\n storagePath: trimmedPath\n };\n}\n\n/**\n * Get a signed URL for a protected file\n * Private files are always in the 'files' bucket, so this always uses 'files' bucket\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param options - URL options including expiry time\n * @returns Signed URL with expiration timestamp, or null if failed\n */\nexport async function getSignedUrl(\n supabase: SupabaseClient,\n path: string,\n options: StorageUrlOptions\n): Promise<{ url: string; expiresAt: string } | null> {\n try {\n // Signed URLs are only for private files, which are always in the 'files' bucket\n const bucketName = getBucketName(false);\n \n const { data, error } = await supabase.storage\n .from(bucketName)\n .createSignedUrl(path, options.expiresIn || 3600);\n\n if (error) {\n log.error('Failed to create signed URL:', error);\n return null;\n }\n\n return {\n url: data.signedUrl,\n expiresAt: new Date(Date.now() + (options.expiresIn || 3600) * 1000).toISOString()\n };\n } catch (error) {\n log.error('Failed to create signed URL:', error);\n return null;\n }\n}\n\n/**\n * Delete a file from storage\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: false)\n * @returns Success status and optional error message\n */\nexport async function deleteFile(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = false\n): Promise<{ success: boolean; error?: string }> {\n try {\n const bucketName = getBucketName(isPublic);\n \n const { error } = await supabase.storage\n .from(bucketName)\n .remove([path]);\n\n if (error) {\n return {\n success: false,\n error: `Delete failed: ${error.message}`\n };\n }\n\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: `Delete failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n\n/**\n * List files in an organization scope\n * @param supabase - Supabase client instance\n * @param options - List options including bucket selection via isPublic\n * @returns List of files with metadata\n */\nexport async function listFiles(\n supabase: SupabaseClient,\n options: StorageListOptions & { isPublic?: boolean }\n): Promise<StorageListResult> {\n try {\n // Select bucket based on isPublic flag (default to private files bucket)\n const bucketName = getBucketName(options.isPublic || false);\n \n // Organization-first structure: {orgId}/{category}/\n const pathPrefix = `${options.orgId}/`;\n const searchPath = options.pathPrefix ? `${pathPrefix}${options.pathPrefix}` : pathPrefix;\n\n const { data, error } = await supabase.storage\n .from(bucketName)\n .list(searchPath, {\n limit: options.limit || 100,\n offset: options.offset || 0,\n sortBy: { column: 'created_at', order: 'desc' }\n });\n\n if (error) {\n log.error('Failed to list files:', error);\n return { files: [], totalCount: 0, hasMore: false };\n }\n\n const files: StorageFileInfo[] = (data || []).map(item => ({\n name: item.name,\n path: `${searchPath}${item.name}`,\n size: item.metadata?.size || 0,\n mimeType: item.metadata?.mimetype || 'application/octet-stream',\n lastModified: item.updated_at || item.created_at || new Date().toISOString(),\n metadata: {\n mimeType: item.metadata?.mimetype || 'application/octet-stream',\n size: item.metadata?.size || 0,\n orgId: options.orgId,\n appName: options.appName,\n uploadedBy: 'unknown',\n uploadedAt: item.created_at || new Date().toISOString(),\n isPublic: options.isPublic || false\n }\n }));\n\n return {\n files,\n totalCount: files.length,\n hasMore: files.length >= (options.limit || 100)\n };\n } catch (error) {\n log.error('Failed to list files:', error);\n return { files: [], totalCount: 0, hasMore: false };\n }\n}\n\n/**\n * Download a file from storage\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: false)\n * @returns File blob with metadata, or null if failed\n */\nexport async function downloadFile(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = false\n): Promise<{ blob: Blob; metadata: { name: string; size: number; type: string } } | null> {\n try {\n const bucketName = getBucketName(isPublic);\n \n const { data, error } = await supabase.storage\n .from(bucketName)\n .download(path);\n\n if (error) {\n log.error('Failed to download file:', error);\n return null;\n }\n\n if (!data) {\n return null;\n }\n\n // Extract file name from path\n const fileName = path.split('/').pop() || 'download';\n \n // Get file metadata\n const { data: fileInfo } = await supabase.storage\n .from(bucketName)\n .list(path.split('/').slice(0, -1).join('/'), {\n search: fileName\n });\n\n const metadata = fileInfo?.[0]?.metadata || {};\n \n return {\n blob: data,\n metadata: {\n name: fileName,\n size: metadata.size || data.size,\n type: metadata.mimetype || 'application/octet-stream'\n }\n };\n } catch (error) {\n log.error('Failed to download file:', error);\n return null;\n }\n}\n\n/**\n * Move a file to archived location (soft delete)\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param options - Archive options including bucket selection via isPublic\n */\nexport async function archiveFile(\n supabase: SupabaseClient,\n path: string,\n options: { appName: string; orgId: string; isPublic?: boolean }\n): Promise<{ success: boolean; error?: string }> {\n try {\n const bucketName = getBucketName(options.isPublic || false);\n \n // Generate archived path for organization-first structure\n const archivedPath = path.replace(`${options.orgId}/`, `archived/${options.orgId}/`);\n \n // Copy file to archived location\n const { error: copyError } = await supabase.storage\n .from(bucketName)\n .copy(path, archivedPath);\n\n if (copyError) {\n return {\n success: false,\n error: `Archive failed: ${copyError.message}`\n };\n }\n\n // Delete original file\n const deleteResult = await deleteFile(supabase, path, options.isPublic || false);\n if (!deleteResult.success) {\n return deleteResult;\n }\n\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: `Archive failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n","/**\n * @file File Display Hook (Authenticated)\n * @package @jmruthers/pace-core\n * @module Hooks\n *\n * A React hook for accessing file references in authenticated contexts.\n * Can handle both public and private files using the file_references system.\n *\n * Features:\n * - Works in authenticated contexts\n * - Supports both public and private files\n * - Automatic signed URL generation for private files\n * - Caching for performance\n * - Error handling and loading states\n * - Supports both single file (category) and multiple files\n *\n * @example\n * ```tsx\n * import { useFileDisplay } from '@jmruthers/pace-core';\n *\n * function FileView() {\n * const { fileUrl, fileReference, isLoading, error } = useFileDisplay(\n * 'event',\n * eventId,\n * organisationId,\n * FileCategory.EVENT_LOGOS\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return fileUrl ? <img src={fileUrl} alt=\"File\" /> : null;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport { FileReference, FileCategory } from '../types/file-reference';\nimport { getPublicUrl, getSignedUrl } from '../utils/storage/helpers';\nimport { createFileReferenceService } from '../utils/file-reference';\nimport { logger } from '../utils/core/logger';\n\n// Simple in-memory cache for authenticated file data\nconst authenticatedFileCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\n// Cache size limit to prevent memory leaks\nconst MAX_CACHE_SIZE = 100;\n\n// Helper function to clean up expired entries and enforce size limit\nfunction cleanupCache() {\n const now = Date.now();\n const entries = Array.from(authenticatedFileCache.entries());\n \n // Remove expired entries\n const expiredKeys: string[] = [];\n entries.forEach(([key, value]) => {\n if (now - value.timestamp >= value.ttl) {\n expiredKeys.push(key);\n }\n });\n expiredKeys.forEach(key => authenticatedFileCache.delete(key));\n \n // If still over limit, remove oldest entries\n if (authenticatedFileCache.size > MAX_CACHE_SIZE) {\n const sorted = entries\n .filter(([key]) => !expiredKeys.includes(key))\n .sort((a, b) => a[1].timestamp - b[1].timestamp);\n const toRemove = sorted.slice(0, authenticatedFileCache.size - MAX_CACHE_SIZE);\n toRemove.forEach(([key]) => authenticatedFileCache.delete(key));\n }\n}\n\nexport interface UseFileDisplayReturn {\n /** Single file URL if category is provided and file found, null otherwise */\n fileUrl: string | null;\n /** Single file reference if category is provided and file found, null otherwise */\n fileReference: FileReference | null;\n /** Array of all file references for the record (when category not provided or for multiple files) */\n fileReferences: FileReference[];\n /** Map of file IDs to URLs for multiple files */\n fileUrls: Map<string, string>;\n /** Total count of files for the record */\n fileCount: number;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UseFileDisplayOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Supabase client instance (required) */\n supabase: SupabaseClient | null;\n}\n\n/**\n * Hook for accessing file references in authenticated contexts\n * \n * This hook provides access to file references for authenticated users.\n * It supports both public and private files, generating appropriate URLs\n * (public URLs for public files, signed URLs for private files).\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to filter by (for single file mode)\n * @param options - Configuration options for caching and behavior\n * @returns Object containing file data, loading state, error, and refetch function\n */\nexport function useFileDisplay(\n table_name: string | undefined,\n record_id: string | undefined,\n organisation_id: string | undefined,\n category: FileCategory | undefined,\n options: UseFileDisplayOptions\n): UseFileDisplayReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n supabase\n } = options;\n\n const [fileUrl, setFileUrl] = useState<string | null>(null);\n const [fileReference, setFileReference] = useState<FileReference | null>(null);\n const [fileReferences, setFileReferences] = useState<FileReference[]>([]);\n const [fileUrls, setFileUrls] = useState<Map<string, string>>(new Map());\n const [fileCount, setFileCount] = useState<number>(0);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchFiles = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisation_id)) {\n logger.warn('useFileDisplay', 'Invalid organisationId format (not a valid UUID):', organisation_id);\n }\n\n // Check cache first\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n if (enableCache) {\n const cached = authenticatedFileCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n const cachedData = cached.data;\n \n // FIX: Regenerate signed URL for private files if missing\n if (cachedData.fileReference && !cachedData.fileUrl && \n cachedData.fileReference.is_public === false && supabase) {\n // Regenerate signed URL without fetching from database\n try {\n const signedUrlResult = await getSignedUrl(supabase, cachedData.fileReference.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n const regeneratedUrl = signedUrlResult?.url || null;\n setFileUrl(regeneratedUrl);\n setFileReference(cachedData.fileReference);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n } catch (err) {\n // If signed URL regeneration fails, fall through to normal fetch\n logger.warn('useFileDisplay', 'Failed to regenerate signed URL from cache, falling back to fetch:', err);\n }\n }\n \n // Normal cache hit for public files or files with URLs\n setFileUrl(cachedData.fileUrl || null);\n setFileReference(cachedData.fileReference || null);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n const service = createFileReferenceService(supabase);\n let files: FileReference[] = [];\n\n // CRITICAL: When category is provided, MUST use RPC function, not direct queries\n // Category is stored in file_metadata JSONB field, not a direct column\n if (category) {\n // Single file mode - get files by category using RPC\n logger.debug('useFileDisplay', 'Using RPC function for category filtering:', {\n table_name,\n record_id,\n category,\n organisation_id\n });\n files = await service.getFilesByCategory(\n table_name,\n record_id,\n category,\n organisation_id\n );\n } else {\n // Multiple files mode - get all files using RPC\n files = await service.listFileReferences(\n table_name,\n record_id,\n organisation_id\n );\n }\n\n if (files.length === 0) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n \n // Cache empty result\n if (enableCache) {\n authenticatedFileCache.set(cacheKey, {\n data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: new Map(), fileCount: 0 },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n cleanupCache();\n }\n return;\n }\n\n setFileReferences(files);\n setFileCount(files.length);\n\n if (category && files.length > 0) {\n // Single file mode - get first file\n const firstFile = files[0];\n logger.debug('useFileDisplay', 'Processing category files, first file:', {\n id: firstFile.id,\n file_path: firstFile.file_path,\n is_public: firstFile.is_public,\n has_file_metadata: !!firstFile.file_metadata,\n category_in_metadata: firstFile.file_metadata?.category\n });\n \n setFileReference(firstFile);\n \n // Generate URL based on file visibility\n let url: string | null = null;\n if (firstFile.is_public) {\n url = getPublicUrl(supabase, firstFile.file_path, true);\n logger.debug('useFileDisplay', 'Generated public URL:', url);\n } else {\n const signedUrlResult = await getSignedUrl(supabase, firstFile.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n url = signedUrlResult?.url || null;\n logger.debug('useFileDisplay', 'Generated signed URL:', url ? 'URL generated' : 'URL generation failed');\n }\n logger.debug('useFileDisplay', 'Setting file URL:', url ? 'URL set' : 'URL is null');\n setFileUrl(url);\n } else {\n // Multiple files mode - generate URLs for all files\n const urlMap = new Map<string, string>();\n for (const fileRef of files) {\n let url: string | null = null;\n if (fileRef.is_public) {\n url = getPublicUrl(supabase, fileRef.file_path, true);\n } else {\n const signedUrlResult = await getSignedUrl(supabase, fileRef.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n url = signedUrlResult?.url || null;\n }\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n setFileUrls(urlMap);\n setFileReference(null);\n setFileUrl(null);\n }\n\n // Cache the result\n if (enableCache) {\n // Prepare cache data\n let cacheData: any = {\n fileReference: category && files.length > 0 ? files[0] : null,\n fileReferences: files,\n fileUrls: new Map(),\n fileCount: files.length\n };\n\n if (category && files.length > 0) {\n const firstFile = files[0];\n let url: string | null = null;\n if (firstFile.is_public) {\n url = getPublicUrl(supabase, firstFile.file_path, true);\n }\n cacheData.fileUrl = url;\n } else {\n const urlMap = new Map<string, string>();\n for (const fileRef of files) {\n if (fileRef.is_public) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n }\n cacheData.fileUrls = urlMap;\n }\n\n authenticatedFileCache.set(cacheKey, {\n data: cacheData,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n cleanupCache();\n }\n\n } catch (err) {\n logger.error('useFileDisplay', 'Error fetching files:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n } finally {\n setIsLoading(false);\n }\n }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);\n\n // Fetch files when parameters change\n useEffect(() => {\n if (table_name && record_id && organisation_id && supabase) {\n fetchFiles();\n } else {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchFiles, table_name, record_id, organisation_id, supabase]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) return;\n \n // Clear cache for this file\n if (enableCache) {\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n authenticatedFileCache.delete(cacheKey);\n }\n await fetchFiles();\n }, [fetchFiles, table_name, record_id, organisation_id, category, supabase, enableCache]);\n\n return {\n fileUrl,\n fileReference,\n fileReferences,\n fileUrls,\n fileCount,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached authenticated file data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearFileDisplayCache(): void {\n for (const [key] of authenticatedFileCache) {\n if (key.startsWith('file_')) {\n authenticatedFileCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getFileDisplayCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(authenticatedFileCache.keys()).filter(key => key.startsWith('file_'));\n return {\n size: keys.length,\n keys\n };\n}\n\n/**\n * Invalidate cache for a specific file display entry\n * Useful for clearing cache after file uploads or updates\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to invalidate (if provided, also invalidates 'all' category)\n */\nexport function invalidateFileDisplayCache(\n table_name: string,\n record_id: string,\n organisation_id: string,\n category?: FileCategory\n): void {\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n authenticatedFileCache.delete(cacheKey);\n // Also invalidate 'all' category if specific category invalidated\n if (category) {\n const allCategoryKey = `file_${table_name}_${record_id}_${organisation_id}_all`;\n authenticatedFileCache.delete(allCategoryKey);\n }\n}\n\n","/**\n * @file Organisation Context Utility\n * @package @jmruthers/pace-core\n * @module Utils/OrganisationContext\n * @since 0.4.0\n *\n * Utility functions for managing organisation context in database sessions.\n * Provides fallback mechanisms for when database functions are not available.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\n\n/**\n * Set organisation context in the database session\n * \n * This function attempts to set the organisation context using a database function.\n * If the function is not available, it falls back gracefully without throwing errors.\n * \n * @param supabase - Supabase client instance\n * @param organisationId - The organisation ID to set as context\n * @returns Promise that resolves when context is set (or falls back gracefully)\n */\nexport async function setOrganisationContext(\n supabase: SupabaseClient,\n organisationId: string\n): Promise<void> {\n if (!supabase || !organisationId) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n // Add timeout to prevent hanging RPC calls\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('RPC timeout after 3 seconds')), 3000);\n });\n \n // Call the database function to set organisation context\n const rpcPromise = supabase.rpc('set_organisation_context', {\n org_id: organisationId\n });\n\n const { error } = await Promise.race([rpcPromise, timeoutPromise]) as any;\n\n if (error) {\n // Function might not exist yet - this is expected during migration\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] RPC function not available or failed, continuing without database context');\n } else {\n console.log('[organisationContext] Organisation context set in database successfully');\n }\n } catch (error) {\n // Handle any other errors gracefully\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] Failed to set database context, continuing without it:', error);\n }\n}\n\n/**\n * Clear organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves when context is cleared\n */\nexport async function clearOrganisationContext(\n supabase: SupabaseClient\n): Promise<void> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n const { error } = await supabase.rpc('rbac_audit_log', {\n p_event_type: 'organisation_switched',\n p_metadata: { action: 'clear_context' }\n });\n \n if (error) {\n // Silent fail - function not available\n // TODO: Replace with proper logging service integration\n } else {\n // TODO: Replace with proper logging service integration\n }\n } catch (error) {\n // Silent fail - error occurred\n // TODO: Replace with proper logging service integration\n }\n}\n\n/**\n * Get current organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to the current organisation ID or null\n */\nexport async function getOrganisationContext(\n supabase: SupabaseClient\n): Promise<string | null> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n\n try {\n // For now, return null since we're not using database context\n // This will be replaced with proper context management\n const data = null;\n const error = null;\n \n if (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n \n // Validate that data is a string (allow empty strings)\n if (typeof data === 'string') {\n return data;\n }\n \n // Return null for invalid data formats\n return null;\n } catch (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n}\n\n/**\n * Check if organisation context functions are available in the database\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to true if functions are available\n */\nexport async function isOrganisationContextAvailable(\n supabase: SupabaseClient\n): Promise<boolean> {\n if (!supabase) {\n return false;\n }\n\n try {\n const { error } = await supabase.rpc('get_organisation_context');\n \n if (error) {\n return false;\n }\n \n return true;\n } catch (error) {\n return false;\n }\n} ","// File Reference Service\n// Provides CRUD operations for the centralized file reference system\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { \n FileReference, \n FileUploadOptions, \n FileReferenceService, \n FileUploadResult,\n FileCategory \n} from '../types/file-reference';\nimport { uploadFile, getPublicUrl, getSignedUrl, deleteFile, extractFileMetadata } from './storage/helpers';\nimport { setOrganisationContext } from './organisationContext';\nimport { invalidateFileDisplayCache } from '../hooks/useFileDisplay';\n\nexport class FileReferenceServiceImpl implements FileReferenceService {\n constructor(private supabase: SupabaseClient) {}\n\n /**\n * Creates a file reference by uploading a file to storage and linking it in the database.\n * \n * Storage Flow:\n * 1. Upload file to storage bucket first (files or public-files based on is_public flag)\n * - Path format: {orgId}/{category}/{timestamp-uuid-filename}\n * - Bucket selection: 'files' (private) or 'public-files' (public)\n * 2. Extract file metadata (dimensions, hash, etc.)\n * 3. Set organisation context for RLS policies\n * 4. Create database reference via RPC function\n * 5. If DB insert fails, rollback by deleting uploaded file\n * \n * This ensures atomicity: either both storage and DB succeed, or both are cleaned up.\n */\n async createFileReference(options: FileUploadOptions, file: File): Promise<FileReference> {\n try {\n\n // Validate required options\n if (!options.organisation_id) {\n throw new Error('organisation_id is required for file upload');\n }\n if (!options.table_name) {\n throw new Error('table_name is required for file upload');\n }\n if (!options.record_id) {\n throw new Error('record_id is required for file upload');\n }\n\n // Step 1: Upload file to storage bucket first\n // This generates a unique path: {orgId}/{category}/{timestamp-uuid-filename}\n // Bucket is automatically selected based on is_public flag\n const uploadResult = await uploadFile(this.supabase, file, {\n appName: 'file-reference',\n orgId: options.organisation_id,\n isPublic: options.is_public || false,\n customPath: options.category // Use category as the custom path segment\n });\n if (!uploadResult.success) {\n throw new Error(`Failed to upload file: ${uploadResult.error}`);\n }\n\n if (!uploadResult.path) {\n throw new Error('File upload did not return a path');\n }\n\n const filePath = uploadResult.path;\n\n // Step 2: Extract file metadata (dimensions, hash, etc.)\n const metadata = await extractFileMetadata(file, {\n appName: 'file-reference',\n orgId: options.organisation_id,\n isPublic: options.is_public || false\n }, 'system');\n\n // Step 3: Set organisation context in database session before creating file reference\n // This ensures RLS policies can check the organisation context\n await setOrganisationContext(this.supabase, options.organisation_id);\n\n // Step 4: Create file reference in database using RPC function\n // This links the storage path to the record in file_references table\n const { data, error } = await this.supabase\n .rpc('data_file_reference_create', {\n p_table_name: options.table_name,\n p_record_id: options.record_id,\n p_file_path: filePath, // Storage path from step 1\n p_organisation_id: options.organisation_id,\n p_app_id: options.app_id,\n p_file_metadata: {\n fileName: file.name,\n fileType: file.type,\n fileSize: file.size,\n category: options.category,\n ...metadata,\n ...options.custom_metadata\n },\n p_is_public: options.is_public || false\n });\n\n // Step 5: Rollback - if database insert fails, clean up uploaded file\n if (error) {\n await deleteFile(this.supabase, filePath, options.is_public || false);\n throw new Error(`Failed to create file reference: ${error.message}`);\n }\n\n // Get the created file reference\n const { data: fileRef, error: fetchError } = await this.supabase\n .from('file_references')\n .select('*')\n .eq('id', data)\n .single();\n\n if (fetchError || !fileRef) {\n throw new Error(`Failed to fetch created file reference: ${fetchError?.message}`);\n }\n\n // Invalidate cache for this file display entry so newly uploaded files appear immediately\n invalidateFileDisplayCache(\n options.table_name,\n options.record_id,\n options.organisation_id,\n options.category\n );\n\n return fileRef as FileReference;\n } catch (error) {\n console.error('Error creating file reference:', error);\n throw error;\n }\n }\n\n async getFileReference(table_name: string, record_id: string, organisation_id: string): Promise<FileReference | null> {\n try {\n const { data, error } = await this.supabase\n .from('file_references')\n .select('*')\n .eq('table_name', table_name)\n .eq('record_id', record_id)\n .eq('organisation_id', organisation_id)\n .single();\n\n if (error) {\n if (error.code === 'PGRST116') {\n return null; // No rows found\n }\n throw new Error(`Failed to get file reference: ${error.message}`);\n }\n\n return data as FileReference;\n } catch (error) {\n console.error('Error getting file reference:', error);\n throw error;\n }\n }\n\n async getFileUrl(table_name: string, record_id: string, organisation_id: string): Promise<string | null> {\n try {\n // Get file reference to check if it's public\n const fileRef = await this.getFileReference(table_name, record_id, organisation_id);\n if (!fileRef) {\n return null;\n }\n\n // For public files, RPC returns file path - generate public URL client-side\n if (fileRef.is_public) {\n const { data: pathData } = await this.supabase\n .rpc('data_file_reference_url_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (!pathData) {\n return null;\n }\n\n // Generate public URL using bucket-aware helper\n return getPublicUrl(this.supabase, pathData, true);\n } else {\n // For private files, use signed URL\n return await this.getSignedUrl(table_name, record_id, organisation_id);\n }\n } catch (error) {\n console.error('Error getting file URL:', error);\n throw error;\n }\n }\n\n async getSignedUrl(table_name: string, record_id: string, organisation_id: string, expires_in: number = 3600): Promise<string | null> {\n try {\n // Get file path from RPC function\n const { data: filePath, error } = await this.supabase\n .rpc('data_file_reference_signed_url_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id,\n p_expires_in: expires_in\n });\n\n if (error) {\n throw new Error(`Failed to get signed URL: ${error.message}`);\n }\n\n if (!filePath) {\n return null;\n }\n\n // Generate signed URL client-side using bucket-aware helper (files bucket for private files)\n const signedUrlResult = await getSignedUrl(this.supabase, filePath, {\n appName: 'file-reference',\n orgId: organisation_id,\n expiresIn: expires_in\n });\n\n return signedUrlResult?.url || null;\n } catch (error) {\n console.error('Error getting signed URL:', error);\n throw error;\n }\n }\n\n async updateFileReference(id: string, updates: Partial<FileReference>): Promise<FileReference> {\n try {\n const { data, error } = await this.supabase\n .from('file_references')\n .update(updates)\n .eq('id', id)\n .select()\n .single();\n\n if (error) {\n throw new Error(`Failed to update file reference: ${error.message}`);\n }\n\n return data as FileReference;\n } catch (error) {\n console.error('Error updating file reference:', error);\n throw error;\n }\n }\n\n async deleteFileReference(table_name: string, record_id: string, organisation_id: string, delete_file: boolean = false): Promise<boolean> {\n try {\n // Get file reference first to determine bucket\n const fileRef = await this.getFileReference(table_name, record_id, organisation_id);\n \n const { error } = await this.supabase\n .rpc('data_file_reference_delete', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id,\n p_delete_file: delete_file\n });\n\n if (error) {\n throw new Error(`Failed to delete file reference: ${error.message}`);\n }\n\n // If delete_file is true and we have the file reference, delete from storage\n if (delete_file && fileRef) {\n await deleteFile(this.supabase, fileRef.file_path, fileRef.is_public || false);\n }\n\n return true;\n } catch (error) {\n console.error('Error deleting file reference:', error);\n throw error;\n }\n }\n\n async listFileReferences(table_name: string, record_id: string, organisation_id: string): Promise<FileReference[]> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to list file references: ${error.message}`);\n }\n\n // RPC returns partial data, need to fetch full file references\n if (!data || data.length === 0) {\n return [];\n }\n\n // Fetch full file reference data for each ID\n const ids = data.map((item: any) => item.id);\n const { data: fullData, error: fetchError } = await this.supabase\n .from('file_references')\n .select('*')\n .in('id', ids);\n\n if (fetchError) {\n throw new Error(`Failed to fetch file references: ${fetchError.message}`);\n }\n\n return (fullData || []) as FileReference[];\n } catch (error) {\n console.error('Error listing file references:', error);\n throw error;\n }\n }\n\n async getFileCount(table_name: string, record_id: string, organisation_id: string): Promise<number> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_count_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to get file count: ${error.message}`);\n }\n\n return data || 0;\n } catch (error) {\n console.error('Error getting file count:', error);\n throw error;\n }\n }\n\n async getFileReferenceById(id: string, organisation_id: string): Promise<FileReference | null> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_get', {\n p_file_reference_id: id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to get file reference by ID: ${error.message}`);\n }\n\n if (!data || data.length === 0) {\n return null;\n }\n\n return data[0] as FileReference;\n } catch (error) {\n console.error('Error getting file reference by ID:', error);\n throw error;\n }\n }\n\n async getFilesByCategory(\n table_name: string, \n record_id: string, \n category: FileCategory, \n organisation_id: string\n ): Promise<FileReference[]> {\n try {\n // CRITICAL: Use RPC function to get files by category - this correctly filters on file_metadata->>'category'\n // NOTE: We MUST use RPC function. Direct queries with .eq('category', ...) will FAIL with HTTP 406\n // because there is NO 'category' column. The category is stored in the JSONB file_metadata field.\n console.log('[FileReferenceService.getFilesByCategory] Calling RPC function:', {\n table_name,\n record_id,\n category,\n organisation_id\n });\n const { data, error } = await this.supabase\n .rpc('data_file_reference_by_category_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_category: category,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n // Provide clear error message about category filtering\n console.error('[FileReferenceService.getFilesByCategory] RPC ERROR:', {\n error,\n errorCode: error.code,\n errorMessage: error.message,\n errorDetails: error.details,\n table_name,\n record_id,\n category,\n organisation_id,\n 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.'\n });\n 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.`);\n }\n\n // RPC returns partial data with: id, file_path, file_metadata, is_public, created_at\n // We have table_name, record_id, organisation_id from function parameters\n // We can construct FileReference objects directly without another query (avoiding RLS issues)\n console.log('[FileReferenceService.getFilesByCategory] RPC response received:', {\n dataLength: data?.length || 0,\n data: data ? data.slice(0, 2) : null, // Log first 2 items for debugging\n fullDataAvailable: data?.every((item: any) => item.id && item.file_path && item.file_metadata)\n });\n\n if (!data || data.length === 0) {\n console.log('[FileReferenceService.getFilesByCategory] No data from RPC, returning empty array');\n return [];\n }\n\n // Construct FileReference objects from RPC response\n // This avoids RLS issues with direct queries - the RPC already validated permissions\n const fileReferences: FileReference[] = data\n .filter((item: any) => {\n // Verify category matches (defensive check)\n const fileCategory = item.file_metadata?.category;\n const matches = fileCategory === category;\n if (!matches) {\n console.warn('[FileReferenceService.getFilesByCategory] File category mismatch in RPC response:', {\n fileId: item.id,\n expectedCategory: category,\n actualCategory: fileCategory\n });\n }\n return matches && item.id && item.file_path && item.file_metadata;\n })\n .map((item: any) => {\n // Construct complete FileReference from RPC response + function parameters\n const fileRef: FileReference = {\n id: item.id,\n table_name: table_name,\n record_id: record_id,\n file_path: item.file_path,\n file_metadata: item.file_metadata || {},\n organisation_id: organisation_id,\n app_id: item.file_metadata?.app_id || '', // May not be in metadata, use empty string\n is_public: item.is_public ?? false,\n created_at: item.created_at || new Date().toISOString(),\n updated_at: item.created_at || new Date().toISOString() // RPC doesn't return updated_at, use created_at\n };\n return fileRef;\n });\n\n console.log('[FileReferenceService.getFilesByCategory] Constructed file references from RPC response:', {\n count: fileReferences.length,\n firstFileId: fileReferences[0]?.id,\n firstFilePath: fileReferences[0]?.file_path,\n firstFileIsPublic: fileReferences[0]?.is_public,\n firstFileHasAllRequiredFields: !!(fileReferences[0]?.id && fileReferences[0]?.file_path && fileReferences[0]?.file_metadata && fileReferences[0]?.table_name && fileReferences[0]?.record_id && fileReferences[0]?.organisation_id)\n });\n\n return fileReferences;\n } catch (error) {\n console.error('Error getting files by category:', error);\n throw error;\n }\n }\n\n async uploadMultipleFiles(\n options: FileUploadOptions,\n files: File[]\n ): Promise<import('../types/file-reference').BulkUploadResult> {\n const success: FileReference[] = [];\n const failed: { file: File; error: string }[] = [];\n const results: Array<{ file: File; result: FileUploadResult | null; error?: string }> = [];\n\n // Upload files sequentially to avoid overwhelming the server\n for (const file of files) {\n try {\n const fileReference = await this.createFileReference(options, file);\n\n success.push(fileReference);\n results.push({\n file,\n result: {\n file_reference: fileReference,\n file_url: fileReference.is_public\n ? getPublicUrl(this.supabase, fileReference.file_path, true)\n : '',\n },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n failed.push({ file, error: message });\n results.push({ file, result: null, error: message });\n }\n }\n\n return {\n success,\n failed,\n total: results.length,\n successful: success.length,\n results,\n };\n }\n}\n\n// Factory function to create file reference service\nexport function createFileReferenceService(supabase: SupabaseClient): FileReferenceService {\n return new FileReferenceServiceImpl(supabase);\n}\n\n// Helper function to upload file and create reference in one operation\nexport async function uploadFileWithReference(\n supabase: SupabaseClient,\n options: FileUploadOptions,\n file: File\n): Promise<FileUploadResult> {\n \n const service = createFileReferenceService(supabase);\n const fileReference = await service.createFileReference(options, file);\n \n const fileUrl = options.is_public \n ? getPublicUrl(supabase, fileReference.file_path, true)\n : await getSignedUrl(supabase, fileReference.file_path, { \n appName: 'file-reference',\n orgId: options.organisation_id,\n expiresIn: 3600 \n });\n\n const urlString = typeof fileUrl === 'string' ? fileUrl : fileUrl?.url || '';\n\n return {\n file_reference: fileReference,\n file_url: urlString,\n signed_url: options.is_public ? undefined : urlString || undefined\n };\n}\n","/**\n * @file Public File Display Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing file references in public contexts without authentication.\n * Provides file URLs and metadata for public pages.\n *\n * Features:\n * - No authentication required\n * - Only returns public files (is_public = true)\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Supports both single file (category) and multiple files\n *\n * @example\n * ```tsx\n * import { usePublicFileDisplay } from '@jmruthers/pace-core';\n *\n * function PublicFileView() {\n * const { fileUrl, fileReference, isLoading, error } = usePublicFileDisplay(\n * 'event',\n * eventId,\n * organisationId,\n * FileCategory.EVENT_LOGOS,\n * { supabase }\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return fileUrl ? <img src={fileUrl} alt=\"File\" /> : null;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { FileReference, FileCategory } from '../../types/file-reference';\nimport { getPublicUrl } from '../../utils/storage/helpers';\nimport { logger } from '../../utils/core/logger';\n\n// Simple in-memory cache for public file data\nconst publicFileCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicFileDisplayReturn {\n /** Single file URL if category is provided and file found, null otherwise */\n fileUrl: string | null;\n /** Single file reference if category is provided and file found, null otherwise */\n fileReference: FileReference | null;\n /** Array of all file references for the record (when category not provided or for multiple files) */\n fileReferences: FileReference[];\n /** Map of file IDs to URLs for multiple files */\n fileUrls: Map<string, string>;\n /** Total count of files for the record */\n fileCount: number;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicFileDisplayOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Supabase client instance (required) */\n supabase: SupabaseClient<Database>;\n}\n\n/**\n * Hook for accessing public file references\n * \n * This hook provides access to file references without requiring\n * authentication. It only returns public files and generates public URLs.\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to filter by (for single file mode)\n * @param options - Configuration options for caching and behavior\n * @returns Object containing file data, loading state, error, and refetch function\n */\nexport function usePublicFileDisplay(\n table_name: string | undefined,\n record_id: string | undefined,\n organisation_id: string | undefined,\n category: FileCategory | undefined,\n options: UsePublicFileDisplayOptions\n): UsePublicFileDisplayReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n supabase\n } = options;\n\n const [fileUrl, setFileUrl] = useState<string | null>(null);\n const [fileReference, setFileReference] = useState<FileReference | null>(null);\n const [fileReferences, setFileReferences] = useState<FileReference[]>([]);\n const [fileUrls, setFileUrls] = useState<Map<string, string>>(new Map());\n const [fileCount, setFileCount] = useState<number>(0);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchFiles = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisation_id)) {\n logger.warn('usePublicFileDisplay', 'Invalid organisationId format (not a valid UUID)', { organisation_id });\n }\n\n // Check cache first\n const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n if (enableCache) {\n const cached = publicFileCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n const cachedData = cached.data;\n setFileUrl(cachedData.fileUrl || null);\n setFileReference(cachedData.fileReference || null);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n let files: any[] = [];\n\n // CRITICAL: When category is provided, MUST use RPC function, not direct queries\n // Category is stored in file_metadata JSONB field, not a direct column\n if (category) {\n // Single file mode - use RPC to get files by category\n const rpcParams = {\n p_table_name: table_name,\n p_record_id: record_id,\n p_category: category,\n p_organisation_id: organisation_id\n };\n \n const { data, error: rpcError } = await (supabase as any)\n .rpc('data_file_reference_by_category_list', rpcParams);\n\n if (rpcError) {\n logger.error('usePublicFileDisplay', 'RPC function error', {\n function: 'data_file_reference_by_category_list',\n table_name,\n record_id,\n category,\n organisation_id,\n error: rpcError.message,\n errorCode: rpcError.code,\n errorDetails: rpcError.details,\n errorHint: rpcError.hint\n });\n throw new Error(rpcError.message || 'Failed to fetch file reference');\n }\n\n // RPC returns partial data with: id, file_path, file_metadata, is_public, created_at\n // We have table_name, record_id, organisation_id from function parameters\n // We can construct FileReference objects directly without another query (avoiding RLS issues)\n if (!data || data.length === 0) {\n files = [];\n } else {\n // Construct file reference objects from RPC response\n // This avoids RLS issues - the RPC already validated permissions and filtered public files\n files = data\n .filter((item: any) => {\n // RPC should only return public files for public context, but verify\n return item.is_public === true && item.id && item.file_path && item.file_metadata;\n })\n .map((item: any) => {\n // Construct complete file reference from RPC response + function parameters\n return {\n id: item.id,\n table_name: table_name,\n record_id: record_id,\n file_path: item.file_path,\n file_metadata: item.file_metadata || {},\n organisation_id: organisation_id,\n app_id: item.file_metadata?.app_id || null,\n is_public: true, // RPC already filtered for public files\n created_at: item.created_at || new Date().toISOString(),\n updated_at: item.created_at || new Date().toISOString()\n };\n });\n }\n } else {\n // Multiple files mode - use RPC to get all files\n const { data: fileIds, error: rpcError } = await (supabase as any)\n .rpc('data_file_reference_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (rpcError) {\n logger.error('usePublicFileDisplay', 'RPC function error', {\n function: 'data_file_reference_list',\n table_name,\n record_id,\n organisation_id,\n error: rpcError.message,\n errorCode: rpcError.code,\n errorDetails: rpcError.details,\n errorHint: rpcError.hint\n });\n throw new Error(rpcError.message || 'Failed to fetch file references');\n }\n\n if (!fileIds || fileIds.length === 0) {\n files = [];\n } else {\n // Fetch full file reference data for each ID, but only public files\n const ids = fileIds.map((item: any) => item.id);\n const { data: fullData, error: fetchError } = await supabase\n .from('file_references')\n .select('*')\n .in('id', ids)\n .eq('is_public', true); // Only public files in public context\n\n if (fetchError) {\n throw new Error(fetchError.message || 'Failed to fetch file references');\n }\n\n files = fullData || [];\n }\n }\n\n // Ensure all files are public (category RPC might return both)\n const publicFiles = files.filter((f: any) => f.is_public === true);\n\n if (publicFiles.length === 0) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n \n // Cache empty result\n if (enableCache) {\n publicFileCache.set(cacheKey, {\n data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: new Map(), fileCount: 0 },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n return;\n }\n\n // Convert to FileReference format\n const fileRefs: FileReference[] = publicFiles.map((f: any) => ({\n id: f.id,\n table_name: f.table_name,\n record_id: f.record_id,\n file_path: f.file_path,\n file_metadata: f.file_metadata || {},\n organisation_id: f.organisation_id,\n app_id: f.app_id,\n is_public: f.is_public ?? true,\n created_at: f.created_at,\n updated_at: f.updated_at\n }));\n\n setFileReferences(fileRefs);\n setFileCount(fileRefs.length);\n\n if (category && fileRefs.length > 0) {\n // Single file mode - get first file\n const firstFile = fileRefs[0];\n setFileReference(firstFile);\n \n // Generate public URL\n const url = getPublicUrl(supabase, firstFile.file_path, true);\n setFileUrl(url);\n } else {\n // Multiple files mode - generate URLs for all files\n const urlMap = new Map<string, string>();\n for (const fileRef of fileRefs) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n setFileUrls(urlMap);\n setFileReference(null);\n setFileUrl(null);\n }\n\n // Cache the result\n if (enableCache) {\n publicFileCache.set(cacheKey, {\n data: {\n fileUrl: category ? (fileRefs.length > 0 ? getPublicUrl(supabase, fileRefs[0].file_path, true) : null) : null,\n fileReference: category && fileRefs.length > 0 ? fileRefs[0] : null,\n fileReferences: fileRefs,\n fileUrls: category ? new Map() : (() => {\n const urlMap = new Map<string, string>();\n for (const fileRef of fileRefs) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n return urlMap;\n })(),\n fileCount: fileRefs.length\n },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n logger.error('usePublicFileDisplay', 'Error fetching files', {\n table_name,\n record_id,\n organisation_id,\n category,\n error: err instanceof Error ? err.message : 'Unknown error',\n errorDetails: err instanceof Error ? err.stack : String(err)\n });\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n } finally {\n setIsLoading(false);\n }\n }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);\n\n // Fetch files when parameters change\n useEffect(() => {\n if (table_name && record_id && organisation_id) {\n fetchFiles();\n } else {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchFiles, table_name, record_id, organisation_id]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id) return;\n \n // Clear cache for this file\n if (enableCache) {\n const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n publicFileCache.delete(cacheKey);\n }\n await fetchFiles();\n }, [fetchFiles, table_name, record_id, organisation_id, category, enableCache]);\n\n return {\n fileUrl,\n fileReference,\n fileReferences,\n fileUrls,\n fileCount,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public file data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicFileDisplayCache(): void {\n for (const [key] of publicFileCache) {\n if (key.startsWith('public_file_')) {\n publicFileCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicFileDisplayCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicFileCache.keys()).filter(key => key.startsWith('public_file_'));\n return {\n size: keys.length,\n keys\n };\n}\n\n"],"mappings":"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","names":["error","jsx","useMemo","useContext","useContext","useMemo","log","useEffect","error","useEffect","useState","useEffect","useCallback","useState","useCallback","error","useEffect"]}
@@ -2,7 +2,7 @@ import {
2
2
  UnifiedAuthProvider,
3
3
  init_UnifiedAuthProvider,
4
4
  useUnifiedAuth
5
- } from "./chunk-4MT5BGGL.js";
5
+ } from "./chunk-YCWDTTUK.js";
6
6
  import {
7
7
  __esm,
8
8
  __export
@@ -24,4 +24,4 @@ export {
24
24
  UnifiedAuthProvider_exports,
25
25
  init_UnifiedAuthProvider2 as init_UnifiedAuthProvider
26
26
  };
27
- //# sourceMappingURL=chunk-2TWNJ46Y.js.map
27
+ //# sourceMappingURL=chunk-6LAAY47Q.js.map
@@ -1,7 +1,89 @@
1
+ import {
2
+ createLogger,
3
+ init_logger
4
+ } from "./chunk-XDNLUEXI.js";
1
5
  import {
2
6
  __esm
3
7
  } from "./chunk-PLDDJCW6.js";
4
8
 
9
+ // src/utils/context/organisationContext.ts
10
+ async function setOrganisationContext(supabase, organisationId) {
11
+ if (!supabase || !organisationId) {
12
+ return;
13
+ }
14
+ try {
15
+ const timeoutPromise = new Promise((_, reject) => {
16
+ setTimeout(() => reject(new Error("RPC timeout after 3 seconds")), 3e3);
17
+ });
18
+ const rpcPromise = supabase.rpc("set_organisation_context", {
19
+ org_id: organisationId
20
+ });
21
+ const { error } = await Promise.race([rpcPromise, timeoutPromise]);
22
+ if (error) {
23
+ log.debug("RPC function not available or failed, continuing without database context");
24
+ } else {
25
+ log.debug("Organisation context set in database successfully");
26
+ }
27
+ } catch (error) {
28
+ log.debug("Failed to set database context, continuing without it:", error);
29
+ }
30
+ }
31
+ async function clearOrganisationContext(supabase) {
32
+ if (!supabase) {
33
+ return;
34
+ }
35
+ try {
36
+ const { error } = await supabase.rpc("rbac_audit_log", {
37
+ p_event_type: "organisation_switched",
38
+ p_metadata: { action: "clear_context" }
39
+ });
40
+ if (error) {
41
+ } else {
42
+ }
43
+ } catch (error) {
44
+ }
45
+ }
46
+ async function getOrganisationContext(supabase) {
47
+ if (!supabase) {
48
+ return null;
49
+ }
50
+ try {
51
+ const data = null;
52
+ const error = null;
53
+ if (error) {
54
+ return null;
55
+ }
56
+ if (typeof data === "string") {
57
+ return data;
58
+ }
59
+ return null;
60
+ } catch (error) {
61
+ return null;
62
+ }
63
+ }
64
+ async function isOrganisationContextAvailable(supabase) {
65
+ if (!supabase) {
66
+ return false;
67
+ }
68
+ try {
69
+ const { error } = await supabase.rpc("get_organisation_context");
70
+ if (error) {
71
+ return false;
72
+ }
73
+ return true;
74
+ } catch (error) {
75
+ return false;
76
+ }
77
+ }
78
+ var log;
79
+ var init_organisationContext = __esm({
80
+ "src/utils/context/organisationContext.ts"() {
81
+ "use strict";
82
+ init_logger();
83
+ log = createLogger("organisationContext");
84
+ }
85
+ });
86
+
5
87
  // src/utils/security/secureStorage.ts
6
88
  var SecureStorageImpl, secureStorage;
7
89
  var init_secureStorage = __esm({
@@ -195,7 +277,12 @@ var init_secureStorage = __esm({
195
277
  });
196
278
 
197
279
  export {
280
+ setOrganisationContext,
281
+ clearOrganisationContext,
282
+ getOrganisationContext,
283
+ isOrganisationContextAvailable,
284
+ init_organisationContext,
198
285
  secureStorage,
199
286
  init_secureStorage
200
287
  };
201
- //# sourceMappingURL=chunk-444EZN6N.js.map
288
+ //# sourceMappingURL=chunk-7QCC6MCP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/context/organisationContext.ts","../src/utils/security/secureStorage.ts"],"sourcesContent":["/**\n * @file Organisation Context Utility\n * @package @jmruthers/pace-core\n * @module Utils/OrganisationContext\n * @since 0.4.0\n *\n * Utility functions for managing organisation context in database sessions.\n * Provides fallback mechanisms for when database functions are not available.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('organisationContext');\n\n/**\n * Set organisation context in the database session\n * \n * This function attempts to set the organisation context using a database function.\n * If the function is not available, it falls back gracefully without throwing errors.\n * \n * @param supabase - Supabase client instance\n * @param organisationId - The organisation ID to set as context\n * @returns Promise that resolves when context is set (or falls back gracefully)\n */\nexport async function setOrganisationContext(\n supabase: SupabaseClient,\n organisationId: string\n): Promise<void> {\n if (!supabase || !organisationId) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n // Add timeout to prevent hanging RPC calls\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('RPC timeout after 3 seconds')), 3000);\n });\n \n // Call the database function to set organisation context\n const rpcPromise = supabase.rpc('set_organisation_context', {\n org_id: organisationId\n });\n\n const { error } = await Promise.race([rpcPromise, timeoutPromise]) as any;\n\n if (error) {\n // Function might not exist yet - this is expected during migration\n // Silent fail - will fall back to client-side filtering\n log.debug('RPC function not available or failed, continuing without database context');\n } else {\n log.debug('Organisation context set in database successfully');\n }\n } catch (error) {\n // Handle any other errors gracefully\n // Silent fail - will fall back to client-side filtering\n log.debug('Failed to set database context, continuing without it:', error);\n }\n}\n\n/**\n * Clear organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves when context is cleared\n */\nexport async function clearOrganisationContext(\n supabase: SupabaseClient\n): Promise<void> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n const { error } = await supabase.rpc('rbac_audit_log', {\n p_event_type: 'organisation_switched',\n p_metadata: { action: 'clear_context' }\n });\n \n if (error) {\n // Silent fail - function not available\n // TODO: Replace with proper logging service integration\n } else {\n // TODO: Replace with proper logging service integration\n }\n } catch (error) {\n // Silent fail - error occurred\n // TODO: Replace with proper logging service integration\n }\n}\n\n/**\n * Get current organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to the current organisation ID or null\n */\nexport async function getOrganisationContext(\n supabase: SupabaseClient\n): Promise<string | null> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n\n try {\n // For now, return null since we're not using database context\n // This will be replaced with proper context management\n const data = null;\n const error = null;\n \n if (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n \n // Validate that data is a string (allow empty strings)\n if (typeof data === 'string') {\n return data;\n }\n \n // Return null for invalid data formats\n return null;\n } catch (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n}\n\n/**\n * Check if organisation context functions are available in the database\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to true if functions are available\n */\nexport async function isOrganisationContextAvailable(\n supabase: SupabaseClient\n): Promise<boolean> {\n if (!supabase) {\n return false;\n }\n\n try {\n const { error } = await supabase.rpc('get_organisation_context');\n \n if (error) {\n return false;\n }\n \n return true;\n } catch (error) {\n return false;\n }\n} ","\n/**\n * @file Secure Storage Utilities\n * @description Encrypted storage wrapper for sensitive data\n */\n\nexport interface SecureStorageOptions {\n encrypt?: boolean;\n expiry?: number; // TTL in milliseconds\n}\n\n/**\n * Secure storage implementation with encryption support\n */\nclass SecureStorageImpl {\n private encryptionKey: CryptoKey | null = null;\n private initialized = false;\n\n /**\n * Initialize secure storage with encryption\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n try {\n // Check if Web Crypto API is available\n if (window.crypto && window.crypto.subtle) {\n // Generate or retrieve encryption key\n const keyData = localStorage.getItem('_sec_key');\n if (keyData) {\n try {\n const keyBuffer = this.base64ToArrayBuffer(keyData);\n this.encryptionKey = await window.crypto.subtle.importKey(\n 'raw',\n keyBuffer,\n { name: 'AES-GCM' },\n false,\n ['encrypt', 'decrypt']\n );\n } catch (error) {\n await this.generateNewKey();\n }\n } else {\n await this.generateNewKey();\n }\n }\n this.initialized = true;\n } catch (error) {\n this.initialized = true;\n }\n }\n\n /**\n * Store item securely\n */\n async setItem(\n key: string,\n value: string,\n options: SecureStorageOptions = {}\n ): Promise<void> {\n await this.init();\n\n const data = {\n value,\n timestamp: Date.now(),\n expiry: options.expiry ? Date.now() + options.expiry : undefined,\n };\n\n const serialized = JSON.stringify(data);\n \n if (options.encrypt && this.encryptionKey) {\n try {\n const encrypted = await this.encrypt(serialized);\n localStorage.setItem(`_sec_${key}`, encrypted);\n return;\n } catch (error) {\n // Silent fail - store as plain text\n }\n }\n\n localStorage.setItem(key, serialized);\n }\n\n /**\n * Retrieve item securely\n */\n async getItem(key: string): Promise<string | null> {\n await this.init();\n\n // Try encrypted storage first\n const encryptedData = localStorage.getItem(`_sec_${key}`);\n if (encryptedData && this.encryptionKey) {\n try {\n const decrypted = await this.decrypt(encryptedData);\n const parsed = JSON.parse(decrypted);\n \n // Check expiry\n if (parsed.expiry && Date.now() > parsed.expiry) {\n await this.removeItem(key);\n return null;\n }\n \n return parsed.value;\n } catch (error) {\n // Silent fail - try plain storage\n }\n }\n\n // Fallback to plain storage\n const plainData = localStorage.getItem(key);\n if (!plainData) return null;\n\n try {\n const parsed = JSON.parse(plainData);\n \n // Check expiry\n if (parsed.expiry && Date.now() > parsed.expiry) {\n await this.removeItem(key);\n return null;\n }\n \n return parsed.value || plainData;\n } catch (error) {\n // If parsing fails, return as-is (backward compatibility)\n return plainData;\n }\n }\n\n /**\n * Remove item\n */\n async removeItem(key: string): Promise<void> {\n localStorage.removeItem(key);\n localStorage.removeItem(`_sec_${key}`);\n }\n\n /**\n * Clear all secure storage\n */\n async clear(): Promise<void> {\n const keys = Object.keys(localStorage);\n for (const key of keys) {\n if (key.startsWith('_sec_')) {\n localStorage.removeItem(key);\n }\n }\n }\n\n /**\n * Generate new encryption key\n */\n private async generateNewKey(): Promise<void> {\n if (!window.crypto?.subtle) return;\n\n try {\n this.encryptionKey = await window.crypto.subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt']\n );\n\n // Export and store key\n const exportedKey = await window.crypto.subtle.exportKey('raw', this.encryptionKey);\n const keyData = this.arrayBufferToBase64(exportedKey);\n localStorage.setItem('_sec_key', keyData);\n } catch (error) {\n // Silent fail - encryption not available\n }\n }\n\n /**\n * Encrypt data\n */\n private async encrypt(data: string): Promise<string> {\n if (!this.encryptionKey || !window.crypto?.subtle) {\n throw new Error('Encryption not available');\n }\n\n const encoder = new TextEncoder();\n const dataBuffer = encoder.encode(data);\n const iv = window.crypto.getRandomValues(new Uint8Array(12));\n\n const encrypted = await window.crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n this.encryptionKey,\n dataBuffer\n );\n\n // Combine IV and encrypted data\n const combined = new Uint8Array(iv.length + encrypted.byteLength);\n combined.set(iv);\n combined.set(new Uint8Array(encrypted), iv.length);\n\n return this.arrayBufferToBase64(combined.buffer);\n }\n\n /**\n * Decrypt data\n */\n private async decrypt(encryptedData: string): Promise<string> {\n if (!this.encryptionKey || !window.crypto?.subtle) {\n throw new Error('Decryption not available');\n }\n\n const combined = this.base64ToArrayBuffer(encryptedData);\n const iv = combined.slice(0, 12);\n const encrypted = combined.slice(12);\n\n const decrypted = await window.crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n this.encryptionKey,\n encrypted\n );\n\n const decoder = new TextDecoder();\n return decoder.decode(decrypted);\n }\n\n /**\n * Convert ArrayBuffer to base64\n */\n private arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n /**\n * Convert base64 to ArrayBuffer\n */\n private base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes.buffer;\n }\n}\n\nexport const secureStorage = new SecureStorageImpl();\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  useAccessLevel,
4
4
  useCan,
5
5
  useMultiplePermissions
6
- } from "./chunk-L6PGMCMD.js";
6
+ } from "./chunk-BLCXZEYF.js";
7
7
  import {
8
8
  OrganisationContextRequiredError,
9
9
  RBACCache,
@@ -12,13 +12,13 @@ import {
12
12
  } from "./chunk-BVYWGZVV.js";
13
13
  import {
14
14
  useSecureDataAccess
15
- } from "./chunk-LTV3XIJJ.js";
15
+ } from "./chunk-T6JN6LH6.js";
16
16
  import {
17
17
  init_UnifiedAuthProvider
18
- } from "./chunk-2TWNJ46Y.js";
18
+ } from "./chunk-6LAAY47Q.js";
19
19
  import {
20
20
  useUnifiedAuth
21
- } from "./chunk-4MT5BGGL.js";
21
+ } from "./chunk-YCWDTTUK.js";
22
22
  import {
23
23
  getCurrentAppName
24
24
  } from "./chunk-Q5QRDWKI.js";
@@ -1898,4 +1898,4 @@ export {
1898
1898
  getPermissionsForRole,
1899
1899
  ALL_PERMISSIONS
1900
1900
  };
1901
- //# sourceMappingURL=chunk-FHWWBIHA.js.map
1901
+ //# sourceMappingURL=chunk-BCIBECNB.js.map
@@ -0,0 +1,21 @@
1
+ // src/utils/validation/schema.ts
2
+ import { z } from "zod";
3
+ function pickSchema(schema, keys) {
4
+ const shape = Object.entries(schema.shape).filter(([key]) => keys.includes(key)).reduce((acc, [key, value]) => {
5
+ acc[key] = value;
6
+ return acc;
7
+ }, {});
8
+ return z.object(shape);
9
+ }
10
+ function combineSchemas(schemas) {
11
+ return schemas.reduce(
12
+ (merged, schema) => merged.merge(schema),
13
+ z.object({})
14
+ );
15
+ }
16
+
17
+ export {
18
+ pickSchema,
19
+ combineSchemas
20
+ };
21
+ //# sourceMappingURL=chunk-BJPBT3CU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/validation/schema.ts"],"sourcesContent":["/**\n * @file Schema utility functions\n * @package @jmruthers/pace-core\n * @module Utils/Validation/Schema\n * @since 0.1.0\n * \n * Utility functions for working with Zod schemas.\n */\n\nimport { z } from 'zod';\n\n/**\n * Creates a subset schema from a larger schema\n * \n * @param schema - Original Zod schema\n * @param keys - Array of keys to pick from the schema\n * @returns New schema with only the selected fields\n */\nexport function pickSchema<T extends z.ZodObject<any, any, any>, K extends keyof z.infer<T>>(\n schema: T,\n keys: K[]\n): z.ZodObject<Pick<z.infer<T>, K>> {\n const shape = Object.entries(schema.shape)\n .filter(([key]) => keys.includes(key as K))\n .reduce((acc, [key, value]) => {\n (acc as Record<string, unknown>)[key] = value as unknown;\n return acc;\n }, {} as Record<string, unknown>);\n\n return z.object(shape as Record<string, z.ZodTypeAny>) as z.ZodObject<Pick<z.infer<T>, K>>;\n}\n\n/**\n * Combines multiple Zod schemas into one\n * \n * @param schemas - Array of Zod schemas to combine\n * @returns Combined schema\n */\nexport function combineSchemas<T extends z.ZodObject<any, any, any>[]>(\n schemas: T\n): z.ZodObject<any, any, any> {\n return schemas.reduce(\n (merged, schema) => merged.merge(schema),\n z.object({})\n );\n}\n\n"],"mappings":";AASA,SAAS,SAAS;AASX,SAAS,WACd,QACA,MACkC;AAClC,QAAM,QAAQ,OAAO,QAAQ,OAAO,KAAK,EACtC,OAAO,CAAC,CAAC,GAAG,MAAM,KAAK,SAAS,GAAQ,CAAC,EACzC,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM;AAC7B,IAAC,IAAgC,GAAG,IAAI;AACxC,WAAO;AAAA,EACT,GAAG,CAAC,CAA4B;AAElC,SAAO,EAAE,OAAO,KAAqC;AACvD;AAQO,SAAS,eACd,SAC4B;AAC5B,SAAO,QAAQ;AAAA,IACb,CAAC,QAAQ,WAAW,OAAO,MAAM,MAAM;AAAA,IACvC,EAAE,OAAO,CAAC,CAAC;AAAA,EACb;AACF;","names":[]}
@@ -11,11 +11,11 @@ import {
11
11
  init_useOrganisations,
12
12
  useEvents,
13
13
  useOrganisations
14
- } from "./chunk-SL2YQDR6.js";
14
+ } from "./chunk-MA6EPSGZ.js";
15
15
  import {
16
16
  init_UnifiedAuthProvider,
17
17
  useUnifiedAuth
18
- } from "./chunk-4MT5BGGL.js";
18
+ } from "./chunk-YCWDTTUK.js";
19
19
  import {
20
20
  getCurrentAppName
21
21
  } from "./chunk-Q5QRDWKI.js";
@@ -826,4 +826,4 @@ export {
826
826
  useCachedPermissions,
827
827
  useRoleManagement
828
828
  };
829
- //# sourceMappingURL=chunk-L6PGMCMD.js.map
829
+ //# sourceMappingURL=chunk-BLCXZEYF.js.map