@jmruthers/pace-core 0.6.4 → 0.6.6

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 (387) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/core-usage-manifest.json +93 -0
  4. package/cursor-rules/00-pace-core-compliance.mdc +128 -26
  5. package/cursor-rules/01-standards-compliance.mdc +49 -8
  6. package/cursor-rules/02-project-structure.mdc +6 -0
  7. package/cursor-rules/03-solid-principles.mdc +2 -0
  8. package/cursor-rules/04-testing-standards.mdc +2 -0
  9. package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
  10. package/cursor-rules/06-code-quality.mdc +2 -0
  11. package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
  12. package/cursor-rules/08-markup-quality.mdc +52 -27
  13. package/cursor-rules/09-rbac-compliance.mdc +462 -0
  14. package/cursor-rules/10-error-handling-patterns.mdc +179 -0
  15. package/cursor-rules/11-performance-optimization.mdc +169 -0
  16. package/cursor-rules/12-ci-cd-integration.mdc +150 -0
  17. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  18. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
  19. package/dist/DataTable-LRJL4IRV.js +15 -0
  20. package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
  21. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  22. package/dist/api-Y4MQWOFW.js +4 -0
  23. package/dist/audit-MYQXYZFU.js +3 -0
  24. package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
  25. package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
  26. package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
  27. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  28. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  29. package/dist/chunk-4T7OBVTU.js +62 -0
  30. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  31. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  32. package/dist/{chunk-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
  33. package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
  34. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  35. package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
  36. package/dist/chunk-C7NSAPTL.js +1 -0
  37. package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
  38. package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
  39. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  40. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  41. package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
  42. package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
  43. package/dist/chunk-MBADTM7L.js +64 -0
  44. package/dist/chunk-OHIK3MIO.js +994 -0
  45. package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
  46. package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
  47. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  48. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  49. package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
  50. package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
  51. package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
  52. package/dist/components.d.ts +6 -6
  53. package/dist/components.js +57 -267
  54. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  55. package/dist/eslint-rules/index.cjs +22 -0
  56. package/dist/eslint-rules/rules/compliance.cjs +348 -0
  57. package/dist/eslint-rules/rules/components.cjs +113 -0
  58. package/dist/eslint-rules/rules/imports.cjs +102 -0
  59. package/dist/eslint-rules/rules/rbac.cjs +790 -0
  60. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  61. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  62. package/dist/hooks.d.ts +5 -5
  63. package/dist/hooks.js +62 -270
  64. package/dist/icons/index.d.ts +1 -0
  65. package/dist/icons/index.js +1 -0
  66. package/dist/index.d.ts +36 -26
  67. package/dist/index.js +87 -690
  68. package/dist/providers.d.ts +2 -2
  69. package/dist/providers.js +8 -35
  70. package/dist/rbac/eslint-rules.d.ts +46 -44
  71. package/dist/rbac/eslint-rules.js +7 -4
  72. package/dist/rbac/index.d.ts +124 -594
  73. package/dist/rbac/index.js +14 -207
  74. package/dist/styles/index.js +2 -12
  75. package/dist/theming/runtime.js +3 -19
  76. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  77. package/dist/{types-CkbwOr4Y.d.ts → types-B-K_5VnO.d.ts} +4 -0
  78. package/dist/types-t9H8qKRw.d.ts +55 -0
  79. package/dist/types.d.ts +1 -1
  80. package/dist/types.js +7 -94
  81. package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
  82. package/dist/utils.d.ts +24 -117
  83. package/dist/utils.js +54 -392
  84. package/docs/README.md +16 -6
  85. package/docs/api/README.md +4 -402
  86. package/docs/api/modules.md +454 -930
  87. package/docs/api-reference/components.md +3 -1
  88. package/docs/api-reference/deprecated.md +31 -6
  89. package/docs/api-reference/rpc-functions.md +78 -3
  90. package/docs/best-practices/accessibility.md +6 -3
  91. package/docs/getting-started/cursor-rules.md +3 -23
  92. package/docs/getting-started/dependencies.md +650 -0
  93. package/docs/getting-started/installation-guide.md +20 -7
  94. package/docs/getting-started/quick-start.md +23 -12
  95. package/docs/implementation-guides/permission-enforcement.md +4 -0
  96. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  97. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  98. package/docs/rbac/README.md +12 -3
  99. package/docs/rbac/edge-functions-guide.md +376 -0
  100. package/docs/rbac/secure-client-protection.md +0 -34
  101. package/docs/standards/00-pace-core-compliance.md +967 -0
  102. package/docs/standards/01-standards-compliance.md +188 -0
  103. package/docs/standards/02-project-structure.md +985 -0
  104. package/docs/standards/03-solid-principles.md +39 -0
  105. package/docs/standards/04-testing-standards.md +36 -0
  106. package/docs/standards/05-bug-reports-and-features.md +27 -0
  107. package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
  108. package/docs/standards/07-tech-stack-compliance.md +30 -0
  109. package/docs/standards/08-markup-quality.md +345 -0
  110. package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
  111. package/docs/standards/10-error-handling-patterns.md +401 -0
  112. package/docs/standards/11-performance-optimization.md +348 -0
  113. package/docs/standards/12-ci-cd-integration.md +370 -0
  114. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
  115. package/docs/standards/README.md +62 -33
  116. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  117. package/eslint-config-pace-core.cjs +20 -4
  118. package/package.json +31 -21
  119. package/scripts/audit/audit-compliance.cjs +1295 -0
  120. package/scripts/audit/audit-components.cjs +260 -0
  121. package/scripts/audit/audit-dependencies.cjs +395 -0
  122. package/scripts/audit/audit-rbac.cjs +954 -0
  123. package/scripts/audit/audit-standards.cjs +1268 -0
  124. package/scripts/audit/index.cjs +1898 -194
  125. package/scripts/install-cursor-rules.cjs +259 -8
  126. package/scripts/validate-master.js +1 -1
  127. package/src/__tests__/fixtures/supabase.ts +1 -1
  128. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
  129. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  130. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  131. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +3 -3
  132. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  133. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  134. package/src/__tests__/public-recipe-view.test.ts +38 -9
  135. package/src/components/Button/Button.tsx +5 -1
  136. package/src/components/ContextSelector/ContextSelector.tsx +42 -39
  137. package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
  138. package/src/components/DataTable/components/DataTableBody.tsx +55 -31
  139. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  140. package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
  141. package/src/components/DataTable/components/EditFields.tsx +23 -3
  142. package/src/components/DataTable/components/EditableRow.tsx +7 -2
  143. package/src/components/DataTable/components/ImportModal.tsx +4 -6
  144. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  145. package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
  146. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
  147. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
  148. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  149. package/src/components/DataTable/core/DataTableContext.tsx +1 -1
  150. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  151. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  152. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  153. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  154. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  155. package/src/components/DataTable/types.ts +5 -0
  156. package/src/components/DateTimeField/DateTimeField.tsx +20 -20
  157. package/src/components/DateTimeField/README.md +5 -2
  158. package/src/components/Dialog/Dialog.test.tsx +361 -318
  159. package/src/components/Dialog/Dialog.tsx +1154 -323
  160. package/src/components/Dialog/index.ts +3 -3
  161. package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
  162. package/src/components/FileDisplay/FileDisplay.tsx +28 -22
  163. package/src/components/Form/Form.test.tsx +9 -10
  164. package/src/components/Form/Form.tsx +369 -9
  165. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
  166. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
  167. package/src/components/LoginForm/LoginForm.tsx +2 -2
  168. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  169. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  170. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  171. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  172. package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
  173. package/src/components/PaceAppLayout/README.md +10 -9
  174. package/src/components/PaceAppLayout/test-setup.tsx +40 -31
  175. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  176. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  177. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
  178. package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
  179. package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
  180. package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
  181. package/src/components/Select/Select.tsx +23 -21
  182. package/src/components/Select/types.ts +1 -1
  183. package/src/components/UserMenu/UserMenu.test.tsx +38 -6
  184. package/src/components/UserMenu/UserMenu.tsx +39 -34
  185. package/src/components/index.ts +3 -4
  186. package/src/eslint-rules/index.cjs +22 -0
  187. package/src/eslint-rules/rules/compliance.cjs +348 -0
  188. package/src/eslint-rules/rules/components.cjs +113 -0
  189. package/src/eslint-rules/rules/imports.cjs +102 -0
  190. package/src/eslint-rules/rules/rbac.cjs +790 -0
  191. package/src/eslint-rules/utils/helpers.cjs +42 -0
  192. package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
  193. package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
  194. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  195. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  196. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  197. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  198. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  199. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  200. package/src/hooks/public/usePublicEvent.ts +62 -190
  201. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  202. package/src/hooks/public/usePublicEventLogo.ts +19 -9
  203. package/src/hooks/useAppConfig.ts +26 -24
  204. package/src/hooks/useEventTheme.test.ts +211 -233
  205. package/src/hooks/useEventTheme.ts +19 -28
  206. package/src/hooks/useEvents.ts +11 -7
  207. package/src/hooks/useKeyboardShortcuts.ts +1 -1
  208. package/src/hooks/useOrganisationPermissions.ts +9 -11
  209. package/src/hooks/useOrganisations.ts +13 -7
  210. package/src/hooks/useQueryCache.ts +0 -1
  211. package/src/hooks/useSessionDraft.ts +380 -0
  212. package/src/hooks/useSessionRestoration.ts +3 -1
  213. package/src/icons/index.ts +27 -0
  214. package/src/index.ts +16 -1
  215. package/src/providers/OrganisationProvider.tsx +23 -14
  216. package/src/providers/services/EventServiceProvider.tsx +1 -24
  217. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  218. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
  219. package/src/rbac/README.md +20 -20
  220. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  221. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  222. package/src/rbac/adapters.tsx +7 -295
  223. package/src/rbac/api.test.ts +44 -56
  224. package/src/rbac/api.ts +10 -17
  225. package/src/rbac/cache-invalidation.ts +0 -1
  226. package/src/rbac/compliance/index.ts +10 -0
  227. package/src/rbac/compliance/pattern-detector.ts +553 -0
  228. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  229. package/src/rbac/components/AccessDenied.tsx +150 -0
  230. package/src/rbac/components/NavigationGuard.tsx +12 -20
  231. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  232. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  233. package/src/rbac/components/index.ts +3 -41
  234. package/src/rbac/eslint-rules.js +1 -1
  235. package/src/rbac/hooks/index.ts +0 -3
  236. package/src/rbac/hooks/permissions/index.ts +0 -3
  237. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  238. package/src/rbac/hooks/usePermissions.ts +0 -3
  239. package/src/rbac/hooks/useRBAC.test.ts +21 -3
  240. package/src/rbac/hooks/useRBAC.ts +4 -3
  241. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  242. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  243. package/src/rbac/hooks/useResourcePermissions.test.ts +241 -60
  244. package/src/rbac/hooks/useResourcePermissions.ts +182 -63
  245. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  246. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  247. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  248. package/src/rbac/index.ts +7 -9
  249. package/src/rbac/permissions.ts +17 -17
  250. package/src/rbac/utils/contextValidator.ts +45 -7
  251. package/src/services/AuthService.ts +132 -23
  252. package/src/services/EventService.ts +4 -97
  253. package/src/services/InactivityService.ts +155 -58
  254. package/src/services/OrganisationService.ts +7 -44
  255. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  256. package/src/services/base/BaseService.ts +0 -3
  257. package/src/styles/core.css +4 -0
  258. package/src/types/database.generated.ts +4733 -3809
  259. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  260. package/src/utils/context/organisationContext.test.ts +13 -28
  261. package/src/utils/context/organisationContext.ts +21 -52
  262. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  263. package/src/utils/file-reference/index.ts +39 -15
  264. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  265. package/src/utils/formatting/formatTime.test.ts +3 -2
  266. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  267. package/src/utils/index.ts +4 -1
  268. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  269. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  270. package/src/utils/persistence/keyDerivation.ts +304 -0
  271. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  272. package/src/utils/security/secureStorage.ts +5 -5
  273. package/src/utils/storage/helpers.ts +3 -3
  274. package/src/utils/supabase/createBaseClient.ts +147 -0
  275. package/src/utils/timezone/timezone.test.ts +1 -2
  276. package/src/utils/timezone/timezone.ts +1 -1
  277. package/src/utils/validation/csrf.ts +4 -4
  278. package/cursor-rules/CHANGELOG.md +0 -119
  279. package/cursor-rules/README.md +0 -192
  280. package/dist/DataTable-E7YQZD7D.js +0 -175
  281. package/dist/DataTable-E7YQZD7D.js.map +0 -1
  282. package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
  283. package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
  284. package/dist/api-6LVZTHDS.js +0 -52
  285. package/dist/api-6LVZTHDS.js.map +0 -1
  286. package/dist/audit-V53FV5AG.js +0 -17
  287. package/dist/audit-V53FV5AG.js.map +0 -1
  288. package/dist/chunk-36LVWXB2.js +0 -227
  289. package/dist/chunk-36LVWXB2.js.map +0 -1
  290. package/dist/chunk-3LPHPB62.js.map +0 -1
  291. package/dist/chunk-5DRSZLL2.js.map +0 -1
  292. package/dist/chunk-5EC5MEWX.js.map +0 -1
  293. package/dist/chunk-63FOKYGO.js.map +0 -1
  294. package/dist/chunk-6SOIHG6Z.js.map +0 -1
  295. package/dist/chunk-7JPAB3T5.js.map +0 -1
  296. package/dist/chunk-ATKZM7RX.js +0 -2053
  297. package/dist/chunk-ATKZM7RX.js.map +0 -1
  298. package/dist/chunk-AVMLPIM7.js.map +0 -1
  299. package/dist/chunk-DGUM43GV.js.map +0 -1
  300. package/dist/chunk-E66EQZE6.js.map +0 -1
  301. package/dist/chunk-FFQEQTNW.js.map +0 -1
  302. package/dist/chunk-FMUCXFII.js.map +0 -1
  303. package/dist/chunk-G37KK66H.js.map +0 -1
  304. package/dist/chunk-I6DAQMWX.js.map +0 -1
  305. package/dist/chunk-J36DSWQK.js.map +0 -1
  306. package/dist/chunk-KQCRWDSA.js +0 -1
  307. package/dist/chunk-KQCRWDSA.js.map +0 -1
  308. package/dist/chunk-L4OXEN46.js.map +0 -1
  309. package/dist/chunk-LMC26NLJ.js +0 -84
  310. package/dist/chunk-LMC26NLJ.js.map +0 -1
  311. package/dist/chunk-M43Y4SSO.js.map +0 -1
  312. package/dist/chunk-M7MPQISP.js.map +0 -1
  313. package/dist/chunk-NN6WWZ5U.js.map +0 -1
  314. package/dist/chunk-OEWDTMG7.js.map +0 -1
  315. package/dist/chunk-PWLANIRT.js.map +0 -1
  316. package/dist/chunk-QXHPKYJV.js.map +0 -1
  317. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  318. package/dist/chunk-YKRAFF5K.js.map +0 -1
  319. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  320. package/dist/components.js.map +0 -1
  321. package/dist/contextValidator-OOPCLPZW.js +0 -9
  322. package/dist/contextValidator-OOPCLPZW.js.map +0 -1
  323. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  324. package/dist/hooks.js.map +0 -1
  325. package/dist/index.js.map +0 -1
  326. package/dist/providers.js.map +0 -1
  327. package/dist/rbac/eslint-rules.js.map +0 -1
  328. package/dist/rbac/index.js.map +0 -1
  329. package/dist/styles/index.js.map +0 -1
  330. package/dist/theming/runtime.js.map +0 -1
  331. package/dist/types.js.map +0 -1
  332. package/dist/utils.js.map +0 -1
  333. package/docs/standards/01-architecture-standard.md +0 -44
  334. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  335. package/docs/standards/03-component-standard.md +0 -32
  336. package/docs/standards/05-security-standard.md +0 -44
  337. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  338. package/docs/standards/pace-core-compliance.md +0 -432
  339. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  340. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  341. package/scripts/audit/core/checks/bundle.cjs +0 -142
  342. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  343. package/scripts/audit/core/checks/config.cjs +0 -54
  344. package/scripts/audit/core/checks/coverage.cjs +0 -84
  345. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  346. package/scripts/audit/core/checks/documentation.cjs +0 -268
  347. package/scripts/audit/core/checks/environment.cjs +0 -116
  348. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  349. package/scripts/audit/core/checks/forms.cjs +0 -172
  350. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  351. package/scripts/audit/core/checks/hooks.cjs +0 -334
  352. package/scripts/audit/core/checks/imports.cjs +0 -244
  353. package/scripts/audit/core/checks/performance.cjs +0 -325
  354. package/scripts/audit/core/checks/routes.cjs +0 -117
  355. package/scripts/audit/core/checks/state.cjs +0 -130
  356. package/scripts/audit/core/checks/structure.cjs +0 -65
  357. package/scripts/audit/core/checks/style.cjs +0 -584
  358. package/scripts/audit/core/checks/testing.cjs +0 -122
  359. package/scripts/audit/core/checks/typescript.cjs +0 -61
  360. package/scripts/audit/core/scanner.cjs +0 -199
  361. package/scripts/audit/core/utils.cjs +0 -137
  362. package/scripts/audit/reporters/console.cjs +0 -151
  363. package/scripts/audit/reporters/json.cjs +0 -54
  364. package/scripts/audit/reporters/markdown.cjs +0 -124
  365. package/scripts/audit-consuming-app.cjs +0 -86
  366. package/src/eslint-rules/pace-core-compliance.cjs +0 -510
  367. package/src/eslint-rules/pace-core-compliance.js +0 -638
  368. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  369. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  370. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  371. package/src/rbac/components/NavigationProvider.tsx +0 -345
  372. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  373. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  374. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  375. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  376. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  377. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  378. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  379. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  380. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  381. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  382. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  383. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  384. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  385. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  386. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  387. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -0,0 +1,967 @@
1
+ # pace-core Compliance Enforcement
2
+
3
+ **🤖 Cursor Rule**: See [00-pace-core-compliance.mdc](../../cursor-rules/00-pace-core-compliance.mdc) for AI-optimized directives that automatically enforce these standards.
4
+
5
+ This guide explains how to enforce pace-core usage patterns in consuming apps to ensure consistent design, reduce duplication, and maintain high code quality.
6
+
7
+ ## Overview
8
+
9
+ pace-core provides a comprehensive enforcement system that includes:
10
+
11
+ 1. **ESLint Rules** - Real-time linting during development (15 rules)
12
+ 2. **Static Analysis Script** - Comprehensive codebase scanning for file-system and config checks
13
+ 3. **ESLint Config Preset** - Easy setup for consuming apps
14
+ 4. **Cursor Rules Integration** - AI-assisted enforcement via Cursor IDE
15
+
16
+ ### ESLint vs Audit Scripts
17
+
18
+ **ESLint Rules** (Real-time, AST-based):
19
+ - ✅ Run automatically in your IDE
20
+ - ✅ Provide immediate feedback during development
21
+ - ✅ Check single files using AST analysis
22
+ - ✅ Can be auto-fixed in many cases
23
+ - ✅ Integrated with your development workflow
24
+
25
+ **Audit Scripts** (Comprehensive, file-system based):
26
+ - ✅ Scan entire codebase
27
+ - ✅ Check file structure and configuration
28
+ - ✅ Cross-file analysis (e.g., provider nesting)
29
+ - ✅ Generate detailed reports
30
+ - ✅ Check setup files (main.tsx, app.css, etc.)
31
+
32
+ **What's in ESLint vs Audit Scripts**:
33
+
34
+ | Check | ESLint | Audit Script |
35
+ |-------|--------|--------------|
36
+ | Restricted imports | ✅ | ✅ (reference) |
37
+ | Native HTML elements | ✅ | ✅ (reference) |
38
+ | Custom hooks/utils | ✅ | ✅ (reference) |
39
+ | Inline styles | ✅ | ✅ (reference) |
40
+ | Plain form tags | ✅ | ✅ (reference) |
41
+ | Direct Supabase client | ✅ | ✅ (file location) |
42
+ | RBAC permission loading | ✅ | ✅ (reference) |
43
+ | Direct RBAC RPC/table | ✅ | ✅ (reference) |
44
+ | Hardcoded role checks | ✅ | ✅ (reference) |
45
+ | RESOURCE_NAMES constants | ✅ | ✅ (reference) |
46
+ | RBAC wrapper components | ✅ | ✅ (reference) |
47
+ | RBAC wrapper functions | ✅ | ✅ (reference) |
48
+ | RBAC setup (main.tsx) | ❌ | ✅ |
49
+ | Provider nesting | ❌ | ✅ |
50
+ | Core styles import chain | ❌ | ✅ |
51
+ | PagePermissionGuard coverage | ❌ | ✅ |
52
+ | Edge Functions RBAC | ❌ | ✅ |
53
+
54
+ **Note**: Many checks have been migrated from audit scripts to ESLint for real-time feedback. The audit scripts now focus on file-system and configuration checks that require cross-file analysis.
55
+
56
+ ## Quick Start
57
+
58
+ ### Step 1: Install pace-core
59
+
60
+ ```bash
61
+ npm install @jmruthers/pace-core
62
+ ```
63
+
64
+ ### Step 2: Setup ESLint (Recommended)
65
+
66
+ **Option A: Automated Setup (Easiest)**
67
+
68
+ Use the installation script to set up both Cursor rules and ESLint:
69
+
70
+ ```bash
71
+ node node_modules/@jmruthers/pace-core/scripts/install-cursor-rules.cjs
72
+ ```
73
+
74
+ This script will:
75
+ - ✅ Install Cursor rules to `.cursor/rules/`
76
+ - ✅ Configure ESLint to use pace-core rules
77
+ - ✅ Create `eslint.config.js` if it doesn't exist
78
+ - ✅ Add pace-core config to existing ESLint config
79
+ - ✅ Create backups before modifying files
80
+
81
+ **Options:**
82
+ - `--force` - Force update even if already configured
83
+ - `--skip-cursor` - Skip Cursor rules installation
84
+ - `--skip-eslint` - Skip ESLint config setup
85
+
86
+ **Option B: Manual Setup**
87
+
88
+ The easiest way to enable compliance checking is to use the shareable ESLint config. The config is CommonJS but works with both ES module and CommonJS ESLint configs:
89
+
90
+ **For ES Module ESLint Config (eslint.config.js):**
91
+ ```javascript
92
+ // eslint.config.js (ES modules)
93
+ import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
94
+
95
+ export default [
96
+ ...paceCoreConfig,
97
+ // your other config
98
+ ];
99
+ ```
100
+
101
+ **For CommonJS ESLint Config (.eslintrc.js or eslint.config.cjs):**
102
+ ```javascript
103
+ // eslint.config.cjs (CommonJS)
104
+ const paceCoreConfig = require('@jmruthers/pace-core/eslint-config');
105
+
106
+ module.exports = [
107
+ ...paceCoreConfig,
108
+ // your other config
109
+ ];
110
+ ```
111
+
112
+ **Complete Example (ESLint 9 flat config):**
113
+ ```javascript
114
+ // eslint.config.js
115
+ import js from '@eslint/js';
116
+ import reactHooks from 'eslint-plugin-react-hooks';
117
+ import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
118
+
119
+ export default [
120
+ js.configs.recommended,
121
+ ...paceCoreConfig, // pace-core rules
122
+ {
123
+ files: ['**/*.{ts,tsx}'],
124
+ plugins: {
125
+ 'react-hooks': reactHooks,
126
+ },
127
+ rules: {
128
+ ...reactHooks.configs.recommended.rules,
129
+ // Your other rules
130
+ },
131
+ },
132
+ ];
133
+ ```
134
+
135
+ ### Step 3: Setup Cursor Rules (Optional but Recommended)
136
+
137
+ To enable AI-assisted enforcement in Cursor IDE, copy the pace-core cursor rules to your project:
138
+
139
+ ```bash
140
+ # Create .cursor/rules directory if it doesn't exist
141
+ mkdir -p .cursor/rules
142
+
143
+ # Copy pace-core cursor rules
144
+ cp node_modules/@jmruthers/pace-core/cursor-rules/*.mdc .cursor/rules/
145
+ ```
146
+
147
+ Or manually create `.cursor/rules/00-pace-core-compliance.mdc` and reference the pace-core rules:
148
+
149
+ ```markdown
150
+ ---
151
+ description: Enforce pace-core usage patterns
152
+ globs: ["src/**/*.{ts,tsx,js,jsx}"]
153
+ alwaysApply: false
154
+ ---
155
+
156
+ # pace-core Compliance
157
+
158
+ **📚 Full Documentation**: See [pace-core compliance docs](node_modules/@jmruthers/pace-core/docs/standards/00-pace-core-compliance.md)
159
+
160
+ **🔧 ESLint Setup**: Ensure ESLint is configured with `@jmruthers/pace-core/eslint-config`
161
+
162
+ This rule enforces pace-core usage patterns. ESLint provides real-time feedback, while this Cursor rule provides AI-assisted guidance.
163
+
164
+ ## Key Requirements
165
+
166
+ - Use pace-core components instead of native HTML or custom implementations
167
+ - Use pace-core hooks instead of custom hooks
168
+ - Use pace-core utilities instead of custom utilities
169
+ - Use `useSecureSupabase()` instead of direct Supabase client creation
170
+ - Follow RBAC patterns from pace-core
171
+ - Use RESOURCE_NAMES constants instead of string literals
172
+
173
+ See the full documentation for complete rules and examples.
174
+ ```
175
+
176
+ **Benefits of Cursor Rules Integration**:
177
+ - AI assistant (like me!) will automatically suggest pace-core alternatives
178
+ - Context-aware suggestions during code writing
179
+ - Works alongside ESLint for comprehensive enforcement
180
+ - Provides explanations and examples in real-time
181
+
182
+ ### Option 2: Manual ESLint Rules Setup
183
+
184
+ If you prefer more control, you can import the rules directly. Note: The rules are CommonJS, so use `createRequire` in ES modules:
185
+
186
+ **For ES Module ESLint Config:**
187
+ ```javascript
188
+ // eslint.config.js
189
+ import { createRequire } from 'module';
190
+ const require = createRequire(import.meta.url);
191
+ const paceCoreRules = require('@jmruthers/pace-core/eslint-rules').rules;
192
+
193
+ export default [
194
+ {
195
+ plugins: {
196
+ 'pace-core-compliance': {
197
+ rules: paceCoreRules
198
+ }
199
+ },
200
+ rules: {
201
+ 'pace-core-compliance/no-restricted-imports': 'error',
202
+ 'pace-core-compliance/prefer-pace-core-components': 'warn',
203
+ 'pace-core-compliance/prefer-pace-core-hooks': 'warn',
204
+ 'pace-core-compliance/prefer-pace-core-utils': 'warn',
205
+ 'pace-core-compliance/no-local-component-duplication': 'error'
206
+ }
207
+ }
208
+ ];
209
+ ```
210
+
211
+ **For CommonJS ESLint Config:**
212
+ ```javascript
213
+ // eslint.config.cjs
214
+ const paceCoreRules = require('@jmruthers/pace-core/eslint-rules').rules;
215
+
216
+ module.exports = [
217
+ {
218
+ plugins: {
219
+ 'pace-core-compliance': {
220
+ rules: paceCoreRules
221
+ }
222
+ },
223
+ rules: {
224
+ 'pace-core-compliance/no-restricted-imports': 'error',
225
+ 'pace-core-compliance/prefer-pace-core-components': 'warn',
226
+ 'pace-core-compliance/prefer-pace-core-hooks': 'warn',
227
+ 'pace-core-compliance/prefer-pace-core-utils': 'warn',
228
+ 'pace-core-compliance/no-local-component-duplication': 'error'
229
+ }
230
+ }
231
+ ];
232
+ ```
233
+
234
+ ## ESLint Rules
235
+
236
+ pace-core provides **15 ESLint rules** organized into four categories:
237
+
238
+ ### Import Rules
239
+
240
+ #### no-restricted-imports
241
+
242
+ **Severity**: Error
243
+
244
+ Blocks direct imports of libraries that pace-core wraps and standardizes.
245
+
246
+ **Restricted Libraries**:
247
+ - `@radix-ui/*` - All Radix UI packages (use pace-core components instead)
248
+ - `lucide-react` - Icons (import from `@jmruthers/pace-core/icons` instead)
249
+ - `react-day-picker` - Use `Calendar` from pace-core
250
+ - `@tanstack/react-table` - Use `DataTable` from pace-core
251
+ - `react-hook-form` - Use `Form` and `useZodForm` from pace-core
252
+ - `zod` - Use validation utilities from pace-core
253
+
254
+ **Example Violation**:
255
+ ```typescript
256
+ // ❌ Bad
257
+ import { Dialog } from '@radix-ui/react-dialog';
258
+ import { ChevronDown, Edit, Trash } from 'lucide-react';
259
+
260
+ // ✅ Good
261
+ import { Dialog } from '@jmruthers/pace-core';
262
+ import { ChevronDown, Edit, Trash } from '@jmruthers/pace-core/icons';
263
+ ```
264
+
265
+ ### Compliance Rules
266
+
267
+ #### prefer-pace-core-components
268
+
269
+ **Severity**: Warning
270
+
271
+ Suggests using pace-core components instead of native HTML elements.
272
+
273
+ **Detected Patterns**:
274
+ - `<button>` → Use `Button` from pace-core
275
+ - `<input>` → Use `Input` from pace-core
276
+ - `<textarea>` → Use `Textarea` from pace-core
277
+ - `<label>` → Use `Label` from pace-core
278
+
279
+ **Example**:
280
+ ```tsx
281
+ // ⚠️ Warning
282
+ <button onClick={handleClick}>Click me</button>
283
+
284
+ // ✅ Recommended
285
+ import { Button } from '@jmruthers/pace-core';
286
+ <Button onClick={handleClick}>Click me</Button>
287
+ ```
288
+
289
+ #### prefer-pace-core-hooks
290
+
291
+ **Severity**: Warning
292
+
293
+ Detects custom hooks that duplicate pace-core functionality.
294
+
295
+ **Common Patterns Detected**:
296
+ - `useToast`, `useNotification` → Use `useToast` from pace-core
297
+ - `useDebounce`, `useDebounced` → Use `useDebounce` from pace-core
298
+ - `useAuth`, `useAuthentication` → Use `useUnifiedAuth` from pace-core
299
+ - `useForm`, `useZodForm` → Use `useZodForm` from pace-core
300
+
301
+ **Example**:
302
+ ```typescript
303
+ // ⚠️ Warning
304
+ function useToast() {
305
+ // custom implementation
306
+ }
307
+
308
+ // ✅ Recommended
309
+ import { useToast } from '@jmruthers/pace-core';
310
+ ```
311
+
312
+ #### prefer-pace-core-utils
313
+
314
+ **Severity**: Warning
315
+
316
+ Detects utility functions that duplicate pace-core functionality.
317
+
318
+ **Common Patterns Detected**:
319
+ - `formatDate`, `dateFormat` → Use `formatDate` from pace-core
320
+ - `formatCurrency`, `formatMoney` → Use `formatCurrency` from pace-core
321
+ - `cn`, `classNames`, `clsx` → Use `cn` from pace-core
322
+ - `validate`, `validateInput` → Use `validateUserInput` from pace-core
323
+
324
+ **Example**:
325
+ ```typescript
326
+ // ⚠️ Warning
327
+ function formatDate(date: Date): string {
328
+ // custom implementation
329
+ }
330
+
331
+ // ✅ Recommended
332
+ import { formatDate } from '@jmruthers/pace-core';
333
+ ```
334
+
335
+ #### no-local-component-duplication
336
+
337
+ **Severity**: Error
338
+
339
+ Prevents creating local components with names matching pace-core components.
340
+
341
+ **Example Violation**:
342
+ ```
343
+ // ❌ Error: components/Button.tsx
344
+ export function Button() { ... }
345
+
346
+ // pace-core already provides Button component
347
+ ```
348
+
349
+ **Fix**: Remove the local component and import from pace-core:
350
+ ```typescript
351
+ import { Button } from '@jmruthers/pace-core';
352
+ ```
353
+
354
+ #### no-inline-styles
355
+
356
+ **Severity**: Error
357
+
358
+ Disallows inline styles. Use pace-core components or Tailwind classes instead.
359
+
360
+ **Example Violation**:
361
+ ```tsx
362
+ // ❌ Error
363
+ <div style={{ color: 'red', padding: '10px' }}>Content</div>
364
+
365
+ // ✅ Good
366
+ <div className="text-acc-500 p-4">Content</div>
367
+ // Or use pace-core components with built-in styling
368
+ ```
369
+
370
+ ### Component Rules
371
+
372
+ #### prefer-pace-core-form
373
+
374
+ **Severity**: Error
375
+
376
+ Disallows plain `<form>` tags and direct `react-hook-form` imports. Use pace-core `Form` component instead.
377
+
378
+ **Detected Patterns**:
379
+ - Plain `<form>` tags
380
+ - Direct imports from `react-hook-form` (except `useFormContext` when using pace-core `Form`)
381
+
382
+ **Example Violation**:
383
+ ```tsx
384
+ // ❌ Error
385
+ import { useForm, FormProvider } from 'react-hook-form';
386
+ <form onSubmit={handleSubmit}>...</form>
387
+
388
+ // ✅ Good
389
+ import { Form, useZodForm } from '@jmruthers/pace-core';
390
+ <Form form={form} onSubmit={handleSubmit}>...</Form>
391
+ ```
392
+
393
+ **Exception**: `useFormContext` is allowed when `Form` is imported from pace-core:
394
+ ```tsx
395
+ // ✅ Allowed
396
+ import { Form } from '@jmruthers/pace-core';
397
+ import { useFormContext } from 'react-hook-form'; // OK when using pace-core Form
398
+ ```
399
+
400
+ ### RBAC Rules
401
+
402
+ #### no-direct-supabase-client
403
+
404
+ **Severity**: Error
405
+
406
+ Disallows direct `createClient()` calls from `@supabase/supabase-js`. Use `useSecureSupabase()` from pace-core instead.
407
+
408
+ **Why**: Direct client creation bypasses organisation context and RLS policies, leading to security vulnerabilities.
409
+
410
+ **Example Violation**:
411
+ ```typescript
412
+ // ❌ Error
413
+ import { createClient } from '@supabase/supabase-js';
414
+ const supabase = createClient(url, key);
415
+
416
+ // ✅ Good
417
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
418
+ const secureSupabase = useSecureSupabase();
419
+ ```
420
+
421
+ **Exception**: Creating the base client for `UnifiedAuthProvider` in `main.tsx` or `App.tsx` is allowed.
422
+
423
+ #### rbac-permission-loading
424
+
425
+ **Severity**: Error
426
+
427
+ Requires `isLoading` extraction from `useResourcePermissions` and checking it before permission calls in mutations.
428
+
429
+ **Why**: Permission checks may fail if scope resolution is still in progress.
430
+
431
+ **Example Violation**:
432
+ ```typescript
433
+ // ❌ Error
434
+ const { canCreate, canUpdate } = useResourcePermissions(RESOURCE_NAMES.EVENTS);
435
+ // Missing isLoading extraction
436
+
437
+ // In mutation:
438
+ if (canCreate(event)) { ... } // May fail if permissions still loading
439
+
440
+ // ✅ Good
441
+ const { canCreate, canUpdate, isLoading: permissionsLoading } = useResourcePermissions(RESOURCE_NAMES.EVENTS);
442
+
443
+ // In mutation:
444
+ if (permissionsLoading) {
445
+ throw new Error('Permission check in progress. Please wait...');
446
+ }
447
+ if (!canCreate(event)) {
448
+ throw new Error('Insufficient permissions');
449
+ }
450
+ ```
451
+
452
+ #### no-direct-rbac-rpc
453
+
454
+ **Severity**: Error
455
+
456
+ Disallows direct RPC calls to RBAC functions (e.g., `rbac_check_permission_simplified`). Use pace-core RBAC hooks instead.
457
+
458
+ **Example Violation**:
459
+ ```typescript
460
+ // ❌ Error
461
+ const { data } = await supabase.rpc('rbac_check_permission_simplified', {...});
462
+
463
+ // ✅ Good
464
+ import { isPermitted } from '@jmruthers/pace-core/rbac';
465
+ const hasPermission = await isPermitted({...});
466
+ ```
467
+
468
+ #### no-direct-rbac-table
469
+
470
+ **Severity**: Error
471
+
472
+ Disallows direct queries to RBAC tables. Use `useSecureSupabase()` hook or pace-core RBAC API functions instead.
473
+
474
+ **RBAC Tables**:
475
+ - `rbac_organisation_roles`
476
+ - `rbac_event_app_roles`
477
+ - `rbac_global_roles`
478
+ - `rbac_apps`
479
+ - `rbac_app_pages`
480
+ - `rbac_page_permissions`
481
+ - `rbac_user_profiles`
482
+
483
+ **Example Violation**:
484
+ ```typescript
485
+ // ❌ Error
486
+ const { data } = await supabase.from('rbac_organisation_roles').select('*');
487
+
488
+ // ✅ Good
489
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
490
+ const secureSupabase = useSecureSupabase();
491
+ const { data } = await secureSupabase.from('rbac_organisation_roles').select('*');
492
+ ```
493
+
494
+ #### no-hardcoded-role-checks
495
+
496
+ **Severity**: Error
497
+
498
+ Disallows hardcoded role checks. Use `useAccessLevel` hook or `getRoleContext` API from pace-core instead.
499
+
500
+ **Example Violation**:
501
+ ```typescript
502
+ // ❌ Error
503
+ if (user.role === 'admin') { ... }
504
+ if (currentRole === 'org_admin') { ... }
505
+
506
+ // ✅ Good
507
+ import { useAccessLevel } from '@jmruthers/pace-core/rbac';
508
+ const { accessLevel } = useAccessLevel();
509
+ if (accessLevel === 'admin') { ... }
510
+ ```
511
+
512
+ #### rbac-use-resource-names-constants
513
+
514
+ **Severity**: Error
515
+
516
+ Requires `RESOURCE_NAMES` constants instead of string literals in `useResourcePermissions` calls.
517
+
518
+ **Example Violation**:
519
+ ```typescript
520
+ // ❌ Error
521
+ const permissions = useResourcePermissions('organisations');
522
+
523
+ // ✅ Good
524
+ import { RESOURCE_NAMES } from '@/config/resource-names';
525
+ const permissions = useResourcePermissions(RESOURCE_NAMES.ORGANISATIONS);
526
+ ```
527
+
528
+ #### no-rbac-wrapper-components
529
+
530
+ **Severity**: Error
531
+
532
+ Disallows wrapper components around `PagePermissionGuard`. Use `PagePermissionGuard` directly.
533
+
534
+ **Example Violation**:
535
+ ```tsx
536
+ // ❌ Error
537
+ function ProtectedPage({ pageName, children }) {
538
+ return <PagePermissionGuard pageName={pageName}>{children}</PagePermissionGuard>;
539
+ }
540
+
541
+ // ✅ Good
542
+ <PagePermissionGuard pageName="events" operation="read">
543
+ <YourPageContent />
544
+ </PagePermissionGuard>
545
+ ```
546
+
547
+ #### no-rbac-wrapper-functions
548
+
549
+ **Severity**: Error
550
+
551
+ Disallows wrapper functions around pace-core permission hooks. Use hooks directly in components.
552
+
553
+ **Example Violation**:
554
+ ```typescript
555
+ // ❌ Error
556
+ function canEditEvent(eventId: string) {
557
+ const { canUpdate } = useResourcePermissions(RESOURCE_NAMES.EVENTS);
558
+ return canUpdate(eventId) && someOtherCondition;
559
+ }
560
+
561
+ // ✅ Good
562
+ function YourComponent() {
563
+ const { canUpdate } = useResourcePermissions(RESOURCE_NAMES.EVENTS);
564
+ // Use canUpdate directly in component logic
565
+ }
566
+ ```
567
+
568
+ ## Static Analysis Script
569
+
570
+ The static analysis script provides a comprehensive scan of your codebase and generates a detailed compliance report.
571
+
572
+ ### Running the Script
573
+
574
+ ```bash
575
+ # From your consuming app root
576
+ node node_modules/@jmruthers/pace-core/scripts/check-pace-core-compliance.cjs
577
+ ```
578
+
579
+ Or add it to your `package.json`:
580
+
581
+ ```json
582
+ {
583
+ "scripts": {
584
+ "check:pace-core": "node node_modules/@jmruthers/pace-core/scripts/check-pace-core-compliance.cjs"
585
+ }
586
+ }
587
+ ```
588
+
589
+ ### What It Checks
590
+
591
+ 1. **Restricted Imports** - Scans for direct imports of wrapped libraries
592
+ 2. **Duplicate Components** - Finds local components matching pace-core names
593
+ 3. **Duplicate Hooks** - Finds local hooks matching pace-core hooks
594
+ 4. **Duplicate Utils** - Finds local utils matching pace-core utils
595
+ 5. **Suggestions** - Recommends pace-core alternatives for native HTML elements
596
+
597
+ ### Report Output
598
+
599
+ The script generates a color-coded terminal report showing:
600
+
601
+ - ❌ **Errors** - Critical violations that must be fixed
602
+ - ⚠️ **Warnings** - Issues that should be addressed
603
+ - 💡 **Suggestions** - Recommendations for improvement
604
+ - ✅ **Summary** - Overall compliance status
605
+
606
+ Example output:
607
+ ```
608
+ ═══════════════════════════════════════════════════════════
609
+ pace-core Compliance Report
610
+ ═══════════════════════════════════════════════════════════
611
+
612
+ ❌ Restricted Imports Found: 3
613
+
614
+ • src/components/CustomDialog.tsx:5
615
+ Import: @radix-ui/react-dialog
616
+ Reason: Use Dialog component from pace-core instead
617
+
618
+ Summary:
619
+ Total Issues: 3
620
+ - Restricted Imports: 3
621
+ - Duplicate Components/Hooks/Utils: 0
622
+ - Suggestions: 0
623
+ ```
624
+
625
+ ## Best Practices
626
+
627
+ ### 1. Always Import from pace-core
628
+
629
+ ```typescript
630
+ // ✅ Good
631
+ import { Button, Card, Dialog } from '@jmruthers/pace-core';
632
+ import { useToast, useDebounce } from '@jmruthers/pace-core';
633
+ import { formatDate, formatCurrency } from '@jmruthers/pace-core';
634
+ ```
635
+
636
+ ### 2. Use pace-core Components for UI
637
+
638
+ Avoid native HTML elements when pace-core provides a component:
639
+
640
+ ```tsx
641
+ // ❌ Avoid
642
+ <button className="btn">Click</button>
643
+
644
+ // ✅ Use pace-core
645
+ <Button>Click</Button>
646
+ ```
647
+
648
+ ### 3. Leverage pace-core Hooks
649
+
650
+ Don't recreate hooks that pace-core already provides:
651
+
652
+ ```typescript
653
+ // ❌ Avoid
654
+ const [debouncedValue, setDebouncedValue] = useState(value);
655
+ useEffect(() => {
656
+ const timer = setTimeout(() => setDebouncedValue(value), 500);
657
+ return () => clearTimeout(timer);
658
+ }, [value]);
659
+
660
+ // ✅ Use pace-core
661
+ import { useDebounce } from '@jmruthers/pace-core';
662
+ const debouncedValue = useDebounce(value, 500);
663
+ ```
664
+
665
+ ### 4. Use pace-core Utilities
666
+
667
+ Leverage formatting, validation, and other utilities from pace-core:
668
+
669
+ ```typescript
670
+ // ❌ Avoid
671
+ const formatted = new Intl.DateTimeFormat('en-US').format(date);
672
+
673
+ // ✅ Use pace-core
674
+ import { formatDate } from '@jmruthers/pace-core';
675
+ const formatted = formatDate(date);
676
+ ```
677
+
678
+ ### 5. Check Before Creating New Components
679
+
680
+ Before creating a new component, check if pace-core already provides it:
681
+
682
+ 1. Review the [pace-core documentation](https://github.com/your-org/pace-core)
683
+ 2. Check `core-usage-manifest.json` for available components
684
+ 3. Search pace-core exports
685
+
686
+ ## Troubleshooting
687
+
688
+ ### ESLint Rules Not Working
689
+
690
+ 1. **Verify plugin is loaded**: Check that the config is imported correctly:
691
+ ```javascript
692
+ // In your eslint.config.js, add temporarily:
693
+ import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
694
+ console.log('Config:', paceCoreConfig);
695
+ console.log('Has plugins:', paceCoreConfig[0]?.plugins);
696
+ ```
697
+
698
+ 2. **Check rule names**: Rules must be prefixed with `pace-core-compliance/`
699
+
700
+ 3. **Verify manifest exists**: Rules load from `core-usage-manifest.json` in the pace-core package
701
+
702
+ 4. **CommonJS/ES Module issues**: If you're using ES modules and rules aren't loading:
703
+ - Ensure you're using the config preset (Option 1) which handles this automatically
704
+ - Or use `createRequire` when importing rules directly (Option 2)
705
+
706
+ 5. **Verify rules are available**: Check that the rules file exists:
707
+ ```bash
708
+ ls node_modules/@jmruthers/pace-core/dist/eslint-rules/index.cjs
709
+ ```
710
+
711
+ 6. **Check rule count**: Verify all 15 rules are loaded:
712
+ ```bash
713
+ node -e "const rules = require('@jmruthers/pace-core/eslint-rules'); console.log('Rules:', Object.keys(rules.rules).length);"
714
+ ```
715
+ Should output: `Rules: 15`
716
+
717
+ ### Static Analysis Script Errors
718
+
719
+ 1. **Manifest not found**: Ensure `core-usage-manifest.json` exists in pace-core package
720
+ 2. **No files scanned**: Check that you're running from the project root
721
+ 3. **Permission errors**: Ensure script has read access to source files
722
+
723
+ ### False Positives
724
+
725
+ If you encounter false positives:
726
+
727
+ 1. **Component name conflicts**: If you have a legitimate reason to create a local component with a pace-core name, consider:
728
+ - Renaming your component
729
+ - Using a namespace/prefix
730
+ - Documenting why pace-core doesn't meet your needs
731
+
732
+ 2. **Hook/Util patterns**: The pattern matching may flag similar names. Review the suggestion and decide if migration makes sense.
733
+
734
+ ## Integration with CI/CD
735
+
736
+ While CI/CD integration is not included in the initial release, you can easily add it:
737
+
738
+ ```yaml
739
+ # .github/workflows/pace-core-compliance.yml
740
+ name: pace-core Compliance
741
+
742
+ on: [push, pull_request]
743
+
744
+ jobs:
745
+ compliance:
746
+ runs-on: ubuntu-latest
747
+ steps:
748
+ - uses: actions/checkout@v3
749
+ - uses: actions/setup-node@v3
750
+ - run: npm ci
751
+ - run: npm run check:pace-core
752
+ - run: npm run lint
753
+ ```
754
+
755
+ ## Verification
756
+
757
+ After setting up the ESLint config, verify it's working:
758
+
759
+ 1. **Check ESLint can load the config**:
760
+ ```bash
761
+ npx eslint --print-config src/App.tsx
762
+ ```
763
+ Look for `pace-core-compliance` in the plugins section.
764
+
765
+ 2. **Test with a violation**: Create a test file that imports a restricted library:
766
+ ```typescript
767
+ // test-violation.ts
768
+ import { Dialog } from '@radix-ui/react-dialog'; // Should trigger error
769
+ ```
770
+ Run ESLint and verify it reports the violation.
771
+
772
+ 3. **Run static analysis**: Use the compliance script to get a full report:
773
+ ```bash
774
+ npm run check:pace-core
775
+ ```
776
+
777
+ ## Getting Help
778
+
779
+ - **Documentation**: See [pace-core docs](../README.md)
780
+ - **Issues**: Report false positives or rule issues
781
+ - **Manifest**: Check `core-usage-manifest.json` for available exports
782
+ - **ESLint Config**: The config file is at `@jmruthers/pace-core/eslint-config` (CommonJS)
783
+ - **ESLint Rules**: The rules are at `@jmruthers/pace-core/eslint-rules` (CommonJS)
784
+
785
+ ## File Locations
786
+
787
+ When installed in a consuming app, the compliance tools are available at:
788
+
789
+ - **ESLint Config**: `node_modules/@jmruthers/pace-core/eslint-config-pace-core.cjs`
790
+ - **ESLint Rules**: `node_modules/@jmruthers/pace-core/dist/eslint-rules/index.cjs`
791
+ - **Rule Modules**: `node_modules/@jmruthers/pace-core/dist/eslint-rules/rules/*.cjs`
792
+ - **Rule Utils**: `node_modules/@jmruthers/pace-core/dist/eslint-rules/utils/*.cjs`
793
+ - **Static Analysis Script**: `node_modules/@jmruthers/pace-core/scripts/check-pace-core-compliance.cjs`
794
+ - **Manifest**: `node_modules/@jmruthers/pace-core/core-usage-manifest.json`
795
+ - **Cursor Rules**: `node_modules/@jmruthers/pace-core/cursor-rules/*.mdc`
796
+
797
+ ## MUST: Use Secure Supabase Client
798
+
799
+ **All database operations MUST use `useSecureSupabase()` (or the contract-approved pace-core secure client wrapper).**
800
+ Consuming apps **MUST NOT** use the base Supabase client directly for queries.
801
+
802
+ ### Hard Requirements
803
+
804
+ - **MUST NOT** import or call `createClient()` from `@supabase/supabase-js` in consuming app code **except** for creating the base client passed to `UnifiedAuthProvider`.
805
+ - **MUST NOT** export, pass, or store an insecure Supabase client instance for general use.
806
+ - **MUST** perform all `.from(...)`, `.rpc(...)`, `.auth.*`, and storage operations via the secure client returned by `useSecureSupabase()` (or the approved pace-core equivalent).
807
+ - **MUST** create the base Supabase client ONCE and pass it to `UnifiedAuthProvider` as `supabaseClient` prop.
808
+ - **MUST** call `useSecureSupabase()` without parameters - it automatically uses the base client from `useUnifiedAuth()` provider layer.
809
+ - **MUST NOT** pass a base client directly to `useSecureSupabase()` - the hook gets it from the provider automatically.
810
+
811
+ ### Why this is critical
812
+
813
+ Using `createClient()` directly for queries can bypass organisation context enforcement and RLS policies, leading to:
814
+ - Cross-organisation data access
815
+ - Security vulnerabilities
816
+ - Data leakage between organisations
817
+
818
+ ### Correct Pattern
819
+
820
+ ```tsx
821
+ // ✅ CORRECT: Create base client ONCE for UnifiedAuthProvider
822
+ // main.tsx or App.tsx
823
+ import { createClient } from '@supabase/supabase-js';
824
+ import { UnifiedAuthProvider } from '@jmruthers/pace-core';
825
+
826
+ const supabase = createClient(
827
+ import.meta.env.VITE_SUPABASE_URL,
828
+ import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY
829
+ );
830
+
831
+ function App() {
832
+ return (
833
+ <UnifiedAuthProvider
834
+ supabaseClient={supabase} // Pass base client to provider
835
+ appName="YourApp"
836
+ // ... other props
837
+ >
838
+ <YourApp />
839
+ </UnifiedAuthProvider>
840
+ );
841
+ }
842
+
843
+ // ✅ CORRECT: Use secure client in components (no parameters needed)
844
+ // YourComponent.tsx
845
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
846
+
847
+ function YourComponent() {
848
+ const secureSupabase = useSecureSupabase(); // Gets client from provider automatically
849
+
850
+ if (!secureSupabase) {
851
+ return <div>Loading...</div>;
852
+ }
853
+
854
+ // Use secureSupabase for all queries
855
+ const { data } = await secureSupabase.from('users').select('*');
856
+ }
857
+ ```
858
+
859
+ ### Incorrect Patterns
860
+
861
+ ```tsx
862
+ // ❌ FORBIDDEN: Creating client in component or service
863
+ import { createClient } from '@supabase/supabase-js';
864
+ const supabase = createClient(url, key); // Don't do this for queries
865
+
866
+ // ❌ FORBIDDEN: Passing base client to useSecureSupabase
867
+ import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
868
+ import { supabase } from './supabase'; // Don't export base client
869
+ const secureSupabase = useSecureSupabase(supabase); // Don't pass it
870
+
871
+ // ❌ FORBIDDEN: Using base client directly for queries
872
+ const { data } = await supabase.from('users').select('*'); // Bypasses RLS
873
+ ```
874
+
875
+ ### Hook Signature
876
+
877
+ The `useSecureSupabase()` hook signature is:
878
+
879
+ ```typescript
880
+ function useSecureSupabase(
881
+ baseClient?: SupabaseClient<Database> | null
882
+ ): SupabaseClient<Database> | null
883
+ ```
884
+
885
+ **Important**: The `baseClient` parameter is **optional**. The hook automatically gets the base client from `useUnifiedAuth()` provider layer. You should **NOT** pass a base client parameter - call `useSecureSupabase()` without arguments.
886
+
887
+ ### Acceptable Exceptions
888
+
889
+ **The ONLY acceptable use of `createClient()` in consuming app code is:**
890
+
891
+ 1. **Creating the base client for `UnifiedAuthProvider`** - This MUST be in one of these files:
892
+ - `src/main.tsx` (or `main.jsx`)
893
+ - `src/App.tsx` (or `App.jsx`)
894
+ - `src/lib/supabase.ts` (or `supabase.js`) - ONLY if this file is ONLY used to create the base client for the provider
895
+
896
+ **The file containing the base client creation MUST:**
897
+ - Be clearly named (e.g., `supabase.ts`, `main.tsx`)
898
+ - Only create the client once
899
+ - Pass it directly to `UnifiedAuthProvider` (not export it for general use)
900
+ - Include a comment explaining it's the base client for the provider:
901
+ ```tsx
902
+ // Base Supabase client for UnifiedAuthProvider only
903
+ // DO NOT use this client directly - use useSecureSupabase() instead
904
+ const supabase = createClient(...);
905
+ ```
906
+
907
+ **NO OTHER EXCEPTIONS ARE PERMITTED** - All other uses of `createClient()` are security violations and MUST be fixed.
908
+
909
+ ### Detection / Audit
910
+
911
+ - `rg "createClient\(" src` must return **exactly ONE match** in the file that creates the base client for `UnifiedAuthProvider`.
912
+ - That file MUST be one of: `main.tsx`, `App.tsx`, or `lib/supabase.ts` (or `.jsx`/`.js` equivalents).
913
+ - No `.from(` / `.rpc(` calls may be performed on an insecure client reference.
914
+ - All `useSecureSupabase()` calls should be without parameters.
915
+
916
+ ## Compliance Exceptions
917
+
918
+ **In general, pace-core compliance rules do NOT allow exceptions.** The rules are designed to ensure security, consistency, and maintainability across the PACE suite.
919
+
920
+ ### When Exceptions Are NOT Allowed
921
+
922
+ - **Security rules** (e.g., `createClient()` usage) - NO exceptions except the one documented above
923
+ - **RBAC rules** - NO exceptions
924
+ - **Component usage** - NO exceptions (use pace-core components)
925
+ - **Hook usage** - NO exceptions (use pace-core hooks)
926
+
927
+ ### Documenting Legitimate Edge Cases
928
+
929
+ If you encounter a situation where a rule seems to conflict with a legitimate requirement:
930
+
931
+ 1. **First**: Verify that pace-core doesn't provide a solution
932
+ 2. **Second**: Check if the requirement should be added to pace-core
933
+ 3. **Third**: If truly unavoidable, document the case clearly:
934
+ - Add a comment explaining why the exception is necessary
935
+ - Include a reference to this standard
936
+ - Consider opening an issue to add the missing functionality to pace-core
937
+
938
+ **Example of proper documentation:**
939
+ ```tsx
940
+ // @pace-core-compliance-exception: Legacy integration requires direct Supabase client
941
+ // TODO: Migrate to useSecureSupabase() when legacy system is updated
942
+ // See: https://github.com/your-org/pace-core/issues/123
943
+ const legacyClient = createClient(...);
944
+ ```
945
+
946
+ **Note**: Even with documentation, exceptions should be:
947
+ - Temporary (with a plan to remove them)
948
+ - Rare (only when absolutely necessary)
949
+ - Reviewed and approved by the team
950
+ - Tracked for eventual removal
951
+
952
+ ### Audit Handling of Exceptions
953
+
954
+ During audits, documented exceptions will be:
955
+ - **Verified** - Confirmed that the exception is legitimate and properly documented
956
+ - **Categorized** - Marked as "Acceptable Exception" if valid, or flagged for remediation if not
957
+ - **Tracked** - Included in the audit report with a recommendation to remove when possible
958
+
959
+ **Invalid exceptions** (undocumented, unnecessary, or security-related) will be flagged as violations requiring remediation.
960
+
961
+ ## Related Documentation
962
+
963
+ - [Component Standards](./03-component-standard.md)
964
+ - [API & RPC Standards](./02-api-and-rpc-standard.md)
965
+ - [Getting Started Guide](../getting-started/README.md)
966
+ - [RBAC Getting Started](../rbac/getting-started.md) - Secure client usage
967
+