@jmruthers/pace-core 0.5.135 → 0.5.137

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (544) hide show
  1. package/dist/{DataTable-A36PJG6N.js → DataTable-6M4L6BI2.js} +26 -13
  2. package/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
  3. package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-rFL_kRjk.d.ts} +123 -135
  4. package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
  5. package/dist/{UnifiedAuthProvider-CQDZRJIS.js → UnifiedAuthProvider-XIQQ7LVU.js} +5 -5
  6. package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
  7. package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
  8. package/dist/{chunk-F64FFPOZ.js → chunk-22WKWKRX.js} +26 -20
  9. package/dist/chunk-22WKWKRX.js.map +1 -0
  10. package/dist/{chunk-VZ5OR6HD.js → chunk-4C7EXCAR.js} +62 -150
  11. package/dist/chunk-4C7EXCAR.js.map +1 -0
  12. package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
  13. package/dist/chunk-56XJ3TU6.js.map +1 -0
  14. package/dist/{chunk-CTJRBUX2.js → chunk-6LAAY47Q.js} +2 -2
  15. package/dist/{chunk-UJI6WSMD.js → chunk-7QCC6MCP.js} +90 -3
  16. package/dist/chunk-7QCC6MCP.js.map +1 -0
  17. package/dist/{chunk-66C4BSAY.js → chunk-ANBQRTPX.js} +9 -2
  18. package/dist/chunk-ANBQRTPX.js.map +1 -0
  19. package/dist/{chunk-CQZU6TFE.js → chunk-BCIBECNB.js} +100 -62
  20. package/dist/chunk-BCIBECNB.js.map +1 -0
  21. package/dist/{chunk-GKHF54DI.js → chunk-BESYRHQM.js} +10 -4
  22. package/dist/chunk-BESYRHQM.js.map +1 -0
  23. package/dist/chunk-BJPBT3CU.js +21 -0
  24. package/dist/chunk-BJPBT3CU.js.map +1 -0
  25. package/dist/{chunk-BYXRHAIF.js → chunk-BLCXZEYF.js} +23 -14
  26. package/dist/chunk-BLCXZEYF.js.map +1 -0
  27. package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
  28. package/dist/chunk-BVYWGZVV.js.map +1 -0
  29. package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
  30. package/dist/chunk-ERISIBYU.js.map +1 -0
  31. package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
  32. package/dist/chunk-FMUCXFII.js.map +1 -0
  33. package/dist/{chunk-GVDR7WNV.js → chunk-HAWZXGR2.js} +334 -614
  34. package/dist/chunk-HAWZXGR2.js.map +1 -0
  35. package/dist/{chunk-ZV77RZMU.js → chunk-INQLMHPF.js} +2 -2
  36. package/dist/chunk-JISYG63F.js +70 -0
  37. package/dist/chunk-JISYG63F.js.map +1 -0
  38. package/dist/{chunk-HMNOSGVA.js → chunk-KYRHUBIU.js} +576 -767
  39. package/dist/chunk-KYRHUBIU.js.map +1 -0
  40. package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
  41. package/dist/chunk-LS353YLY.js.map +1 -0
  42. package/dist/{chunk-TGIY2AR2.js → chunk-MA6EPSGZ.js} +4 -3
  43. package/dist/{chunk-TGIY2AR2.js.map → chunk-MA6EPSGZ.js.map} +1 -1
  44. package/dist/chunk-OWAG3GSU.js +58 -0
  45. package/dist/chunk-OWAG3GSU.js.map +1 -0
  46. package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
  47. package/dist/chunk-Q5QRDWKI.js.map +1 -0
  48. package/dist/chunk-S5OFRT4M.js +94 -0
  49. package/dist/chunk-S5OFRT4M.js.map +1 -0
  50. package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
  51. package/dist/chunk-SBVILCCA.js.map +1 -0
  52. package/dist/{chunk-ZYZCRSBD.js → chunk-T6JN6LH6.js} +16 -11
  53. package/dist/chunk-T6JN6LH6.js.map +1 -0
  54. package/dist/chunk-XDNLUEXI.js +138 -0
  55. package/dist/chunk-XDNLUEXI.js.map +1 -0
  56. package/dist/{chunk-3CG5L6RN.js → chunk-YCWDTTUK.js} +90 -75
  57. package/dist/chunk-YCWDTTUK.js.map +1 -0
  58. package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
  59. package/dist/chunk-ZZ2SS7NI.js.map +1 -0
  60. package/dist/components.d.ts +7 -287
  61. package/dist/components.js +27 -157
  62. package/dist/components.js.map +1 -1
  63. package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
  64. package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
  65. package/dist/hooks.d.ts +3 -3
  66. package/dist/hooks.js +21 -16
  67. package/dist/hooks.js.map +1 -1
  68. package/dist/index.d.ts +101 -9
  69. package/dist/index.js +44 -31
  70. package/dist/index.js.map +1 -1
  71. package/dist/providers.d.ts +1 -1
  72. package/dist/providers.js +4 -4
  73. package/dist/rbac/index.js +12 -12
  74. package/dist/schema-DTDZQe2u.d.ts +28 -0
  75. package/dist/styles/index.js +2 -1
  76. package/dist/theming/runtime.d.ts +2 -19
  77. package/dist/theming/runtime.js +2 -1
  78. package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
  79. package/dist/types.d.ts +153 -4
  80. package/dist/types.js +51 -16
  81. package/dist/types.js.map +1 -1
  82. package/dist/{useInactivityTracker-MRUU55XI.js → useInactivityTracker-TO6ZOF35.js} +3 -2
  83. package/dist/{usePublicRouteParams-Dyt1tzI9.d.ts → usePublicRouteParams-B7PabvuH.d.ts} +1 -1
  84. package/dist/utils.d.ts +221 -173
  85. package/dist/utils.js +185 -225
  86. package/dist/utils.js.map +1 -1
  87. package/dist/validation.d.ts +24 -115
  88. package/dist/validation.js +19 -474
  89. package/dist/validation.js.map +1 -1
  90. package/docs/api/classes/ColumnFactory.md +1 -1
  91. package/docs/api/classes/ErrorBoundary.md +6 -6
  92. package/docs/api/classes/InvalidScopeError.md +1 -1
  93. package/docs/api/classes/MissingUserContextError.md +1 -1
  94. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  95. package/docs/api/classes/PermissionDeniedError.md +1 -1
  96. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  97. package/docs/api/classes/RBACAuditManager.md +6 -6
  98. package/docs/api/classes/RBACCache.md +1 -1
  99. package/docs/api/classes/RBACEngine.md +7 -7
  100. package/docs/api/classes/RBACError.md +1 -1
  101. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  102. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  103. package/docs/api/classes/StorageUtils.md +1 -1
  104. package/docs/api/enums/FileCategory.md +1 -1
  105. package/docs/api/interfaces/AggregateConfig.md +4 -4
  106. package/docs/api/interfaces/BadgeProps.md +27 -0
  107. package/docs/api/interfaces/ButtonProps.md +1 -1
  108. package/docs/api/interfaces/CardProps.md +1 -1
  109. package/docs/api/interfaces/ColorPalette.md +1 -1
  110. package/docs/api/interfaces/ColorShade.md +29 -4
  111. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  112. package/docs/api/interfaces/DataRecord.md +1 -1
  113. package/docs/api/interfaces/DataTableAction.md +18 -18
  114. package/docs/api/interfaces/DataTableColumn.md +61 -1
  115. package/docs/api/interfaces/DataTableProps.md +1 -1
  116. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  117. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  118. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +14 -14
  119. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  120. package/docs/api/interfaces/EventLogoProps.md +152 -0
  121. package/docs/api/interfaces/ExportColumn.md +1 -1
  122. package/docs/api/interfaces/ExportOptions.md +8 -8
  123. package/docs/api/interfaces/FileDisplayProps.md +15 -15
  124. package/docs/api/interfaces/FileMetadata.md +1 -1
  125. package/docs/api/interfaces/FileReference.md +1 -1
  126. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  127. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  128. package/docs/api/interfaces/FileUploadProps.md +1 -1
  129. package/docs/api/interfaces/FooterProps.md +1 -1
  130. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  131. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  132. package/docs/api/interfaces/InputProps.md +1 -1
  133. package/docs/api/interfaces/LabelProps.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +1 -1
  135. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  136. package/docs/api/interfaces/NavigationContextType.md +9 -9
  137. package/docs/api/interfaces/NavigationGuardProps.md +10 -10
  138. package/docs/api/interfaces/NavigationItem.md +1 -1
  139. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  140. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  149. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  152. package/docs/api/interfaces/PaletteData.md +4 -4
  153. package/docs/api/interfaces/PermissionEnforcerProps.md +11 -11
  154. package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
  155. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  156. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  157. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  158. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  159. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  160. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  161. package/docs/api/interfaces/RBACConfig.md +1 -1
  162. package/docs/api/interfaces/RBACLogger.md +1 -1
  163. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  164. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  165. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  166. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  167. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  168. package/docs/api/interfaces/RouteConfig.md +10 -10
  169. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  170. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  171. package/docs/api/interfaces/SessionRestorationLoaderProps.md +21 -0
  172. package/docs/api/interfaces/StorageConfig.md +1 -1
  173. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  174. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  175. package/docs/api/interfaces/StorageListOptions.md +1 -1
  176. package/docs/api/interfaces/StorageListResult.md +1 -1
  177. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  178. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  179. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  180. package/docs/api/interfaces/StyleImport.md +1 -1
  181. package/docs/api/interfaces/SwitchProps.md +1 -1
  182. package/docs/api/interfaces/ToastActionElement.md +1 -1
  183. package/docs/api/interfaces/ToastProps.md +1 -1
  184. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  185. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  186. package/docs/api/interfaces/UseInactivityTrackerOptions.md +9 -9
  187. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  188. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  189. package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
  190. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  191. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +9 -9
  192. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  193. package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
  194. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  195. package/docs/api/interfaces/UserEventAccess.md +11 -11
  196. package/docs/api/interfaces/UserMenuProps.md +1 -1
  197. package/docs/api/interfaces/UserProfile.md +1 -1
  198. package/docs/api/modules.md +591 -220
  199. package/docs/api-reference/components.md +106 -26
  200. package/docs/architecture/README.md +0 -3
  201. package/docs/implementation-guides/data-tables.md +277 -13
  202. package/docs/implementation-guides/forms.md +1 -16
  203. package/docs/implementation-guides/permission-enforcement.md +8 -2
  204. package/docs/styles/README.md +0 -2
  205. package/examples/README.md +30 -14
  206. package/examples/STRUCTURE.md +125 -0
  207. package/examples/components 2/DataTable/HierarchicalActionsExample.tsx +421 -0
  208. package/examples/components 2/DataTable/HierarchicalExample.tsx +475 -0
  209. package/examples/components 2/DataTable/InitialPageSizeExample.tsx +177 -0
  210. package/examples/components 2/DataTable/PerformanceExample.tsx +506 -0
  211. package/examples/components 2/DataTable/index.ts +13 -0
  212. package/examples/components 2/Dialog/BasicHtmlTest.tsx +55 -0
  213. package/examples/components 2/Dialog/DebugHtmlExample.tsx +68 -0
  214. package/examples/components 2/Dialog/HtmlDialogExample.tsx +202 -0
  215. package/examples/components 2/Dialog/ScrollableDialogExample.tsx +290 -0
  216. package/examples/components 2/Dialog/SimpleHtmlTest.tsx +61 -0
  217. package/examples/components 2/Dialog/SmartDialogExample.tsx +322 -0
  218. package/examples/components 2/Dialog/index.ts +15 -0
  219. package/examples/components 2/index.ts +11 -0
  220. package/examples/features/index.ts +12 -0
  221. package/{src/examples → examples/features/public-pages}/CorrectPublicPageImplementation.tsx +14 -17
  222. package/{src/examples → examples/features/public-pages}/PublicEventPage.tsx +14 -27
  223. package/{src/examples → examples/features/public-pages}/PublicPageApp.tsx +15 -28
  224. package/{src/examples → examples/features/public-pages}/PublicPageUsageExample.tsx +8 -10
  225. package/examples/features/public-pages/index.ts +14 -0
  226. package/examples/features/rbac/CompleteRBACExample.tsx +324 -0
  227. package/examples/features/rbac/EventBasedApp.tsx +239 -0
  228. package/examples/features/rbac/PermissionExample.tsx +151 -0
  229. package/examples/features/rbac/index.ts +13 -0
  230. package/examples/index.ts +11 -3
  231. package/package.json +30 -19
  232. package/src/__tests__/TEST_STANDARD.md +92 -0
  233. package/src/components/Alert/Alert.tsx +1 -1
  234. package/src/components/Avatar/Avatar.tsx +1 -1
  235. package/src/components/Badge/Badge.test.tsx +314 -0
  236. package/src/components/Badge/Badge.tsx +304 -0
  237. package/src/components/Badge/index.ts +3 -0
  238. package/src/components/Button/Button.tsx +1 -1
  239. package/src/components/Card/Card.tsx +1 -1
  240. package/src/components/Checkbox/Checkbox.tsx +1 -1
  241. package/src/components/DataTable/DataTable.test.tsx +1 -1
  242. package/src/components/DataTable/DataTable.tsx +1 -30
  243. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
  244. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  245. package/src/components/DataTable/__tests__/styles.test.ts +3 -3
  246. package/src/components/DataTable/components/ActionButtons.tsx +0 -15
  247. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  248. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  249. package/src/components/DataTable/components/DataTableCore.tsx +4 -185
  250. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
  251. package/src/components/DataTable/components/DataTableModals.tsx +1 -27
  252. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  253. package/src/components/DataTable/components/EditableRow.tsx +1 -1
  254. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  255. package/src/components/DataTable/components/ImportModal.tsx +2 -14
  256. package/src/components/DataTable/components/PaginationControls.tsx +2 -1
  257. package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
  258. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  259. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  260. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
  261. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  262. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
  263. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
  264. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  265. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
  266. package/src/components/DataTable/core/ActionManager.ts +235 -0
  267. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  268. package/src/components/DataTable/core/DataManager.ts +188 -0
  269. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  270. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  271. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  272. package/src/components/DataTable/core/StateManager.ts +311 -0
  273. package/src/components/DataTable/core/interfaces.ts +338 -0
  274. package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
  275. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
  276. package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
  277. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
  278. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
  280. package/src/components/DataTable/index.ts +4 -0
  281. package/src/components/DataTable/styles.ts +28 -7
  282. package/src/components/DataTable/types.ts +13 -0
  283. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  284. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
  285. package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
  286. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  287. package/src/components/DataTable/utils/debugTools.ts +609 -0
  288. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  289. package/src/components/DataTable/utils/flexibleImport.ts +1 -11
  290. package/src/components/DataTable/utils/index.ts +2 -0
  291. package/src/components/DataTable/utils/paginationUtils.ts +1 -1
  292. package/src/components/Dialog/Dialog.tsx +2 -2
  293. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +8 -1
  294. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
  295. package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
  296. package/src/components/EventSelector/EventSelector.tsx +3 -2
  297. package/src/components/FileDisplay/FileDisplay.tsx +2 -36
  298. package/src/components/FileUpload/FileUpload.test.tsx +2 -2
  299. package/src/components/FileUpload/FileUpload.tsx +2 -2
  300. package/src/components/Footer/Footer.test.tsx +1 -1
  301. package/src/components/Footer/Footer.tsx +1 -1
  302. package/src/components/Form/Form.test.tsx +5 -510
  303. package/src/components/Form/Form.tsx +1 -1
  304. package/src/components/Form/FormField.tsx +1 -1
  305. package/src/components/Form/index.ts +0 -12
  306. package/src/components/Header/Header.tsx +1 -1
  307. package/src/components/Input/Input.tsx +1 -1
  308. package/src/components/Label/Label.tsx +1 -1
  309. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  310. package/src/components/LoginForm/LoginForm.tsx +1 -1
  311. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
  312. package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
  313. package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
  314. package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
  315. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
  316. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  317. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
  318. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
  319. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
  320. package/src/components/PaceLoginPage/PaceLoginPage.tsx +14 -13
  321. package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
  322. package/src/components/PasswordReset/index.ts +0 -2
  323. package/src/components/Progress/Progress.tsx +1 -1
  324. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
  325. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
  326. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  327. package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
  328. package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
  329. package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
  330. package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
  331. package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
  332. package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
  333. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  334. package/src/components/PublicLayout/index.ts +4 -2
  335. package/src/components/Select/Select.test.tsx +1 -1
  336. package/src/components/Select/Select.tsx +21 -9
  337. package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
  338. package/src/components/SessionRestorationLoader/index.ts +3 -0
  339. package/src/components/Switch/Switch.tsx +1 -1
  340. package/src/components/Table/Table.tsx +1 -1
  341. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  342. package/src/components/Toast/Toast.tsx +1 -1
  343. package/src/components/Tooltip/Tooltip.tsx +1 -1
  344. package/src/components/index.ts +7 -10
  345. package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
  346. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
  347. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
  348. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
  349. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  350. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
  351. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
  352. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
  353. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
  354. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
  355. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
  356. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
  357. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
  358. package/src/hooks/public/usePublicEvent.ts +7 -6
  359. package/src/hooks/public/usePublicEventLogo.ts +7 -4
  360. package/src/hooks/public/usePublicFileDisplay.ts +6 -150
  361. package/src/hooks/useComponentPerformance.ts +4 -1
  362. package/src/hooks/useDataTablePerformance.ts +4 -3
  363. package/src/hooks/useEventTheme.test.ts +18 -5
  364. package/src/hooks/useEventTheme.ts +4 -1
  365. package/src/hooks/useEvents.ts +2 -0
  366. package/src/hooks/useFileDisplay.ts +9 -8
  367. package/src/hooks/useFileReference.ts +4 -1
  368. package/src/hooks/useFileUrl.ts +4 -1
  369. package/src/hooks/useInactivityTracker.ts +5 -4
  370. package/src/hooks/useOrganisationSecurity.test.ts +33 -12
  371. package/src/hooks/useOrganisationSecurity.ts +8 -7
  372. package/src/hooks/usePerformanceMonitor.ts +6 -3
  373. package/src/hooks/usePermissionCache.ts +13 -6
  374. package/src/hooks/useSecureDataAccess.test.ts +2 -2
  375. package/src/hooks/useSecureDataAccess.ts +9 -8
  376. package/src/hooks/useSessionRestoration.ts +4 -1
  377. package/src/hooks/useStorage.ts +4 -1
  378. package/src/index.ts +20 -7
  379. package/src/providers/services/AuthServiceProvider.tsx +3 -2
  380. package/src/providers/services/EventServiceProvider.tsx +2 -1
  381. package/src/providers/services/InactivityServiceProvider.tsx +2 -1
  382. package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
  383. package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
  384. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
  385. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
  386. package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
  387. package/src/rbac/api.ts +5 -2
  388. package/src/rbac/audit-enhanced.ts +6 -6
  389. package/src/rbac/audit.test.ts +60 -38
  390. package/src/rbac/audit.ts +8 -8
  391. package/src/rbac/cache-invalidation.ts +7 -4
  392. package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
  393. package/src/rbac/components/NavigationGuard.tsx +7 -3
  394. package/src/rbac/components/NavigationProvider.tsx +6 -3
  395. package/src/rbac/components/PagePermissionGuard.tsx +28 -16
  396. package/src/rbac/components/PagePermissionProvider.tsx +4 -1
  397. package/src/rbac/components/PermissionEnforcer.tsx +9 -3
  398. package/src/rbac/components/RoleBasedRouter.tsx +3 -1
  399. package/src/rbac/components/SecureDataProvider.tsx +7 -3
  400. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
  401. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
  402. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
  403. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
  404. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
  405. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
  406. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
  407. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
  408. package/src/rbac/engine.ts +15 -7
  409. package/src/rbac/hooks/usePermissions.ts +7 -3
  410. package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
  411. package/src/rbac/hooks/useResolvedScope.ts +10 -7
  412. package/src/rbac/permissions.ts +5 -2
  413. package/src/rbac/security.test.ts +27 -16
  414. package/src/rbac/security.ts +5 -4
  415. package/src/services/AuthService.ts +22 -21
  416. package/src/services/EventService.ts +12 -12
  417. package/src/services/InactivityService.ts +5 -4
  418. package/src/services/OrganisationService.ts +26 -25
  419. package/src/services/__tests__/AuthService.test.ts +51 -19
  420. package/src/services/__tests__/EventService.test.ts +37 -5
  421. package/src/services/__tests__/InactivityService.test.ts +38 -4
  422. package/src/services/__tests__/OrganisationService.test.ts +3 -8
  423. package/src/services/base/BaseService.ts +3 -1
  424. package/src/styles/core.css +3 -0
  425. package/src/theming/__tests__/runtime.test.ts +21 -12
  426. package/src/theming/parseEventColours.ts +5 -19
  427. package/src/theming/runtime.ts +8 -4
  428. package/src/types/validation.ts +2 -29
  429. package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
  430. package/src/utils/__tests__/audit.unit.test.ts +1 -1
  431. package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
  432. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
  433. package/src/utils/__tests__/cn.unit.test.ts +1 -1
  434. package/src/utils/__tests__/debugLogger.test.ts +1 -1
  435. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
  436. package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
  437. package/src/utils/__tests__/formatting.unit.test.ts +1 -1
  438. package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
  439. package/src/utils/__tests__/logger.unit.test.ts +1 -1
  440. package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
  441. package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
  442. package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
  443. package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
  444. package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
  445. package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
  446. package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
  447. package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
  448. package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
  449. package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
  450. package/src/utils/__tests__/security.unit.test.ts +40 -18
  451. package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
  452. package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
  453. package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
  454. package/src/utils/app/appConfig.ts +47 -0
  455. package/src/utils/app/appIdResolver.test.ts +497 -0
  456. package/src/utils/app/appIdResolver.ts +133 -0
  457. package/src/utils/app/appNameResolver.simple.test.ts +212 -0
  458. package/src/utils/app/appNameResolver.test.ts +121 -0
  459. package/src/utils/app/appNameResolver.ts +195 -0
  460. package/src/utils/audit/audit.ts +127 -0
  461. package/src/utils/context/organisationContext.test.ts +322 -0
  462. package/src/utils/context/organisationContext.ts +156 -0
  463. package/src/utils/context/sessionTracking.ts +125 -0
  464. package/src/utils/core/cn.ts +7 -0
  465. package/src/utils/core/debugLogger.ts +67 -0
  466. package/src/utils/core/logger.ts +181 -0
  467. package/src/utils/device/deviceFingerprint.ts +215 -0
  468. package/src/utils/dynamic/dynamicUtils.ts +105 -0
  469. package/src/utils/dynamic/lazyLoad.tsx +44 -0
  470. package/src/utils/file-reference/__tests__/file-reference.test.ts +788 -0
  471. package/src/utils/file-reference/index.ts +501 -0
  472. package/src/utils/formatting/formatDate.test.ts +237 -0
  473. package/src/utils/formatting/formatting.ts +133 -0
  474. package/src/utils/index.ts +39 -54
  475. package/src/utils/performance/bundleAnalysis.ts +129 -0
  476. package/src/utils/performance/performanceBenchmark.ts +64 -0
  477. package/src/utils/performance/performanceBudgets.ts +110 -0
  478. package/src/utils/permissions/permissionTypes.ts +37 -0
  479. package/src/utils/permissions/permissionUtils.test.ts +393 -0
  480. package/src/utils/permissions/permissionUtils.ts +34 -0
  481. package/src/utils/security/auth-utils.ts +96 -0
  482. package/src/utils/security/secureDataAccess.test.ts +711 -0
  483. package/src/utils/security/secureDataAccess.ts +377 -0
  484. package/src/utils/security/secureErrors.ts +82 -0
  485. package/src/utils/security/secureStorage.ts +244 -0
  486. package/src/utils/security/security.ts +159 -0
  487. package/src/utils/security/securityMonitor.ts +45 -0
  488. package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
  489. package/src/utils/storage/helpers.ts +15 -8
  490. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +598 -0
  491. package/src/{validation → utils/validation}/csrf.ts +1 -1
  492. package/src/utils/validation/htmlSanitization.ts +184 -0
  493. package/src/utils/validation/index.ts +79 -0
  494. package/src/utils/validation/sanitization.ts +333 -0
  495. package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
  496. package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
  497. package/src/utils/validation/validation.ts +111 -0
  498. package/src/utils/validation/validationUtils.ts +123 -0
  499. package/src/validation/index.ts +3 -34
  500. package/dist/chunk-24MKLB7U.js +0 -81
  501. package/dist/chunk-24MKLB7U.js.map +0 -1
  502. package/dist/chunk-3CG5L6RN.js.map +0 -1
  503. package/dist/chunk-3DBFLLLU.js.map +0 -1
  504. package/dist/chunk-5F3NDPJV.js.map +0 -1
  505. package/dist/chunk-66C4BSAY.js.map +0 -1
  506. package/dist/chunk-BDZUMRBD.js +0 -87
  507. package/dist/chunk-BDZUMRBD.js.map +0 -1
  508. package/dist/chunk-BYXRHAIF.js.map +0 -1
  509. package/dist/chunk-CDQ3PX7L.js +0 -18
  510. package/dist/chunk-CDQ3PX7L.js.map +0 -1
  511. package/dist/chunk-CQZU6TFE.js.map +0 -1
  512. package/dist/chunk-F64FFPOZ.js.map +0 -1
  513. package/dist/chunk-GEVIB2UB.js.map +0 -1
  514. package/dist/chunk-GKHF54DI.js.map +0 -1
  515. package/dist/chunk-GVDR7WNV.js.map +0 -1
  516. package/dist/chunk-HMNOSGVA.js.map +0 -1
  517. package/dist/chunk-JCQZ6LA7.js.map +0 -1
  518. package/dist/chunk-M6DDYFUD.js.map +0 -1
  519. package/dist/chunk-O3NWNXDY.js.map +0 -1
  520. package/dist/chunk-PYUXFQJ3.js.map +0 -1
  521. package/dist/chunk-UJI6WSMD.js.map +0 -1
  522. package/dist/chunk-VZ5OR6HD.js.map +0 -1
  523. package/dist/chunk-WP5I5GLN.js.map +0 -1
  524. package/dist/chunk-ZYZCRSBD.js.map +0 -1
  525. package/dist/validation-DnhrNMju.d.ts +0 -159
  526. package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
  527. package/src/validation/__tests__/common.unit.test.ts +0 -101
  528. package/src/validation/__tests__/csrf.unit.test.ts +0 -365
  529. package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
  530. package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
  531. package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
  532. package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
  533. package/src/validation/__tests__/user.unit.test.ts +0 -440
  534. package/src/validation/sanitization.ts +0 -96
  535. /package/dist/{DataTable-A36PJG6N.js.map → DataTable-6M4L6BI2.js.map} +0 -0
  536. /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  537. /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
  538. /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
  539. /package/dist/{chunk-CTJRBUX2.js.map → chunk-6LAAY47Q.js.map} +0 -0
  540. /package/dist/{chunk-ZV77RZMU.js.map → chunk-INQLMHPF.js.map} +0 -0
  541. /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
  542. /package/src/{validation → utils/validation}/common.ts +0 -0
  543. /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
  544. /package/src/{validation → utils/validation}/user.ts +0 -0
@@ -0,0 +1,497 @@
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 the Logger module
16
+ vi.mock('../core/logger', () => {
17
+ const mockLoggerInstance = {
18
+ error: vi.fn(),
19
+ };
20
+ return {
21
+ createLogger: vi.fn(() => mockLoggerInstance),
22
+ };
23
+ });
24
+
25
+ // Get the mock instance after mock is set up
26
+ import { createLogger } from '../core/logger';
27
+ const getMockLogger = () => createLogger('test');
28
+
29
+ // Mock Supabase client
30
+ const createMockSupabaseClient = () => {
31
+ const queryBuilder = {
32
+ select: vi.fn().mockReturnThis(),
33
+ ilike: vi.fn().mockReturnThis(),
34
+ eq: vi.fn().mockReturnThis(),
35
+ or: vi.fn().mockReturnThis(),
36
+ single: vi.fn().mockResolvedValue({ data: null, error: null }),
37
+ mockResolvedValue: vi.fn(),
38
+ mockRejectedValue: vi.fn()
39
+ };
40
+
41
+ // Make the chained methods return the query builder with mock methods
42
+ queryBuilder.select.mockReturnValue(queryBuilder);
43
+ queryBuilder.ilike.mockReturnValue(queryBuilder);
44
+ queryBuilder.eq.mockReturnValue(queryBuilder);
45
+ queryBuilder.or.mockReturnValue(queryBuilder);
46
+
47
+ return {
48
+ from: vi.fn(() => queryBuilder),
49
+ queryBuilder // Export the query builder for direct access
50
+ };
51
+ };
52
+
53
+ describe('App ID Resolver', () => {
54
+ let mockSupabase: any;
55
+ let mockQueryBuilder: any;
56
+
57
+ beforeEach(() => {
58
+ vi.clearAllMocks();
59
+ mockSupabase = createMockSupabaseClient();
60
+ mockQueryBuilder = mockSupabase.queryBuilder;
61
+ const logger = getMockLogger();
62
+ vi.mocked(logger.error).mockClear();
63
+ });
64
+
65
+ describe('getAppId', () => {
66
+ it('resolves app ID for valid app name', async () => {
67
+ mockQueryBuilder.single.mockResolvedValue({
68
+ data: { id: 'app-123' },
69
+ error: null
70
+ });
71
+
72
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
73
+
74
+ expect(result).toBe('app-123');
75
+ expect(mockSupabase.from).toHaveBeenCalledWith('rbac_apps');
76
+ expect(mockQueryBuilder.select).toHaveBeenCalledWith('id');
77
+ expect(mockQueryBuilder.ilike).toHaveBeenCalledWith('name', 'MY_APP');
78
+ expect(mockQueryBuilder.eq).toHaveBeenCalledWith('is_active', true);
79
+ });
80
+
81
+ it('returns null when app not found', async () => {
82
+ mockQueryBuilder.single.mockResolvedValue({
83
+ data: null,
84
+ error: { message: 'No rows found' }
85
+ });
86
+
87
+ const result = await getAppId(mockSupabase as any, 'NONEXISTENT_APP');
88
+
89
+ expect(result).toBeNull();
90
+ });
91
+
92
+ it('handles database errors gracefully', async () => {
93
+ mockQueryBuilder.single.mockResolvedValue({
94
+ data: null,
95
+ error: { message: 'Database error' }
96
+ });
97
+
98
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
99
+
100
+ expect(result).toBeNull();
101
+ const logger = getMockLogger();
102
+ expect(vi.mocked(logger.error)).toHaveBeenCalledWith(
103
+ 'Failed to resolve app ID for app name:',
104
+ 'MY_APP',
105
+ { message: 'Database error' }
106
+ );
107
+ });
108
+
109
+ it('handles exceptions gracefully', async () => {
110
+ mockQueryBuilder.single.mockRejectedValue(new Error('Network error'));
111
+
112
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
113
+
114
+ expect(result).toBeNull();
115
+ const logger = getMockLogger();
116
+ expect(vi.mocked(logger.error)).toHaveBeenCalledWith(
117
+ 'Error resolving app ID for app name:',
118
+ 'MY_APP',
119
+ expect.any(Error)
120
+ );
121
+ });
122
+
123
+ it('handles null/undefined app name', async () => {
124
+ const result1 = await getAppId(mockSupabase as any, null as any);
125
+ const result2 = await getAppId(mockSupabase as any, undefined as any);
126
+
127
+ expect(result1).toBeNull();
128
+ expect(result2).toBeNull();
129
+ });
130
+
131
+ it('handles empty app name', async () => {
132
+ const result = await getAppId(mockSupabase as any, '');
133
+
134
+ expect(result).toBeNull();
135
+ });
136
+ });
137
+
138
+ describe('getAppIds', () => {
139
+ it('resolves multiple app IDs', async () => {
140
+ // Mock the chained call sequence for getAppIds
141
+ const mockChain = {
142
+ select: vi.fn().mockReturnThis(),
143
+ or: vi.fn().mockReturnThis(),
144
+ eq: vi.fn().mockResolvedValue({
145
+ data: [
146
+ { id: 'app-123', name: 'MY_APP' },
147
+ { id: 'app-456', name: 'ANOTHER_APP' }
148
+ ],
149
+ error: null
150
+ })
151
+ };
152
+
153
+ mockQueryBuilder.select.mockReturnValue(mockChain);
154
+ mockQueryBuilder.or.mockReturnValue(mockChain);
155
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
156
+
157
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP', 'MISSING_APP']);
158
+
159
+ expect(result).toEqual({
160
+ 'MY_APP': 'app-123',
161
+ 'ANOTHER_APP': 'app-456',
162
+ 'MISSING_APP': null
163
+ });
164
+ });
165
+
166
+ it('handles case-insensitive matching', async () => {
167
+ // Mock the chained call sequence for getAppIds
168
+ const mockChain = {
169
+ select: vi.fn().mockReturnThis(),
170
+ or: vi.fn().mockReturnThis(),
171
+ eq: vi.fn().mockResolvedValue({
172
+ data: [
173
+ { id: 'app-123', name: 'my_app' },
174
+ { id: 'app-456', name: 'another_app' }
175
+ ],
176
+ error: null
177
+ })
178
+ };
179
+
180
+ mockQueryBuilder.select.mockReturnValue(mockChain);
181
+ mockQueryBuilder.or.mockReturnValue(mockChain);
182
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
183
+
184
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
185
+
186
+ expect(result).toEqual({
187
+ 'MY_APP': 'app-123',
188
+ 'ANOTHER_APP': 'app-456'
189
+ });
190
+ });
191
+
192
+ it('returns empty object for empty app names array', async () => {
193
+ const result = await getAppIds(mockSupabase as any, []);
194
+
195
+ expect(result).toEqual({});
196
+ });
197
+
198
+ it('handles database errors gracefully', async () => {
199
+ const mockChain = {
200
+ select: vi.fn().mockReturnThis(),
201
+ or: vi.fn().mockReturnThis(),
202
+ eq: vi.fn().mockResolvedValue({
203
+ data: null,
204
+ error: { message: 'Database error' }
205
+ })
206
+ };
207
+
208
+ mockQueryBuilder.select.mockReturnValue(mockChain);
209
+ mockQueryBuilder.or.mockReturnValue(mockChain);
210
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
211
+
212
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
213
+
214
+ expect(result).toEqual({});
215
+ const logger = getMockLogger();
216
+ expect(vi.mocked(logger.error)).toHaveBeenCalledWith(
217
+ 'Failed to resolve app IDs for app names:',
218
+ ['MY_APP', 'ANOTHER_APP'],
219
+ { message: 'Database error' }
220
+ );
221
+ });
222
+
223
+ it('handles exceptions gracefully', async () => {
224
+ const mockChain = {
225
+ select: vi.fn().mockReturnThis(),
226
+ or: vi.fn().mockReturnThis(),
227
+ eq: vi.fn().mockRejectedValue(new Error('Network error'))
228
+ };
229
+
230
+ mockQueryBuilder.select.mockReturnValue(mockChain);
231
+ mockQueryBuilder.or.mockReturnValue(mockChain);
232
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
233
+
234
+ const result = await getAppIds(mockSupabase as any, ['MY_APP', 'ANOTHER_APP']);
235
+
236
+ expect(result).toEqual({});
237
+ const logger = getMockLogger();
238
+ expect(vi.mocked(logger.error)).toHaveBeenCalledWith(
239
+ 'Error resolving app IDs for app names:',
240
+ ['MY_APP', 'ANOTHER_APP'],
241
+ expect.any(Error)
242
+ );
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('expires cached results after TTL', async () => {
276
+ resolver = new CachedAppIdResolver();
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
+ // Clear cache to simulate TTL expiration
287
+ resolver.clearCacheForApp('MY_APP');
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 cache clear
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 result = await resolver.getAppId(mockSupabase as any, 'MY_APP');
312
+
313
+ expect(result).toBeNull();
314
+ const logger = getMockLogger();
315
+ expect(vi.mocked(logger.error)).toHaveBeenCalledWith(
316
+ 'Failed to resolve app ID for app name:',
317
+ 'MY_APP',
318
+ { message: 'Database error' }
319
+ );
320
+ });
321
+
322
+ it('clears cache correctly', async () => {
323
+ mockQueryBuilder.single.mockResolvedValue({
324
+ data: { id: 'app-123' },
325
+ error: null
326
+ });
327
+
328
+ await resolver.getAppId(mockSupabase as any, 'MY_APP');
329
+ resolver.clearCache();
330
+
331
+ const result = await resolver.getAppId(mockSupabase as any, 'MY_APP');
332
+
333
+ expect(result).toBe('app-123');
334
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(2); // Should call again after cache clear
335
+ });
336
+
337
+ it('respects max cache size', async () => {
338
+ resolver = new CachedAppIdResolver(60000, 2); // 2 max cache entries
339
+
340
+ mockQueryBuilder.single.mockResolvedValue({
341
+ data: { id: 'app-123' },
342
+ error: null
343
+ });
344
+
345
+ // Fill cache beyond max size
346
+ await resolver.getAppId(mockSupabase as any, 'APP1');
347
+ await resolver.getAppId(mockSupabase as any, 'APP2');
348
+ await resolver.getAppId(mockSupabase as any, 'APP3');
349
+
350
+ // Should have called database for all three apps
351
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(3);
352
+ });
353
+ });
354
+
355
+ describe('Integration Tests', () => {
356
+ it('integrates with real Supabase client structure', async () => {
357
+ const mockClient = {
358
+ from: vi.fn(() => ({
359
+ select: vi.fn().mockReturnThis(),
360
+ ilike: vi.fn().mockReturnThis(),
361
+ eq: vi.fn().mockReturnThis(),
362
+ single: vi.fn().mockResolvedValue({
363
+ data: { id: 'app-123' },
364
+ error: null
365
+ })
366
+ }))
367
+ };
368
+
369
+ const result = await getAppId(mockClient as any, 'MY_APP');
370
+
371
+ expect(result).toBe('app-123');
372
+ expect(mockClient.from).toHaveBeenCalledWith('rbac_apps');
373
+ });
374
+
375
+ it('handles complex app name patterns', async () => {
376
+ const appNames = [
377
+ 'MY_APP',
378
+ 'my-app',
379
+ 'my_app',
380
+ 'MyApp',
381
+ 'myapp'
382
+ ];
383
+
384
+ const mockChain = {
385
+ select: vi.fn().mockReturnThis(),
386
+ or: vi.fn().mockReturnThis(),
387
+ eq: vi.fn().mockResolvedValue({
388
+ data: [
389
+ { id: 'app-123', name: 'my_app' },
390
+ { id: 'app-456', name: 'my-app' }
391
+ ],
392
+ error: null
393
+ })
394
+ };
395
+
396
+ mockQueryBuilder.select.mockReturnValue(mockChain);
397
+ mockQueryBuilder.or.mockReturnValue(mockChain);
398
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
399
+
400
+ const result = await getAppIds(mockSupabase as any, appNames);
401
+
402
+ expect(result).toEqual({
403
+ 'MY_APP': 'app-123',
404
+ 'my-app': 'app-456',
405
+ 'my_app': null,
406
+ 'MyApp': null,
407
+ 'myapp': null
408
+ });
409
+ });
410
+ });
411
+
412
+ describe('Error Handling', () => {
413
+ it('handles network timeouts', async () => {
414
+ mockQueryBuilder.single.mockRejectedValue(new Error('Request timeout'));
415
+
416
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
417
+
418
+ expect(result).toBeNull();
419
+ const logger = getMockLogger();
420
+ expect(vi.mocked(logger.error)).toHaveBeenCalledWith(
421
+ 'Error resolving app ID for app name:',
422
+ 'MY_APP',
423
+ expect.any(Error)
424
+ );
425
+ });
426
+
427
+ it('handles malformed responses', async () => {
428
+ mockQueryBuilder.single.mockResolvedValue({
429
+ data: { invalid: 'structure' },
430
+ error: null
431
+ });
432
+
433
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
434
+
435
+ expect(result).toBeNull();
436
+ });
437
+
438
+ it('handles null data responses', async () => {
439
+ mockQueryBuilder.single.mockResolvedValue({
440
+ data: null,
441
+ error: null
442
+ });
443
+
444
+ const result = await getAppId(mockSupabase as any, 'MY_APP');
445
+
446
+ expect(result).toBeNull();
447
+ });
448
+ });
449
+
450
+ describe('Performance', () => {
451
+ it('handles large numbers of app names efficiently', async () => {
452
+ const appNames = Array.from({ length: 100 }, (_, i) => `APP_${i}`);
453
+
454
+ const mockChain = {
455
+ select: vi.fn().mockReturnThis(),
456
+ or: vi.fn().mockReturnThis(),
457
+ eq: vi.fn().mockResolvedValue({
458
+ data: appNames.map((name, i) => ({ id: `app-${i}`, name: name.toLowerCase() })),
459
+ error: null
460
+ })
461
+ };
462
+
463
+ mockQueryBuilder.select.mockReturnValue(mockChain);
464
+ mockQueryBuilder.or.mockReturnValue(mockChain);
465
+ mockQueryBuilder.eq.mockReturnValue(mockChain);
466
+
467
+ const startTime = Date.now();
468
+ const result = await getAppIds(mockSupabase as any, appNames);
469
+ const endTime = Date.now();
470
+
471
+ expect(Object.keys(result)).toHaveLength(100);
472
+ expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
473
+ });
474
+
475
+ it('caches results efficiently', async () => {
476
+ const resolver = new CachedAppIdResolver();
477
+
478
+ mockQueryBuilder.single.mockResolvedValue({
479
+ data: { id: 'app-123' },
480
+ error: null
481
+ });
482
+
483
+ const startTime = Date.now();
484
+
485
+ // First call - should hit database
486
+ await resolver.getAppId(mockSupabase as any, 'MY_APP');
487
+
488
+ // Second call - should hit cache
489
+ await resolver.getAppId(mockSupabase as any, 'MY_APP');
490
+
491
+ const endTime = Date.now();
492
+
493
+ expect(mockQueryBuilder.single).toHaveBeenCalledTimes(1);
494
+ expect(endTime - startTime).toBeLessThan(100); // Should be very fast due to caching
495
+ });
496
+ });
497
+ });
@@ -0,0 +1,133 @@
1
+ /**
2
+ * App ID Resolution Utility
3
+ * @package @jmruthers/pace-core
4
+ * @module Utils/AppIdResolver
5
+ * @since 1.0.0
6
+ *
7
+ * This module provides utilities to resolve app names to app IDs for database operations.
8
+ */
9
+
10
+ import type { SupabaseClient } from '@supabase/supabase-js';
11
+ import type { Database } from '../../types/database';
12
+ import { createLogger } from '../core/logger';
13
+
14
+ const log = createLogger('AppIdResolver');
15
+
16
+ /**
17
+ * Resolves an app name to its corresponding app ID
18
+ *
19
+ * @param supabase - Supabase client instance
20
+ * @param appName - The app name to resolve
21
+ * @returns Promise resolving to the app ID or null if not found
22
+ */
23
+ export async function getAppId(
24
+ supabase: SupabaseClient<Database>,
25
+ appName: string
26
+ ): Promise<string | null> {
27
+ try {
28
+ const { data, error } = await supabase
29
+ .from('rbac_apps')
30
+ .select('id')
31
+ .ilike('name', appName)
32
+ .eq('is_active', true)
33
+ .single();
34
+
35
+ if (error) {
36
+ log.error('Failed to resolve app ID for app name:', appName, error);
37
+ return null;
38
+ }
39
+
40
+ return (data as { id: string } | null)?.id || null;
41
+ } catch (error) {
42
+ log.error('Error resolving app ID for app name:', appName, error);
43
+ return null;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Resolves multiple app names to their corresponding app IDs
49
+ *
50
+ * @param supabase - Supabase client instance
51
+ * @param appNames - Array of app names to resolve
52
+ * @returns Promise resolving to a map of app names to app IDs
53
+ */
54
+ export async function getAppIds(
55
+ supabase: SupabaseClient<Database>,
56
+ appNames: string[]
57
+ ): Promise<Record<string, string | null>> {
58
+ try {
59
+ // For case-insensitive matching with multiple values, we need to use OR conditions
60
+ // since PostgreSQL doesn't support case-insensitive IN with ILIKE
61
+ const orConditions = appNames.map(name => `name.ilike.${name}`).join(',');
62
+
63
+ const { data, error } = await supabase
64
+ .from('rbac_apps')
65
+ .select('id, name')
66
+ .or(orConditions)
67
+ .eq('is_active', true);
68
+
69
+ if (error) {
70
+ log.error('Failed to resolve app IDs for app names:', appNames, error);
71
+ return {};
72
+ }
73
+
74
+ const result: Record<string, string | null> = {};
75
+
76
+ // Initialize all app names with null
77
+ appNames.forEach(name => {
78
+ result[name] = null;
79
+ });
80
+
81
+ // Set resolved app IDs - match case-insensitively
82
+ (data as { id: string; name: string }[] | null)?.forEach(app => {
83
+ // Find the original app name that matches (case-insensitive)
84
+ const originalName = appNames.find(name =>
85
+ name.toLowerCase() === app.name.toLowerCase()
86
+ );
87
+ if (originalName) {
88
+ result[originalName] = app.id;
89
+ }
90
+ });
91
+
92
+ return result;
93
+ } catch (error) {
94
+ log.error('Error resolving app IDs for app names:', appNames, error);
95
+ return {};
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Cached app ID resolver with TTL
101
+ */
102
+ export class CachedAppIdResolver {
103
+ private cache = new Map<string, { id: string | null; expires: number }>();
104
+ private ttl = 5 * 60 * 1000; // 5 minutes
105
+
106
+ async getAppId(
107
+ supabase: SupabaseClient<Database>,
108
+ appName: string
109
+ ): Promise<string | null> {
110
+ const now = Date.now();
111
+ const cached = this.cache.get(appName);
112
+
113
+ if (cached && cached.expires > now) {
114
+ return cached.id;
115
+ }
116
+
117
+ const id = await getAppId(supabase, appName);
118
+ this.cache.set(appName, { id, expires: now + this.ttl });
119
+
120
+ return id;
121
+ }
122
+
123
+ clearCache(): void {
124
+ this.cache.clear();
125
+ }
126
+
127
+ clearCacheForApp(appName: string): void {
128
+ this.cache.delete(appName);
129
+ }
130
+ }
131
+
132
+ // Export singleton instance
133
+ export const cachedAppIdResolver = new CachedAppIdResolver();