@jmruthers/pace-core 0.5.185 → 0.5.187

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 (300) hide show
  1. package/dist/{DataTable-Z9NLVJh0.d.ts → DataTable-IVYljGJ6.d.ts} +1 -1
  2. package/dist/{DataTable-IX2NBUTP.js → DataTable-K3RJRSOX.js} +7 -7
  3. package/dist/{PublicPageProvider-BABf6JCh.d.ts → PublicPageProvider-DrLDztHt.d.ts} +214 -107
  4. package/dist/{UnifiedAuthProvider-A4BCQRJY.js → UnifiedAuthProvider-B76OWOAT.js} +2 -2
  5. package/dist/{api-BMFCXVQX.js → api-YP7XD5L6.js} +3 -3
  6. package/dist/{audit-WRS3KJKI.js → audit-B5P6FFIR.js} +2 -2
  7. package/dist/{chunk-445GEP27.js → chunk-3IC5WCMO.js} +33 -8
  8. package/dist/chunk-3IC5WCMO.js.map +1 -0
  9. package/dist/{chunk-OKI34GZD.js → chunk-3NFNJOO7.js} +8 -8
  10. package/dist/chunk-3NFNJOO7.js.map +1 -0
  11. package/dist/{chunk-FSFQFJCU.js → chunk-63FOKYGO.js} +174 -6
  12. package/dist/chunk-63FOKYGO.js.map +1 -0
  13. package/dist/{chunk-MX3EIJGQ.js → chunk-C4OYJOV4.js} +631 -97
  14. package/dist/chunk-C4OYJOV4.js.map +1 -0
  15. package/dist/{chunk-HGPQUCBC.js → chunk-FMTK4XNN.js} +3 -3
  16. package/dist/{chunk-U6WNSFX5.js → chunk-HEHYGYOX.js} +279 -44
  17. package/dist/chunk-HEHYGYOX.js.map +1 -0
  18. package/dist/{chunk-XAUHJD3L.js → chunk-K2JGDXGU.js} +2 -2
  19. package/dist/{chunk-HC67NW5K.js → chunk-LBBUPSSC.js} +863 -552
  20. package/dist/chunk-LBBUPSSC.js.map +1 -0
  21. package/dist/{chunk-IXSNYUCT.js → chunk-SAUPYVLF.js} +1 -1
  22. package/dist/chunk-SAUPYVLF.js.map +1 -0
  23. package/dist/{chunk-AISXLWGZ.js → chunk-T6ZJVI3A.js} +27 -23
  24. package/dist/chunk-T6ZJVI3A.js.map +1 -0
  25. package/dist/{chunk-STTZQK2I.js → chunk-ULX5FYEM.js} +9 -7
  26. package/dist/chunk-ULX5FYEM.js.map +1 -0
  27. package/dist/{chunk-FXFJRTKI.js → chunk-WK2Y6TGA.js} +3 -3
  28. package/dist/chunk-WK2Y6TGA.js.map +1 -0
  29. package/dist/chunk-YHCN776L.js +447 -0
  30. package/dist/chunk-YHCN776L.js.map +1 -0
  31. package/dist/components.d.ts +4 -4
  32. package/dist/components.js +12 -10
  33. package/dist/components.js.map +1 -1
  34. package/dist/{database.generated-CBmg2950.d.ts → database.generated-DI89OQeI.d.ts} +63 -9
  35. package/dist/{file-reference-BjR39ktt.d.ts → file-reference-D037xOFK.d.ts} +3 -1
  36. package/dist/hooks.d.ts +265 -6
  37. package/dist/hooks.js +148 -49
  38. package/dist/hooks.js.map +1 -1
  39. package/dist/index.d.ts +25 -10
  40. package/dist/index.js +65 -30
  41. package/dist/index.js.map +1 -1
  42. package/dist/providers.js +1 -1
  43. package/dist/rbac/index.d.ts +125 -8
  44. package/dist/rbac/index.js +27 -7
  45. package/dist/{types-DUyCRSTj.d.ts → types-Bwgl--Xo.d.ts} +162 -1
  46. package/dist/types.d.ts +2 -2
  47. package/dist/types.js +1 -1
  48. package/dist/{usePublicRouteParams-CvnC3d-e.d.ts → usePublicRouteParams-CTDELQ7H.d.ts} +3 -3
  49. package/dist/utils.d.ts +214 -4
  50. package/dist/utils.js +22 -2
  51. package/dist/utils.js.map +1 -1
  52. package/docs/api/classes/ColumnFactory.md +1 -1
  53. package/docs/api/classes/ErrorBoundary.md +1 -1
  54. package/docs/api/classes/InvalidScopeError.md +1 -1
  55. package/docs/api/classes/Logger.md +1 -1
  56. package/docs/api/classes/MissingUserContextError.md +1 -1
  57. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  58. package/docs/api/classes/PermissionDeniedError.md +1 -1
  59. package/docs/api/classes/RBACAuditManager.md +21 -17
  60. package/docs/api/classes/RBACCache.md +31 -23
  61. package/docs/api/classes/RBACEngine.md +6 -6
  62. package/docs/api/classes/RBACError.md +1 -1
  63. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  64. package/docs/api/classes/SecureSupabaseClient.md +5 -5
  65. package/docs/api/classes/StorageUtils.md +1 -1
  66. package/docs/api/enums/FileCategory.md +1 -1
  67. package/docs/api/enums/LogLevel.md +1 -1
  68. package/docs/api/enums/RBACErrorCode.md +1 -1
  69. package/docs/api/enums/RPCFunction.md +1 -1
  70. package/docs/api/interfaces/AddressFieldProps.md +241 -0
  71. package/docs/api/interfaces/AddressFieldRef.md +94 -0
  72. package/docs/api/interfaces/AggregateConfig.md +1 -1
  73. package/docs/api/interfaces/AutocompleteOptions.md +75 -0
  74. package/docs/api/interfaces/BadgeProps.md +1 -1
  75. package/docs/api/interfaces/ButtonProps.md +1 -1
  76. package/docs/api/interfaces/CalendarProps.md +1 -1
  77. package/docs/api/interfaces/CardProps.md +1 -1
  78. package/docs/api/interfaces/ColorPalette.md +1 -1
  79. package/docs/api/interfaces/ColorShade.md +1 -1
  80. package/docs/api/interfaces/ComplianceResult.md +1 -1
  81. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  82. package/docs/api/interfaces/DataRecord.md +1 -1
  83. package/docs/api/interfaces/DataTableAction.md +1 -1
  84. package/docs/api/interfaces/DataTableColumn.md +1 -1
  85. package/docs/api/interfaces/DataTableProps.md +1 -1
  86. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  87. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  88. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  89. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  90. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  91. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  92. package/docs/api/interfaces/ExportColumn.md +1 -1
  93. package/docs/api/interfaces/ExportOptions.md +1 -1
  94. package/docs/api/interfaces/FileDisplayProps.md +15 -15
  95. package/docs/api/interfaces/FileMetadata.md +1 -1
  96. package/docs/api/interfaces/FileReference.md +1 -1
  97. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  98. package/docs/api/interfaces/FileUploadOptions.md +33 -9
  99. package/docs/api/interfaces/FileUploadProps.md +36 -14
  100. package/docs/api/interfaces/FooterProps.md +1 -1
  101. package/docs/api/interfaces/FormFieldProps.md +1 -1
  102. package/docs/api/interfaces/FormProps.md +1 -1
  103. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  104. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  105. package/docs/api/interfaces/InputProps.md +1 -1
  106. package/docs/api/interfaces/LabelProps.md +1 -1
  107. package/docs/api/interfaces/LoggerConfig.md +1 -1
  108. package/docs/api/interfaces/LoginFormProps.md +1 -1
  109. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  110. package/docs/api/interfaces/NavigationContextType.md +1 -1
  111. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  112. package/docs/api/interfaces/NavigationItem.md +1 -1
  113. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  115. package/docs/api/interfaces/Organisation.md +1 -1
  116. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  117. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  118. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  119. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  120. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  121. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  122. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  123. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  124. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  125. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  126. package/docs/api/interfaces/PaletteData.md +1 -1
  127. package/docs/api/interfaces/ParsedAddress.md +120 -0
  128. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  129. package/docs/api/interfaces/ProgressProps.md +1 -1
  130. package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
  131. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  132. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  133. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  134. package/docs/api/interfaces/QuickFix.md +1 -1
  135. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  136. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  137. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  138. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  139. package/docs/api/interfaces/RBACConfig.md +27 -4
  140. package/docs/api/interfaces/RBACContext.md +1 -1
  141. package/docs/api/interfaces/RBACLogger.md +5 -5
  142. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  143. package/docs/api/interfaces/RBACPerformanceMetrics.md +138 -0
  144. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  145. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  146. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  147. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  148. package/docs/api/interfaces/RBACResult.md +1 -1
  149. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  150. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  151. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  152. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  153. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  154. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  155. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  156. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  157. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  158. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  159. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  160. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  161. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  162. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  163. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  164. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  165. package/docs/api/interfaces/RouteConfig.md +1 -1
  166. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  167. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  168. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  169. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  170. package/docs/api/interfaces/SetupIssue.md +1 -1
  171. package/docs/api/interfaces/StorageConfig.md +1 -1
  172. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  173. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  174. package/docs/api/interfaces/StorageListOptions.md +1 -1
  175. package/docs/api/interfaces/StorageListResult.md +1 -1
  176. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  177. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  178. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  179. package/docs/api/interfaces/StyleImport.md +1 -1
  180. package/docs/api/interfaces/SwitchProps.md +1 -1
  181. package/docs/api/interfaces/TabsContentProps.md +1 -1
  182. package/docs/api/interfaces/TabsListProps.md +1 -1
  183. package/docs/api/interfaces/TabsProps.md +1 -1
  184. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  185. package/docs/api/interfaces/TextareaProps.md +1 -1
  186. package/docs/api/interfaces/ToastActionElement.md +1 -1
  187. package/docs/api/interfaces/ToastProps.md +1 -1
  188. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  189. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  190. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  191. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  192. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  193. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  194. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  195. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  196. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  197. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  198. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  199. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  200. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  201. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  202. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  203. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  204. package/docs/api/interfaces/UserEventAccess.md +1 -1
  205. package/docs/api/interfaces/UserMenuProps.md +1 -1
  206. package/docs/api/interfaces/UserProfile.md +1 -1
  207. package/docs/api/modules.md +328 -69
  208. package/docs/api-reference/components.md +26 -12
  209. package/docs/best-practices/performance.md +11 -0
  210. package/docs/implementation-guides/file-reference-system.md +24 -2
  211. package/docs/implementation-guides/file-upload-storage.md +38 -1
  212. package/docs/rbac/README.md +2 -1
  213. package/docs/rbac/api-reference.md +11 -0
  214. package/docs/rbac/performance.md +320 -0
  215. package/docs/standards/01-architecture-standard.md +5 -0
  216. package/docs/standards/05-security-standard.md +12 -0
  217. package/package.json +1 -1
  218. package/scripts/check-pace-core-compliance.js +512 -0
  219. package/src/components/AddressField/AddressField.test.tsx +411 -0
  220. package/src/components/AddressField/AddressField.tsx +323 -0
  221. package/src/components/AddressField/README.md +336 -0
  222. package/src/components/AddressField/index.ts +10 -0
  223. package/src/components/AddressField/types.ts +65 -0
  224. package/src/components/FileDisplay/FileDisplay.test.tsx +454 -0
  225. package/src/components/FileDisplay/FileDisplay.tsx +28 -1
  226. package/src/components/FileUpload/FileUpload.test.tsx +2 -0
  227. package/src/components/FileUpload/FileUpload.tsx +7 -1
  228. package/src/components/Header/Header.tsx +2 -5
  229. package/src/components/ProtectedRoute/ProtectedRoute.tsx +134 -1
  230. package/src/components/index.ts +2 -0
  231. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +30 -5
  232. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +11 -10
  233. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +31 -6
  234. package/src/hooks/index.ts +9 -0
  235. package/src/hooks/public/usePublicFileDisplay.ts +8 -10
  236. package/src/hooks/useAddressAutocomplete.test.ts +318 -0
  237. package/src/hooks/useAddressAutocomplete.ts +268 -0
  238. package/src/hooks/useFileDisplay.ts +3 -15
  239. package/src/hooks/useFileReference.test.ts +21 -3
  240. package/src/hooks/useFileReference.ts +3 -24
  241. package/src/hooks/useFileUrlCache.ts +246 -0
  242. package/src/hooks/useInactivityTracker.ts +31 -20
  243. package/src/hooks/useOrganisationSecurity.test.ts +10 -7
  244. package/src/hooks/useOrganisationSecurity.ts +3 -3
  245. package/src/hooks/usePreventTabReload.ts +106 -0
  246. package/src/hooks/useQueryCache.ts +315 -0
  247. package/src/hooks/useSecureDataAccess.ts +2 -2
  248. package/src/index.ts +2 -0
  249. package/src/providers/services/EventServiceProvider.tsx +4 -1
  250. package/src/rbac/__tests__/rbac-role-isolation.test.ts +456 -0
  251. package/src/rbac/api.test.ts +21 -6
  252. package/src/rbac/api.ts +32 -11
  253. package/src/rbac/audit-batched.ts +223 -0
  254. package/src/rbac/audit-enhanced.ts +2 -2
  255. package/src/rbac/audit.test.ts +6 -5
  256. package/src/rbac/audit.ts +34 -6
  257. package/src/rbac/cache-invalidation.ts +63 -12
  258. package/src/rbac/cache.test.ts +2 -2
  259. package/src/rbac/cache.ts +61 -14
  260. package/src/rbac/components/PagePermissionGuard.tsx +19 -10
  261. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +248 -0
  262. package/src/rbac/config.ts +9 -0
  263. package/src/rbac/engine.ts +2 -21
  264. package/src/rbac/hooks/usePermissions.ts +21 -5
  265. package/src/rbac/index.ts +19 -0
  266. package/src/rbac/performance.ts +210 -0
  267. package/src/rbac/request-deduplication.ts +87 -0
  268. package/src/rbac/utils/deep-equal.ts +93 -0
  269. package/src/styles/core.css +5 -5
  270. package/src/types/database.generated.ts +63 -9
  271. package/src/types/file-reference.ts +3 -1
  272. package/src/utils/file-reference/__tests__/file-reference.test.ts +89 -8
  273. package/src/utils/file-reference/index.ts +56 -17
  274. package/src/utils/google-places/googlePlacesUtils.test.ts +403 -0
  275. package/src/utils/google-places/googlePlacesUtils.ts +475 -0
  276. package/src/utils/google-places/index.ts +26 -0
  277. package/src/utils/google-places/loadGoogleMapsScript.ts +207 -0
  278. package/src/utils/google-places/types.ts +94 -0
  279. package/src/utils/index.ts +23 -0
  280. package/src/utils/request-deduplication.ts +165 -0
  281. package/src/utils/security/secureDataAccess.ts +1 -1
  282. package/src/utils/storage/helpers.ts +211 -4
  283. package/dist/chunk-445GEP27.js.map +0 -1
  284. package/dist/chunk-AISXLWGZ.js.map +0 -1
  285. package/dist/chunk-FMUCXFII.js +0 -76
  286. package/dist/chunk-FMUCXFII.js.map +0 -1
  287. package/dist/chunk-FSFQFJCU.js.map +0 -1
  288. package/dist/chunk-FXFJRTKI.js.map +0 -1
  289. package/dist/chunk-HC67NW5K.js.map +0 -1
  290. package/dist/chunk-IXSNYUCT.js.map +0 -1
  291. package/dist/chunk-MX3EIJGQ.js.map +0 -1
  292. package/dist/chunk-OKI34GZD.js.map +0 -1
  293. package/dist/chunk-STTZQK2I.js.map +0 -1
  294. package/dist/chunk-U6WNSFX5.js.map +0 -1
  295. /package/dist/{DataTable-IX2NBUTP.js.map → DataTable-K3RJRSOX.js.map} +0 -0
  296. /package/dist/{UnifiedAuthProvider-A4BCQRJY.js.map → UnifiedAuthProvider-B76OWOAT.js.map} +0 -0
  297. /package/dist/{api-BMFCXVQX.js.map → api-YP7XD5L6.js.map} +0 -0
  298. /package/dist/{audit-WRS3KJKI.js.map → audit-B5P6FFIR.js.map} +0 -0
  299. /package/dist/{chunk-HGPQUCBC.js.map → chunk-FMTK4XNN.js.map} +0 -0
  300. /package/dist/{chunk-XAUHJD3L.js.map → chunk-K2JGDXGU.js.map} +0 -0
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Query Result Caching Hook
3
+ * @package @jmruthers/pace-core
4
+ * @module Hooks/QueryCache
5
+ * @since 2.0.0
6
+ *
7
+ * Provides in-memory caching for frequently accessed data to eliminate duplicate queries.
8
+ * Useful for caching user profiles, app pages, and other relatively static data.
9
+ */
10
+
11
+ import { useCallback, useRef, useEffect } from 'react';
12
+ import { SupabaseClient } from '@supabase/supabase-js';
13
+ import { Database } from '../types/database';
14
+ import { createLogger } from '../utils/core/logger';
15
+
16
+ const log = createLogger('useQueryCache');
17
+
18
+ interface CachedQueryEntry<T> {
19
+ data: T;
20
+ expiresAt: number; // Unix timestamp
21
+ promise?: Promise<T>; // For in-flight requests
22
+ }
23
+
24
+ /**
25
+ * In-memory cache for query results
26
+ * Key format: `table:filterKey:filterValue`
27
+ */
28
+ const queryCache = new Map<string, CachedQueryEntry<any>>();
29
+
30
+ // Cleanup interval (every 5 minutes)
31
+ const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
32
+ let cleanupTimer: ReturnType<typeof setInterval> | null = null;
33
+
34
+ function runCacheCleanup() {
35
+ const now = Date.now();
36
+ const expiredKeys: string[] = [];
37
+
38
+ queryCache.forEach((entry, key) => {
39
+ if (entry.expiresAt <= now) {
40
+ expiredKeys.push(key);
41
+ }
42
+ });
43
+
44
+ expiredKeys.forEach(key => {
45
+ queryCache.delete(key);
46
+ log.debug(`Removed expired query from cache: ${key}`);
47
+ });
48
+ }
49
+
50
+ // Initialize cleanup timer once
51
+ if (typeof window !== 'undefined' && !cleanupTimer) {
52
+ cleanupTimer = setInterval(runCacheCleanup, CLEANUP_INTERVAL_MS);
53
+ log.debug('Query cache cleanup initialized.');
54
+ }
55
+
56
+ export interface UseQueryCacheOptions {
57
+ /** Time to live in seconds (default: 300 = 5 minutes) */
58
+ ttl?: number;
59
+ /** Whether to enable caching (default: true) */
60
+ enabled?: boolean;
61
+ }
62
+
63
+ export interface UseQueryCacheReturn {
64
+ /** Get cached query result or fetch if not cached */
65
+ getCachedQuery: <T>(
66
+ table: string,
67
+ filterKey: string,
68
+ filterValue: string,
69
+ fetchFn: () => Promise<T>,
70
+ options?: UseQueryCacheOptions
71
+ ) => Promise<T>;
72
+ /** Invalidate a specific cached query */
73
+ invalidateQuery: (table: string, filterKey: string, filterValue: string) => void;
74
+ /** Clear all cached queries */
75
+ clearCache: () => void;
76
+ /** Get cache statistics */
77
+ getCacheStats: () => { size: number; keys: string[] };
78
+ }
79
+
80
+ /**
81
+ * Hook for query result caching
82
+ *
83
+ * Provides caching for frequently accessed data to eliminate duplicate queries.
84
+ * Automatically handles cache expiration and cleanup.
85
+ *
86
+ * @param supabase - Supabase client (optional, can be passed in fetchFn)
87
+ * @returns Query cache utilities
88
+ *
89
+ * @example
90
+ * ```tsx
91
+ * const { getCachedQuery } = useQueryCache(supabase);
92
+ *
93
+ * const person = await getCachedQuery(
94
+ * 'pace_person',
95
+ * 'user_id',
96
+ * userId,
97
+ * async () => {
98
+ * const { data } = await supabase
99
+ * .from('pace_person')
100
+ * .select('id, first_name, last_name, email')
101
+ * .eq('user_id', userId)
102
+ * .single();
103
+ * return data;
104
+ * },
105
+ * { ttl: 300 } // 5 minutes
106
+ * );
107
+ * ```
108
+ */
109
+ export function useQueryCache(supabase?: SupabaseClient<Database>): UseQueryCacheReturn {
110
+ const getCachedQuery = useCallback(async <T,>(
111
+ table: string,
112
+ filterKey: string,
113
+ filterValue: string,
114
+ fetchFn: () => Promise<T>,
115
+ options: UseQueryCacheOptions = {}
116
+ ): Promise<T> => {
117
+ const { ttl = 300, enabled = true } = options; // Default 5 minutes
118
+ const cacheKey = `${table}:${filterKey}:${filterValue}`;
119
+ const now = Date.now();
120
+
121
+ if (!enabled) {
122
+ return fetchFn();
123
+ }
124
+
125
+ // Check cache
126
+ const cached = queryCache.get(cacheKey);
127
+ if (cached) {
128
+ // If data is still valid, return it
129
+ if (cached.expiresAt > now && cached.data !== undefined) {
130
+ log.debug(`Cache hit for query: ${cacheKey}`);
131
+ return cached.data as T;
132
+ }
133
+
134
+ // If there's an in-flight request, wait for it
135
+ if (cached.promise) {
136
+ log.debug(`Waiting for in-flight request: ${cacheKey}`);
137
+ return cached.promise as Promise<T>;
138
+ }
139
+ }
140
+
141
+ // Fetch data
142
+ log.debug(`Cache miss for query: ${cacheKey}, fetching...`);
143
+ const fetchPromise = fetchFn();
144
+
145
+ // Store promise for in-flight request deduplication
146
+ queryCache.set(cacheKey, {
147
+ data: undefined as any,
148
+ expiresAt: now + (ttl * 1000),
149
+ promise: fetchPromise,
150
+ });
151
+
152
+ try {
153
+ const data = await fetchPromise;
154
+
155
+ // Update cache with actual data
156
+ queryCache.set(cacheKey, {
157
+ data,
158
+ expiresAt: now + (ttl * 1000),
159
+ });
160
+
161
+ log.debug(`Cached query result: ${cacheKey}, expires in ${ttl}s`);
162
+ return data;
163
+ } catch (error) {
164
+ // Remove failed request from cache
165
+ queryCache.delete(cacheKey);
166
+ log.error(`Query failed for ${cacheKey}:`, error);
167
+ throw error;
168
+ }
169
+ }, []);
170
+
171
+ const invalidateQuery = useCallback((table: string, filterKey: string, filterValue: string) => {
172
+ const cacheKey = `${table}:${filterKey}:${filterValue}`;
173
+ queryCache.delete(cacheKey);
174
+ log.debug(`Invalidated query cache: ${cacheKey}`);
175
+ }, []);
176
+
177
+ const clearCache = useCallback(() => {
178
+ queryCache.clear();
179
+ log.debug('Cleared all query cache entries.');
180
+ }, []);
181
+
182
+ const getCacheStats = useCallback(() => {
183
+ return {
184
+ size: queryCache.size,
185
+ keys: Array.from(queryCache.keys()),
186
+ };
187
+ }, []);
188
+
189
+ return {
190
+ getCachedQuery,
191
+ invalidateQuery,
192
+ clearCache,
193
+ getCacheStats,
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Pre-configured cache helpers for common queries
199
+ */
200
+ export const queryCacheHelpers = {
201
+ /**
202
+ * Cache pace_person queries by user_id
203
+ * TTL: 5 minutes
204
+ */
205
+ pacePersonByUserId: <T>(
206
+ supabase: SupabaseClient<Database>,
207
+ userId: string,
208
+ fetchFn: () => Promise<T>
209
+ ): Promise<T> => {
210
+ const cacheKey = `pace_person:user_id:${userId}`;
211
+ const now = Date.now();
212
+ const ttl = 300 * 1000; // 5 minutes
213
+
214
+ const cached = queryCache.get(cacheKey);
215
+ if (cached && cached.expiresAt > now && cached.data !== undefined) {
216
+ return Promise.resolve(cached.data as T);
217
+ }
218
+
219
+ if (cached?.promise) {
220
+ return cached.promise as Promise<T>;
221
+ }
222
+
223
+ const promise = fetchFn();
224
+ queryCache.set(cacheKey, {
225
+ data: undefined as any,
226
+ expiresAt: now + ttl,
227
+ promise,
228
+ });
229
+
230
+ promise.then(data => {
231
+ queryCache.set(cacheKey, { data, expiresAt: now + ttl });
232
+ }).catch(() => {
233
+ queryCache.delete(cacheKey);
234
+ });
235
+
236
+ return promise;
237
+ },
238
+
239
+ /**
240
+ * Cache pace_member queries by person_id
241
+ * TTL: 5 minutes
242
+ */
243
+ paceMemberByPersonId: <T>(
244
+ supabase: SupabaseClient<Database>,
245
+ personId: string,
246
+ fetchFn: () => Promise<T>
247
+ ): Promise<T> => {
248
+ const cacheKey = `pace_member:person_id:${personId}`;
249
+ const now = Date.now();
250
+ const ttl = 300 * 1000; // 5 minutes
251
+
252
+ const cached = queryCache.get(cacheKey);
253
+ if (cached && cached.expiresAt > now && cached.data !== undefined) {
254
+ return Promise.resolve(cached.data as T);
255
+ }
256
+
257
+ if (cached?.promise) {
258
+ return cached.promise as Promise<T>;
259
+ }
260
+
261
+ const promise = fetchFn();
262
+ queryCache.set(cacheKey, {
263
+ data: undefined as any,
264
+ expiresAt: now + ttl,
265
+ promise,
266
+ });
267
+
268
+ promise.then(data => {
269
+ queryCache.set(cacheKey, { data, expiresAt: now + ttl });
270
+ }).catch(() => {
271
+ queryCache.delete(cacheKey);
272
+ });
273
+
274
+ return promise;
275
+ },
276
+
277
+ /**
278
+ * Cache rbac_app_pages queries by app_id
279
+ * TTL: 15 minutes (app pages are relatively static)
280
+ */
281
+ rbacAppPagesByAppId: <T>(
282
+ supabase: SupabaseClient<Database>,
283
+ appId: string,
284
+ fetchFn: () => Promise<T>
285
+ ): Promise<T> => {
286
+ const cacheKey = `rbac_app_pages:app_id:${appId}`;
287
+ const now = Date.now();
288
+ const ttl = 15 * 60 * 1000; // 15 minutes
289
+
290
+ const cached = queryCache.get(cacheKey);
291
+ if (cached && cached.expiresAt > now && cached.data !== undefined) {
292
+ return Promise.resolve(cached.data as T);
293
+ }
294
+
295
+ if (cached?.promise) {
296
+ return cached.promise as Promise<T>;
297
+ }
298
+
299
+ const promise = fetchFn();
300
+ queryCache.set(cacheKey, {
301
+ data: undefined as any,
302
+ expiresAt: now + ttl,
303
+ promise,
304
+ });
305
+
306
+ promise.then(data => {
307
+ queryCache.set(cacheKey, { data, expiresAt: now + ttl });
308
+ }).catch(() => {
309
+ queryCache.delete(cacheKey);
310
+ });
311
+
312
+ return promise;
313
+ },
314
+ };
315
+
@@ -219,7 +219,7 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
219
219
  'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',
220
220
  // SECURITY: Phase 3A additions - medical and personal data
221
221
  'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',
222
- 'pace_consent', 'pace_contact', 'pace_id_documents', 'pace_qualifications',
222
+ 'pace_consent', 'pace_contact', 'pace_identification', 'pace_identification_type', 'pace_qualification',
223
223
  'form_responses', 'form_response_values', 'forms',
224
224
  // SECURITY: Phase 3B additions - remaining critical tables
225
225
  'invoice', 'line_item', 'credit_balance', 'payment_method',
@@ -379,7 +379,7 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
379
379
  'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',
380
380
  // SECURITY: Phase 3A additions - medical and personal data
381
381
  'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',
382
- 'pace_consent', 'pace_contact', 'pace_id_documents', 'pace_qualifications',
382
+ 'pace_consent', 'pace_contact', 'pace_identification', 'pace_identification_type', 'pace_qualification',
383
383
  'form_responses', 'form_response_values', 'forms',
384
384
  // SECURITY: Phase 3B additions - remaining critical tables
385
385
  'invoice', 'line_item', 'credit_balance', 'payment_method',
package/src/index.ts CHANGED
@@ -76,6 +76,8 @@ export type { CardProps } from './components/Card/Card';
76
76
 
77
77
  export { Input } from './components/Input/Input';
78
78
  export type { InputProps } from './components/Input/Input';
79
+ export { AddressField } from './components/AddressField';
80
+ export type { AddressFieldProps, AddressFieldRef, ParsedAddress, AutocompleteOptions } from './components/AddressField';
79
81
  export { Label } from './components/Label/Label';
80
82
  export type { LabelProps } from './components/Label/Label';
81
83
 
@@ -50,6 +50,7 @@ export function EventServiceProvider({
50
50
  const eventService = eventServiceRef.current;
51
51
 
52
52
  // Update service dependencies and initialize when dependencies change
53
+ // Note: eventService is a ref and never changes, so we don't include it in dependencies
53
54
  useEffect(() => {
54
55
  let isMounted = true;
55
56
 
@@ -72,7 +73,9 @@ export function EventServiceProvider({
72
73
  return () => {
73
74
  isMounted = false;
74
75
  };
75
- }, [eventService, supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId]);
76
+ // eslint-disable-next-line react-hooks/exhaustive-deps
77
+ // eventService is a ref and never changes, so we exclude it from dependencies
78
+ }, [supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId]);
76
79
 
77
80
  // Cleanup service on unmount only
78
81
  useEffect(() => {