@jmruthers/pace-core 0.6.5 → 0.6.7

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 (473) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/audit-tool/00-dependencies.cjs +394 -0
  4. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  5. package/audit-tool/audits/02-project-structure.cjs +255 -0
  6. package/audit-tool/audits/03-architecture.cjs +196 -0
  7. package/audit-tool/audits/04-code-quality.cjs +149 -0
  8. package/audit-tool/audits/05-styling.cjs +224 -0
  9. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  10. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  11. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  12. package/audit-tool/audits/09-operations.cjs +208 -0
  13. package/audit-tool/index.cjs +291 -0
  14. package/audit-tool/utils/code-utils.cjs +218 -0
  15. package/audit-tool/utils/file-utils.cjs +230 -0
  16. package/audit-tool/utils/report-utils.cjs +241 -0
  17. package/core-usage-manifest.json +93 -0
  18. package/cursor-rules/00-standards-overview.mdc +156 -0
  19. package/cursor-rules/01-pace-core-compliance.mdc +586 -0
  20. package/cursor-rules/02-project-structure.mdc +42 -4
  21. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
  22. package/cursor-rules/04-code-quality.mdc +419 -0
  23. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
  24. package/cursor-rules/06-security-rbac.mdc +518 -0
  25. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  26. package/cursor-rules/08-testing-documentation.mdc +324 -0
  27. package/cursor-rules/09-operations.mdc +365 -0
  28. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  29. package/dist/DataTable-7PMH7XN7.js +15 -0
  30. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  31. package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
  32. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  33. package/dist/api-Y4MQWOFW.js +4 -0
  34. package/dist/audit-MYQXYZFU.js +3 -0
  35. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  36. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  37. package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
  38. package/dist/chunk-6F3IILHI.js +62 -0
  39. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  40. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  41. package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
  42. package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
  43. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  44. package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
  45. package/dist/chunk-C7NSAPTL.js +1 -0
  46. package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
  47. package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
  48. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  49. package/dist/chunk-GHYHJTYV.js +994 -0
  50. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  51. package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
  52. package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
  53. package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
  54. package/dist/chunk-MBADTM7L.js +64 -0
  55. package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
  56. package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
  57. package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
  58. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  59. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  60. package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
  61. package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
  62. package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
  63. package/dist/components.d.ts +7 -5
  64. package/dist/components.js +46 -257
  65. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  66. package/dist/eslint-rules/index.cjs +35 -0
  67. package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
  68. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  69. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  70. package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
  71. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  72. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  73. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  74. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  75. package/dist/hooks.d.ts +6 -6
  76. package/dist/hooks.js +62 -172
  77. package/dist/icons/index.d.ts +1 -0
  78. package/dist/icons/index.js +1 -0
  79. package/dist/index.d.ts +12 -11
  80. package/dist/index.js +67 -660
  81. package/dist/providers.d.ts +2 -2
  82. package/dist/providers.js +8 -35
  83. package/dist/rbac/eslint-rules.d.ts +46 -44
  84. package/dist/rbac/eslint-rules.js +7 -4
  85. package/dist/rbac/index.d.ts +109 -586
  86. package/dist/rbac/index.js +14 -207
  87. package/dist/styles/index.js +2 -12
  88. package/dist/theming/runtime.d.ts +14 -1
  89. package/dist/theming/runtime.js +3 -19
  90. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  91. package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
  92. package/dist/types-t9H8qKRw.d.ts +55 -0
  93. package/dist/types.d.ts +1 -1
  94. package/dist/types.js +7 -94
  95. package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
  96. package/dist/utils.d.ts +24 -117
  97. package/dist/utils.js +54 -392
  98. package/docs/README.md +17 -7
  99. package/docs/api/README.md +4 -402
  100. package/docs/api/modules.md +301 -871
  101. package/docs/api-reference/components.md +21 -21
  102. package/docs/api-reference/deprecated.md +31 -6
  103. package/docs/api-reference/hooks.md +80 -80
  104. package/docs/api-reference/rpc-functions.md +78 -3
  105. package/docs/api-reference/types.md +1 -1
  106. package/docs/api-reference/utilities.md +1 -1
  107. package/docs/architecture/README.md +1 -1
  108. package/docs/core-concepts/events.md +3 -3
  109. package/docs/core-concepts/organisations.md +6 -6
  110. package/docs/core-concepts/permissions.md +6 -6
  111. package/docs/documentation-index.md +12 -18
  112. package/docs/getting-started/cursor-rules.md +3 -23
  113. package/docs/getting-started/dependencies.md +650 -0
  114. package/docs/getting-started/documentation-index.md +1 -1
  115. package/docs/getting-started/examples/README.md +4 -4
  116. package/docs/getting-started/examples/full-featured-app.md +1 -1
  117. package/docs/getting-started/faq.md +2 -2
  118. package/docs/getting-started/installation-guide.md +20 -7
  119. package/docs/getting-started/quick-reference.md +4 -4
  120. package/docs/getting-started/quick-start.md +23 -12
  121. package/docs/implementation-guides/authentication.md +15 -15
  122. package/docs/implementation-guides/component-styling.md +1 -1
  123. package/docs/implementation-guides/data-tables.md +126 -33
  124. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  125. package/docs/implementation-guides/dynamic-colors.md +3 -3
  126. package/docs/implementation-guides/file-upload-storage.md +2 -2
  127. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  128. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  129. package/docs/implementation-guides/large-datasets.md +3 -2
  130. package/docs/implementation-guides/organisation-security.md +2 -2
  131. package/docs/implementation-guides/performance.md +2 -2
  132. package/docs/implementation-guides/permission-enforcement.md +5 -1
  133. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  134. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  135. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  136. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  137. package/docs/rbac/README.md +17 -8
  138. package/docs/rbac/advanced-patterns.md +6 -6
  139. package/docs/rbac/api-reference.md +20 -20
  140. package/docs/rbac/edge-functions-guide.md +376 -0
  141. package/docs/rbac/event-based-apps.md +3 -3
  142. package/docs/rbac/examples.md +41 -41
  143. package/docs/rbac/getting-started.md +37 -37
  144. package/docs/rbac/performance.md +1 -1
  145. package/docs/rbac/quick-start.md +52 -52
  146. package/docs/rbac/secure-client-protection.md +1 -35
  147. package/docs/rbac/troubleshooting.md +1 -1
  148. package/docs/security/README.md +5 -5
  149. package/docs/standards/0-standards-overview.md +220 -0
  150. package/docs/standards/1-pace-core-compliance-standards.md +986 -0
  151. package/docs/standards/2-project-structure-standards.md +949 -0
  152. package/docs/standards/3-architecture-standards.md +606 -0
  153. package/docs/standards/4-code-quality-standards.md +728 -0
  154. package/docs/standards/5-styling-standards.md +348 -0
  155. package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
  156. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  157. package/docs/standards/8-testing-documentation-standards.md +401 -0
  158. package/docs/standards/9-operations-standards.md +1102 -0
  159. package/docs/standards/README.md +185 -57
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/common-issues.md +2 -2
  162. package/docs/troubleshooting/debugging.md +9 -9
  163. package/docs/troubleshooting/migration.md +4 -4
  164. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  165. package/eslint-config-pace-core.cjs +33 -6
  166. package/package.json +35 -23
  167. package/scripts/install-cursor-rules.cjs +25 -6
  168. package/scripts/install-eslint-config.cjs +284 -0
  169. package/src/__tests__/fixtures/supabase.ts +1 -1
  170. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
  171. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  172. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  173. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  174. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
  175. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  176. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  177. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  178. package/src/__tests__/public-recipe-view.test.ts +38 -9
  179. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  180. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  181. package/src/__tests__/templates/component.test.template.tsx +18 -15
  182. package/src/components/Button/Button.tsx +5 -1
  183. package/src/components/Calendar/Calendar.tsx +201 -47
  184. package/src/components/ContextSelector/ContextSelector.tsx +106 -119
  185. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  186. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  187. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  188. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  189. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  190. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  191. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  192. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  193. package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
  194. package/src/components/DataTable/components/EditFields.tsx +23 -3
  195. package/src/components/DataTable/components/EditableRow.tsx +12 -9
  196. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  197. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  198. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  199. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  200. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  201. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  202. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  203. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  204. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  205. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  206. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  207. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  208. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  209. package/src/components/DataTable/components/index.ts +2 -1
  210. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  211. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  212. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  213. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  214. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  215. package/src/components/DataTable/types.ts +5 -18
  216. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  217. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  218. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  219. package/src/components/DateTimeField/DateTimeField.tsx +10 -9
  220. package/src/components/Dialog/Dialog.test.tsx +128 -104
  221. package/src/components/Dialog/Dialog.tsx +742 -24
  222. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  223. package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
  224. package/src/components/FileDisplay/FileDisplay.tsx +23 -17
  225. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  226. package/src/components/FileUpload/FileUpload.tsx +112 -130
  227. package/src/components/Form/Form.test.tsx +6 -8
  228. package/src/components/Form/Form.tsx +365 -4
  229. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  230. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  231. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  232. package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
  233. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  234. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  235. package/src/components/Progress/Progress.tsx +2 -4
  236. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  237. package/src/components/Select/Select.tsx +109 -98
  238. package/src/components/Select/types.ts +4 -1
  239. package/src/components/UserMenu/UserMenu.tsx +9 -6
  240. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  241. package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
  242. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  243. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  244. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  245. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  246. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  247. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  248. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  249. package/src/hooks/public/usePublicEvent.ts +67 -195
  250. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  251. package/src/hooks/public/usePublicEventLogo.ts +24 -14
  252. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  253. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  254. package/src/hooks/useAppConfig.ts +28 -26
  255. package/src/hooks/useEventTheme.test.ts +217 -239
  256. package/src/hooks/useEventTheme.ts +16 -28
  257. package/src/hooks/useFileDisplay.ts +2 -2
  258. package/src/hooks/useOrganisationPermissions.ts +5 -7
  259. package/src/hooks/useQueryCache.ts +0 -1
  260. package/src/hooks/useSessionDraft.ts +380 -0
  261. package/src/hooks/useSessionRestoration.ts +3 -1
  262. package/src/icons/index.ts +27 -0
  263. package/src/index.ts +5 -0
  264. package/src/providers/OrganisationProvider.tsx +23 -14
  265. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  266. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  267. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  268. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  269. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  270. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  271. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  272. package/src/providers/services/EventServiceProvider.tsx +1 -24
  273. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  274. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  275. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
  276. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  277. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  278. package/src/rbac/adapters.tsx +7 -295
  279. package/src/rbac/api.test.ts +44 -56
  280. package/src/rbac/api.ts +10 -17
  281. package/src/rbac/cache-invalidation.ts +0 -1
  282. package/src/rbac/compliance/index.ts +10 -0
  283. package/src/rbac/compliance/pattern-detector.ts +553 -0
  284. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  285. package/src/rbac/components/AccessDenied.tsx +150 -0
  286. package/src/rbac/components/NavigationGuard.tsx +12 -20
  287. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  288. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  289. package/src/rbac/components/index.ts +3 -41
  290. package/src/rbac/eslint-rules.js +1 -1
  291. package/src/rbac/hooks/index.ts +0 -3
  292. package/src/rbac/hooks/permissions/index.ts +0 -3
  293. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  294. package/src/rbac/hooks/usePermissions.ts +0 -3
  295. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  296. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  297. package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
  298. package/src/rbac/hooks/useResourcePermissions.ts +139 -48
  299. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  300. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  301. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  302. package/src/rbac/index.ts +7 -9
  303. package/src/rbac/utils/contextValidator.ts +9 -7
  304. package/src/services/AuthService.ts +130 -18
  305. package/src/services/EventService.ts +4 -97
  306. package/src/services/InactivityService.ts +16 -0
  307. package/src/services/OrganisationService.ts +7 -44
  308. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  309. package/src/services/base/BaseService.ts +0 -3
  310. package/src/styles/core.css +7 -0
  311. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  312. package/src/theming/parseEventColours.ts +22 -10
  313. package/src/types/database.generated.ts +4733 -3809
  314. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  315. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  316. package/src/utils/context/organisationContext.test.ts +13 -28
  317. package/src/utils/context/organisationContext.ts +21 -52
  318. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  319. package/src/utils/file-reference/index.ts +39 -15
  320. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  321. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  322. package/src/utils/index.ts +4 -1
  323. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  324. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  325. package/src/utils/persistence/keyDerivation.ts +304 -0
  326. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  327. package/src/utils/security/secureStorage.ts +5 -5
  328. package/src/utils/storage/README.md +1 -1
  329. package/src/utils/storage/helpers.ts +3 -3
  330. package/src/utils/supabase/createBaseClient.ts +147 -0
  331. package/src/utils/timezone/timezone.test.ts +1 -2
  332. package/src/utils/timezone/timezone.ts +1 -1
  333. package/src/utils/validation/csrf.ts +4 -4
  334. package/cursor-rules/00-pace-core-compliance.mdc +0 -331
  335. package/cursor-rules/01-standards-compliance.mdc +0 -244
  336. package/cursor-rules/04-testing-standards.mdc +0 -268
  337. package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
  338. package/cursor-rules/06-code-quality.mdc +0 -309
  339. package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
  340. package/cursor-rules/CHANGELOG.md +0 -119
  341. package/cursor-rules/README.md +0 -192
  342. package/dist/DataTable-AOVNCPTX.js +0 -175
  343. package/dist/DataTable-AOVNCPTX.js.map +0 -1
  344. package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
  345. package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
  346. package/dist/api-O6HTBX5Y.js +0 -52
  347. package/dist/api-O6HTBX5Y.js.map +0 -1
  348. package/dist/audit-V53FV5AG.js +0 -17
  349. package/dist/audit-V53FV5AG.js.map +0 -1
  350. package/dist/chunk-5DRSZLL2.js.map +0 -1
  351. package/dist/chunk-63FOKYGO.js.map +0 -1
  352. package/dist/chunk-6COVEUS7.js.map +0 -1
  353. package/dist/chunk-AFVQODI2.js +0 -263
  354. package/dist/chunk-AFVQODI2.js.map +0 -1
  355. package/dist/chunk-DGUM43GV.js.map +0 -1
  356. package/dist/chunk-E66EQZE6.js.map +0 -1
  357. package/dist/chunk-EFN2EIMK.js.map +0 -1
  358. package/dist/chunk-FFQEQTNW.js.map +0 -1
  359. package/dist/chunk-FMUCXFII.js.map +0 -1
  360. package/dist/chunk-G37KK66H.js.map +0 -1
  361. package/dist/chunk-G7QEZTYQ.js +0 -2053
  362. package/dist/chunk-G7QEZTYQ.js.map +0 -1
  363. package/dist/chunk-HU2C6SSC.js.map +0 -1
  364. package/dist/chunk-IHB5DR3H.js.map +0 -1
  365. package/dist/chunk-IVOFDYWT.js.map +0 -1
  366. package/dist/chunk-J36DSWQK.js.map +0 -1
  367. package/dist/chunk-JGRYX5UX.js.map +0 -1
  368. package/dist/chunk-KQCRWDSA.js +0 -1
  369. package/dist/chunk-KQCRWDSA.js.map +0 -1
  370. package/dist/chunk-L4OXEN46.js.map +0 -1
  371. package/dist/chunk-LMC26NLJ.js +0 -84
  372. package/dist/chunk-LMC26NLJ.js.map +0 -1
  373. package/dist/chunk-M43Y4SSO.js.map +0 -1
  374. package/dist/chunk-M7MPQISP.js.map +0 -1
  375. package/dist/chunk-NTM7ZSB6.js.map +0 -1
  376. package/dist/chunk-PWLANIRT.js.map +0 -1
  377. package/dist/chunk-QXHPKYJV.js.map +0 -1
  378. package/dist/chunk-RGAWHO7N.js.map +0 -1
  379. package/dist/chunk-UPPMRMYG.js.map +0 -1
  380. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  381. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  382. package/dist/components.js.map +0 -1
  383. package/dist/contextValidator-5OGXSPKS.js +0 -9
  384. package/dist/contextValidator-5OGXSPKS.js.map +0 -1
  385. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  386. package/dist/hooks.js.map +0 -1
  387. package/dist/index.js.map +0 -1
  388. package/dist/providers.js.map +0 -1
  389. package/dist/rbac/eslint-rules.js.map +0 -1
  390. package/dist/rbac/index.js.map +0 -1
  391. package/dist/styles/index.js.map +0 -1
  392. package/dist/theming/runtime.js.map +0 -1
  393. package/dist/types.js.map +0 -1
  394. package/dist/utils.js.map +0 -1
  395. package/docs/best-practices/README.md +0 -472
  396. package/docs/best-practices/accessibility.md +0 -601
  397. package/docs/best-practices/common-patterns.md +0 -516
  398. package/docs/best-practices/deployment.md +0 -1103
  399. package/docs/best-practices/performance.md +0 -1328
  400. package/docs/best-practices/security.md +0 -940
  401. package/docs/best-practices/testing.md +0 -1034
  402. package/docs/rbac/compliance/compliance-guide.md +0 -544
  403. package/docs/standards/01-architecture-standard.md +0 -44
  404. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  405. package/docs/standards/03-component-standard.md +0 -32
  406. package/docs/standards/04-code-style-standard.md +0 -32
  407. package/docs/standards/05-security-standard.md +0 -44
  408. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  409. package/docs/standards/pace-core-compliance.md +0 -432
  410. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  411. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  412. package/scripts/audit/core/checks/bundle.cjs +0 -142
  413. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  414. package/scripts/audit/core/checks/config.cjs +0 -54
  415. package/scripts/audit/core/checks/coverage.cjs +0 -84
  416. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  417. package/scripts/audit/core/checks/documentation.cjs +0 -268
  418. package/scripts/audit/core/checks/environment.cjs +0 -116
  419. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  420. package/scripts/audit/core/checks/forms.cjs +0 -172
  421. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  422. package/scripts/audit/core/checks/hooks.cjs +0 -334
  423. package/scripts/audit/core/checks/imports.cjs +0 -244
  424. package/scripts/audit/core/checks/performance.cjs +0 -325
  425. package/scripts/audit/core/checks/routes.cjs +0 -117
  426. package/scripts/audit/core/checks/state.cjs +0 -130
  427. package/scripts/audit/core/checks/structure.cjs +0 -65
  428. package/scripts/audit/core/checks/style.cjs +0 -584
  429. package/scripts/audit/core/checks/testing.cjs +0 -122
  430. package/scripts/audit/core/checks/typescript.cjs +0 -61
  431. package/scripts/audit/core/scanner.cjs +0 -199
  432. package/scripts/audit/core/utils.cjs +0 -137
  433. package/scripts/audit/index.cjs +0 -223
  434. package/scripts/audit/reporters/console.cjs +0 -151
  435. package/scripts/audit/reporters/json.cjs +0 -54
  436. package/scripts/audit/reporters/markdown.cjs +0 -124
  437. package/scripts/audit-consuming-app.cjs +0 -86
  438. package/src/components/DataTable/components/DataTableBody.tsx +0 -454
  439. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  440. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  441. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  442. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  443. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  444. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  445. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  446. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  447. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  448. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  449. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  450. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  451. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  452. package/src/components/DataTable/utils/debugTools.ts +0 -514
  453. package/src/eslint-rules/pace-core-compliance.js +0 -638
  454. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  455. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  456. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  457. package/src/rbac/components/NavigationProvider.tsx +0 -345
  458. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  459. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  460. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  461. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  462. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  463. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  464. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  465. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  466. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  467. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  468. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  469. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  470. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  471. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  472. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  473. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -0,0 +1,553 @@
1
+ /**
2
+ * Pattern Detector for RBAC Compliance
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Compliance/PatternDetector
5
+ * @since 1.0.0
6
+ *
7
+ * This module provides static and runtime pattern detection for RBAC violations.
8
+ * It detects direct RPC calls, direct table queries, and other non-standard patterns.
9
+ */
10
+
11
+ import { getRBACLogger } from '../config';
12
+
13
+ export interface PatternViolation {
14
+ type: 'direct-rpc-call' | 'direct-table-query' | 'bypass-pattern' | 'custom-component' | 'hardcoded-role-check' | 'custom-permission-utility' | 'ui-only-access-control' | 'permission-bypass-comment' | 'resource-permission-string-literal' | 'permission-wrapper-function';
15
+ file?: string;
16
+ line?: number;
17
+ message: string;
18
+ recommendation: string;
19
+ }
20
+
21
+ export interface PatternDetectionResult {
22
+ violations: PatternViolation[];
23
+ isCompliant: boolean;
24
+ summary: {
25
+ directRpcCalls: number;
26
+ directTableQueries: number;
27
+ bypassPatterns: number;
28
+ customComponents: number;
29
+ hardcodedRoleChecks: number;
30
+ customPermissionUtilities: number;
31
+ uiOnlyAccessControl: number;
32
+ permissionBypassComments: number;
33
+ resourcePermissionStringLiterals: number;
34
+ permissionWrapperFunctions: number;
35
+ };
36
+ }
37
+
38
+ /**
39
+ * RBAC table names that should not be queried directly
40
+ */
41
+ const RBAC_TABLES = [
42
+ 'rbac_organisation_roles',
43
+ 'rbac_event_app_roles',
44
+ 'rbac_global_roles',
45
+ 'rbac_apps',
46
+ 'rbac_app_pages',
47
+ 'rbac_page_permissions',
48
+ 'rbac_user_profiles'
49
+ ];
50
+
51
+ /**
52
+ * Detect direct RPC calls to rbac_check_permission_simplified
53
+ *
54
+ * @param code - Source code to analyze
55
+ * @param filePath - File path for reporting
56
+ * @returns Array of violations
57
+ */
58
+ export function detectDirectRpcCalls(code: string, filePath?: string): PatternViolation[] {
59
+ const violations: PatternViolation[] = [];
60
+ const lines = code.split('\n');
61
+
62
+ // Skip if this is in pace-core package itself
63
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
64
+ return violations;
65
+ }
66
+
67
+ // Pattern: .rpc('rbac_check_permission_simplified', ...)
68
+ const rpcPattern = /\.rpc\s*\(\s*['"]rbac_check_permission_simplified['"]/g;
69
+ let match;
70
+ let lineNumber = 1;
71
+
72
+ for (const line of lines) {
73
+ if (rpcPattern.test(line)) {
74
+ violations.push({
75
+ type: 'direct-rpc-call',
76
+ file: filePath,
77
+ line: lineNumber,
78
+ message: `Direct RPC call to 'rbac_check_permission_simplified' detected`,
79
+ recommendation: "Use isPermitted() from '@jmruthers/pace-core/rbac' instead of calling the RPC directly."
80
+ });
81
+ }
82
+ lineNumber++;
83
+ }
84
+
85
+ return violations;
86
+ }
87
+
88
+ /**
89
+ * Detect direct queries to RBAC tables
90
+ *
91
+ * @param code - Source code to analyze
92
+ * @param filePath - File path for reporting
93
+ * @returns Array of violations
94
+ */
95
+ export function detectDirectTableQueries(code: string, filePath?: string): PatternViolation[] {
96
+ const violations: PatternViolation[] = [];
97
+ const lines = code.split('\n');
98
+
99
+ // Skip if this is in pace-core package itself
100
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
101
+ return violations;
102
+ }
103
+
104
+ // Pattern: .from('rbac_*')
105
+ const fromPattern = /\.from\s*\(\s*['"](rbac_\w+)['"]/g;
106
+ let lineNumber = 1;
107
+
108
+ for (const line of lines) {
109
+ let match;
110
+ while ((match = fromPattern.exec(line)) !== null) {
111
+ const tableName = match[1];
112
+ if (RBAC_TABLES.includes(tableName) || tableName.startsWith('rbac_')) {
113
+ violations.push({
114
+ type: 'direct-table-query',
115
+ file: filePath,
116
+ line: lineNumber,
117
+ message: `Direct query to RBAC table '${tableName}' detected`,
118
+ recommendation: `Use pace-core RBAC API functions from '@jmruthers/pace-core/rbac' instead of querying '${tableName}' directly.`
119
+ });
120
+ }
121
+ }
122
+ lineNumber++;
123
+ }
124
+
125
+ return violations;
126
+ }
127
+
128
+ /**
129
+ * Detect custom access denied components
130
+ *
131
+ * @param code - Source code to analyze
132
+ * @param filePath - File path for reporting
133
+ * @returns Array of violations
134
+ */
135
+ export function detectCustomAccessDeniedComponents(code: string, filePath?: string): PatternViolation[] {
136
+ const violations: PatternViolation[] = [];
137
+ const lines = code.split('\n');
138
+
139
+ // Skip if this is in pace-core package itself
140
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
141
+ return violations;
142
+ }
143
+
144
+ // Check if AccessDenied is imported from pace-core
145
+ const hasPaceCoreAccessDenied = /from\s+['"]@jmruthers\/pace-core\/rbac['"].*AccessDenied/.test(code) ||
146
+ /import.*AccessDenied.*from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(code);
147
+
148
+ if (hasPaceCoreAccessDenied) {
149
+ return violations; // Using pace-core AccessDenied, no violation
150
+ }
151
+
152
+ // Pattern: function AccessDenied, const AccessDenied, export AccessDenied, etc.
153
+ const accessDeniedPatterns = [
154
+ /(?:function|const|export\s+(?:function|const)?)\s+(AccessDenied|PermissionDenied|AccessDeniedPage|PermissionDeniedPage|Unauthorized|UnauthorizedPage)\s*[=:]/g,
155
+ /export\s+default\s+(?:function\s+)?(AccessDenied|PermissionDenied|AccessDeniedPage|PermissionDeniedPage|Unauthorized|UnauthorizedPage)/g
156
+ ];
157
+
158
+ let lineNumber = 1;
159
+
160
+ for (const line of lines) {
161
+ for (const pattern of accessDeniedPatterns) {
162
+ const match = pattern.exec(line);
163
+ if (match) {
164
+ const componentName = match[1];
165
+ violations.push({
166
+ type: 'custom-component',
167
+ file: filePath,
168
+ line: lineNumber,
169
+ message: `Custom access denied component '${componentName}' detected`,
170
+ recommendation: `Use AccessDenied from '@jmruthers/pace-core/rbac' instead of creating a custom '${componentName}' component.`
171
+ });
172
+ }
173
+ }
174
+ lineNumber++;
175
+ }
176
+
177
+ return violations;
178
+ }
179
+
180
+ /**
181
+ * Detect hardcoded role checks (e.g., role === 'admin', user.role === 'org_admin')
182
+ *
183
+ * @param code - Source code to analyze
184
+ * @param filePath - File path for reporting
185
+ * @returns Array of violations
186
+ */
187
+ export function detectHardcodedRoleChecks(code: string, filePath?: string): PatternViolation[] {
188
+ const violations: PatternViolation[] = [];
189
+ const lines = code.split('\n');
190
+
191
+ // Skip if this is in pace-core package itself
192
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
193
+ return violations;
194
+ }
195
+
196
+ // Pattern: role === 'admin', user.role === 'org_admin', etc.
197
+ // Common role names to detect
198
+ const roleNames = ['admin', 'org_admin', 'event_admin', 'user', 'member', 'viewer', 'editor'];
199
+ const roleCheckPattern = new RegExp(
200
+ `(?:role|user\\.role|userRole|currentRole|userRoleName)\\s*(?:===|!==|==|!=)\\s*['"](${roleNames.join('|')})['"]`,
201
+ 'gi'
202
+ );
203
+
204
+ let lineNumber = 1;
205
+
206
+ for (const line of lines) {
207
+ const match = roleCheckPattern.exec(line);
208
+ if (match) {
209
+ const roleName = match[1];
210
+ violations.push({
211
+ type: 'hardcoded-role-check',
212
+ file: filePath,
213
+ line: lineNumber,
214
+ message: `Hardcoded role check detected: comparing role to '${roleName}'`,
215
+ recommendation: "Use useAccessLevel hook or getRoleContext API from '@jmruthers/pace-core/rbac' instead of hardcoded role checks."
216
+ });
217
+ }
218
+ lineNumber++;
219
+ }
220
+
221
+ return violations;
222
+ }
223
+
224
+ /**
225
+ * Detect custom permission utility functions (e.g., checkPermission, hasPermission, canAccess)
226
+ *
227
+ * @param code - Source code to analyze
228
+ * @param filePath - File path for reporting
229
+ * @returns Array of violations
230
+ */
231
+ export function detectCustomPermissionUtilities(code: string, filePath?: string): PatternViolation[] {
232
+ const violations: PatternViolation[] = [];
233
+ const lines = code.split('\n');
234
+
235
+ // Skip if this is in pace-core package itself
236
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
237
+ return violations;
238
+ }
239
+
240
+ // Check if pace-core permission functions are imported
241
+ const hasPaceCoreImports = /from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(code);
242
+
243
+ // Pattern: function checkPermission, const hasPermission, export canAccess, etc.
244
+ const permissionUtilityPatterns = [
245
+ /(?:function|const|export\s+(?:function|const)?)\s+(checkPermission|hasPermission|canAccess|checkAccess|verifyPermission|hasAccess|canPerform|checkCan|hasCan)\s*[=:]/g,
246
+ /export\s+default\s+(?:function\s+)?(checkPermission|hasPermission|canAccess|checkAccess|verifyPermission|hasAccess|canPerform|checkCan|hasCan)/g
247
+ ];
248
+
249
+ let lineNumber = 1;
250
+
251
+ for (const line of lines) {
252
+ for (const pattern of permissionUtilityPatterns) {
253
+ const match = pattern.exec(line);
254
+ if (match) {
255
+ const functionName = match[1];
256
+ // Only flag if not importing from pace-core (might be using pace-core functions)
257
+ if (!hasPaceCoreImports || !line.includes('@jmruthers/pace-core')) {
258
+ violations.push({
259
+ type: 'custom-permission-utility',
260
+ file: filePath,
261
+ line: lineNumber,
262
+ message: `Custom permission utility function '${functionName}' detected`,
263
+ recommendation: `Use isPermitted API or useCan hook from '@jmruthers/pace-core/rbac' instead of creating custom permission utilities.`
264
+ });
265
+ }
266
+ }
267
+ }
268
+ lineNumber++;
269
+ }
270
+
271
+ return violations;
272
+ }
273
+
274
+ /**
275
+ * Detect permission bypass comments (e.g., TODO: add permission check, FIXME: security)
276
+ *
277
+ * @param code - Source code to analyze
278
+ * @param filePath - File path for reporting
279
+ * @returns Array of violations
280
+ */
281
+ export function detectPermissionBypassComments(code: string, filePath?: string): PatternViolation[] {
282
+ const violations: PatternViolation[] = [];
283
+ const lines = code.split('\n');
284
+
285
+ // Skip if this is in pace-core package itself
286
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
287
+ return violations;
288
+ }
289
+
290
+ // Pattern: TODO/FIXME comments about permissions or security
291
+ const bypassCommentPattern = /(?:TODO|FIXME|XXX|HACK|NOTE):\s*(?:add|missing|need|fix|implement|check)\s+(?:permission|rbac|access|security|authorization|auth)/gi;
292
+
293
+ let lineNumber = 1;
294
+
295
+ for (const line of lines) {
296
+ if (bypassCommentPattern.test(line)) {
297
+ violations.push({
298
+ type: 'permission-bypass-comment',
299
+ file: filePath,
300
+ line: lineNumber,
301
+ message: `Permission bypass comment detected: ${line.trim()}`,
302
+ recommendation: "Add proper permission checks using pace-core APIs (isPermitted, useCan, useResourcePermissions) from '@jmruthers/pace-core/rbac'."
303
+ });
304
+ }
305
+ lineNumber++;
306
+ }
307
+
308
+ return violations;
309
+ }
310
+
311
+ /**
312
+ * Detect permission wrapper functions that wrap pace-core hooks with additional logic
313
+ *
314
+ * @param code - Source code to analyze
315
+ * @param filePath - File path for reporting
316
+ * @returns Array of violations
317
+ */
318
+ export function detectPermissionWrapperFunctions(code: string, filePath?: string): PatternViolation[] {
319
+ const violations: PatternViolation[] = [];
320
+ const lines = code.split('\n');
321
+
322
+ // Skip if this is in pace-core package itself
323
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
324
+ return violations;
325
+ }
326
+
327
+ // Check if pace-core RBAC is imported
328
+ const hasPaceCoreRBACImport = /from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(code);
329
+
330
+ if (!hasPaceCoreRBACImport) {
331
+ return violations; // Can't have wrapper functions without pace-core imports
332
+ }
333
+
334
+ // Pattern: Functions that start with 'can' and use permission hooks/functions with additional logic
335
+ // Examples:
336
+ // - const canEdit = (postId: string) => { const hasPermission = canUpdate('journal'); const post = posts.find(...); return hasPermission && !!post; };
337
+ // - function canDeletePost(postId: string) { if (!canDelete('journal')) return false; return posts.find(...); }
338
+ const permissionHookNames = ['useCan', 'useResourcePermissions', 'usePermissions', 'useMultiplePermissions'];
339
+ const permissionFunctionNames = ['canCreate', 'canUpdate', 'canDelete', 'canRead', 'can'];
340
+
341
+ // Simple pattern matching for common wrapper function patterns
342
+ // This is a heuristic - it looks for functions that:
343
+ // 1. Start with 'can' or contain 'permission'/'access'
344
+ // 2. Use permission hooks or functions
345
+ // 3. Have additional logic (&&, ||, .find, .filter, etc.)
346
+
347
+ let lineNumber = 1;
348
+ let functionStart = -1;
349
+ let functionName = '';
350
+ let functionBody = '';
351
+ let braceDepth = 0;
352
+ let inFunction = false;
353
+
354
+ for (const line of lines) {
355
+ // Check for function start
356
+ const functionMatch = line.match(/(?:function|const|export\s+(?:function|const)?)\s+((?:can\w+|.*Permission.*|.*Access.*))\s*[=:]/);
357
+ if (functionMatch && !inFunction) {
358
+ const name = functionMatch[1];
359
+ // Check if name suggests a permission wrapper
360
+ if (name.toLowerCase().startsWith('can') ||
361
+ name.toLowerCase().includes('permission') ||
362
+ name.toLowerCase().includes('access')) {
363
+ inFunction = true;
364
+ functionStart = lineNumber;
365
+ functionName = name;
366
+ functionBody = line;
367
+ braceDepth = (line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
368
+ continue;
369
+ }
370
+ }
371
+
372
+ // If we're in a function, accumulate the body
373
+ if (inFunction) {
374
+ functionBody += '\n' + line;
375
+ braceDepth += (line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
376
+
377
+ // Check if function uses permission hooks/functions
378
+ const usesPermissionHooks = permissionHookNames.some(hook => functionBody.includes(hook));
379
+ const usesPermissionFunctions = permissionFunctionNames.some(fn => functionBody.includes(fn));
380
+
381
+ // Check if function has additional logic beyond permission checking
382
+ const hasAdditionalLogic = (
383
+ functionBody.includes('&&') ||
384
+ functionBody.includes('||') ||
385
+ functionBody.includes('if') ||
386
+ functionBody.includes('.find') ||
387
+ functionBody.includes('.filter') ||
388
+ functionBody.includes('.map') ||
389
+ (functionBody.match(/return/g) || []).length > 1
390
+ );
391
+
392
+ // Function ends when brace depth returns to 0 or we hit a semicolon for arrow functions
393
+ if (braceDepth <= 0 || (functionBody.includes('=>') && line.includes(';') && braceDepth === 0)) {
394
+ // Check if this is a wrapper function
395
+ if ((usesPermissionHooks || usesPermissionFunctions) && hasAdditionalLogic && functionBody.includes('(') && functionBody.includes(')')) {
396
+ violations.push({
397
+ type: 'permission-wrapper-function',
398
+ file: filePath,
399
+ line: functionStart,
400
+ message: `Permission wrapper function '${functionName}' detected`,
401
+ recommendation: "Use pace-core hooks (useCan, useResourcePermissions) directly in components instead of wrapping them with additional logic."
402
+ });
403
+ }
404
+
405
+ // Reset
406
+ inFunction = false;
407
+ functionBody = '';
408
+ functionName = '';
409
+ braceDepth = 0;
410
+ functionStart = -1;
411
+ }
412
+ }
413
+
414
+ lineNumber++;
415
+ }
416
+
417
+ return violations;
418
+ }
419
+
420
+ /**
421
+ * Detect resource permission string literals (e.g., useResourcePermissions('journal'))
422
+ *
423
+ * @param code - Source code to analyze
424
+ * @param filePath - File path for reporting
425
+ * @returns Array of violations
426
+ */
427
+ export function detectResourcePermissionStringLiterals(code: string, filePath?: string): PatternViolation[] {
428
+ const violations: PatternViolation[] = [];
429
+ const lines = code.split('\n');
430
+
431
+ // Skip if this is in pace-core package itself
432
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
433
+ return violations;
434
+ }
435
+
436
+ // Check if RESOURCE_NAMES constant is imported
437
+ const hasResourceNamesImport = /import.*RESOURCE_NAMES.*from/.test(code);
438
+
439
+ // Pattern: useResourcePermissions('string-literal') or useResourcePermissions("string-literal")
440
+ // Exclude if using RESOURCE_NAMES constant
441
+ const resourcePermissionPattern = /useResourcePermissions\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
442
+
443
+ let lineNumber = 1;
444
+
445
+ for (const line of lines) {
446
+ // Skip if line uses RESOURCE_NAMES constant
447
+ if (line.includes('RESOURCE_NAMES.')) {
448
+ lineNumber++;
449
+ continue;
450
+ }
451
+
452
+ let match;
453
+ while ((match = resourcePermissionPattern.exec(line)) !== null) {
454
+ const resourceName = match[1];
455
+ // Only flag if not using RESOURCE_NAMES constant
456
+ if (!hasResourceNamesImport || !line.includes('RESOURCE_NAMES')) {
457
+ violations.push({
458
+ type: 'resource-permission-string-literal',
459
+ file: filePath,
460
+ line: lineNumber,
461
+ message: `Resource permission string literal detected: useResourcePermissions('${resourceName}')`,
462
+ recommendation: "Use RESOURCE_NAMES constant object instead of string literals. Create RESOURCE_NAMES constant and use RESOURCE_NAMES.JOURNAL, RESOURCE_NAMES.RISKS, etc."
463
+ });
464
+ }
465
+ }
466
+ lineNumber++;
467
+ }
468
+
469
+ return violations;
470
+ }
471
+
472
+ /**
473
+ * Detect all RBAC pattern violations in code
474
+ *
475
+ * @param code - Source code to analyze
476
+ * @param filePath - File path for reporting
477
+ * @returns Pattern detection result
478
+ */
479
+ export function detectPatternViolations(code: string, filePath?: string): PatternDetectionResult {
480
+ const rpcViolations = detectDirectRpcCalls(code, filePath);
481
+ const tableViolations = detectDirectTableQueries(code, filePath);
482
+ const componentViolations = detectCustomAccessDeniedComponents(code, filePath);
483
+ const roleCheckViolations = detectHardcodedRoleChecks(code, filePath);
484
+ const permissionUtilityViolations = detectCustomPermissionUtilities(code, filePath);
485
+ const bypassCommentViolations = detectPermissionBypassComments(code, filePath);
486
+ const resourceLiteralViolations = detectResourcePermissionStringLiterals(code, filePath);
487
+ const wrapperFunctionViolations = detectPermissionWrapperFunctions(code, filePath);
488
+
489
+ const allViolations = [
490
+ ...rpcViolations,
491
+ ...tableViolations,
492
+ ...componentViolations,
493
+ ...roleCheckViolations,
494
+ ...permissionUtilityViolations,
495
+ ...bypassCommentViolations,
496
+ ...resourceLiteralViolations,
497
+ ...wrapperFunctionViolations
498
+ ];
499
+
500
+ return {
501
+ violations: allViolations,
502
+ isCompliant: allViolations.length === 0,
503
+ summary: {
504
+ directRpcCalls: rpcViolations.length,
505
+ directTableQueries: tableViolations.length,
506
+ bypassPatterns: 0, // This would require route analysis
507
+ customComponents: componentViolations.length,
508
+ hardcodedRoleChecks: roleCheckViolations.length,
509
+ customPermissionUtilities: permissionUtilityViolations.length,
510
+ uiOnlyAccessControl: 0, // This would require cross-file analysis
511
+ permissionBypassComments: bypassCommentViolations.length,
512
+ resourcePermissionStringLiterals: resourceLiteralViolations.length,
513
+ permissionWrapperFunctions: wrapperFunctionViolations.length
514
+ }
515
+ };
516
+ }
517
+
518
+ /**
519
+ * Log pattern violations to console
520
+ *
521
+ * @param result - Pattern detection result
522
+ */
523
+ export function logPatternViolations(result: PatternDetectionResult): void {
524
+ const logger = getRBACLogger();
525
+
526
+ if (result.isCompliant) {
527
+ logger.info('[RBAC Pattern Detection] No violations detected');
528
+ return;
529
+ }
530
+
531
+ logger.warn(`[RBAC Pattern Detection] Found ${result.violations.length} violation(s):`);
532
+
533
+ result.violations.forEach((violation, index) => {
534
+ const location = violation.file && violation.line
535
+ ? `${violation.file}:${violation.line}`
536
+ : violation.file || 'unknown location';
537
+
538
+ logger.warn(
539
+ ` ${index + 1}. [${violation.type}] ${violation.message}\n` +
540
+ ` Location: ${location}\n` +
541
+ ` Recommendation: ${violation.recommendation}`
542
+ );
543
+ });
544
+
545
+ logger.warn(`\nSummary: ${result.summary.directRpcCalls} direct RPC calls, ` +
546
+ `${result.summary.directTableQueries} direct table queries, ` +
547
+ `${result.summary.customComponents} custom components, ` +
548
+ `${result.summary.hardcodedRoleChecks} hardcoded role checks, ` +
549
+ `${result.summary.customPermissionUtilities} custom permission utilities, ` +
550
+ `${result.summary.permissionBypassComments} permission bypass comments, ` +
551
+ `${result.summary.resourcePermissionStringLiterals} resource permission string literals`);
552
+ }
553
+
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { validateRBACSetup, SetupIssue } from './setup-validator';
11
11
  import { getRBACLogger } from '../config';
12
+ import { detectPatternViolations, logPatternViolations, type PatternDetectionResult } from './pattern-detector';
12
13
 
13
14
  export interface RuntimeComplianceResult {
14
15
  setup: {
@@ -20,6 +21,7 @@ export interface RuntimeComplianceResult {
20
21
  available: boolean;
21
22
  message?: string;
22
23
  };
24
+ patternDetection?: PatternDetectionResult;
23
25
  }
24
26
 
25
27
  /**
@@ -75,3 +77,23 @@ export function validateAndWarn(): void {
75
77
  }
76
78
  }
77
79
 
80
+ /**
81
+ * Check pattern violations in source code
82
+ *
83
+ * This function analyzes source code for RBAC pattern violations.
84
+ * It can be used for static analysis or runtime code inspection.
85
+ *
86
+ * @param code - Source code to analyze
87
+ * @param filePath - Optional file path for better error reporting
88
+ * @returns Pattern detection result
89
+ */
90
+ export function checkPatternCompliance(code: string, filePath?: string): PatternDetectionResult {
91
+ const result = detectPatternViolations(code, filePath);
92
+
93
+ if (!result.isCompliant) {
94
+ logPatternViolations(result);
95
+ }
96
+
97
+ return result;
98
+ }
99
+