@jmruthers/pace-core 0.5.53 → 0.5.55

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 (398) hide show
  1. package/README.md +0 -4
  2. package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
  3. package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
  4. package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
  5. package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
  6. package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
  7. package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
  8. package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
  9. package/dist/chunk-5BSLGBYI.js.map +1 -0
  10. package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
  11. package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
  12. package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
  13. package/dist/chunk-AJ2KMES7.js.map +1 -0
  14. package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
  15. package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
  16. package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
  17. package/dist/chunk-B2WTCLCV.js.map +1 -0
  18. package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
  19. package/dist/chunk-D7ARGIA3.js.map +1 -0
  20. package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
  21. package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
  22. package/dist/chunk-KJDPSM64.js.map +1 -0
  23. package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
  24. package/dist/chunk-KLPVOPRI.js.map +1 -0
  25. package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
  26. package/dist/chunk-MPQDF75X.js.map +1 -0
  27. package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
  28. package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
  29. package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
  30. package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
  31. package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
  32. package/dist/chunk-STT7INZR.js.map +1 -0
  33. package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
  34. package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
  35. package/dist/chunk-UQE2Y64H.js.map +1 -0
  36. package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
  37. package/dist/chunk-W66AZIOH.js.map +1 -0
  38. package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
  39. package/dist/chunk-YNUBMSMV.js.map +1 -0
  40. package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
  41. package/dist/chunk-ZOD2ZY6X.js.map +1 -0
  42. package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
  43. package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
  44. package/dist/components.d.ts +22 -899
  45. package/dist/components.js +436 -3118
  46. package/dist/components.js.map +1 -1
  47. package/dist/file-reference-9xUOnwyt.d.ts +70 -0
  48. package/dist/hooks.d.ts +2 -2
  49. package/dist/hooks.js +10 -10
  50. package/dist/hooks.js.map +1 -1
  51. package/dist/index.d.ts +49 -9
  52. package/dist/index.js +190 -25
  53. package/dist/index.js.map +1 -1
  54. package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
  55. package/dist/providers.d.ts +2 -2
  56. package/dist/providers.js +5 -5
  57. package/dist/rbac/index.d.ts +65 -46
  58. package/dist/rbac/index.js +10 -12
  59. package/dist/styles/core.css +0 -125
  60. package/dist/types.d.ts +2 -1
  61. package/dist/types.js +3 -1
  62. package/dist/types.js.map +1 -1
  63. package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +44 -13
  66. package/dist/utils.js.map +1 -1
  67. package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
  68. package/docs/INDEX.md +7 -5
  69. package/docs/README.md +0 -1
  70. package/docs/api/README.md +0 -4
  71. package/docs/api/classes/ErrorBoundary.md +1 -1
  72. package/docs/api/classes/InvalidScopeError.md +1 -1
  73. package/docs/api/classes/MissingUserContextError.md +1 -1
  74. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  75. package/docs/api/classes/PermissionDeniedError.md +2 -2
  76. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  77. package/docs/api/classes/RBACAuditManager.md +12 -12
  78. package/docs/api/classes/RBACCache.md +1 -1
  79. package/docs/api/classes/RBACEngine.md +6 -6
  80. package/docs/api/classes/RBACError.md +1 -1
  81. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  82. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  83. package/docs/api/classes/StorageUtils.md +281 -0
  84. package/docs/api/interfaces/AggregateConfig.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CardProps.md +1 -1
  87. package/docs/api/interfaces/ColorPalette.md +1 -1
  88. package/docs/api/interfaces/ColorShade.md +1 -1
  89. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  90. package/docs/api/interfaces/DataTableAction.md +1 -1
  91. package/docs/api/interfaces/DataTableColumn.md +1 -1
  92. package/docs/api/interfaces/DataTableProps.md +1 -1
  93. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  94. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  95. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/EventContextType.md +1 -1
  97. package/docs/api/interfaces/EventLogoProps.md +1 -1
  98. package/docs/api/interfaces/EventProviderProps.md +1 -1
  99. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  100. package/docs/api/interfaces/FileUploadProps.md +1 -1
  101. package/docs/api/interfaces/FooterProps.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoginFormProps.md +1 -1
  106. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  107. package/docs/api/interfaces/NavigationContextType.md +1 -1
  108. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  109. package/docs/api/interfaces/NavigationItem.md +1 -1
  110. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  112. package/docs/api/interfaces/Organisation.md +1 -1
  113. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  114. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  115. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  116. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  117. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  118. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  119. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  120. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  121. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  122. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  123. package/docs/api/interfaces/PaletteData.md +1 -1
  124. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  125. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  126. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  127. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  128. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  131. package/docs/api/interfaces/RBACConfig.md +1 -1
  132. package/docs/api/interfaces/RBACContextType.md +1 -1
  133. package/docs/api/interfaces/RBACLogger.md +1 -1
  134. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  135. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  136. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  137. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  138. package/docs/api/interfaces/RouteConfig.md +2 -2
  139. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  140. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  141. package/docs/api/interfaces/StorageConfig.md +1 -1
  142. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  143. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  144. package/docs/api/interfaces/StorageListOptions.md +1 -1
  145. package/docs/api/interfaces/StorageListResult.md +1 -1
  146. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  147. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  148. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  149. package/docs/api/interfaces/StyleImport.md +1 -1
  150. package/docs/api/interfaces/ToastActionElement.md +1 -1
  151. package/docs/api/interfaces/ToastProps.md +1 -1
  152. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  153. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  155. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  159. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  160. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  161. package/docs/api/interfaces/UserEventAccess.md +1 -1
  162. package/docs/api/interfaces/UserMenuProps.md +1 -1
  163. package/docs/api/interfaces/UserProfile.md +1 -1
  164. package/docs/api/modules.md +204 -200
  165. package/docs/api-reference/components.md +141 -163
  166. package/docs/api-reference/hooks.md +347 -0
  167. package/docs/core-concepts/rbac-system.md +69 -16
  168. package/docs/getting-started/examples/basic-auth-app.md +0 -1
  169. package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
  170. package/docs/implementation-guides/file-upload-storage.md +733 -0
  171. package/docs/implementation-guides/inactivity-tracking.md +779 -0
  172. package/docs/implementation-guides/organisation-security.md +748 -0
  173. package/docs/implementation-guides/public-pages-advanced.md +1022 -0
  174. package/docs/migration/MIGRATION_GUIDE.md +684 -0
  175. package/docs/migration/README.md +13 -2
  176. package/docs/migration/rbac-migration.md +73 -0
  177. package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
  178. package/docs/style-guide.md +269 -1
  179. package/package.json +1 -1
  180. package/src/__tests__/TESTING_GUIDELINES.md +331 -18
  181. package/src/__tests__/helpers/supabaseMock.ts +99 -0
  182. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
  183. package/src/__tests__/shared.ts +6 -0
  184. package/src/components/DataTable/components/ActionButtons.tsx +2 -2
  185. package/src/components/DataTable/components/DataTableCore.tsx +2 -2
  186. package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
  187. package/src/components/DataTable/utils/debugTools.ts +2 -2
  188. package/src/components/Dialog/Dialog.test.tsx +12 -2
  189. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
  190. package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
  191. package/src/components/FileDisplay.tsx +233 -0
  192. package/src/components/FileUpload.tsx +176 -0
  193. package/src/components/Footer/Footer.test.tsx +7 -7
  194. package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
  195. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
  196. package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
  197. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
  198. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  199. package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
  200. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
  201. package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
  202. package/src/components/Select/Select.test.tsx +50 -15
  203. package/src/components/SuperAdminGuard.tsx +2 -2
  204. package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
  205. package/src/components/index.ts +0 -183
  206. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
  207. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
  208. package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
  209. package/src/hooks/public/usePublicEvent.ts +2 -2
  210. package/src/hooks/useAppConfig.ts +3 -3
  211. package/src/hooks/useComponentPerformance.ts +1 -1
  212. package/src/hooks/useDataTablePerformance.ts +1 -1
  213. package/src/hooks/useFileReference.ts +232 -0
  214. package/src/hooks/useOrganisationPermissions.test.ts +254 -344
  215. package/src/hooks/useOrganisationPermissions.ts +15 -7
  216. package/src/hooks/useOrganisationSecurity.test.ts +390 -402
  217. package/src/hooks/usePerformanceMonitor.ts +1 -1
  218. package/src/hooks/usePermissionCache.test.ts +264 -395
  219. package/src/hooks/usePermissionCache.ts +34 -4
  220. package/src/hooks/useSecureDataAccess.test.ts +486 -0
  221. package/src/hooks/useSecureDataAccess.ts +4 -1
  222. package/src/providers/InactivityProvider.tsx +2 -2
  223. package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
  224. package/src/providers/OrganisationProvider.test.tsx +168 -0
  225. package/src/providers/OrganisationProvider.tsx +18 -31
  226. package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
  227. package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
  228. package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
  229. package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
  230. package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
  231. package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
  232. package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
  233. package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
  234. package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
  235. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
  236. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
  237. package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
  238. package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
  239. package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
  240. package/src/rbac/api.test.ts +6 -6
  241. package/src/rbac/api.ts +2 -2
  242. package/src/rbac/audit.test.ts +485 -0
  243. package/src/rbac/audit.ts +7 -1
  244. package/src/rbac/cache-invalidation.ts +318 -0
  245. package/src/rbac/cache.test.ts +286 -0
  246. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
  247. package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
  248. package/src/rbac/components/NavigationProvider.test.tsx +449 -0
  249. package/src/rbac/components/PagePermissionGuard.tsx +4 -4
  250. package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
  251. package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
  252. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
  253. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
  254. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
  255. package/src/rbac/config.test.ts +410 -0
  256. package/src/rbac/engine.test.simple.ts +237 -0
  257. package/src/rbac/engine.test.ts +233 -0
  258. package/src/rbac/engine.ts +37 -41
  259. package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
  260. package/src/rbac/examples/EventBasedApp.tsx +4 -4
  261. package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
  262. package/src/rbac/hooks/useRBAC.test.ts +207 -455
  263. package/src/rbac/hooks/useRBAC.ts +30 -22
  264. package/src/rbac/permissions.test.ts +128 -0
  265. package/src/rbac/permissions.ts +56 -141
  266. package/src/rbac/providers/RBACProvider.tsx +1 -1
  267. package/src/rbac/secureClient.test.ts +444 -0
  268. package/src/rbac/security.test.ts +390 -0
  269. package/src/rbac/security.ts +1 -1
  270. package/src/rbac/types.test.ts +382 -0
  271. package/src/rbac/types.ts +2 -2
  272. package/src/styles/base.css +208 -0
  273. package/src/styles/core.css +0 -125
  274. package/src/styles/semantic.css +24 -0
  275. package/src/types/file-reference.ts +77 -0
  276. package/src/types/rbac-functions.ts +290 -0
  277. package/src/types/supabase.ts +10 -28
  278. package/src/types/unified.ts +4 -1
  279. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
  280. package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
  281. package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
  282. package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
  283. package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
  284. package/src/utils/appConfig.ts +1 -1
  285. package/src/utils/appIdResolver.test.ts +503 -0
  286. package/src/utils/appIdResolver.ts +1 -1
  287. package/src/utils/appNameResolver.test.ts +494 -0
  288. package/src/utils/appNameResolver.ts +3 -2
  289. package/src/utils/bundleAnalysis.ts +3 -3
  290. package/src/utils/debugLogger.ts +1 -1
  291. package/src/utils/file-reference.ts +263 -0
  292. package/src/utils/formatDate.test.ts +2 -2
  293. package/src/utils/organisationContext.test.ts +340 -0
  294. package/src/utils/organisationContext.ts +19 -6
  295. package/src/utils/performanceBudgets.ts +2 -2
  296. package/src/utils/permissionUtils.test.ts +393 -0
  297. package/src/utils/permissionUtils.ts +5 -2
  298. package/src/utils/secureDataAccess.test.ts +715 -0
  299. package/src/utils/secureDataAccess.ts +21 -5
  300. package/src/utils/sessionTracking.ts +34 -4
  301. package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
  302. package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
  303. package/src/utils/storage/helpers.ts +20 -25
  304. package/src/utils/storage/index.ts +29 -1
  305. package/src/vite-env.d.ts +17 -0
  306. package/dist/chunk-22KLBHPS.js.map +0 -1
  307. package/dist/chunk-7BNPOCLL.js.map +0 -1
  308. package/dist/chunk-BC3S53OZ.js.map +0 -1
  309. package/dist/chunk-GWSBHC4J.js.map +0 -1
  310. package/dist/chunk-MYP2EGHX.js.map +0 -1
  311. package/dist/chunk-MZBUOP4P.js.map +0 -1
  312. package/dist/chunk-NYUJ4FJR.js.map +0 -1
  313. package/dist/chunk-NZ655MWE.js.map +0 -1
  314. package/dist/chunk-SS3E6QLB.js.map +0 -1
  315. package/dist/chunk-TRIZ7IB7.js.map +0 -1
  316. package/dist/chunk-WJARTBCT.js.map +0 -1
  317. package/dist/chunk-YDJW5XTN.js.map +0 -1
  318. package/docs/print-components/README.md +0 -258
  319. package/docs/print-components/api-reference.md +0 -636
  320. package/docs/print-components/examples/README.md +0 -204
  321. package/docs/print-components/examples/basic-report.tsx +0 -92
  322. package/docs/print-components/examples/card-catalog.tsx +0 -149
  323. package/docs/print-components/examples/cover-page-report.tsx +0 -163
  324. package/docs/print-components/quick-start.md +0 -363
  325. package/src/components/PrintButton/PrintButton.tsx +0 -321
  326. package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
  327. package/src/components/PrintButton/PrintToolbar.tsx +0 -94
  328. package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
  329. package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
  330. package/src/components/PrintButton/index.ts +0 -33
  331. package/src/components/PrintButton/types.ts +0 -173
  332. package/src/components/PrintCard/PrintCard.tsx +0 -154
  333. package/src/components/PrintCard/PrintCardContent.tsx +0 -57
  334. package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
  335. package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
  336. package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
  337. package/src/components/PrintCard/PrintCardImage.tsx +0 -81
  338. package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
  339. package/src/components/PrintCard/index.ts +0 -34
  340. package/src/components/PrintCard/types.ts +0 -171
  341. package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
  342. package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
  343. package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
  344. package/src/components/PrintDataTable/index.ts +0 -25
  345. package/src/components/PrintDataTable/types.ts +0 -67
  346. package/src/components/PrintFooter/PrintFooter.tsx +0 -183
  347. package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
  348. package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
  349. package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
  350. package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
  351. package/src/components/PrintFooter/index.ts +0 -30
  352. package/src/components/PrintFooter/types.ts +0 -149
  353. package/src/components/PrintGrid/PrintGrid.tsx +0 -180
  354. package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
  355. package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
  356. package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
  357. package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
  358. package/src/components/PrintGrid/index.ts +0 -31
  359. package/src/components/PrintGrid/types.ts +0 -159
  360. package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
  361. package/src/components/PrintHeader/PrintHeader.tsx +0 -150
  362. package/src/components/PrintHeader/index.ts +0 -17
  363. package/src/components/PrintHeader/types.ts +0 -42
  364. package/src/components/PrintLayout/PrintLayout.tsx +0 -122
  365. package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
  366. package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
  367. package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
  368. package/src/components/PrintLayout/index.ts +0 -19
  369. package/src/components/PrintLayout/types.ts +0 -37
  370. package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
  371. package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
  372. package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
  373. package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
  374. package/src/components/PrintPageBreak/index.ts +0 -23
  375. package/src/components/PrintPageBreak/types.ts +0 -94
  376. package/src/components/PrintSection/PrintColumn.tsx +0 -104
  377. package/src/components/PrintSection/PrintDivider.tsx +0 -101
  378. package/src/components/PrintSection/PrintSection.tsx +0 -129
  379. package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
  380. package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
  381. package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
  382. package/src/components/PrintSection/index.ts +0 -33
  383. package/src/components/PrintSection/types.ts +0 -155
  384. package/src/components/PrintText/PrintText.tsx +0 -116
  385. package/src/components/PrintText/index.ts +0 -16
  386. package/src/components/PrintText/types.ts +0 -24
  387. package/src/rbac/__tests__/integration.test.tsx +0 -218
  388. package/src/utils/print/PrintDataProcessor.ts +0 -390
  389. package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
  390. package/src/utils/print/index.ts +0 -29
  391. package/src/utils/print/types.ts +0 -196
  392. package/src/utils/print/usePrintOptimization.ts +0 -272
  393. /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
  394. /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
  395. /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
  396. /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
  397. /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
  398. /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
@@ -0,0 +1,503 @@
1
+ /**
2
+ * @file App ID Resolver Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module Utils/AppIdResolver
5
+ * @since 1.0.0
6
+ *
7
+ * Comprehensive tests for app ID resolution utility functions covering all critical functionality.
8
+ */
9
+
10
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
11
+ import { getAppId, getAppIds, CachedAppIdResolver } from './appIdResolver';
12
+ import type { SupabaseClient } from '@supabase/supabase-js';
13
+ import type { Database } from '../types/database';
14
+
15
+ // Mock Supabase client
16
+ const createMockSupabaseClient = () => {
17
+ const queryBuilder = {
18
+ select: vi.fn().mockReturnThis(),
19
+ ilike: vi.fn().mockReturnThis(),
20
+ eq: vi.fn().mockReturnThis(),
21
+ or: vi.fn().mockReturnThis(),
22
+ single: vi.fn().mockResolvedValue({ data: null, error: null }),
23
+ mockResolvedValue: vi.fn(),
24
+ mockRejectedValue: vi.fn()
25
+ };
26
+
27
+ // Make the chained methods return the query builder with mock methods
28
+ queryBuilder.select.mockReturnValue(queryBuilder);
29
+ queryBuilder.ilike.mockReturnValue(queryBuilder);
30
+ queryBuilder.eq.mockReturnValue(queryBuilder);
31
+ queryBuilder.or.mockReturnValue(queryBuilder);
32
+
33
+ return {
34
+ from: vi.fn(() => queryBuilder),
35
+ queryBuilder // Export the query builder for direct access
36
+ };
37
+ };
38
+
39
+ describe('App ID Resolver', () => {
40
+ let mockSupabase: any;
41
+ let mockQueryBuilder: any;
42
+
43
+ beforeEach(() => {
44
+ vi.clearAllMocks();
45
+ mockSupabase = createMockSupabaseClient();
46
+ mockQueryBuilder = mockSupabase.queryBuilder;
47
+ });
48
+
49
+ afterEach(() => {
50
+ vi.restoreAllMocks();
51
+ });
52
+
53
+ describe('getAppId', () => {
54
+ it('resolves app ID for valid app name', async () => {
55
+ mockQueryBuilder.single.mockResolvedValue({
56
+ data: { id: 'app-123' },
57
+ error: null
58
+ });
59
+
60
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
61
+
62
+ expect(result).toBe('app-123');
63
+ expect(mockSupabase.from).toHaveBeenCalledWith('rbac_apps');
64
+ expect(mockQueryBuilder.select).toHaveBeenCalledWith('id');
65
+ expect(mockQueryBuilder.ilike).toHaveBeenCalledWith('name', 'MY_APP');
66
+ expect(mockQueryBuilder.eq).toHaveBeenCalledWith('is_active', true);
67
+ });
68
+
69
+ it('returns null when app not found', async () => {
70
+ mockQueryBuilder.single.mockResolvedValue({
71
+ data: null,
72
+ error: { message: 'No rows found' }
73
+ });
74
+
75
+ const result = await getAppId(mockSupabase as any, 'NONEXISTENT_APP');
76
+
77
+ expect(result).toBeNull();
78
+ });
79
+
80
+ it('handles database errors gracefully', async () => {
81
+ mockQueryBuilder.single.mockResolvedValue({
82
+ data: null,
83
+ error: { message: 'Database error' }
84
+ });
85
+
86
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
87
+
88
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
89
+
90
+ expect(result).toBeNull();
91
+ expect(consoleSpy).toHaveBeenCalledWith(
92
+ 'Failed to resolve app ID for app name:',
93
+ 'MY_APP',
94
+ { message: 'Database error' }
95
+ );
96
+
97
+ consoleSpy.mockRestore();
98
+ });
99
+
100
+ it('handles exceptions gracefully', async () => {
101
+ mockQueryBuilder.single.mockRejectedValue(new Error('Network error'));
102
+
103
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
104
+
105
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
106
+
107
+ expect(result).toBeNull();
108
+ expect(consoleSpy).toHaveBeenCalledWith(
109
+ 'Error resolving app ID for app name:',
110
+ 'MY_APP',
111
+ expect.any(Error)
112
+ );
113
+
114
+ consoleSpy.mockRestore();
115
+ });
116
+
117
+ it('handles null/undefined app name', async () => {
118
+ const result1 = await getAppId(mockSupabase as any, null as any);
119
+ const result2 = await getAppId(mockSupabase as any, undefined as any);
120
+
121
+ expect(result1).toBeNull();
122
+ expect(result2).toBeNull();
123
+ });
124
+
125
+ it('handles empty app name', async () => {
126
+ const result = await getAppId(mockSupabase as any, '');
127
+
128
+ expect(result).toBeNull();
129
+ });
130
+ });
131
+
132
+ describe('getAppIds', () => {
133
+ it('resolves multiple app IDs', async () => {
134
+ // Mock the chained call sequence for getAppIds
135
+ const mockChain = {
136
+ select: vi.fn().mockReturnThis(),
137
+ or: vi.fn().mockReturnThis(),
138
+ eq: vi.fn().mockResolvedValue({
139
+ data: [
140
+ { id: 'app-123', name: 'MY_APP' },
141
+ { id: 'app-456', name: 'ANOTHER_APP' }
142
+ ],
143
+ error: null
144
+ })
145
+ };
146
+
147
+ mockQueryBuilder.select.mockReturnValue(mockChain);
148
+ mockQueryBuilder.or.mockReturnValue(mockChain);
149
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
150
+
151
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP', 'MISSING_APP']);
152
+
153
+ expect(result).toEqual({
154
+ 'MY_APP': 'app-123',
155
+ 'ANOTHER_APP': 'app-456',
156
+ 'MISSING_APP': null
157
+ });
158
+ });
159
+
160
+ it('handles case-insensitive matching', async () => {
161
+ // Mock the chained call sequence for getAppIds
162
+ const mockChain = {
163
+ select: vi.fn().mockReturnThis(),
164
+ or: vi.fn().mockReturnThis(),
165
+ eq: vi.fn().mockResolvedValue({
166
+ data: [
167
+ { id: 'app-123', name: 'my_app' },
168
+ { id: 'app-456', name: 'another_app' }
169
+ ],
170
+ error: null
171
+ })
172
+ };
173
+
174
+ mockQueryBuilder.select.mockReturnValue(mockChain);
175
+ mockQueryBuilder.or.mockReturnValue(mockChain);
176
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
177
+
178
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
179
+
180
+ expect(result).toEqual({
181
+ 'MY_APP': 'app-123',
182
+ 'ANOTHER_APP': 'app-456'
183
+ });
184
+ });
185
+
186
+ it('returns empty object for empty app names array', async () => {
187
+ const result = await getAppIds(mockSupabase as any, []);
188
+
189
+ expect(result).toEqual({});
190
+ });
191
+
192
+ it('handles database errors gracefully', async () => {
193
+ const mockChain = {
194
+ select: vi.fn().mockReturnThis(),
195
+ or: vi.fn().mockReturnThis(),
196
+ eq: vi.fn().mockResolvedValue({
197
+ data: null,
198
+ error: { message: 'Database error' }
199
+ })
200
+ };
201
+
202
+ mockQueryBuilder.select.mockReturnValue(mockChain);
203
+ mockQueryBuilder.or.mockReturnValue(mockChain);
204
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
205
+
206
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
207
+
208
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
209
+
210
+ expect(result).toEqual({});
211
+ expect(consoleSpy).toHaveBeenCalledWith(
212
+ 'Failed to resolve app IDs for app names:',
213
+ ['MY_APP', 'ANOTHER_APP'],
214
+ { message: 'Database error' }
215
+ );
216
+
217
+ consoleSpy.mockRestore();
218
+ });
219
+
220
+ it('handles exceptions gracefully', async () => {
221
+ const mockChain = {
222
+ select: vi.fn().mockReturnThis(),
223
+ or: vi.fn().mockReturnThis(),
224
+ eq: vi.fn().mockRejectedValue(new Error('Network error'))
225
+ };
226
+
227
+ mockQueryBuilder.select.mockReturnValue(mockChain);
228
+ mockQueryBuilder.or.mockReturnValue(mockChain);
229
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
230
+
231
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
232
+
233
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
234
+
235
+ expect(result).toEqual({});
236
+ expect(consoleSpy).toHaveBeenCalledWith(
237
+ 'Error resolving app IDs for app names:',
238
+ ['MY_APP', 'ANOTHER_APP'],
239
+ expect.any(Error)
240
+ );
241
+
242
+ consoleSpy.mockRestore();
243
+ });
244
+
245
+ it('handles null/undefined app names array', async () => {
246
+ const result1 = await getAppIds(mockSupabase as any, null as any);
247
+ const result2 = await getAppIds(mockSupabase as any, undefined as any);
248
+
249
+ expect(result1).toEqual({});
250
+ expect(result2).toEqual({});
251
+ });
252
+ });
253
+
254
+ describe('CachedAppIdResolver', () => {
255
+ let resolver: CachedAppIdResolver;
256
+
257
+ beforeEach(() => {
258
+ resolver = new CachedAppIdResolver();
259
+ });
260
+
261
+ it('resolves app ID and caches result', async () => {
262
+ mockQueryBuilder.single.mockResolvedValue({
263
+ data: { id: 'app-123' },
264
+ error: null
265
+ });
266
+
267
+ const result1 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
268
+ const result2 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
269
+
270
+ expect(result1).toBe('app-123');
271
+ expect(result2).toBe('app-123');
272
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(1); // Should only call once due to caching
273
+ });
274
+
275
+ it.skip('expires cached results after TTL', async () => {
276
+ resolver = new CachedAppIdResolver(100); // 100ms TTL
277
+
278
+ mockQueryBuilder.single.mockResolvedValue({
279
+ data: { id: 'app-123' },
280
+ error: null
281
+ });
282
+
283
+ const result1 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
284
+ expect(result1).toBe('app-123');
285
+
286
+ // Wait for TTL to expire
287
+ await new Promise(resolve => setTimeout(resolve, 150));
288
+
289
+ const result2 = await resolver.getAppId(mockSupabase as any, 'MY_APP');
290
+ expect(result2).toBe('app-123');
291
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(2); // Should call again after TTL
292
+ });
293
+
294
+ it('handles cache misses gracefully', async () => {
295
+ mockQueryBuilder.single.mockResolvedValue({
296
+ data: null,
297
+ error: { message: 'No rows found' }
298
+ });
299
+
300
+ const result = await resolver.getAppId(mockSupabase as any, 'NONEXISTENT_APP');
301
+
302
+ expect(result).toBeNull();
303
+ });
304
+
305
+ it('handles database errors gracefully', async () => {
306
+ mockQueryBuilder.single.mockResolvedValue({
307
+ data: null,
308
+ error: { message: 'Database error' }
309
+ });
310
+
311
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
312
+
313
+ const result = await resolver.getAppId(mockSupabase as any, 'MY_APP');
314
+
315
+ expect(result).toBeNull();
316
+ expect(consoleSpy).toHaveBeenCalledWith(
317
+ 'Failed to resolve app ID for app name:',
318
+ 'MY_APP',
319
+ { message: 'Database error' }
320
+ );
321
+
322
+ consoleSpy.mockRestore();
323
+ });
324
+
325
+ it('clears cache correctly', async () => {
326
+ mockQueryBuilder.single.mockResolvedValue({
327
+ data: { id: 'app-123' },
328
+ error: null
329
+ });
330
+
331
+ await resolver.getAppId(mockSupabase as any, 'MY_APP');
332
+ resolver.clearCache();
333
+
334
+ const result = await resolver.getAppId(mockSupabase as any, 'MY_APP');
335
+
336
+ expect(result).toBe('app-123');
337
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(2); // Should call again after cache clear
338
+ });
339
+
340
+ it('respects max cache size', async () => {
341
+ resolver = new CachedAppIdResolver(60000, 2); // 2 max cache entries
342
+
343
+ mockQueryBuilder.single.mockResolvedValue({
344
+ data: { id: 'app-123' },
345
+ error: null
346
+ });
347
+
348
+ // Fill cache beyond max size
349
+ await resolver.getAppId(mockSupabase as any, 'APP1');
350
+ await resolver.getAppId(mockSupabase as any, 'APP2');
351
+ await resolver.getAppId(mockSupabase as any, 'APP3');
352
+
353
+ // Should have called database for all three apps
354
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(3);
355
+ });
356
+ });
357
+
358
+ describe('Integration Tests', () => {
359
+ it('integrates with real Supabase client structure', async () => {
360
+ const mockClient = {
361
+ from: vi.fn(() => ({
362
+ select: vi.fn().mockReturnThis(),
363
+ ilike: vi.fn().mockReturnThis(),
364
+ eq: vi.fn().mockReturnThis(),
365
+ single: vi.fn().mockResolvedValue({
366
+ data: { id: 'app-123' },
367
+ error: null
368
+ })
369
+ }))
370
+ };
371
+
372
+ const result = await getAppId(mockClient as any, 'MY_APP');
373
+
374
+ expect(result).toBe('app-123');
375
+ expect(mockClient.from).toHaveBeenCalledWith('rbac_apps');
376
+ });
377
+
378
+ it('handles complex app name patterns', async () => {
379
+ const appNames = [
380
+ 'MY_APP',
381
+ 'my-app',
382
+ 'my_app',
383
+ 'MyApp',
384
+ 'myapp'
385
+ ];
386
+
387
+ const mockChain = {
388
+ select: vi.fn().mockReturnThis(),
389
+ or: vi.fn().mockReturnThis(),
390
+ eq: vi.fn().mockResolvedValue({
391
+ data: [
392
+ { id: 'app-123', name: 'my_app' },
393
+ { id: 'app-456', name: 'my-app' }
394
+ ],
395
+ error: null
396
+ })
397
+ };
398
+
399
+ mockQueryBuilder.select.mockReturnValue(mockChain);
400
+ mockQueryBuilder.or.mockReturnValue(mockChain);
401
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
402
+
403
+ const result = await getAppIds(mockSupabase as any, appNames);
404
+
405
+ expect(result).toEqual({
406
+ 'MY_APP': 'app-123',
407
+ 'my-app': 'app-456',
408
+ 'my_app': null,
409
+ 'MyApp': null,
410
+ 'myapp': null
411
+ });
412
+ });
413
+ });
414
+
415
+ describe('Error Handling', () => {
416
+ it('handles network timeouts', async () => {
417
+ mockQueryBuilder.single.mockRejectedValue(new Error('Request timeout'));
418
+
419
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
420
+
421
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
422
+
423
+ expect(result).toBeNull();
424
+ expect(consoleSpy).toHaveBeenCalledWith(
425
+ 'Error resolving app ID for app name:',
426
+ 'MY_APP',
427
+ expect.any(Error)
428
+ );
429
+
430
+ consoleSpy.mockRestore();
431
+ });
432
+
433
+ it('handles malformed responses', async () => {
434
+ mockQueryBuilder.single.mockResolvedValue({
435
+ data: { invalid: 'structure' },
436
+ error: null
437
+ });
438
+
439
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
440
+
441
+ expect(result).toBeNull();
442
+ });
443
+
444
+ it('handles null data responses', async () => {
445
+ mockQueryBuilder.single.mockResolvedValue({
446
+ data: null,
447
+ error: null
448
+ });
449
+
450
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
451
+
452
+ expect(result).toBeNull();
453
+ });
454
+ });
455
+
456
+ describe('Performance', () => {
457
+ it('handles large numbers of app names efficiently', async () => {
458
+ const appNames = Array.from({ length: 100 }, (_, i) => `APP_${i}`);
459
+
460
+ const mockChain = {
461
+ select: vi.fn().mockReturnThis(),
462
+ or: vi.fn().mockReturnThis(),
463
+ eq: vi.fn().mockResolvedValue({
464
+ data: appNames.map((name, i) => ({ id: `app-${i}`, name: name.toLowerCase() })),
465
+ error: null
466
+ })
467
+ };
468
+
469
+ mockQueryBuilder.select.mockReturnValue(mockChain);
470
+ mockQueryBuilder.or.mockReturnValue(mockChain);
471
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
472
+
473
+ const startTime = Date.now();
474
+ const result = await getAppIds(mockSupabase as any, appNames);
475
+ const endTime = Date.now();
476
+
477
+ expect(Object.keys(result)).toHaveLength(100);
478
+ expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
479
+ });
480
+
481
+ it('caches results efficiently', async () => {
482
+ const resolver = new CachedAppIdResolver();
483
+
484
+ mockQueryBuilder.single.mockResolvedValue({
485
+ data: { id: 'app-123' },
486
+ error: null
487
+ });
488
+
489
+ const startTime = Date.now();
490
+
491
+ // First call - should hit database
492
+ await resolver.getAppId(mockSupabase as any, 'MY_APP');
493
+
494
+ // Second call - should hit cache
495
+ await resolver.getAppId(mockSupabase as any, 'MY_APP');
496
+
497
+ const endTime = Date.now();
498
+
499
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(1);
500
+ expect(endTime - startTime).toBeLessThan(100); // Should be very fast due to caching
501
+ });
502
+ });
503
+ });
@@ -96,7 +96,7 @@ export async function getAppIds(
96
96
  /**
97
97
  * Cached app ID resolver with TTL
98
98
  */
99
- class CachedAppIdResolver {
99
+ export class CachedAppIdResolver {
100
100
  private cache = new Map<string, { id: string | null; expires: number }>();
101
101
  private ttl = 5 * 60 * 1000; // 5 minutes
102
102