@jmruthers/pace-core 0.6.1 → 0.6.3

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 (549) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +46 -87
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
  17. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
  18. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
  19. package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
  20. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  21. package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
  22. package/dist/chunk-2T2IG7T7.js.map +1 -0
  23. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  24. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  25. package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
  26. package/dist/chunk-6Z7LTB3D.js.map +1 -0
  27. package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
  28. package/dist/chunk-CNCQDFLN.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
  31. package/dist/chunk-DWUBLJJM.js.map +1 -0
  32. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  33. package/dist/chunk-FFQEQTNW.js.map +1 -0
  34. package/dist/chunk-FMUCXFII.js +76 -0
  35. package/dist/chunk-FMUCXFII.js.map +1 -0
  36. package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
  37. package/dist/chunk-HFZBI76P.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
  43. package/dist/chunk-M7MPQISP.js.map +1 -0
  44. package/dist/chunk-PQBSKX33.js +7793 -0
  45. package/dist/chunk-PQBSKX33.js.map +1 -0
  46. package/dist/chunk-QRPVRXYT.js +226 -0
  47. package/dist/chunk-QRPVRXYT.js.map +1 -0
  48. package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
  49. package/dist/chunk-RWEBCB47.js.map +1 -0
  50. package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
  51. package/dist/chunk-YDQHOZNA.js.map +1 -0
  52. package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
  53. package/dist/chunk-ZNIWI3UC.js.map +1 -0
  54. package/dist/components.d.ts +5 -5
  55. package/dist/components.js +18 -16
  56. package/dist/components.js.map +1 -1
  57. package/dist/contextValidator-3JNZKUTX.js +9 -0
  58. package/dist/contextValidator-3JNZKUTX.js.map +1 -0
  59. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  60. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  61. package/dist/hooks.d.ts +55 -122
  62. package/dist/hooks.js +10 -13
  63. package/dist/hooks.js.map +1 -1
  64. package/dist/index.d.ts +60 -13
  65. package/dist/index.js +30 -25
  66. package/dist/index.js.map +1 -1
  67. package/dist/providers.d.ts +21 -3
  68. package/dist/providers.js +4 -3
  69. package/dist/rbac/index.d.ts +210 -139
  70. package/dist/rbac/index.js +17 -13
  71. package/dist/styles/index.js +1 -1
  72. package/dist/theming/runtime.d.ts +1 -13
  73. package/dist/theming/runtime.js +2 -2
  74. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  75. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  76. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  77. package/dist/types.d.ts +2 -2
  78. package/dist/types.js +1 -1
  79. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
  80. package/dist/utils.d.ts +4 -5
  81. package/dist/utils.js +17 -19
  82. package/dist/utils.js.map +1 -1
  83. package/docs/api/README.md +21 -17
  84. package/docs/api/modules.md +4191 -2967
  85. package/docs/architecture/database-schema-requirements.md +161 -0
  86. package/docs/components/context-selector.md +126 -0
  87. package/docs/core-concepts/rbac-system.md +3 -3
  88. package/docs/documentation-index.md +2 -4
  89. package/docs/getting-started/cursor-rules.md +2 -1
  90. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  91. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  92. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  93. package/docs/migration/README.md +52 -6
  94. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  95. package/docs/migration/database-changes-december-2025.md +3 -3
  96. package/docs/pace-mint-fix-auto-selection.md +218 -0
  97. package/docs/pace-mint-rbac-setup.md +391 -0
  98. package/docs/rbac/event-based-apps.md +1 -1
  99. package/docs/rbac/getting-started.md +1 -1
  100. package/docs/rbac/quick-start.md +1 -1
  101. package/docs/rbac/secure-client-protection.md +330 -0
  102. package/docs/standards/README.md +1 -0
  103. package/package.json +4 -3
  104. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  105. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  106. package/scripts/audit/core/checks/bundle.cjs +142 -0
  107. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
  108. package/scripts/audit/core/checks/config.cjs +54 -0
  109. package/scripts/audit/core/checks/coverage.cjs +84 -0
  110. package/scripts/audit/core/checks/dependencies.cjs +985 -0
  111. package/scripts/audit/core/checks/documentation.cjs +268 -0
  112. package/scripts/audit/core/checks/environment.cjs +116 -0
  113. package/scripts/audit/core/checks/error-handling.cjs +340 -0
  114. package/scripts/audit/core/checks/forms.cjs +172 -0
  115. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  116. package/scripts/audit/core/checks/hooks.cjs +334 -0
  117. package/scripts/audit/core/checks/imports.cjs +244 -0
  118. package/scripts/audit/core/checks/performance.cjs +325 -0
  119. package/scripts/audit/core/checks/routes.cjs +117 -0
  120. package/scripts/audit/core/checks/state.cjs +130 -0
  121. package/scripts/audit/core/checks/structure.cjs +65 -0
  122. package/scripts/audit/core/checks/style.cjs +584 -0
  123. package/scripts/audit/core/checks/testing.cjs +122 -0
  124. package/scripts/audit/core/checks/typescript.cjs +61 -0
  125. package/scripts/audit/core/scanner.cjs +199 -0
  126. package/scripts/audit/core/utils.cjs +137 -0
  127. package/scripts/audit/index.cjs +223 -0
  128. package/scripts/audit/reporters/console.cjs +151 -0
  129. package/scripts/audit/reporters/json.cjs +54 -0
  130. package/scripts/audit/reporters/markdown.cjs +124 -0
  131. package/scripts/audit-consuming-app.cjs +61 -936
  132. package/scripts/build-docs/build-decision.js +240 -0
  133. package/scripts/build-docs/cache-utils.js +105 -0
  134. package/scripts/build-docs/content-normalization.js +150 -0
  135. package/scripts/build-docs/file-utils.js +105 -0
  136. package/scripts/build-docs/git-utils.js +86 -0
  137. package/scripts/build-docs/hash-utils.js +116 -0
  138. package/scripts/build-docs/typedoc-runner.js +220 -0
  139. package/scripts/build-docs-incremental.js +77 -913
  140. package/scripts/utils/command-runner.js +16 -11
  141. package/scripts/validate-formats.js +61 -56
  142. package/scripts/validate-master.js +74 -69
  143. package/scripts/validate-pre-publish.js +70 -65
  144. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  145. package/src/components/Alert/Alert.test.tsx +12 -18
  146. package/src/components/Alert/Alert.tsx +5 -7
  147. package/src/components/Avatar/Avatar.test.tsx +4 -4
  148. package/src/components/Badge/Badge.tsx +14 -0
  149. package/src/components/Button/Button.tsx +22 -0
  150. package/src/components/Calendar/Calendar.tsx +8 -2
  151. package/src/components/Card/Card.tsx +4 -0
  152. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  153. package/src/components/Checkbox/Checkbox.tsx +2 -2
  154. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  155. package/src/components/ContextSelector/index.ts +3 -0
  156. package/src/components/DataTable/DataTable.tsx +38 -4
  157. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  158. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  159. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  160. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  161. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  162. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  163. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  164. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  165. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  166. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  167. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  168. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  169. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  170. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  171. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  172. package/src/components/DataTable/components/EditFields.tsx +307 -0
  173. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  174. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  176. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  177. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  178. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  179. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  180. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  181. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  182. package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
  183. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  184. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  185. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  186. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  187. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  188. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  189. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  190. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  191. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  192. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  193. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  194. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  195. package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
  196. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  197. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  198. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  199. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  200. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  201. package/src/components/DataTable/styles.ts +6 -6
  202. package/src/components/DataTable/types.ts +6 -10
  203. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  204. package/src/components/DataTable/utils/debugTools.ts +18 -113
  205. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  206. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  207. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  208. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  209. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  210. package/src/components/Dialog/Dialog.tsx +31 -3
  211. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  212. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  213. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  214. package/src/components/ErrorBoundary/index.ts +27 -2
  215. package/src/components/FileDisplay/FileDisplay.tsx +74 -28
  216. package/src/components/FileUpload/FileUpload.tsx +22 -2
  217. package/src/components/Footer/Footer.test.tsx +16 -16
  218. package/src/components/Footer/Footer.tsx +14 -11
  219. package/src/components/Form/Form.tsx +1 -0
  220. package/src/components/Header/Header.test.tsx +43 -73
  221. package/src/components/Header/Header.tsx +59 -49
  222. package/src/components/Input/Input.test.tsx +2 -2
  223. package/src/components/Input/Input.tsx +8 -4
  224. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  225. package/src/components/LoginForm/LoginForm.tsx +4 -0
  226. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  227. package/src/components/NavigationMenu/types.ts +56 -0
  228. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  229. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  230. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  231. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  232. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
  233. package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
  234. package/src/components/PaceAppLayout/README.md +14 -17
  235. package/src/components/PaceAppLayout/test-setup.tsx +3 -4
  236. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  237. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  238. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  239. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  240. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  241. package/src/components/Select/Select.tsx +80 -434
  242. package/src/components/Select/context.ts +23 -0
  243. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  244. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  245. package/src/components/Select/hooks/useSelectState.ts +104 -0
  246. package/src/components/Select/index.ts +9 -1
  247. package/src/components/Select/types.ts +123 -0
  248. package/src/components/Select/utils/text.ts +26 -0
  249. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  250. package/src/components/Switch/Switch.tsx +4 -4
  251. package/src/components/Tabs/Tabs.tsx +1 -1
  252. package/src/components/Toast/Toast.tsx +4 -0
  253. package/src/components/Tooltip/Tooltip.tsx +2 -2
  254. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  255. package/src/components/UserMenu/UserMenu.tsx +21 -18
  256. package/src/components/index.ts +7 -7
  257. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  258. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  259. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  260. package/src/hooks/index.ts +1 -2
  261. package/src/hooks/public/usePublicEvent.ts +4 -0
  262. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  263. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  264. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  265. package/src/hooks/services/useAuth.ts +32 -0
  266. package/src/hooks/services/useCurrentEvent.ts +6 -0
  267. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  268. package/src/hooks/useAppConfig.ts +15 -30
  269. package/src/hooks/useDebounce.ts +9 -0
  270. package/src/hooks/useEventTheme.ts +6 -0
  271. package/src/hooks/useFileDisplay.ts +81 -50
  272. package/src/hooks/useFileReference.ts +25 -7
  273. package/src/hooks/useFileUrl.ts +11 -1
  274. package/src/hooks/useFocusManagement.ts +14 -0
  275. package/src/hooks/useFocusTrap.ts +3 -0
  276. package/src/hooks/useInactivityTracker.ts +3 -0
  277. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  278. package/src/hooks/useOrganisationPermissions.ts +4 -0
  279. package/src/hooks/useOrganisationSecurity.ts +4 -0
  280. package/src/hooks/usePerformanceMonitor.ts +4 -0
  281. package/src/hooks/usePermissionCache.ts +7 -0
  282. package/src/hooks/useQueryCache.ts +12 -1
  283. package/src/hooks/useSessionRestoration.ts +4 -0
  284. package/src/hooks/useStorage.ts +4 -0
  285. package/src/hooks/useToast.ts +1 -1
  286. package/src/index.ts +6 -6
  287. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  288. package/src/providers/services/AuthServiceProvider.tsx +35 -7
  289. package/src/providers/services/EventServiceProvider.tsx +51 -5
  290. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  291. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  292. package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
  293. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  294. package/src/rbac/README.md +1 -1
  295. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  296. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  297. package/src/rbac/adapters.tsx +12 -3
  298. package/src/rbac/api.test.ts +59 -51
  299. package/src/rbac/api.ts +246 -167
  300. package/src/rbac/components/NavigationProvider.tsx +4 -1
  301. package/src/rbac/components/PagePermissionGuard.tsx +185 -17
  302. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  303. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  304. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  305. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  306. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  307. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  308. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  309. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  310. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  311. package/src/rbac/engine.ts +38 -14
  312. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  313. package/src/rbac/hooks/permissions/index.ts +7 -0
  314. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  315. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  316. package/src/rbac/hooks/permissions/useCan.ts +377 -0
  317. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  318. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  319. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  320. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  321. package/src/rbac/hooks/useCan.test.ts +64 -66
  322. package/src/rbac/hooks/usePermissions.ts +14 -995
  323. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  324. package/src/rbac/hooks/useRBAC.ts +36 -37
  325. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  326. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  327. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  328. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  329. package/src/rbac/hooks/useSecureSupabase.ts +27 -7
  330. package/src/rbac/index.ts +7 -0
  331. package/src/rbac/permissions.ts +0 -30
  332. package/src/rbac/secureClient.test.ts +22 -18
  333. package/src/rbac/secureClient.ts +294 -68
  334. package/src/rbac/security.ts +0 -17
  335. package/src/rbac/types.ts +9 -0
  336. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  337. package/src/rbac/utils/clientSecurity.ts +93 -0
  338. package/src/rbac/utils/contextValidator.ts +77 -168
  339. package/src/services/AuthService.ts +39 -7
  340. package/src/services/EventService.ts +186 -54
  341. package/src/services/OrganisationService.ts +81 -14
  342. package/src/services/__tests__/EventService.test.ts +1 -2
  343. package/src/services/base/BaseService.ts +3 -0
  344. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  345. package/src/theming/parseEventColours.ts +5 -19
  346. package/src/types/vitest-globals.d.ts +51 -26
  347. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  348. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  349. package/src/utils/__tests__/index.unit.test.ts +2 -2
  350. package/src/utils/audit/audit.ts +0 -3
  351. package/src/utils/core/cn.ts +1 -1
  352. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  353. package/src/utils/file-reference/index.ts +53 -1
  354. package/src/utils/formatting/formatting.ts +8 -18
  355. package/src/utils/index.ts +0 -1
  356. package/dist/chunk-3QRJFVBR.js.map +0 -1
  357. package/dist/chunk-3XTALGJF.js.map +0 -1
  358. package/dist/chunk-4N5C5XZU.js.map +0 -1
  359. package/dist/chunk-4ZC4GX36.js.map +0 -1
  360. package/dist/chunk-7D4SUZUM.js +0 -38
  361. package/dist/chunk-BYFSK72L.js.map +0 -1
  362. package/dist/chunk-EXUD6RNJ.js +0 -451
  363. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  364. package/dist/chunk-GLK6VM3F.js.map +0 -1
  365. package/dist/chunk-I7PSE6JW.js.map +0 -1
  366. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  367. package/dist/chunk-KNC55RTG.js.map +0 -1
  368. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  369. package/dist/chunk-R77UEZ4E.js.map +0 -1
  370. package/dist/chunk-SQGMNID3.js.map +0 -1
  371. package/dist/chunk-T33XF5ZC.js +0 -12922
  372. package/dist/chunk-T33XF5ZC.js.map +0 -1
  373. package/dist/chunk-XM25TVIE.js.map +0 -1
  374. package/docs/api/classes/ColumnFactory.md +0 -243
  375. package/docs/api/classes/ErrorBoundary.md +0 -144
  376. package/docs/api/classes/InvalidScopeError.md +0 -73
  377. package/docs/api/classes/Logger.md +0 -178
  378. package/docs/api/classes/MissingUserContextError.md +0 -66
  379. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  380. package/docs/api/classes/PermissionDeniedError.md +0 -73
  381. package/docs/api/classes/RBACAuditManager.md +0 -297
  382. package/docs/api/classes/RBACCache.md +0 -322
  383. package/docs/api/classes/RBACEngine.md +0 -171
  384. package/docs/api/classes/RBACError.md +0 -76
  385. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  386. package/docs/api/classes/SecureSupabaseClient.md +0 -160
  387. package/docs/api/classes/StorageUtils.md +0 -328
  388. package/docs/api/enums/FileCategory.md +0 -184
  389. package/docs/api/enums/LogLevel.md +0 -54
  390. package/docs/api/enums/RBACErrorCode.md +0 -228
  391. package/docs/api/enums/RPCFunction.md +0 -118
  392. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  393. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  394. package/docs/api/interfaces/AggregateConfig.md +0 -43
  395. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  396. package/docs/api/interfaces/AvatarProps.md +0 -128
  397. package/docs/api/interfaces/BadgeProps.md +0 -27
  398. package/docs/api/interfaces/ButtonProps.md +0 -53
  399. package/docs/api/interfaces/CalendarProps.md +0 -70
  400. package/docs/api/interfaces/CardProps.md +0 -66
  401. package/docs/api/interfaces/ColorPalette.md +0 -7
  402. package/docs/api/interfaces/ColorShade.md +0 -66
  403. package/docs/api/interfaces/ComplianceResult.md +0 -30
  404. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  405. package/docs/api/interfaces/DataRecord.md +0 -11
  406. package/docs/api/interfaces/DataTableAction.md +0 -249
  407. package/docs/api/interfaces/DataTableColumn.md +0 -504
  408. package/docs/api/interfaces/DataTableProps.md +0 -625
  409. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  410. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  411. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  412. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  413. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  414. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  415. package/docs/api/interfaces/ExportColumn.md +0 -90
  416. package/docs/api/interfaces/ExportOptions.md +0 -126
  417. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  418. package/docs/api/interfaces/FileMetadata.md +0 -129
  419. package/docs/api/interfaces/FileReference.md +0 -118
  420. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  421. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  422. package/docs/api/interfaces/FileUploadProps.md +0 -293
  423. package/docs/api/interfaces/FooterProps.md +0 -105
  424. package/docs/api/interfaces/FormFieldProps.md +0 -166
  425. package/docs/api/interfaces/FormProps.md +0 -113
  426. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  427. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  428. package/docs/api/interfaces/InputProps.md +0 -53
  429. package/docs/api/interfaces/LabelProps.md +0 -107
  430. package/docs/api/interfaces/LoggerConfig.md +0 -62
  431. package/docs/api/interfaces/LoginFormProps.md +0 -184
  432. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  433. package/docs/api/interfaces/NavigationContextType.md +0 -164
  434. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  435. package/docs/api/interfaces/NavigationItem.md +0 -120
  436. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  437. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  438. package/docs/api/interfaces/Organisation.md +0 -140
  439. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  440. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  441. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  442. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  443. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
  444. package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
  445. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  446. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  447. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  448. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  449. package/docs/api/interfaces/PaletteData.md +0 -41
  450. package/docs/api/interfaces/ParsedAddress.md +0 -120
  451. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  452. package/docs/api/interfaces/ProgressProps.md +0 -42
  453. package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
  454. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  455. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  456. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
  457. package/docs/api/interfaces/QuickFix.md +0 -52
  458. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  459. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  460. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  461. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  462. package/docs/api/interfaces/RBACConfig.md +0 -133
  463. package/docs/api/interfaces/RBACContext.md +0 -52
  464. package/docs/api/interfaces/RBACLogger.md +0 -112
  465. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  466. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  467. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  468. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  469. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  470. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  471. package/docs/api/interfaces/RBACResult.md +0 -58
  472. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  473. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  474. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  475. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  476. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  477. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  478. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  479. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  480. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  481. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  482. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  483. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  484. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  485. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  486. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  487. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  488. package/docs/api/interfaces/RouteConfig.md +0 -134
  489. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  490. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  491. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  492. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  493. package/docs/api/interfaces/SetupIssue.md +0 -41
  494. package/docs/api/interfaces/StorageConfig.md +0 -41
  495. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  496. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  497. package/docs/api/interfaces/StorageListOptions.md +0 -99
  498. package/docs/api/interfaces/StorageListResult.md +0 -41
  499. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  500. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  501. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  502. package/docs/api/interfaces/StyleImport.md +0 -19
  503. package/docs/api/interfaces/SwitchProps.md +0 -34
  504. package/docs/api/interfaces/TabsContentProps.md +0 -9
  505. package/docs/api/interfaces/TabsListProps.md +0 -9
  506. package/docs/api/interfaces/TabsProps.md +0 -9
  507. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  508. package/docs/api/interfaces/TextareaProps.md +0 -53
  509. package/docs/api/interfaces/ToastActionElement.md +0 -9
  510. package/docs/api/interfaces/ToastProps.md +0 -9
  511. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
  512. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
  513. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  514. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  515. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
  516. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  517. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  518. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
  519. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  520. package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
  521. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  522. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
  523. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
  524. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  525. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  526. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  527. package/docs/api/interfaces/UserEventAccess.md +0 -118
  528. package/docs/api/interfaces/UserMenuProps.md +0 -86
  529. package/docs/api/interfaces/UserProfile.md +0 -63
  530. package/docs/migration/quick-migration-guide.md +0 -356
  531. package/docs/migration/service-architecture.md +0 -281
  532. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  533. package/src/components/EventSelector/EventSelector.tsx +0 -420
  534. package/src/components/EventSelector/index.ts +0 -3
  535. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  536. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
  537. package/src/components/OrganisationSelector/index.ts +0 -9
  538. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  539. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  540. package/src/hooks/useSecureDataAccess.ts +0 -681
  541. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
  542. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
  543. /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
  544. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  545. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  546. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  547. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  548. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  549. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  description: Enforce SOLID architecture principles in consuming apps
3
3
  globs: ["src/**/*.{ts,tsx}"]
4
- alwaysApply: true
5
- paceCoreVersion: "0.5.x"
6
- rulesVersion: "2025-01-15"
4
+ alwaysApply: false
5
+ paceCoreVersion: "0.6.x"
6
+ rulesVersion: "2025-01-28"
7
7
  ---
8
8
  # SOLID Principles Guide
9
9
 
@@ -18,36 +18,19 @@ This guide enforces SOLID architecture principles to ensure maintainable, extens
18
18
  **Each function/component SHOULD do one thing:**
19
19
 
20
20
  ```tsx
21
- // ❌ WRONG - Multiple responsibilities
22
- function UserProfile({ userId }: { userId: string }) {
21
+ // ❌ WRONG: Multiple responsibilities (fetching, formatting, rendering in one component)
22
+ function UserProfile({ userId }) {
23
23
  const [user, setUser] = useState(null);
24
24
  const [events, setEvents] = useState([]);
25
-
26
- useEffect(() => {
27
- // Fetching user
28
- fetchUser(userId).then(setUser);
29
- // Fetching events
30
- fetchEvents(userId).then(setEvents);
31
- // Formatting data
32
- const formatted = formatUserData(user);
33
- // Rendering UI
34
- return <div>...</div>;
35
- }, [userId]);
36
-
25
+ useEffect(() => { fetchUser(userId).then(setUser); fetchEvents(userId).then(setEvents); }, [userId]);
37
26
  return <div>...</div>;
38
27
  }
39
28
 
40
- // ✅ CORRECT - Separated responsibilities
41
- function UserProfile({ userId }: { userId: string }) {
29
+ // ✅ CORRECT: Separated responsibilities (hooks for data, components for UI)
30
+ function UserProfile({ userId }) {
42
31
  const user = useUser(userId);
43
32
  const events = useUserEvents(userId);
44
-
45
- return (
46
- <div>
47
- <UserInfo user={user} />
48
- <UserEvents events={events} />
49
- </div>
50
- );
33
+ return <div><UserInfo user={user} /><UserEvents events={events} /></div>;
51
34
  }
52
35
  ```
53
36
 
@@ -56,37 +39,19 @@ function UserProfile({ userId }: { userId: string }) {
56
39
  **Complex logic SHOULD be extracted into hooks, services, or utilities:**
57
40
 
58
41
  ```tsx
59
- // ❌ WRONG - Business logic in component
42
+ // ❌ WRONG: Business logic in component (filtering, sorting, formatting)
60
43
  function EventList() {
61
44
  const [events, setEvents] = useState([]);
62
45
  const [filtered, setFiltered] = useState([]);
63
-
64
- useEffect(() => {
65
- fetchEvents().then(setEvents);
66
- }, []);
67
-
68
- useEffect(() => {
69
- const filtered = events
70
- .filter(e => e.status === 'active')
71
- .sort((a, b) => a.date - b.date)
72
- .map(e => ({ ...e, formattedDate: formatDate(e.date) }));
73
- setFiltered(filtered);
74
- }, [events]);
75
-
46
+ useEffect(() => { /* complex filtering/sorting logic */ }, [events]);
76
47
  return <div>{filtered.map(...)}</div>;
77
48
  }
78
49
 
79
- // ✅ CORRECT - Logic in hook
50
+ // ✅ CORRECT: Logic extracted to hook
80
51
  function useFilteredEvents() {
81
52
  const events = useEvents();
82
- return useMemo(() => {
83
- return events
84
- .filter(e => e.status === 'active')
85
- .sort((a, b) => a.date - b.date)
86
- .map(e => ({ ...e, formattedDate: formatDate(e.date) }));
87
- }, [events]);
53
+ return useMemo(() => events.filter(...).sort(...).map(...), [events]);
88
54
  }
89
-
90
55
  function EventList() {
91
56
  const filteredEvents = useFilteredEvents();
92
57
  return <div>{filteredEvents.map(...)}</div>;
@@ -102,28 +67,16 @@ function EventList() {
102
67
  **Extend functionality through composition, not modification:**
103
68
 
104
69
  ```tsx
105
- // ❌ WRONG - Modifying base component
106
- function BaseButton({ onClick, ...props }) {
107
- return <button onClick={onClick} {...props} />;
108
- }
109
-
70
+ // ❌ WRONG: Modifying base component behavior
110
71
  function SpecialButton({ onClick, ...props }) {
111
- // Modifying base behavior
112
- const handleClick = (e) => {
113
- trackEvent('button-click');
114
- onClick?.(e);
115
- };
116
- return <BaseButton onClick={handleClick} {...props} />;
72
+ const handleClick = (e) => { trackEvent('button-click'); onClick?.(e); };
73
+ return <BaseButton onClick={handleClick} {...props} />; // Modifies base
117
74
  }
118
75
 
119
- // ✅ CORRECT - Composition with pace-core
76
+ // ✅ CORRECT: Composition with pace-core (extend, don't modify)
120
77
  import { Button } from '@jmruthers/pace-core';
121
-
122
78
  function SpecialButton({ onClick, ...props }) {
123
- const handleClick = (e) => {
124
- trackEvent('button-click');
125
- onClick?.(e);
126
- };
79
+ const handleClick = (e) => { trackEvent('button-click'); onClick?.(e); };
127
80
  return <Button onClick={handleClick} {...props} />;
128
81
  }
129
82
  ```
@@ -133,13 +86,8 @@ function SpecialButton({ onClick, ...props }) {
133
86
  **Extend behavior through configuration:**
134
87
 
135
88
  ```tsx
136
- // ✅ CORRECT - Configurable behavior
137
- interface DataTableConfig {
138
- columns: Column[];
139
- features: FeatureConfig;
140
- rbac: RBACConfig;
141
- }
142
-
89
+ // ✅ CORRECT: Extend through configuration, not code changes
90
+ interface DataTableConfig { columns: Column[]; features: FeatureConfig; rbac: RBACConfig; }
143
91
  function MyDataTable({ config }: { config: DataTableConfig }) {
144
92
  return <DataTable {...config} />;
145
93
  }
@@ -154,22 +102,12 @@ function MyDataTable({ config }: { config: DataTableConfig }) {
154
102
  **Derived components/hooks MUST maintain the same interface:**
155
103
 
156
104
  ```tsx
157
- // ✅ CORRECT - Maintains interface
158
- interface BaseHook {
159
- data: Data | null;
160
- isLoading: boolean;
161
- error: Error | null;
162
- }
163
-
105
+ // ✅ CORRECT: Derived hooks maintain same interface (substitutable)
106
+ interface BaseHook { data: Data | null; isLoading: boolean; error: Error | null; }
164
107
  function useBaseData(): BaseHook { ... }
165
-
166
108
  function useExtendedData(): BaseHook {
167
109
  const base = useBaseData();
168
- // Extends but maintains same interface
169
- return {
170
- ...base,
171
- // Additional properties are optional
172
- };
110
+ return { ...base }; // Maintains interface, extends behavior
173
111
  }
174
112
  ```
175
113
 
@@ -182,7 +120,7 @@ function useExtendedData(): BaseHook {
182
120
  **Interfaces SHOULD be small and focused:**
183
121
 
184
122
  ```tsx
185
- // ❌ WRONG - Large interface
123
+ // ❌ WRONG: Large interface with many responsibilities
186
124
  interface UserService {
187
125
  getUser(id: string): Promise<User>;
188
126
  createUser(data: UserData): Promise<User>;
@@ -190,24 +128,12 @@ interface UserService {
190
128
  deleteUser(id: string): Promise<void>;
191
129
  getUserEvents(id: string): Promise<Event[]>;
192
130
  getUserOrganisations(id: string): Promise<Organisation[]>;
193
- // ... many more methods
194
- }
195
-
196
- // ✅ CORRECT - Segregated interfaces
197
- interface UserReader {
198
- getUser(id: string): Promise<User>;
199
- getUserEvents(id: string): Promise<Event[]>;
200
131
  }
201
132
 
202
- interface UserWriter {
203
- createUser(data: UserData): Promise<User>;
204
- updateUser(id: string, data: Partial<User>): Promise<User>;
205
- deleteUser(id: string): Promise<void>;
206
- }
207
-
208
- interface UserOrganisationService {
209
- getUserOrganisations(id: string): Promise<Organisation[]>;
210
- }
133
+ // CORRECT: Segregated interfaces (focused, specific)
134
+ interface UserReader { getUser(id: string): Promise<User>; getUserEvents(id: string): Promise<Event[]>; }
135
+ interface UserWriter { createUser(data: UserData): Promise<User>; updateUser(id: string, data: Partial<User>): Promise<User>; deleteUser(id: string): Promise<void>; }
136
+ interface UserOrganisationService { getUserOrganisations(id: string): Promise<Organisation[]>; }
211
137
  ```
212
138
 
213
139
  ### SHOULD: Use Specific Props
@@ -215,22 +141,12 @@ interface UserOrganisationService {
215
141
  **Component props SHOULD be specific, not generic:**
216
142
 
217
143
  ```tsx
218
- // ❌ WRONG - Generic props object
219
- function UserCard({ user, config }: { user: User; config: any }) {
220
- // ...
221
- }
222
-
223
- // ✅ CORRECT - Specific props
224
- interface UserCardProps {
225
- user: User;
226
- showEmail?: boolean;
227
- showAvatar?: boolean;
228
- onEdit?: (user: User) => void;
229
- }
144
+ // ❌ WRONG: Generic props object (config: any)
145
+ function UserCard({ user, config }: { user: User; config: any }) { ... }
230
146
 
231
- function UserCard({ user, showEmail, showAvatar, onEdit }: UserCardProps) {
232
- // ...
233
- }
147
+ // CORRECT: Specific, focused props interface
148
+ interface UserCardProps { user: User; showEmail?: boolean; showAvatar?: boolean; onEdit?: (user: User) => void; }
149
+ function UserCard({ user, showEmail, showAvatar, onEdit }: UserCardProps) { ... }
234
150
  ```
235
151
 
236
152
  ## Dependency Inversion Principle (DIP)
@@ -242,50 +158,25 @@ function UserCard({ user, showEmail, showAvatar, onEdit }: UserCardProps) {
242
158
  **Depend on interfaces/types, not concrete implementations:**
243
159
 
244
160
  ```tsx
245
- // ❌ WRONG - Direct dependency on implementation
161
+ // ❌ WRONG: Direct dependency on implementation (tight coupling)
246
162
  function UserService() {
247
163
  const supabase = useSecureSupabase();
248
-
249
164
  async function getUser(id: string) {
250
- const { data } = await supabase
251
- .from('users')
252
- .select('*')
253
- .eq('id', id)
254
- .single();
165
+ const { data } = await supabase.from('users').select('*').eq('id', id).single();
255
166
  return data;
256
167
  }
257
-
258
168
  return { getUser };
259
169
  }
260
170
 
261
- // ✅ CORRECT - Abstracted interface
262
- interface UserRepository {
263
- getUser(id: string): Promise<User | null>;
264
- }
265
-
171
+ // ✅ CORRECT: Abstracted interface (depend on abstraction)
172
+ interface UserRepository { getUser(id: string): Promise<User | null>; }
266
173
  function createUserRepository(supabase: SupabaseClient): UserRepository {
267
- return {
268
- async getUser(id: string) {
269
- const { data } = await supabase
270
- .from('users')
271
- .select('*')
272
- .eq('id', id)
273
- .single();
274
- return data;
275
- }
276
- };
174
+ return { async getUser(id: string) { /* implementation */ } };
277
175
  }
278
-
279
176
  function useUserService() {
280
177
  const supabase = useSecureSupabase();
281
- const repository = useMemo(
282
- () => createUserRepository(supabase),
283
- [supabase]
284
- );
285
-
286
- return {
287
- getUser: repository.getUser,
288
- };
178
+ const repository = useMemo(() => createUserRepository(supabase), [supabase]);
179
+ return { getUser: repository.getUser };
289
180
  }
290
181
  ```
291
182
 
@@ -294,22 +185,12 @@ function useUserService() {
294
185
  **Inject dependencies rather than creating them:**
295
186
 
296
187
  ```tsx
297
- // ❌ WRONG - Hard-coded dependency
298
- function EventService() {
299
- const api = new ApiClient('https://api.example.com');
300
- // ...
301
- }
188
+ // ❌ WRONG: Hard-coded dependency
189
+ function EventService() { const api = new ApiClient('https://api.example.com'); }
302
190
 
303
- // ✅ CORRECT - Injected dependency
304
- function EventService(api: ApiClient) {
305
- // ...
306
- }
307
-
308
- // Or with React context
309
- function useEventService() {
310
- const api = useApiClient(); // From context
311
- return useMemo(() => new EventService(api), [api]);
312
- }
191
+ // ✅ CORRECT: Injected dependency (dependency injection)
192
+ function EventService(api: ApiClient) { ... }
193
+ // Or with React context: const api = useApiClient(); useMemo(() => new EventService(api), [api]);
313
194
  ```
314
195
 
315
196
  ## SOLID Checklist
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  description: Enforce testing framework consistency and standards for consuming apps
3
3
  globs: ["**/*.{test,spec}.{ts,tsx}"]
4
- alwaysApply: true
5
- paceCoreVersion: "0.5.x"
6
- rulesVersion: "2025-01-15"
4
+ alwaysApply: false
5
+ paceCoreVersion: "0.6.x"
6
+ rulesVersion: "2025-01-28"
7
7
  ---
8
8
  # Testing Standards Guide
9
9
 
@@ -25,24 +25,18 @@ npm run test:coverage
25
25
  **MUST use React Testing Library + userEvent for all component tests.**
26
26
 
27
27
  ```tsx
28
- // ✅ CORRECT - React Testing Library
28
+ // ✅ CORRECT: React Testing Library + userEvent
29
29
  import { render, screen } from '@testing-library/react';
30
30
  import userEvent from '@testing-library/user-event';
31
- import { Button } from '@jmruthers/pace-core';
32
-
33
31
  test('button clicks work', async () => {
34
32
  const user = userEvent.setup();
35
33
  const handleClick = vi.fn();
36
-
37
34
  render(<Button onClick={handleClick}>Click me</Button>);
38
-
39
35
  await user.click(screen.getByRole('button', { name: /click me/i }));
40
-
41
36
  expect(handleClick).toHaveBeenCalledTimes(1);
42
37
  });
43
38
 
44
- // ❌ WRONG - Enzyme or other testing libraries
45
- import { shallow } from 'enzyme';
39
+ // ❌ WRONG: Enzyme or other testing libraries
46
40
  ```
47
41
 
48
42
  ## MUST: Colocate Tests
@@ -94,21 +88,15 @@ export default defineConfig({
94
88
  **Tests MUST focus on what users see and do:**
95
89
 
96
90
  ```tsx
97
- // ❌ WRONG - Testing implementation
98
- test('calls setState', () => {
99
- const component = render(<Counter />);
100
- expect(component.state.count).toBe(0);
101
- });
91
+ // ❌ WRONG: Testing implementation (component.state.count)
92
+ test('calls setState', () => { const component = render(<Counter />); expect(component.state.count).toBe(0); });
102
93
 
103
- // ✅ CORRECT - Testing user behavior
94
+ // ✅ CORRECT: Testing user behavior (what user sees and does)
104
95
  test('displays count and increments on button click', async () => {
105
96
  const user = userEvent.setup();
106
97
  render(<Counter />);
107
-
108
98
  expect(screen.getByText('Count: 0')).toBeInTheDocument();
109
-
110
99
  await user.click(screen.getByRole('button', { name: /increment/i }));
111
-
112
100
  expect(screen.getByText('Count: 1')).toBeInTheDocument();
113
101
  });
114
102
  ```
@@ -118,14 +106,11 @@ test('displays count and increments on button click', async () => {
118
106
  **MUST prefer accessible queries (byRole, byLabelText, etc.):**
119
107
 
120
108
  ```tsx
121
- // ✅ CORRECT - Accessible queries
109
+ // ✅ CORRECT: Accessible queries (byRole, byLabelText, byText)
122
110
  screen.getByRole('button', { name: /submit/i });
123
111
  screen.getByLabelText(/email address/i);
124
- screen.getByText(/welcome/i);
125
112
 
126
- // ❌ AVOID - Non-accessible queries (use as last resort)
127
- screen.getByTestId('submit-button');
128
- screen.getByClassName('btn-primary');
113
+ // ❌ AVOID: Non-accessible queries (getByTestId, getByClassName - use as last resort)
129
114
  ```
130
115
 
131
116
  ## SHOULD: Test Critical Paths
@@ -174,19 +159,10 @@ describe('EventCard', () => {
174
159
  **MUST NOT mock unless necessary:**
175
160
 
176
161
  ```tsx
177
- // ❌ WRONG - Unnecessary mock
178
- const mockFetch = vi.fn();
179
- global.fetch = mockFetch;
180
-
181
- // ✅ CORRECT - Use real implementation or MSW
162
+ // ❌ WRONG: Unnecessary mock (global.fetch = mockFetch)
163
+ // CORRECT: Use real implementation or MSW
182
164
  import { server } from './mocks/server';
183
- import { rest } from 'msw';
184
-
185
- server.use(
186
- rest.get('/api/events', (req, res, ctx) => {
187
- return res(ctx.json([{ id: '1', name: 'Event' }]));
188
- })
189
- );
165
+ server.use(rest.get('/api/events', (req, res, ctx) => res(ctx.json([{ id: '1', name: 'Event' }]))));
190
166
  ```
191
167
 
192
168
  ## MUST: Test Async Code Properly
@@ -194,16 +170,11 @@ server.use(
194
170
  **MUST handle async operations correctly:**
195
171
 
196
172
  ```tsx
197
- // ✅ CORRECT - Async testing
173
+ // ✅ CORRECT: Async testing with waitFor
198
174
  test('loads and displays events', async () => {
199
175
  render(<EventList />);
200
-
201
176
  expect(screen.getByText(/loading/i)).toBeInTheDocument();
202
-
203
- await waitFor(() => {
204
- expect(screen.getByText('Event 1')).toBeInTheDocument();
205
- });
206
-
177
+ await waitFor(() => expect(screen.getByText('Event 1')).toBeInTheDocument());
207
178
  expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
208
179
  });
209
180
  ```
@@ -213,11 +184,8 @@ test('loads and displays events', async () => {
213
184
  **MUST clean up resources:**
214
185
 
215
186
  ```tsx
216
- // ✅ CORRECT - Cleanup
217
- afterEach(() => {
218
- cleanup();
219
- vi.clearAllMocks();
220
- });
187
+ // ✅ CORRECT: Cleanup after tests
188
+ afterEach(() => { cleanup(); vi.clearAllMocks(); });
221
189
  ```
222
190
 
223
191
  ## SHOULD: Use Test Utilities
@@ -225,23 +193,11 @@ afterEach(() => {
225
193
  **SHOULD create reusable test utilities:**
226
194
 
227
195
  ```tsx
228
- // test-utils.tsx
229
- import { render } from '@testing-library/react';
230
- import { UnifiedAuthProvider } from '@jmruthers/pace-core';
231
-
196
+ // ✅ CORRECT: Reusable test utilities
232
197
  export function renderWithProviders(ui: React.ReactElement) {
233
- return render(
234
- <UnifiedAuthProvider supabaseClient={mockSupabase} appName="Test App">
235
- {ui}
236
- </UnifiedAuthProvider>
237
- );
198
+ return render(<UnifiedAuthProvider supabaseClient={mockSupabase} appName="Test App">{ui}</UnifiedAuthProvider>);
238
199
  }
239
-
240
- // Usage
241
- test('component works with providers', () => {
242
- renderWithProviders(<MyComponent />);
243
- // ...
244
- });
200
+ // Usage: renderWithProviders(<MyComponent />);
245
201
  ```
246
202
 
247
203
  ## MUST: Include Timeout Parameters
@@ -249,12 +205,9 @@ test('component works with providers', () => {
249
205
  **Tests MUST include timeout parameters to prevent hanging:**
250
206
 
251
207
  ```tsx
252
- // ✅ CORRECT - With timeout
208
+ // ✅ CORRECT: Include timeout parameters to prevent hanging
253
209
  test('async operation completes', async () => {
254
- await waitFor(
255
- () => expect(screen.getByText('Loaded')).toBeInTheDocument(),
256
- { timeout: 5000 }
257
- );
210
+ await waitFor(() => expect(screen.getByText('Loaded')).toBeInTheDocument(), { timeout: 5000 });
258
211
  }, { timeout: 10000 });
259
212
  ```
260
213
 
@@ -2,8 +2,8 @@
2
2
  description: Standardized templates for bug reports and feature requests for pace-core
3
3
  globs: []
4
4
  alwaysApply: false
5
- paceCoreVersion: "0.5.x"
6
- rulesVersion: "2025-01-15"
5
+ paceCoreVersion: "0.6.x"
6
+ rulesVersion: "2025-01-28"
7
7
  ---
8
8
  # Bug Reports and Feature Requests Guide
9
9