@jmruthers/pace-core 0.5.190 → 0.5.193

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 (334) hide show
  1. package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
  2. package/dist/{DataTable-ON3IXISJ.js → DataTable-5FU7IESH.js} +7 -6
  3. package/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
  4. package/dist/{PublicPageProvider-C4uxosp6.d.ts → PublicPageProvider-C0Sm_e5k.d.ts} +4 -2
  5. package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
  6. package/dist/{UnifiedAuthProvider-X5NXANVI.js → UnifiedAuthProvider-RGJTDE2C.js} +3 -3
  7. package/dist/{api-I6UCQ5S6.js → api-N774RPUA.js} +2 -2
  8. package/dist/chunk-6C4YBBJM 5.js +628 -0
  9. package/dist/chunk-7D4SUZUM.js 2.map +1 -0
  10. package/dist/{chunk-73HSNNOQ.js → chunk-7EQTDTTJ.js} +47 -74
  11. package/dist/chunk-7EQTDTTJ.js 2.map +1 -0
  12. package/dist/chunk-7EQTDTTJ.js.map +1 -0
  13. package/dist/{chunk-J2XXC7R5.js → chunk-7FLMSG37.js} +409 -244
  14. package/dist/chunk-7FLMSG37.js 2.map +1 -0
  15. package/dist/chunk-7FLMSG37.js.map +1 -0
  16. package/dist/{chunk-NIU6J6OX.js → chunk-BC4IJKSL.js} +23 -32
  17. package/dist/chunk-BC4IJKSL.js.map +1 -0
  18. package/dist/{chunk-SDMHPX3X.js → chunk-E3SPN4VZ 5.js } +198 -53
  19. package/dist/chunk-E3SPN4VZ.js +12917 -0
  20. package/dist/{chunk-SDMHPX3X.js.map → chunk-E3SPN4VZ.js.map} +1 -1
  21. package/dist/chunk-E66EQZE6 5.js +37 -0
  22. package/dist/chunk-E66EQZE6.js 2.map +1 -0
  23. package/dist/{chunk-DZWK57KZ.js → chunk-G37KK66H.js} +1 -1
  24. package/dist/{chunk-DZWK57KZ.js.map → chunk-G37KK66H.js.map} +1 -1
  25. package/dist/{chunk-STYK4OH2.js → chunk-HWIIPPNI.js} +44 -225
  26. package/dist/chunk-HWIIPPNI.js.map +1 -0
  27. package/dist/chunk-I7PSE6JW 5.js +191 -0
  28. package/dist/chunk-I7PSE6JW.js 2.map +1 -0
  29. package/dist/{chunk-Y4BUBBHD.js → chunk-IIELH4DL.js} +211 -136
  30. package/dist/chunk-IIELH4DL.js.map +1 -0
  31. package/dist/{chunk-RUYZKXOD.js → chunk-KNC55RTG.js} +17 -5
  32. package/dist/chunk-KNC55RTG.js 5.map +1 -0
  33. package/dist/chunk-KNC55RTG.js.map +1 -0
  34. package/dist/chunk-KQCRWDSA.js 5.map +1 -0
  35. package/dist/{chunk-4QYC5L4K.js → chunk-LFNCN2SP.js} +26 -30
  36. package/dist/chunk-LFNCN2SP.js 2.map +1 -0
  37. package/dist/chunk-LFNCN2SP.js.map +1 -0
  38. package/dist/chunk-LMC26NLJ 2.js +84 -0
  39. package/dist/{chunk-VVBAW5A5.js → chunk-NOAYCWCX 5.js } +118 -110
  40. package/dist/chunk-NOAYCWCX.js +4993 -0
  41. package/dist/chunk-NOAYCWCX.js.map +1 -0
  42. package/dist/chunk-QWWZ5CAQ.js 3.map +1 -0
  43. package/dist/chunk-QXHPKYJV 3.js +113 -0
  44. package/dist/chunk-R77UEZ4E 3.js +68 -0
  45. package/dist/chunk-VBXEHIUJ.js 6.map +1 -0
  46. package/dist/{chunk-HQVPB5MZ.js → chunk-XNXXZ43G.js} +77 -33
  47. package/dist/chunk-XNXXZ43G.js.map +1 -0
  48. package/dist/chunk-ZSAAAMVR 6.js +25 -0
  49. package/dist/components.d.ts +4 -4
  50. package/dist/components.js +8 -8
  51. package/dist/components.js 5.map +1 -0
  52. package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
  53. package/dist/hooks.d.ts +12 -12
  54. package/dist/hooks.js +9 -9
  55. package/dist/index.d.ts +11 -11
  56. package/dist/index.js +20 -27
  57. package/dist/index.js.map +1 -1
  58. package/dist/providers.d.ts +3 -3
  59. package/dist/providers.js +2 -2
  60. package/dist/rbac/index.d.ts +2 -20
  61. package/dist/rbac/index.js +7 -9
  62. package/dist/styles/index 2.js +12 -0
  63. package/dist/styles/index.js 5.map +1 -0
  64. package/dist/theming/runtime 5.js +19 -0
  65. package/dist/theming/runtime.js 5.map +1 -0
  66. package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
  67. package/dist/types.d.ts +2 -2
  68. package/dist/{usePublicRouteParams-DxIDS4bC.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +1 -1
  69. package/dist/utils.d.ts +8 -8
  70. package/dist/utils.js +2 -2
  71. package/docs/api/classes/ColumnFactory.md +1 -1
  72. package/docs/api/classes/ErrorBoundary.md +1 -1
  73. package/docs/api/classes/InvalidScopeError.md +1 -1
  74. package/docs/api/classes/Logger.md +1 -1
  75. package/docs/api/classes/MissingUserContextError.md +1 -1
  76. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  77. package/docs/api/classes/PermissionDeniedError.md +2 -2
  78. package/docs/api/classes/RBACAuditManager.md +2 -2
  79. package/docs/api/classes/RBACCache.md +1 -1
  80. package/docs/api/classes/RBACEngine.md +2 -2
  81. package/docs/api/classes/RBACError.md +1 -1
  82. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  83. package/docs/api/classes/SecureSupabaseClient.md +10 -10
  84. package/docs/api/classes/StorageUtils.md +1 -1
  85. package/docs/api/enums/FileCategory.md +1 -1
  86. package/docs/api/enums/LogLevel.md +1 -1
  87. package/docs/api/enums/RBACErrorCode.md +1 -1
  88. package/docs/api/enums/RPCFunction.md +1 -1
  89. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  90. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  91. package/docs/api/interfaces/AggregateConfig.md +1 -1
  92. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  93. package/docs/api/interfaces/AvatarProps.md +1 -1
  94. package/docs/api/interfaces/BadgeProps.md +1 -1
  95. package/docs/api/interfaces/ButtonProps.md +1 -1
  96. package/docs/api/interfaces/CalendarProps.md +1 -1
  97. package/docs/api/interfaces/CardProps.md +1 -1
  98. package/docs/api/interfaces/ColorPalette.md +1 -1
  99. package/docs/api/interfaces/ColorShade.md +1 -1
  100. package/docs/api/interfaces/ComplianceResult.md +1 -1
  101. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  102. package/docs/api/interfaces/DataRecord.md +1 -1
  103. package/docs/api/interfaces/DataTableAction.md +1 -1
  104. package/docs/api/interfaces/DataTableColumn.md +1 -1
  105. package/docs/api/interfaces/DataTableProps.md +1 -1
  106. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  107. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  109. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  110. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  112. package/docs/api/interfaces/ExportColumn.md +1 -1
  113. package/docs/api/interfaces/ExportOptions.md +1 -1
  114. package/docs/api/interfaces/FileDisplayProps.md +24 -11
  115. package/docs/api/interfaces/FileMetadata.md +1 -1
  116. package/docs/api/interfaces/FileReference.md +1 -1
  117. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  118. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  119. package/docs/api/interfaces/FileUploadProps.md +1 -1
  120. package/docs/api/interfaces/FooterProps.md +1 -1
  121. package/docs/api/interfaces/FormFieldProps.md +1 -1
  122. package/docs/api/interfaces/FormProps.md +1 -1
  123. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  124. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  125. package/docs/api/interfaces/InputProps.md +1 -1
  126. package/docs/api/interfaces/LabelProps.md +1 -1
  127. package/docs/api/interfaces/LoggerConfig.md +1 -1
  128. package/docs/api/interfaces/LoginFormProps.md +1 -1
  129. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  130. package/docs/api/interfaces/NavigationContextType.md +1 -1
  131. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  132. package/docs/api/interfaces/NavigationItem.md +1 -1
  133. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  134. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  135. package/docs/api/interfaces/Organisation.md +1 -1
  136. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  137. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  138. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  139. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  140. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  141. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  142. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  143. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  144. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  145. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  146. package/docs/api/interfaces/PaletteData.md +1 -1
  147. package/docs/api/interfaces/ParsedAddress.md +2 -2
  148. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  149. package/docs/api/interfaces/ProgressProps.md +1 -1
  150. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  151. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  152. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  153. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  154. package/docs/api/interfaces/QuickFix.md +1 -1
  155. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  156. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  157. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  158. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  159. package/docs/api/interfaces/RBACConfig.md +2 -2
  160. package/docs/api/interfaces/RBACContext.md +1 -1
  161. package/docs/api/interfaces/RBACLogger.md +1 -1
  162. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  163. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  164. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  165. package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
  166. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  167. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  168. package/docs/api/interfaces/RBACResult.md +1 -1
  169. package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
  170. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  171. package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
  172. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  173. package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
  174. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  175. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  176. package/docs/api/interfaces/RBACRolesListResult.md +2 -2
  177. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  178. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  179. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  180. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  181. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  182. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  183. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  184. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  185. package/docs/api/interfaces/RouteConfig.md +2 -2
  186. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  187. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  188. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  189. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  190. package/docs/api/interfaces/SetupIssue.md +1 -1
  191. package/docs/api/interfaces/StorageConfig.md +1 -1
  192. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  193. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  194. package/docs/api/interfaces/StorageListOptions.md +1 -1
  195. package/docs/api/interfaces/StorageListResult.md +1 -1
  196. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  197. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  198. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  199. package/docs/api/interfaces/StyleImport.md +1 -1
  200. package/docs/api/interfaces/SwitchProps.md +1 -1
  201. package/docs/api/interfaces/TabsContentProps.md +1 -1
  202. package/docs/api/interfaces/TabsListProps.md +1 -1
  203. package/docs/api/interfaces/TabsProps.md +1 -1
  204. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  205. package/docs/api/interfaces/TextareaProps.md +1 -1
  206. package/docs/api/interfaces/ToastActionElement.md +1 -1
  207. package/docs/api/interfaces/ToastProps.md +1 -1
  208. package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
  209. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  210. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  211. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  212. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  213. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  214. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  215. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  216. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  217. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  218. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  219. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  220. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  221. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  222. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  223. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  224. package/docs/api/interfaces/UserEventAccess.md +1 -1
  225. package/docs/api/interfaces/UserMenuProps.md +1 -1
  226. package/docs/api/interfaces/UserProfile.md +1 -1
  227. package/docs/api/modules.md +202 -217
  228. package/docs/migration/README.md +18 -0
  229. package/docs/migration/database-changes-december-2025.md +768 -0
  230. package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
  231. package/docs/rbac/event-based-apps.md +124 -6
  232. package/package.json +1 -1
  233. package/scripts/check-pace-core-compliance.cjs +292 -57
  234. package/src/__tests__/public-recipe-view.test.ts +10 -10
  235. package/src/__tests__/rls-policies.test.ts +16 -14
  236. package/src/components/AddressField/README.md +6 -6
  237. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
  238. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
  239. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
  240. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
  241. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
  242. package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
  243. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +75 -11
  244. package/src/components/DataTable/components/UnifiedTableBody.tsx +85 -14
  245. package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
  246. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -1
  247. package/src/components/FileDisplay/FileDisplay.tsx +16 -4
  248. package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
  249. package/src/components/NavigationMenu/NavigationMenu.tsx +1 -10
  250. package/src/components/OrganisationSelector/OrganisationSelector.tsx +35 -16
  251. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +25 -2
  252. package/src/components/PaceAppLayout/PaceAppLayout.tsx +97 -68
  253. package/src/components/PaceLoginPage/PaceLoginPage.tsx +0 -7
  254. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
  255. package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
  256. package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
  257. package/src/components/Select/Select.test.tsx +4 -1
  258. package/src/components/Select/Select.tsx +60 -15
  259. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +192 -0
  260. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +741 -0
  261. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +703 -0
  262. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +581 -0
  263. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +23 -15
  264. package/src/hooks/public/usePublicEvent.ts +8 -8
  265. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  266. package/src/hooks/services/useAuthService.ts +21 -3
  267. package/src/hooks/services/useEventService.ts +21 -3
  268. package/src/hooks/services/useInactivityService.ts +21 -3
  269. package/src/hooks/services/useOrganisationService.ts +21 -3
  270. package/src/hooks/useFileDisplay.ts +18 -26
  271. package/src/hooks/useQueryCache.ts +6 -6
  272. package/src/hooks/useSecureDataAccess.test.ts +24 -17
  273. package/src/hooks/useSecureDataAccess.ts +18 -13
  274. package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
  275. package/src/providers/services/EventServiceProvider.tsx +0 -8
  276. package/src/providers/services/UnifiedAuthProvider.tsx +174 -24
  277. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +10 -16
  278. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
  279. package/src/rbac/adapters.tsx +3 -22
  280. package/src/rbac/api.test.ts +2 -2
  281. package/src/rbac/api.ts +7 -1
  282. package/src/rbac/components/EnhancedNavigationMenu.tsx +2 -15
  283. package/src/rbac/components/NavigationGuard.tsx +1 -10
  284. package/src/rbac/components/NavigationProvider.tsx +0 -1
  285. package/src/rbac/components/PermissionEnforcer.tsx +45 -12
  286. package/src/rbac/components/SecureDataProvider.tsx +0 -1
  287. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
  288. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
  289. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
  290. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
  291. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
  292. package/src/rbac/engine.ts +14 -2
  293. package/src/rbac/hooks/index.ts +0 -3
  294. package/src/rbac/hooks/usePermissions.ts +51 -11
  295. package/src/rbac/hooks/useRBAC.simple.test.ts +95 -0
  296. package/src/rbac/hooks/useRBAC.ts +3 -13
  297. package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
  298. package/src/rbac/hooks/useResolvedScope.ts +58 -33
  299. package/src/rbac/hooks/useSecureSupabase.ts +4 -9
  300. package/src/rbac/secureClient.ts +31 -0
  301. package/src/rbac/utils/__tests__/eventContext.test.ts +2 -2
  302. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +490 -0
  303. package/src/rbac/utils/eventContext.ts +5 -2
  304. package/src/services/AuthService.ts +37 -8
  305. package/src/services/EventService.ts +4 -57
  306. package/src/services/InactivityService.ts +127 -34
  307. package/src/services/OrganisationService.ts +160 -149
  308. package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
  309. package/src/services/__tests__/OrganisationService.test.ts +218 -86
  310. package/src/types/database.generated.ts +166 -201
  311. package/src/types/supabase.ts +2 -2
  312. package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
  313. package/src/utils/file-reference/index.ts +4 -4
  314. package/src/utils/google-places/googlePlacesUtils.ts +1 -1
  315. package/src/utils/google-places/types.ts +1 -1
  316. package/src/utils/request-deduplication.ts +4 -4
  317. package/src/utils/security/secureDataAccess.test.ts +1 -1
  318. package/src/utils/security/secureDataAccess.ts +7 -4
  319. package/src/utils/storage/README.md +1 -1
  320. package/dist/chunk-4QYC5L4K.js.map +0 -1
  321. package/dist/chunk-73HSNNOQ.js.map +0 -1
  322. package/dist/chunk-HQVPB5MZ.js.map +0 -1
  323. package/dist/chunk-J2XXC7R5.js.map +0 -1
  324. package/dist/chunk-NIU6J6OX.js.map +0 -1
  325. package/dist/chunk-RUYZKXOD.js.map +0 -1
  326. package/dist/chunk-STYK4OH2.js.map +0 -1
  327. package/dist/chunk-VVBAW5A5.js.map +0 -1
  328. package/dist/chunk-Y4BUBBHD.js.map +0 -1
  329. package/scripts/check-pace-core-compliance.js +0 -512
  330. package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
  331. package/src/utils/context/superAdminOverride.ts +0 -58
  332. /package/dist/{DataTable-ON3IXISJ.js.map → DataTable-5FU7IESH.js.map} +0 -0
  333. /package/dist/{UnifiedAuthProvider-X5NXANVI.js.map → UnifiedAuthProvider-RGJTDE2C.js.map} +0 -0
  334. /package/dist/{api-I6UCQ5S6.js.map → api-N774RPUA.js.map} +0 -0
@@ -0,0 +1,703 @@
1
+ import React from 'react';
2
+ import { renderHook, waitFor } from '@testing-library/react';
3
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
4
+ import { usePublicEvent, clearPublicEventCache, getPublicEventCacheStats } from '../public/usePublicEvent';
5
+ import { usePublicPageContext } from '../../components/PublicLayout/PublicPageProvider';
6
+
7
+ // Mock the PublicPageProvider
8
+ vi.mock('../../components/PublicLayout/PublicPageProvider', () => ({
9
+ usePublicPageContext: vi.fn(() => ({
10
+ environment: {
11
+ supabaseUrl: 'https://test.supabase.co',
12
+ supabaseKey: 'test-anon-key'
13
+ }
14
+ }))
15
+ }));
16
+
17
+ // Mock Supabase client
18
+ const mockSupabaseClient = {
19
+ rpc: vi.fn(),
20
+ from: vi.fn(() => ({
21
+ select: vi.fn(() => ({
22
+ eq: vi.fn(() => ({
23
+ eq: vi.fn(() => ({
24
+ not: vi.fn(() => ({
25
+ limit: vi.fn(() => ({
26
+ single: vi.fn()
27
+ }))
28
+ }))
29
+ }))
30
+ }))
31
+ }))
32
+ }))
33
+ };
34
+
35
+ // Mock createClient
36
+ vi.mock('@supabase/supabase-js', () => ({
37
+ createClient: vi.fn(() => mockSupabaseClient)
38
+ }));
39
+
40
+ // Mock environment variables
41
+ const originalEnv = import.meta.env;
42
+
43
+ describe('usePublicEvent - Simple Tests', () => {
44
+ let originalMode: string | undefined;
45
+
46
+ beforeEach(() => {
47
+ // Ensure logger is enabled by setting MODE to development
48
+ originalMode = import.meta.env.MODE;
49
+ (import.meta.env as any).MODE = 'development';
50
+
51
+ import('../../utils/core/logger').then(({ Logger, LogLevel }) => {
52
+ Logger.configure({
53
+ level: LogLevel.DEBUG,
54
+ includeTimestamp: false,
55
+ includeComponent: true,
56
+ });
57
+ });
58
+
59
+ vi.clearAllMocks();
60
+ clearPublicEventCache();
61
+
62
+ // Reset environment
63
+ Object.defineProperty(import.meta, 'env', {
64
+ value: {
65
+ VITE_SUPABASE_URL: 'https://test.supabase.co',
66
+ VITE_SUPABASE_ANON_KEY: 'test-anon-key'
67
+ },
68
+ writable: true
69
+ });
70
+
71
+ // Mock window object
72
+ Object.defineProperty(window, 'location', {
73
+ value: { href: 'https://test.com' },
74
+ writable: true
75
+ });
76
+ });
77
+
78
+ afterEach(() => {
79
+ // Restore original mode
80
+ if (originalMode !== undefined) {
81
+ (import.meta.env as any).MODE = originalMode;
82
+ }
83
+ vi.clearAllMocks();
84
+ clearPublicEventCache();
85
+ Object.defineProperty(import.meta, 'env', {
86
+ value: originalEnv,
87
+ writable: true
88
+ });
89
+ });
90
+
91
+ describe('Basic Functionality', () => {
92
+ it('should initialize with loading state', () => {
93
+ const { result } = renderHook(() => usePublicEvent('test-event'));
94
+
95
+ expect(result.current.isLoading).toBe(true);
96
+ expect(result.current.event).toBe(null);
97
+ expect(result.current.error).toBe(null);
98
+ });
99
+
100
+ it('should fetch event data successfully via RPC', async () => {
101
+ const mockEventData = {
102
+ event_id: '123',
103
+ event_name: 'Test Event',
104
+ event_date: '2024-01-01',
105
+ event_venue: 'Test Venue',
106
+ event_participants: 100,
107
+ event_colours: { primary: '#000000' },
108
+ organisation_id: 'org-123',
109
+ event_days: 1,
110
+ event_typicalunit: 'km',
111
+ event_rounddown: false,
112
+ event_youthmultiplier: 1.0,
113
+ event_catering_email: 'test@example.com',
114
+ event_news: 'Test news',
115
+ event_billing: 'Test billing',
116
+ event_footer: 'Test footer',
117
+ event_email: 'event@example.com',
118
+ event_logo: null
119
+ };
120
+
121
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
122
+ data: [mockEventData],
123
+ error: null
124
+ });
125
+
126
+ const { result } = renderHook(() => usePublicEvent('test-event'));
127
+
128
+ await waitFor(() => {
129
+ expect(result.current.isLoading).toBe(false);
130
+ });
131
+
132
+ expect(result.current.event).toEqual({
133
+ id: '123',
134
+ event_id: '123',
135
+ event_name: 'Test Event',
136
+ event_code: 'test-event',
137
+ event_date: '2024-01-01',
138
+ event_venue: 'Test Venue',
139
+ event_participants: 100,
140
+ event_logo: null,
141
+ event_colours: { primary: '#000000' },
142
+ organisation_id: 'org-123',
143
+ is_visible: true,
144
+ created_at: expect.any(String),
145
+ updated_at: expect.any(String)
146
+ });
147
+ expect(result.current.error).toBe(null);
148
+ });
149
+
150
+ it('should handle event not found', async () => {
151
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
152
+ data: [],
153
+ error: null
154
+ });
155
+
156
+ const { result } = renderHook(() => usePublicEvent('nonexistent-event'));
157
+
158
+ await waitFor(() => {
159
+ expect(result.current.isLoading).toBe(false);
160
+ });
161
+
162
+ expect(result.current.event).toBe(null);
163
+ expect(result.current.error).toEqual(new Error('Event not found'));
164
+ });
165
+
166
+ it('should handle invalid event code', async () => {
167
+ const { result } = renderHook(() => usePublicEvent(''));
168
+
169
+ await waitFor(() => {
170
+ expect(result.current.isLoading).toBe(false);
171
+ });
172
+
173
+ expect(result.current.event).toBe(null);
174
+ expect(result.current.error).toBeInstanceOf(Error);
175
+ expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
176
+ });
177
+ });
178
+
179
+ describe('Caching', () => {
180
+ it('should cache event data', async () => {
181
+ const mockEventData = {
182
+ event_id: '123',
183
+ event_name: 'Test Event',
184
+ event_date: '2024-01-01',
185
+ event_venue: 'Test Venue',
186
+ event_participants: 100,
187
+ event_colours: { primary: '#000000' },
188
+ organisation_id: 'org-123',
189
+ event_days: 1,
190
+ event_typicalunit: 'km',
191
+ event_rounddown: false,
192
+ event_youthmultiplier: 1.0,
193
+ event_catering_email: 'test@example.com',
194
+ event_news: 'Test news',
195
+ event_billing: 'Test billing',
196
+ event_footer: 'Test footer',
197
+ event_email: 'event@example.com',
198
+ event_logo: null
199
+ };
200
+
201
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
202
+ data: [mockEventData],
203
+ error: null
204
+ });
205
+
206
+ const { result, rerender } = renderHook(() => usePublicEvent('test-event'));
207
+
208
+ await waitFor(() => {
209
+ expect(result.current.isLoading).toBe(false);
210
+ });
211
+
212
+ // Rerender with same event code - should use cache
213
+ rerender();
214
+
215
+ // Should not call RPC again
216
+ expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(1);
217
+ });
218
+ });
219
+
220
+ describe('Provider Context Integration', () => {
221
+ it('should use PublicPageContext when available', async () => {
222
+ const mockContext = {
223
+ environment: {
224
+ supabaseUrl: 'https://context.supabase.co',
225
+ supabaseKey: 'context-anon-key'
226
+ }
227
+ };
228
+
229
+ vi.mocked(usePublicPageContext).mockReturnValue(mockContext);
230
+
231
+ const mockEventData = {
232
+ event_id: '123',
233
+ event_name: 'Test Event',
234
+ event_date: '2024-01-01',
235
+ event_venue: 'Test Venue',
236
+ event_participants: 100,
237
+ event_colours: { primary: '#000000' },
238
+ organisation_id: 'org-123',
239
+ event_days: 1,
240
+ event_typicalunit: 'km',
241
+ event_rounddown: false,
242
+ event_youthmultiplier: 1.0,
243
+ event_catering_email: 'test@example.com',
244
+ event_news: 'Test news',
245
+ event_billing: 'Test billing',
246
+ event_footer: 'Test footer',
247
+ event_email: 'event@example.com',
248
+ event_logo: null
249
+ };
250
+
251
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
252
+ data: [mockEventData],
253
+ error: null
254
+ });
255
+
256
+ const { result } = renderHook(() => usePublicEvent('test-event'));
257
+
258
+ await waitFor(() => {
259
+ expect(result.current.isLoading).toBe(false);
260
+ });
261
+
262
+ expect(result.current.event).toBeTruthy();
263
+ });
264
+ });
265
+
266
+ describe('Cache Management Utilities', () => {
267
+ it('should clear public event cache', () => {
268
+ // Add some data to cache
269
+ const stats = getPublicEventCacheStats();
270
+ expect(stats.size).toBe(0);
271
+
272
+ // Clear cache
273
+ clearPublicEventCache();
274
+
275
+ const statsAfter = getPublicEventCacheStats();
276
+ expect(statsAfter.size).toBe(0);
277
+ });
278
+
279
+ it('should get cache statistics', () => {
280
+ const stats = getPublicEventCacheStats();
281
+ expect(stats).toEqual({
282
+ size: 0,
283
+ keys: []
284
+ });
285
+ });
286
+ });
287
+
288
+ describe('Refetch Functionality', () => {
289
+ it('refetches data when refetch is called', async () => {
290
+ const mockEventData = {
291
+ event_id: '123',
292
+ event_name: 'Test Event',
293
+ event_date: '2024-01-01',
294
+ event_venue: 'Test Venue',
295
+ event_participants: 100,
296
+ event_colours: { primary: '#000000' },
297
+ organisation_id: 'org-123',
298
+ event_days: 1,
299
+ event_typicalunit: 'km',
300
+ event_rounddown: false,
301
+ event_youthmultiplier: 1.0,
302
+ event_catering_email: 'test@example.com',
303
+ event_news: 'Test news',
304
+ event_billing: 'Test billing',
305
+ event_footer: 'Test footer',
306
+ event_email: 'event@example.com',
307
+ event_logo: null
308
+ };
309
+
310
+ mockSupabaseClient.rpc.mockResolvedValue({
311
+ data: [mockEventData],
312
+ error: null
313
+ });
314
+
315
+ const { result } = renderHook(() => usePublicEvent('test-event', { enableCache: true }));
316
+
317
+ await waitFor(() => {
318
+ expect(result.current.isLoading).toBe(false);
319
+ });
320
+
321
+ const firstCallCount = mockSupabaseClient.rpc.mock.calls.length;
322
+
323
+ // Update mock to return different data
324
+ const updatedEventData = { ...mockEventData, event_name: 'Updated Event' };
325
+ mockSupabaseClient.rpc.mockResolvedValue({
326
+ data: [updatedEventData],
327
+ error: null
328
+ });
329
+
330
+ await result.current.refetch();
331
+
332
+ await waitFor(() => {
333
+ expect(result.current.event?.event_name).toBe('Updated Event');
334
+ }, { timeout: 2000 });
335
+
336
+ // Should have made another call
337
+ expect(mockSupabaseClient.rpc.mock.calls.length).toBeGreaterThan(firstCallCount);
338
+ });
339
+
340
+ it('clears cache before refetching', async () => {
341
+ const mockEventData = {
342
+ event_id: '123',
343
+ event_name: 'Test Event',
344
+ event_date: '2024-01-01',
345
+ event_venue: 'Test Venue',
346
+ event_participants: 100,
347
+ event_colours: { primary: '#000000' },
348
+ organisation_id: 'org-123',
349
+ event_days: 1,
350
+ event_typicalunit: 'km',
351
+ event_rounddown: false,
352
+ event_youthmultiplier: 1.0,
353
+ event_catering_email: 'test@example.com',
354
+ event_news: 'Test news',
355
+ event_billing: 'Test billing',
356
+ event_footer: 'Test footer',
357
+ event_email: 'event@example.com',
358
+ event_logo: null
359
+ };
360
+
361
+ mockSupabaseClient.rpc.mockResolvedValue({
362
+ data: [mockEventData],
363
+ error: null
364
+ });
365
+
366
+ const { result } = renderHook(() => usePublicEvent('test-event', { enableCache: true }));
367
+
368
+ await waitFor(() => {
369
+ expect(result.current.isLoading).toBe(false);
370
+ });
371
+
372
+ // Verify cache exists
373
+ const statsBefore = getPublicEventCacheStats();
374
+ expect(statsBefore.size).toBeGreaterThan(0);
375
+
376
+ await result.current.refetch();
377
+
378
+ // Cache should be cleared and then repopulated
379
+ await waitFor(() => {
380
+ expect(result.current.event).toBeDefined();
381
+ }, { timeout: 2000 });
382
+ });
383
+ });
384
+
385
+ describe('Caching Options', () => {
386
+ it('respects cacheTtl option', async () => {
387
+ const mockEventData = {
388
+ event_id: '123',
389
+ event_name: 'Test Event',
390
+ event_date: '2024-01-01',
391
+ event_venue: 'Test Venue',
392
+ event_participants: 100,
393
+ event_colours: { primary: '#000000' },
394
+ organisation_id: 'org-123',
395
+ event_days: 1,
396
+ event_typicalunit: 'km',
397
+ event_rounddown: false,
398
+ event_youthmultiplier: 1.0,
399
+ event_catering_email: 'test@example.com',
400
+ event_news: 'Test news',
401
+ event_billing: 'Test billing',
402
+ event_footer: 'Test footer',
403
+ event_email: 'event@example.com',
404
+ event_logo: null
405
+ };
406
+
407
+ mockSupabaseClient.rpc.mockResolvedValue({
408
+ data: [mockEventData],
409
+ error: null
410
+ });
411
+
412
+ const { result } = renderHook(() => usePublicEvent('test-event', {
413
+ enableCache: true,
414
+ cacheTtl: 1000 // 1 second
415
+ }));
416
+
417
+ await waitFor(() => {
418
+ expect(result.current.isLoading).toBe(false);
419
+ });
420
+
421
+ expect(result.current.event).toBeTruthy();
422
+ });
423
+
424
+ it('disables caching when enableCache is false', async () => {
425
+ const mockEventData = {
426
+ event_id: '123',
427
+ event_name: 'Test Event',
428
+ event_date: '2024-01-01',
429
+ event_venue: 'Test Venue',
430
+ event_participants: 100,
431
+ event_colours: { primary: '#000000' },
432
+ organisation_id: 'org-123',
433
+ event_days: 1,
434
+ event_typicalunit: 'km',
435
+ event_rounddown: false,
436
+ event_youthmultiplier: 1.0,
437
+ event_catering_email: 'test@example.com',
438
+ event_news: 'Test news',
439
+ event_billing: 'Test billing',
440
+ event_footer: 'Test footer',
441
+ event_email: 'event@example.com',
442
+ event_logo: null
443
+ };
444
+
445
+ mockSupabaseClient.rpc.mockResolvedValue({
446
+ data: [mockEventData],
447
+ error: null
448
+ });
449
+
450
+ const { result, rerender } = renderHook(() => usePublicEvent('test-event', {
451
+ enableCache: false
452
+ }));
453
+
454
+ await waitFor(() => {
455
+ expect(result.current.isLoading).toBe(false);
456
+ });
457
+
458
+ const firstCallCount = mockSupabaseClient.rpc.mock.calls.length;
459
+
460
+ // Rerender - should make another call since caching is disabled
461
+ rerender();
462
+
463
+ await waitFor(() => {
464
+ expect(result.current.event).toBeTruthy();
465
+ }, { timeout: 2000 });
466
+ });
467
+ });
468
+
469
+ describe('Error Handling', () => {
470
+ it('handles RPC errors that are not schema cache issues', async () => {
471
+ const rpcError = { message: 'Permission denied', code: 'PGRST301' };
472
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
473
+ data: null,
474
+ error: rpcError
475
+ });
476
+
477
+ const { result } = renderHook(() => usePublicEvent('test-event'));
478
+
479
+ await waitFor(() => {
480
+ expect(result.current.isLoading).toBe(false);
481
+ }, { timeout: 2000 });
482
+
483
+ expect(result.current.error).toBeInstanceOf(Error);
484
+ expect(result.current.error?.message).toBe('Permission denied');
485
+ expect(result.current.event).toBe(null);
486
+ });
487
+
488
+ it('handles exceptions during event fetch', async () => {
489
+ const error = new Error('Network timeout');
490
+ // Mock RPC to reject, and table access to also fail
491
+ mockSupabaseClient.rpc.mockRejectedValue(error);
492
+ mockSupabaseClient.from.mockReturnValue({
493
+ select: vi.fn().mockReturnThis(),
494
+ eq: vi.fn().mockReturnThis(),
495
+ not: vi.fn().mockReturnThis(),
496
+ limit: vi.fn().mockReturnThis(),
497
+ single: vi.fn().mockRejectedValue(error)
498
+ });
499
+
500
+ const { result } = renderHook(() => usePublicEvent('test-event'));
501
+
502
+ await waitFor(() => {
503
+ expect(result.current.isLoading).toBe(false);
504
+ }, { timeout: 2000 });
505
+
506
+ expect(result.current.error).toBeInstanceOf(Error);
507
+ expect(result.current.error?.message).toBe('Network timeout');
508
+ expect(result.current.event).toBe(null);
509
+ });
510
+
511
+ it('handles non-Error exceptions', async () => {
512
+ const stringError = 'String error';
513
+ // Mock RPC to reject, and table access to also fail
514
+ mockSupabaseClient.rpc.mockRejectedValue(stringError);
515
+ mockSupabaseClient.from.mockReturnValue({
516
+ select: vi.fn().mockReturnThis(),
517
+ eq: vi.fn().mockReturnThis(),
518
+ not: vi.fn().mockReturnThis(),
519
+ limit: vi.fn().mockReturnThis(),
520
+ single: vi.fn().mockRejectedValue(stringError)
521
+ });
522
+
523
+ const { result } = renderHook(() => usePublicEvent('test-event'));
524
+
525
+ await waitFor(() => {
526
+ expect(result.current.isLoading).toBe(false);
527
+ }, { timeout: 2000 });
528
+
529
+ expect(result.current.error).toBeInstanceOf(Error);
530
+ expect(result.current.error?.message).toBe('Unknown error occurred');
531
+ });
532
+ });
533
+
534
+ describe('Parameter Changes', () => {
535
+ it('refetches when eventCode changes', async () => {
536
+ const mockEventData1 = {
537
+ event_id: '123',
538
+ event_name: 'Event 1',
539
+ event_date: '2024-01-01',
540
+ event_venue: 'Venue 1',
541
+ event_participants: 100,
542
+ event_colours: { primary: '#000000' },
543
+ organisation_id: 'org-123',
544
+ event_days: 1,
545
+ event_typicalunit: 'km',
546
+ event_rounddown: false,
547
+ event_youthmultiplier: 1.0,
548
+ event_catering_email: 'test@example.com',
549
+ event_news: 'Test news',
550
+ event_billing: 'Test billing',
551
+ event_footer: 'Test footer',
552
+ event_email: 'event@example.com',
553
+ event_logo: null
554
+ };
555
+
556
+ const mockEventData2 = {
557
+ ...mockEventData1,
558
+ event_id: '456',
559
+ event_name: 'Event 2'
560
+ };
561
+
562
+ mockSupabaseClient.rpc
563
+ .mockResolvedValueOnce({
564
+ data: [mockEventData1],
565
+ error: null
566
+ })
567
+ .mockResolvedValueOnce({
568
+ data: [mockEventData2],
569
+ error: null
570
+ });
571
+
572
+ const { result, rerender } = renderHook(
573
+ ({ eventCode }) => usePublicEvent(eventCode),
574
+ { initialProps: { eventCode: 'event-1' } }
575
+ );
576
+
577
+ await waitFor(() => {
578
+ expect(result.current.isLoading).toBe(false);
579
+ }, { timeout: 2000 });
580
+
581
+ expect(result.current.event?.event_name).toBe('Event 1');
582
+
583
+ rerender({ eventCode: 'event-2' });
584
+
585
+ await waitFor(() => {
586
+ expect(result.current.isLoading).toBe(false);
587
+ }, { timeout: 2000 });
588
+
589
+ expect(result.current.event?.event_name).toBe('Event 2');
590
+ });
591
+ });
592
+
593
+ describe('Edge Cases', () => {
594
+ it('should handle null event data from RPC', async () => {
595
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
596
+ data: null,
597
+ error: null
598
+ });
599
+
600
+ const { result } = renderHook(() => usePublicEvent('test-event'));
601
+
602
+ await waitFor(() => {
603
+ expect(result.current.isLoading).toBe(false);
604
+ }, { timeout: 2000 });
605
+
606
+ expect(result.current.event).toBe(null);
607
+ expect(result.current.error).toEqual(new Error('Event not found'));
608
+ });
609
+
610
+ it('should handle empty event data array from RPC', async () => {
611
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
612
+ data: [],
613
+ error: null
614
+ });
615
+
616
+ const { result } = renderHook(() => usePublicEvent('test-event'));
617
+
618
+ await waitFor(() => {
619
+ expect(result.current.isLoading).toBe(false);
620
+ }, { timeout: 2000 });
621
+
622
+ expect(result.current.event).toBe(null);
623
+ expect(result.current.error).toEqual(new Error('Event not found'));
624
+ });
625
+
626
+ it('should handle undefined event data from RPC', async () => {
627
+ mockSupabaseClient.rpc.mockResolvedValueOnce({
628
+ data: [undefined],
629
+ error: null
630
+ });
631
+
632
+ const { result } = renderHook(() => usePublicEvent('test-event'));
633
+
634
+ await waitFor(() => {
635
+ expect(result.current.isLoading).toBe(false);
636
+ }, { timeout: 2000 });
637
+
638
+ expect(result.current.event).toBe(null);
639
+ expect(result.current.error).toEqual(new Error('Event not found'));
640
+ });
641
+
642
+ it('handles missing Supabase environment variables', async () => {
643
+ // Mock usePublicPageContext to return null environment
644
+ vi.mocked(usePublicPageContext).mockReturnValue({
645
+ environment: {
646
+ supabaseUrl: null,
647
+ supabaseKey: null
648
+ }
649
+ } as any);
650
+
651
+ // Mock environment variables to be undefined
652
+ Object.defineProperty(import.meta, 'env', {
653
+ value: {},
654
+ writable: true
655
+ });
656
+
657
+ const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
658
+
659
+ const { result } = renderHook(() => usePublicEvent('test-event'));
660
+
661
+ await waitFor(() => {
662
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
663
+ expect.stringContaining('[WARN] [usePublicEvent] Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are set in your environment.')
664
+ );
665
+ }, { timeout: 2000 });
666
+
667
+ // Should still initialize but with error
668
+ expect(result.current.isLoading).toBe(false);
669
+ expect(result.current.error).toBeInstanceOf(Error);
670
+ expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
671
+
672
+ consoleWarnSpy.mockRestore();
673
+ });
674
+
675
+ it('handles server-side rendering (window undefined)', () => {
676
+ // Test that the hook handles missing window by checking the implementation
677
+ // The hook checks `typeof window === 'undefined'` and returns null supabase client
678
+ // We can't actually delete window in the test environment as React needs it
679
+ // Instead, we test the behavior when supabase client is null (which happens when window is undefined)
680
+
681
+ // Mock usePublicPageContext to return null environment
682
+ vi.mocked(usePublicPageContext).mockReturnValue({
683
+ environment: {
684
+ supabaseUrl: null,
685
+ supabaseKey: null
686
+ }
687
+ } as any);
688
+
689
+ // Mock environment variables to be undefined
690
+ Object.defineProperty(import.meta, 'env', {
691
+ value: {},
692
+ writable: true
693
+ });
694
+
695
+ const { result } = renderHook(() => usePublicEvent('test-event'));
696
+
697
+ // When supabase client is null (as it would be in SSR), the hook should handle it gracefully
698
+ expect(result.current.isLoading).toBe(false);
699
+ expect(result.current.error).toBeInstanceOf(Error);
700
+ expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
701
+ });
702
+ });
703
+ });