@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
@@ -0,0 +1,330 @@
1
+ # Secure Supabase Client Protection
2
+
3
+ This document describes the multi-layered protection system to prevent consuming apps from using insecure Supabase clients that bypass organisation context and RLS policies.
4
+
5
+ ## Problem
6
+
7
+ Using `createClient()` from `@supabase/supabase-js` directly bypasses:
8
+ - Organisation context enforcement
9
+ - RLS (Row Level Security) policies
10
+ - Event and app context injection
11
+ - Security safeguards built into pace-core
12
+
13
+ This can lead to:
14
+ - **Cross-organisation data access** - Users accessing data from organisations they shouldn't
15
+ - **Security vulnerabilities** - Bypassing permission checks
16
+ - **Data leakage** - Accidental exposure of sensitive data
17
+
18
+ ## Protection Layers
19
+
20
+ ### 1. ESLint Rule: `no-direct-supabase-client`
21
+
22
+ **Location**: `packages/core/src/eslint-rules/pace-core-compliance.cjs`
23
+
24
+ **What it does**:
25
+ - Detects `createClient` imports from `@supabase/supabase-js`
26
+ - Detects `createClient()` function calls
27
+ - Reports errors with helpful suggestions
28
+
29
+ **How to use**:
30
+ ```js
31
+ // In your consuming app's eslint.config.js:
32
+ import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
33
+
34
+ export default [
35
+ ...paceCoreConfig,
36
+ // pace-core-compliance/no-direct-supabase-client is automatically enabled
37
+ ];
38
+ ```
39
+
40
+ **Example violations**:
41
+ ```tsx
42
+ // ❌ ERROR: Direct import detected
43
+ import { createClient } from '@supabase/supabase-js';
44
+
45
+ // ❌ ERROR: Direct client creation
46
+ const supabase = createClient(url, key);
47
+ ```
48
+
49
+ ### 2. Runtime Detection: `warnIfInsecureClient()`
50
+
51
+ **Location**: `packages/core/src/rbac/utils/clientSecurity.ts`
52
+
53
+ **What it does**:
54
+ - Checks if a client is marked as secure
55
+ - Warns in development mode when insecure clients are detected
56
+ - Provides helpful error messages with fix suggestions
57
+
58
+ **How to use**:
59
+ ```tsx
60
+ import { warnIfInsecureClient } from '@jmruthers/pace-core/rbac';
61
+
62
+ function MyComponent() {
63
+ const supabase = useSecureSupabase();
64
+
65
+ // Optional: Warn if client is insecure (development only)
66
+ warnIfInsecureClient(supabase, 'MyComponent');
67
+
68
+ // Use supabase...
69
+ }
70
+ ```
71
+
72
+ **Output** (development mode only):
73
+ ```
74
+ [pace-core Security Warning] Non-secure Supabase client detected in MyComponent.
75
+ You are using a Supabase client created with createClient() instead of useSecureSupabase().
76
+ This bypasses organisation context enforcement and RLS policies...
77
+ ```
78
+
79
+ ### 3. Type Safety: `isSecureClient()`
80
+
81
+ **Location**: `packages/core/src/rbac/utils/clientSecurity.ts`
82
+
83
+ **What it does**:
84
+ - Type guard to check if a client is secure
85
+ - Returns `true` only for clients created via `useSecureSupabase()` or `createSecureClient()`
86
+ - Can be used for runtime checks and TypeScript narrowing
87
+
88
+ **How to use**:
89
+ ```tsx
90
+ import { isSecureClient } from '@jmruthers/pace-core/rbac';
91
+
92
+ function MyComponent() {
93
+ const supabase = useSecureSupabase();
94
+
95
+ if (isSecureClient(supabase)) {
96
+ // TypeScript knows supabase is secure here
97
+ // Safe to use for database operations
98
+ } else {
99
+ // Client is not secure - handle error
100
+ console.error('Insecure client detected!');
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### 4. Automatic Client Marking
106
+
107
+ **Location**: `packages/core/src/rbac/secureClient.ts`
108
+
109
+ **What it does**:
110
+ - Automatically marks clients created via `SecureSupabaseClient` as secure
111
+ - Uses a Symbol (`SECURE_CLIENT_SYMBOL`) to mark secure clients
112
+ - Works transparently - no action needed from developers
113
+
114
+ **How it works**:
115
+ ```tsx
116
+ // When you use useSecureSupabase():
117
+ const supabase = useSecureSupabase();
118
+ // Client is automatically marked as secure internally
119
+
120
+ // When you use createSecureClient():
121
+ const secureClient = createSecureClient(url, key, orgId);
122
+ const supabase = secureClient.getClient();
123
+ // Client is automatically marked as secure internally
124
+ ```
125
+
126
+ ## Correct Usage
127
+
128
+ ### ✅ Use `useSecureSupabase()` Hook
129
+
130
+ ```tsx
131
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
132
+
133
+ function MyComponent() {
134
+ const supabase = useSecureSupabase();
135
+
136
+ if (!supabase) {
137
+ return <div>Loading...</div>;
138
+ }
139
+
140
+ // Organisation context is automatically enforced
141
+ const { data } = await supabase.from('users').select('*');
142
+ }
143
+ ```
144
+
145
+ ### ✅ Use `createSecureClient()` for Non-React Code
146
+
147
+ ```tsx
148
+ import { createSecureClient } from '@jmruthers/pace-core/rbac';
149
+
150
+ // For server-side or non-React code
151
+ const secureClient = createSecureClient(
152
+ url,
153
+ key,
154
+ organisationId,
155
+ eventId,
156
+ appId,
157
+ isSuperAdmin
158
+ );
159
+ const supabase = secureClient.getClient();
160
+ ```
161
+
162
+ ### ✅ Verify Client Security (Optional)
163
+
164
+ ```tsx
165
+ import { useSecureSupabase, isSecureClient, warnIfInsecureClient } from '@jmruthers/pace-core/rbac';
166
+
167
+ function MyComponent() {
168
+ const supabase = useSecureSupabase();
169
+
170
+ // Development-only warning
171
+ warnIfInsecureClient(supabase, 'MyComponent');
172
+
173
+ // Runtime check
174
+ if (!isSecureClient(supabase)) {
175
+ throw new Error('Insecure client detected!');
176
+ }
177
+
178
+ // Use supabase...
179
+ }
180
+ ```
181
+
182
+ ## Incorrect Usage (Will Be Detected)
183
+
184
+ ### ❌ Direct `createClient()` Import
185
+
186
+ ```tsx
187
+ // ESLint will report error
188
+ import { createClient } from '@supabase/supabase-js';
189
+ const supabase = createClient(url, key);
190
+ ```
191
+
192
+ ### ❌ Direct `createClient()` Call
193
+
194
+ ```tsx
195
+ // ESLint will report error
196
+ const supabase = createClient(url, key);
197
+ ```
198
+
199
+ ### ❌ Using Base Client for Queries
200
+
201
+ ```tsx
202
+ // Even if imported from config file, using for queries is wrong
203
+ import { supabase } from './lib/supabase'; // Base client
204
+ const { data } = await supabase.from('users').select('*'); // ❌ Bypasses security
205
+ ```
206
+
207
+ ## Configuration Files Exception
208
+
209
+ The ESLint rule allows `createClient()` in configuration files (files matching `supabase*.ts/js` or `*client*.ts/js`). This is intentional because:
210
+
211
+ 1. Base clients are needed for authentication setup
212
+ 2. Base clients should only be used for auth operations
213
+ 3. Base clients should be passed to `useSecureSupabase()` as fallback
214
+
215
+ **Example of acceptable config file**:
216
+ ```tsx
217
+ // supabaseClient.ts - Config file (allowed)
218
+ import { createClient } from '@supabase/supabase-js';
219
+
220
+ export const supabase = createClient(url, key);
221
+
222
+ // Then in components:
223
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
224
+ import { supabase } from './supabaseClient';
225
+
226
+ function MyComponent() {
227
+ // ✅ Correct: Pass base client as fallback
228
+ const secureSupabase = useSecureSupabase(supabase);
229
+ }
230
+ ```
231
+
232
+ ## Audit Script Detection
233
+
234
+ The pace-core audit script comprehensively detects insecure client usage:
235
+
236
+ ```bash
237
+ npm run audit
238
+ ```
239
+
240
+ The audit will report violations including:
241
+ - ✅ Direct `createClient` imports from `@supabase/supabase-js`
242
+ - ✅ Direct `createClient()` function calls
243
+ - ✅ Usage of non-secure clients for database queries (`.from()` calls)
244
+ - ✅ Files that import `createClient` but don't use `useSecureSupabase()`
245
+ - ✅ Variables created with `createClient()` that are used for queries
246
+
247
+ **Example audit output**:
248
+ ```
249
+ ❌ Direct Supabase client usage detected
250
+ File: src/components/UserList.tsx
251
+ Line: 15
252
+ Variable: supabase
253
+ Table: users
254
+ Reason: Direct Supabase client usage detected. Variable 'supabase' is created with createClient() and used for database queries. You MUST use useSecureSupabase() instead to ensure RLS policies and organisation context are enforced.
255
+ Recommendation: Replace with: import { useSecureSupabase } from '@jmruthers/pace-core/rbac'; const supabase = useSecureSupabase();
256
+ ```
257
+
258
+ The audit tool provides the same level of detection as the ESLint rule, making it useful for:
259
+ - Pre-commit checks
260
+ - CI/CD pipelines
261
+ - Code reviews
262
+ - Migration validation
263
+
264
+ ## Best Practices
265
+
266
+ 1. **Always use `useSecureSupabase()`** in React components
267
+ 2. **Use `createSecureClient()`** for server-side or non-React code
268
+ 3. **Never import `createClient`** from `@supabase/supabase-js` in component files
269
+ 4. **Verify client security** in critical code paths (optional but recommended)
270
+ 5. **Run ESLint** regularly to catch violations early
271
+ 6. **Run audit script** before deploying to catch any missed violations
272
+
273
+ ## Troubleshooting
274
+
275
+ ### "ESLint reports error but I need createClient for auth"
276
+
277
+ **Solution**: Create a config file (e.g., `supabaseClient.ts`) and use `useSecureSupabase()` with the base client as fallback:
278
+
279
+ ```tsx
280
+ // supabaseClient.ts
281
+ import { createClient } from '@supabase/supabase-js';
282
+ export const supabase = createClient(url, key);
283
+
284
+ // MyComponent.tsx
285
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
286
+ import { supabase } from './supabaseClient';
287
+
288
+ function MyComponent() {
289
+ const secureSupabase = useSecureSupabase(supabase);
290
+ // Use secureSupabase for all database operations
291
+ }
292
+ ```
293
+
294
+ ### "I'm getting runtime warnings in development"
295
+
296
+ **Solution**: Ensure you're using `useSecureSupabase()` instead of a base client:
297
+
298
+ ```tsx
299
+ // ❌ Wrong
300
+ const supabase = createClient(url, key);
301
+
302
+ // ✅ Correct
303
+ const supabase = useSecureSupabase();
304
+ ```
305
+
306
+ ### "How do I check if my client is secure?"
307
+
308
+ **Solution**: Use `isSecureClient()`:
309
+
310
+ ```tsx
311
+ import { isSecureClient } from '@jmruthers/pace-core/rbac';
312
+
313
+ if (isSecureClient(supabase)) {
314
+ console.log('Client is secure!');
315
+ } else {
316
+ console.error('Client is NOT secure!');
317
+ }
318
+ ```
319
+
320
+ ## Summary
321
+
322
+ The protection system provides:
323
+ - ✅ **ESLint rules** to catch violations at development time
324
+ - ✅ **Runtime warnings** to alert developers in development mode
325
+ - ✅ **Type safety** to verify client security
326
+ - ✅ **Automatic marking** of secure clients
327
+ - ✅ **Audit scripts** to catch violations before deployment
328
+
329
+ By following these guidelines, you ensure that all database operations respect organisation context and RLS policies, preventing security vulnerabilities and data leakage.
330
+
@@ -57,6 +57,7 @@ The cursor rules cover:
57
57
  - **05-bug-reports-and-features** - Templates for issue reporting
58
58
  - **06-code-quality** - Enforce code quality standards
59
59
  - **07-tech-stack-compliance** - Enforce tech stack versions
60
+ - **08-markup-quality** - Enforce clean markup standards, semantic HTML usage, and pace-core component patterns
60
61
 
61
62
  ### Audit Tool
62
63
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -73,6 +73,7 @@
73
73
  "./core-usage-manifest.json": "./core-usage-manifest.json",
74
74
  "./cursor-rules": "./cursor-rules",
75
75
  "./scripts/install-cursor-rules": "./scripts/install-cursor-rules.cjs",
76
+ "./scripts/audit": "./scripts/audit/index.cjs",
76
77
  "./scripts/audit-consuming-app": "./scripts/audit-consuming-app.cjs",
77
78
  "./source": {
78
79
  "import": "./src/index.ts",
@@ -237,10 +238,10 @@
237
238
  "globals": "^16.3.0",
238
239
  "jsdom": "^25.0.1",
239
240
  "react-router-dom": "^6.26.2",
240
- "typedoc": "^0.25.13",
241
+ "tsup": "^8.5.0",
242
+ "typedoc": "^0.26.11",
241
243
  "typedoc-plugin-markdown": "^3.17.1",
242
244
  "typedoc-plugin-merge-modules": "^5.1.0",
243
- "tsup": "^8.5.0",
244
245
  "typescript": "^5.4.0",
245
246
  "typescript-eslint": "^8.39.0",
246
247
  "vite": "^6.0.3"
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Accessibility Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Accessibility
7
+ *
8
+ * Checks for:
9
+ * - Missing aria-* attributes
10
+ * - Missing keyboard navigation
11
+ * - Missing focus management
12
+ * - Missing alt text on images
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const { getRelativePath, getLineNumber } = require('../utils.cjs');
17
+
18
+ const accessibilityCheck = {
19
+ name: 'accessibility',
20
+ description: 'Accessibility checks (aria attributes, keyboard navigation, alt text)',
21
+ severity: 'warning',
22
+
23
+ async run(context) {
24
+ const { projectRoot, files } = context;
25
+ const issues = [];
26
+ const warnings = [];
27
+ const suggestions = [];
28
+
29
+ if (!files || files.length === 0) {
30
+ return { issues, warnings, suggestions };
31
+ }
32
+
33
+ for (const filePath of files) {
34
+ try {
35
+ // Only check React component files
36
+ if (!filePath.match(/\.(tsx|jsx)$/)) {
37
+ continue;
38
+ }
39
+
40
+ const content = fs.readFileSync(filePath, 'utf8');
41
+ const relativePath = getRelativePath(filePath, projectRoot);
42
+ const normalizedPath = relativePath.replace(/\\/g, '/');
43
+
44
+ // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
45
+ // Note: We DO check packages/core/ files because accessibility issues (missing alt attributes, missing form labels) are real issues that should be fixed
46
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
47
+ if (isRootSrc) {
48
+ continue; // Skip demo app files
49
+ }
50
+
51
+ // Skip scripts directory - utility scripts don't need accessibility validation
52
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
53
+ if (isScript) {
54
+ continue; // Skip script files
55
+ }
56
+
57
+ // Skip pace-core library components and examples - these are designed to accept accessibility props
58
+ // Library components accept id, aria-label, etc. via props spread
59
+ // Examples are demonstration code, not production code
60
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
61
+ const isExample = normalizedPath.includes('/examples/');
62
+ const isLibraryComponent = isPaceCorePackage && !isExample;
63
+
64
+ // Check for images without alt text
65
+ // Skip library components - they accept alt as a prop
66
+ if (!isLibraryComponent) {
67
+ const imgPattern = /<img[^>]*>/g;
68
+ let imgMatch;
69
+ while ((imgMatch = imgPattern.exec(content)) !== null) {
70
+ const imgTag = imgMatch[0];
71
+ // Check for alt attribute, including React props (alt=, alt =, alt={)
72
+ const hasAlt = imgTag.includes('alt=') || imgTag.includes('alt =') || imgTag.includes('alt={');
73
+ if (!hasAlt) {
74
+ warnings.push({
75
+ type: 'missing-alt-text',
76
+ file: relativePath,
77
+ line: getLineNumber(content, imgMatch.index),
78
+ message: 'Image element missing alt attribute',
79
+ recommendation: 'Add alt text to all images for accessibility: <img alt="description" ... />'
80
+ });
81
+ }
82
+ }
83
+ }
84
+
85
+ // Check for buttons/clickable elements without aria-label or accessible text
86
+ const buttonPattern = /<(button|a|div)\s+[^>]*(onClick|role=["']button["'])[^>]*>/g;
87
+ let buttonMatch;
88
+ while ((buttonMatch = buttonPattern.exec(content)) !== null) {
89
+ const buttonTag = buttonMatch[0];
90
+ const hasAriaLabel = buttonTag.includes('aria-label=') || buttonTag.includes('ariaLabel=');
91
+ const hasAccessibleText = buttonTag.includes('>') && content.substring(buttonMatch.index).match(/<[^>]*>([^<]+)</);
92
+
93
+ if (!hasAriaLabel && !hasAccessibleText) {
94
+ warnings.push({
95
+ type: 'missing-aria-label',
96
+ file: relativePath,
97
+ line: getLineNumber(content, buttonMatch.index),
98
+ message: 'Interactive element missing accessible label',
99
+ recommendation: 'Add aria-label or ensure element contains accessible text content'
100
+ });
101
+ }
102
+ }
103
+
104
+ // Check for form inputs without labels
105
+ // Skip library components - they accept id, aria-label, etc. via props spread
106
+ // Skip examples - they're demonstration code
107
+ if (!isLibraryComponent && !isExample) {
108
+ const inputPattern = /<input[^>]*>/g;
109
+ let inputMatch;
110
+ while ((inputMatch = inputPattern.exec(content)) !== null) {
111
+ // Get the full input tag including multi-line attributes
112
+ const inputStart = inputMatch.index;
113
+ const afterInput = content.substring(inputStart, Math.min(content.length, inputStart + 500));
114
+ const inputTagEnd = afterInput.indexOf('>');
115
+ const fullInputTag = inputTagEnd !== -1 ? afterInput.substring(0, inputTagEnd + 1) : inputMatch[0];
116
+
117
+ // Check for id (including React props)
118
+ const hasId = /id=["']([^"']+)["']|id=\{/.test(fullInputTag);
119
+ // Check for aria-label (including React props)
120
+ const hasAriaLabel = /aria-label=["']|ariaLabel=|aria-label=\{/.test(fullInputTag);
121
+
122
+ if (hasId) {
123
+ const idMatch = fullInputTag.match(/id=["']([^"']+)["']/);
124
+ if (idMatch) {
125
+ const id = idMatch[1];
126
+ // Check if there's a corresponding label
127
+ const beforeInput = content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index);
128
+ const hasLabel = new RegExp(`<label[^>]*for=["']${id}["']`, 'i').test(beforeInput);
129
+
130
+ if (!hasLabel && !hasAriaLabel) {
131
+ warnings.push({
132
+ type: 'missing-input-label',
133
+ file: relativePath,
134
+ line: getLineNumber(content, inputMatch.index),
135
+ message: 'Form input missing associated label',
136
+ recommendation: 'Add a <label> element with for attribute matching input id, or use aria-label'
137
+ });
138
+ }
139
+ }
140
+ } else if (!hasAriaLabel) {
141
+ warnings.push({
142
+ type: 'missing-input-label',
143
+ file: relativePath,
144
+ line: getLineNumber(content, inputMatch.index),
145
+ message: 'Form input missing id and label',
146
+ recommendation: 'Add id to input and corresponding label, or use aria-label'
147
+ });
148
+ }
149
+ }
150
+ }
151
+
152
+ // Check for missing focus management in modals/dialogs
153
+ const dialogPattern = /<Dialog|<dialog/gi;
154
+ if (dialogPattern.test(content)) {
155
+ // Skip Dialog.tsx itself - it IS the Dialog component
156
+ if (normalizedPath.includes('/Dialog/Dialog.tsx') || normalizedPath.includes('/Dialog/Dialog.jsx')) {
157
+ continue; // Skip the Dialog component file itself
158
+ }
159
+
160
+ // Check if Dialog from pace-core is used (which handles focus automatically)
161
+ // Check for both published package import and relative imports (within pace-core repo)
162
+ const usesPaceCoreDialog = content.includes('from \'@jmruthers/pace-core\'') ||
163
+ content.includes('from "@jmruthers/pace-core"') ||
164
+ content.includes('from \'../Dialog') ||
165
+ content.includes('from "../Dialog') ||
166
+ content.includes('from \'../../Dialog') ||
167
+ content.includes('from "../../Dialog') ||
168
+ content.includes('from \'../../../Dialog') ||
169
+ content.includes('from "../../../Dialog') ||
170
+ content.includes('from \'./Dialog') ||
171
+ content.includes('from "./Dialog');
172
+
173
+ if (!usesPaceCoreDialog) {
174
+ suggestions.push({
175
+ type: 'dialog-focus-management',
176
+ file: relativePath,
177
+ message: 'Custom dialog implementation detected',
178
+ recommendation: 'Use Dialog component from @jmruthers/pace-core which handles focus management automatically'
179
+ });
180
+ }
181
+ }
182
+
183
+ // Check for color contrast issues (heuristic - check for color props without sufficient contrast)
184
+ const colorPattern = /(?:color|bg-|text-)=["']([^"']+)["']/g;
185
+ // This is a simplified check - full implementation would need color contrast calculation
186
+ // For now, just suggest using pace-core components which handle contrast
187
+
188
+ } catch (error) {
189
+ // Skip files with errors
190
+ }
191
+ }
192
+
193
+ return { issues, warnings, suggestions };
194
+ }
195
+ };
196
+
197
+ module.exports = accessibilityCheck;