@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,35 @@
1
+ /**
2
+ * Main entry point for pace-core ESLint rules
3
+ * @package @jmruthers/pace-core
4
+ * @module ESLintRules
5
+ *
6
+ * This module exports all pace-core ESLint rules organized by standards.
7
+ * Rules are organized to match the 10-file standards structure.
8
+ *
9
+ * Reference: packages/core/docs/standards/README.md
10
+ */
11
+
12
+ const paceCoreComplianceRules = require('./rules/01-pace-core-compliance.cjs');
13
+ const codeQualityRules = require('./rules/04-code-quality.cjs');
14
+ const stylingRules = require('./rules/05-styling.cjs');
15
+ const securityRbacRules = require('./rules/06-security-rbac.cjs');
16
+ const apiTechStackRules = require('./rules/07-api-tech-stack.cjs');
17
+ const testingRules = require('./rules/08-testing.cjs');
18
+
19
+ module.exports = {
20
+ rules: {
21
+ // Standard 1: pace-core Compliance
22
+ ...paceCoreComplianceRules.rules,
23
+ // Standard 4: Code Quality
24
+ ...codeQualityRules.rules,
25
+ // Standard 5: Styling
26
+ ...stylingRules.rules,
27
+ // Standard 6: Security & RBAC
28
+ ...securityRbacRules.rules,
29
+ // Standard 7: API & Tech Stack
30
+ ...apiTechStackRules.rules,
31
+ // Standard 8: Testing
32
+ ...testingRules.rules,
33
+ }
34
+ };
35
+
@@ -1,159 +1,25 @@
1
1
  /**
2
- * ESLint Rules for pace-core Compliance Enforcement
2
+ * pace-core Compliance Rules (Standard 1)
3
3
  * @package @jmruthers/pace-core
4
- * @module ESLintRules/pace-core-compliance
5
- * @since 1.0.0
4
+ * @module ESLintRules/rules/pace-core-compliance
6
5
  *
7
- * This module provides ESLint rules to enforce pace-core usage patterns
8
- * and prevent consuming apps from creating local alternatives.
6
+ * Enforces pace-core usage patterns from Standard 1:
7
+ * - Use pace-core components instead of native HTML or custom implementations
8
+ * - Use pace-core hooks instead of custom hooks
9
+ * - Use pace-core utilities instead of custom utilities
10
+ * - Block restricted imports (libraries wrapped by pace-core)
11
+ * - Prefer pace-core Form component
12
+ * - Prevent component duplication
13
+ *
14
+ * Reference: packages/core/docs/standards/1-pace-core-compliance-standards.md
9
15
  */
10
16
 
11
- const fs = require('fs');
12
17
  const path = require('path');
13
-
14
- // Load manifest data
15
- let manifestData = null;
16
- try {
17
- const manifestPath = path.join(__dirname, '../../core-usage-manifest.json');
18
- if (fs.existsSync(manifestPath)) {
19
- manifestData = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
20
- }
21
- } catch (error) {
22
- // If manifest can't be loaded, rules will use hardcoded defaults
23
- console.warn('Warning: Could not load core-usage-manifest.json, using defaults');
24
- }
25
-
26
- // Get restricted imports from manifest or use defaults
27
- const getRestrictedImports = () => {
28
- if (manifestData && manifestData.restrictedImports) {
29
- return manifestData.restrictedImports;
30
- }
31
- // Fallback defaults
32
- return [
33
- { module: '@radix-ui/react-avatar', reason: 'Use Avatar component from pace-core instead' },
34
- { module: '@radix-ui/react-checkbox', reason: 'Use Checkbox component from pace-core instead' },
35
- { module: '@radix-ui/react-dialog', reason: 'Use Dialog component from pace-core instead' },
36
- { module: '@radix-ui/react-label', reason: 'Use Label component from pace-core instead' },
37
- { module: '@radix-ui/react-slot', reason: 'Use Button component from pace-core which handles slot composition' },
38
- { module: '@radix-ui/react-switch', reason: 'Use Switch component from pace-core instead' },
39
- { module: '@radix-ui/react-tabs', reason: 'Use Tabs component from pace-core instead' },
40
- { module: '@radix-ui/react-toast', reason: 'Use Toast component and useToast hook from pace-core instead' },
41
- { module: '@radix-ui/react-tooltip', reason: 'Use Tooltip component from pace-core instead' },
42
- { module: 'react-day-picker', reason: 'Use Calendar component from pace-core instead' },
43
- { module: '@tanstack/react-table', reason: 'Use DataTable component and related hooks from pace-core instead. DataTable wraps and standardizes table functionality' },
44
- { module: 'react-hook-form', reason: 'Use Form component and useZodForm hook from pace-core instead' },
45
- { module: 'zod', reason: 'Use validation utilities and schemas from pace-core instead. pace-core provides standardized validation helpers' }
46
- ];
47
- };
48
-
49
- // Get pace-core components from manifest or use defaults
50
- const getPaceCoreComponents = () => {
51
- if (manifestData && manifestData.components) {
52
- return manifestData.components;
53
- }
54
- return ['Button', 'Card', 'Dialog', 'Input', 'Form', 'Select', 'Alert', 'Badge', 'Checkbox', 'Switch', 'Textarea', 'Label', 'Table', 'DataTable', 'Toast', 'Tooltip', 'Tabs', 'Calendar', 'Avatar', 'Progress'];
55
- };
56
-
57
- // Get pace-core hooks from manifest or use defaults
58
- const getPaceCoreHooks = () => {
59
- if (manifestData && manifestData.hooks) {
60
- return manifestData.hooks;
61
- }
62
- return ['useToast', 'useDebounce', 'useUnifiedAuth', 'useEvents', 'useOrganisations', 'useFileReference', 'useStorage', 'useZodForm', 'useRBAC', 'usePermissions'];
63
- };
64
-
65
- // Get pace-core utils from manifest or use defaults
66
- const getPaceCoreUtils = () => {
67
- if (manifestData && manifestData.utils) {
68
- return manifestData.utils;
69
- }
70
- return ['formatDate', 'formatCurrency', 'formatNumber', 'formatTime', 'formatDateTime', 'cn', 'validateUserInput', 'sanitizeUserInput', 'hasPermission', 'getAppConfig'];
71
- };
18
+ const { getPaceCoreComponents, getPaceCoreHooks, getPaceCoreUtils, getRestrictedImports } = require('../utils/manifest-loader.cjs');
19
+ const { getPaceCoreAlternative, isPaceCoreSourceFile } = require('../utils/helpers.cjs');
72
20
 
73
21
  module.exports = {
74
22
  rules: {
75
- /**
76
- * Block direct imports of libraries wrapped by pace-core
77
- */
78
- 'no-restricted-imports': {
79
- meta: {
80
- type: 'problem',
81
- docs: {
82
- description: 'Disallow direct imports of libraries that pace-core wraps',
83
- category: 'Best Practices',
84
- recommended: true
85
- },
86
- fixable: 'code',
87
- hasSuggestions: true,
88
- messages: {
89
- restrictedImport: '{{message}} Import from {{alternative}} instead.',
90
- restrictedImportWithReason: '{{message}} {{reason}}'
91
- }
92
- },
93
- create(context) {
94
- const restrictedImports = getRestrictedImports();
95
- const restrictedModules = restrictedImports.map(imp => imp.module);
96
-
97
- // Also catch @radix-ui/* patterns
98
- const radixPattern = /^@radix-ui\//;
99
-
100
- return {
101
- ImportDeclaration(node) {
102
- const importSource = node.source.value;
103
-
104
- // Check exact matches
105
- const restricted = restrictedImports.find(imp => imp.module === importSource);
106
- if (restricted) {
107
- context.report({
108
- node: node.source,
109
- messageId: 'restrictedImportWithReason',
110
- data: {
111
- message: `Direct import of '${importSource}' is not allowed.`,
112
- reason: restricted.reason
113
- },
114
- suggest: [{
115
- desc: `Use pace-core alternative: ${restricted.reason}`,
116
- fix(fixer) {
117
- // Suggest importing from pace-core instead
118
- const paceCoreAlternative = getPaceCoreAlternative(importSource);
119
- if (paceCoreAlternative) {
120
- return fixer.replaceText(
121
- node.source,
122
- `'@jmruthers/pace-core${paceCoreAlternative}'`
123
- );
124
- }
125
- return null;
126
- }
127
- }]
128
- });
129
- return;
130
- }
131
-
132
- // Check @radix-ui/* pattern
133
- if (radixPattern.test(importSource) && !restrictedModules.includes(importSource)) {
134
- context.report({
135
- node: node.source,
136
- messageId: 'restrictedImport',
137
- data: {
138
- message: `Direct import of '${importSource}' is not allowed.`,
139
- alternative: '@jmruthers/pace-core'
140
- },
141
- suggest: [{
142
- desc: 'Use pace-core component instead',
143
- fix(fixer) {
144
- return fixer.replaceText(
145
- node.source,
146
- "'@jmruthers/pace-core'"
147
- );
148
- }
149
- }]
150
- });
151
- }
152
- }
153
- };
154
- }
155
- },
156
-
157
23
  /**
158
24
  * Prefer pace-core components over native HTML elements or custom implementations
159
25
  */
@@ -163,7 +29,8 @@ module.exports = {
163
29
  docs: {
164
30
  description: 'Suggest using pace-core components instead of native HTML elements',
165
31
  category: 'Best Practices',
166
- recommended: true
32
+ recommended: true,
33
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/1-pace-core-compliance-standards.md'
167
34
  },
168
35
  hasSuggestions: true,
169
36
  messages: {
@@ -175,6 +42,13 @@ module.exports = {
175
42
  }
176
43
  },
177
44
  create(context) {
45
+ const filename = context.getFilename();
46
+
47
+ // Exclude pace-core source files - these rules are for consuming apps
48
+ if (isPaceCoreSourceFile(filename)) {
49
+ return {};
50
+ }
51
+
178
52
  const paceCoreComponents = getPaceCoreComponents();
179
53
 
180
54
  return {
@@ -183,6 +57,16 @@ module.exports = {
183
57
 
184
58
  if (!elementName) return;
185
59
 
60
+ // Only flag lowercase native HTML elements, not capitalized components
61
+ // Native HTML: <button>, <input>, <label>, <textarea>
62
+ // Components: <Button>, <Input>, <Label>, <Textarea>
63
+ const isNativeHTMLElement = elementName === elementName.toLowerCase();
64
+
65
+ if (!isNativeHTMLElement) {
66
+ // This is a capitalized component, not a native HTML element
67
+ return;
68
+ }
69
+
186
70
  // Check for native HTML elements that have pace-core alternatives
187
71
  const nativeToPaceCore = {
188
72
  'button': 'Button',
@@ -191,8 +75,8 @@ module.exports = {
191
75
  'label': 'Label'
192
76
  };
193
77
 
194
- if (nativeToPaceCore[elementName.toLowerCase()]) {
195
- const paceCoreComponent = nativeToPaceCore[elementName.toLowerCase()];
78
+ if (nativeToPaceCore[elementName]) {
79
+ const paceCoreComponent = nativeToPaceCore[elementName];
196
80
  if (paceCoreComponents.includes(paceCoreComponent)) {
197
81
  context.report({
198
82
  node,
@@ -221,13 +105,21 @@ module.exports = {
221
105
  docs: {
222
106
  description: 'Suggest using pace-core hooks instead of custom implementations',
223
107
  category: 'Best Practices',
224
- recommended: true
108
+ recommended: true,
109
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/1-pace-core-compliance-standards.md'
225
110
  },
226
111
  messages: {
227
112
  preferPaceCoreHook: "Consider using '{{hook}}' from '@jmruthers/pace-core' instead of custom hook '{{customHook}}'"
228
113
  }
229
114
  },
230
115
  create(context) {
116
+ const filename = context.getFilename();
117
+
118
+ // Exclude pace-core source files - these rules are for consuming apps
119
+ if (isPaceCoreSourceFile(filename)) {
120
+ return {};
121
+ }
122
+
231
123
  const paceCoreHooks = getPaceCoreHooks();
232
124
  const hookPatterns = {
233
125
  'useToast': ['useToast', 'useNotification', 'useSnackbar'],
@@ -275,32 +167,67 @@ module.exports = {
275
167
  docs: {
276
168
  description: 'Suggest using pace-core utilities instead of custom implementations',
277
169
  category: 'Best Practices',
278
- recommended: true
170
+ recommended: true,
171
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/1-pace-core-compliance-standards.md'
279
172
  },
280
173
  messages: {
281
174
  preferPaceCoreUtil: "Consider using '{{util}}' from '@jmruthers/pace-core' instead of custom function '{{customUtil}}'"
282
175
  }
283
176
  },
284
177
  create(context) {
178
+ const filename = context.getFilename();
179
+
180
+ // Exclude pace-core source files - these rules are for consuming apps
181
+ if (isPaceCoreSourceFile(filename)) {
182
+ return {};
183
+ }
184
+
285
185
  const paceCoreUtils = getPaceCoreUtils();
286
186
  const utilPatterns = {
287
187
  'formatDate': ['formatDate', 'formatDateTime', 'dateFormat'],
288
188
  'formatCurrency': ['formatCurrency', 'formatMoney', 'currencyFormat'],
289
189
  'formatNumber': ['formatNumber', 'numberFormat'],
290
190
  'cn': ['cn', 'classNames', 'clsx', 'mergeClasses'],
291
- 'validateUserInput': ['validate', 'validateInput', 'validateUser'],
191
+ 'validateUserInput': ['validateInput', 'validateUser', 'validateUserInput'],
292
192
  'sanitizeUserInput': ['sanitize', 'sanitizeInput', 'sanitizeUser']
293
193
  };
294
194
 
195
+ // Patterns that should NOT trigger validateUserInput warnings
196
+ // These are for API/config validation, not user input validation
197
+ const excludePatterns = [
198
+ 'validateApi',
199
+ 'validateRequest',
200
+ 'validateConfig',
201
+ 'validateKey',
202
+ 'validateToken',
203
+ 'validateAuth',
204
+ 'validateRateLimit',
205
+ 'validatePermission',
206
+ 'validateAccess'
207
+ ];
208
+
295
209
  return {
296
210
  FunctionDeclaration(node) {
297
211
  const functionName = node.id?.name;
298
212
  if (!functionName) return;
299
213
 
214
+ // Skip if function name matches exclude patterns (API/config validation, not user input)
215
+ const lowerName = functionName.toLowerCase();
216
+ if (excludePatterns.some(pattern => lowerName.includes(pattern.toLowerCase()))) {
217
+ return;
218
+ }
219
+
300
220
  // Check if this looks like a util that pace-core provides
301
221
  for (const [paceCoreUtil, patterns] of Object.entries(utilPatterns)) {
302
222
  if (paceCoreUtils.includes(paceCoreUtil)) {
303
223
  for (const pattern of patterns) {
224
+ // Only match if the pattern is a significant part of the function name
225
+ // Don't match generic "validate" - it's too broad
226
+ if (paceCoreUtil === 'validateUserInput' && pattern === 'validate') {
227
+ // Skip generic "validate" - too many false positives
228
+ continue;
229
+ }
230
+
304
231
  if (functionName.toLowerCase().includes(pattern.toLowerCase())) {
305
232
  context.report({
306
233
  node: node.id,
@@ -329,16 +256,23 @@ module.exports = {
329
256
  docs: {
330
257
  description: 'Disallow local components with names matching pace-core components',
331
258
  category: 'Best Practices',
332
- recommended: true
259
+ recommended: true,
260
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/1-pace-core-compliance-standards.md'
333
261
  },
334
262
  messages: {
335
263
  duplicateComponent: "Component '{{componentName}}' conflicts with pace-core component. Use '@jmruthers/pace-core' instead of creating a local version."
336
264
  }
337
265
  },
338
266
  create(context) {
339
- const paceCoreComponents = getPaceCoreComponents();
340
267
  const filename = context.getFilename();
341
268
 
269
+ // Exclude pace-core source files - these rules are for consuming apps
270
+ if (isPaceCoreSourceFile(filename)) {
271
+ return {};
272
+ }
273
+
274
+ const paceCoreComponents = getPaceCoreComponents();
275
+
342
276
  // Only check component files (components/, src/components/, etc.)
343
277
  if (!filename.match(/(components|Components)\//)) {
344
278
  return {};
@@ -381,100 +315,186 @@ module.exports = {
381
315
  },
382
316
 
383
317
  /**
384
- * Disallow direct Supabase client creation - must use useSecureSupabase
318
+ * Block direct imports of libraries wrapped by pace-core
385
319
  */
386
- 'no-direct-supabase-client': {
320
+ 'no-restricted-imports': {
387
321
  meta: {
388
322
  type: 'problem',
389
323
  docs: {
390
- description: 'Disallow direct createClient calls from @supabase/supabase-js. Use useSecureSupabase() from pace-core instead to ensure organisation context and RLS policies are enforced.',
391
- category: 'Security',
392
- recommended: true
324
+ description: 'Disallow direct imports of libraries that pace-core wraps',
325
+ category: 'Best Practices',
326
+ recommended: true,
327
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/1-pace-core-compliance-standards.md'
393
328
  },
329
+ fixable: 'code',
330
+ hasSuggestions: true,
394
331
  messages: {
395
- directClientCreation: "Direct Supabase client creation detected. You MUST use useSecureSupabase() from '@jmruthers/pace-core/rbac' instead to ensure organisation context and RLS policies are enforced. This prevents cross-organisation data access.",
396
- directClientImport: "Direct import of createClient from @supabase/supabase-js is not allowed. Use useSecureSupabase() from '@jmruthers/pace-core/rbac' instead."
332
+ restrictedImport: '{{message}} Import from {{alternative}} instead.',
333
+ restrictedImportWithReason: '{{message}} {{reason}}'
334
+ }
335
+ },
336
+ create(context) {
337
+ const filename = context.getFilename();
338
+
339
+ // Exclude pace-core source files - these rules are for consuming apps
340
+ if (isPaceCoreSourceFile(filename)) {
341
+ return {};
342
+ }
343
+
344
+ const restrictedImports = getRestrictedImports();
345
+ const restrictedModules = restrictedImports.map(imp => imp.module);
346
+
347
+ // Also catch @radix-ui/* patterns
348
+ const radixPattern = /^@radix-ui\//;
349
+
350
+ return {
351
+ ImportDeclaration(node) {
352
+ const importSource = node.source.value;
353
+
354
+ // Check exact matches
355
+ const restricted = restrictedImports.find(imp => imp.module === importSource);
356
+ if (restricted) {
357
+ context.report({
358
+ node: node.source,
359
+ messageId: 'restrictedImportWithReason',
360
+ data: {
361
+ message: `Direct import of '${importSource}' is not allowed.`,
362
+ reason: restricted.reason
363
+ },
364
+ suggest: [{
365
+ desc: `Use pace-core alternative: ${restricted.reason}`,
366
+ fix(fixer) {
367
+ // Suggest importing from pace-core instead
368
+ const paceCoreAlternative = getPaceCoreAlternative(importSource);
369
+ if (paceCoreAlternative) {
370
+ return fixer.replaceText(
371
+ node.source,
372
+ `'@jmruthers/pace-core${paceCoreAlternative}'`
373
+ );
374
+ }
375
+ return null;
376
+ }
377
+ }]
378
+ });
379
+ return;
380
+ }
381
+
382
+ // Check @radix-ui/* pattern
383
+ if (radixPattern.test(importSource) && !restrictedModules.includes(importSource)) {
384
+ context.report({
385
+ node: node.source,
386
+ messageId: 'restrictedImport',
387
+ data: {
388
+ message: `Direct import of '${importSource}' is not allowed.`,
389
+ alternative: '@jmruthers/pace-core'
390
+ },
391
+ suggest: [{
392
+ desc: 'Use pace-core component instead',
393
+ fix(fixer) {
394
+ return fixer.replaceText(
395
+ node.source,
396
+ "'@jmruthers/pace-core'"
397
+ );
398
+ }
399
+ }]
400
+ });
401
+ }
402
+ }
403
+ };
404
+ }
405
+ },
406
+
407
+ /**
408
+ * Prefer pace-core Form component over plain form tags and direct react-hook-form usage
409
+ */
410
+ 'prefer-pace-core-form': {
411
+ meta: {
412
+ type: 'problem',
413
+ docs: {
414
+ description: 'Disallow plain <form> tags and direct react-hook-form imports. Use pace-core Form component instead.',
415
+ category: 'Best Practices',
416
+ recommended: true,
417
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/1-pace-core-compliance-standards.md'
418
+ },
419
+ messages: {
420
+ plainFormTag: "Plain <form> tag detected. Use pace-core Form component instead.",
421
+ reactHookFormImport: "Direct import from 'react-hook-form' detected: {{imports}}. Use pace-core Form component instead. useFormContext is allowed when Form is imported from pace-core."
397
422
  },
398
423
  hasSuggestions: true
399
424
  },
400
425
  create(context) {
401
- let hasSupabaseImport = false;
402
- let createClientImported = false;
403
426
  const filename = context.getFilename();
404
427
 
405
- // Allow createClient in specific config files (supabaseClient.ts/js, etc.)
406
- const isConfigFile = /(supabase|client)\.(ts|js|tsx|jsx)$/i.test(filename) &&
407
- (filename.includes('supabase') || filename.includes('client'));
428
+ // Exclude pace-core source files - these rules are for consuming apps
429
+ if (isPaceCoreSourceFile(filename)) {
430
+ return {};
431
+ }
432
+
433
+ let hasPaceCoreForm = false;
434
+ const problematicImports = ['useForm', 'FormProvider', 'Controller', 'useWatch', 'useFieldArray', 'useController'];
408
435
 
409
436
  return {
410
437
  ImportDeclaration(node) {
411
438
  const importSource = node.source.value;
412
439
 
413
- // Check for @supabase/supabase-js import
414
- if (importSource === '@supabase/supabase-js') {
415
- hasSupabaseImport = true;
416
-
417
- // Check if createClient is imported
418
- const hasCreateClient = node.specifiers.some(spec => {
440
+ // Check if Form is imported from pace-core
441
+ if (importSource === '@jmruthers/pace-core' ||
442
+ importSource === '@jmruthers/pace-core/components') {
443
+ const hasForm = node.specifiers.some(spec => {
419
444
  if (spec.type === 'ImportSpecifier') {
420
- return spec.imported.name === 'createClient';
421
- }
422
- if (spec.type === 'ImportNamespaceSpecifier') {
423
- return true; // import * as supabase
445
+ return spec.imported.name === 'Form';
424
446
  }
425
447
  return false;
426
448
  });
427
-
428
- if (hasCreateClient) {
429
- createClientImported = true;
430
-
431
- // Only report if not in a config file
432
- if (!isConfigFile) {
433
- context.report({
434
- node: node.source,
435
- messageId: 'directClientImport',
436
- suggest: [{
437
- desc: 'Replace with useSecureSupabase hook',
438
- fix(fixer) {
439
- // Remove the import
440
- return fixer.remove(node);
441
- }
442
- }]
443
- });
444
- }
449
+ if (hasForm) {
450
+ hasPaceCoreForm = true;
445
451
  }
446
452
  }
447
- },
448
-
449
- CallExpression(node) {
450
- // Check for createClient() calls
451
- if (node.callee.type === 'Identifier' && node.callee.name === 'createClient') {
452
- // Only report if not in a config file
453
- if (!isConfigFile) {
453
+
454
+ // Check for react-hook-form imports
455
+ if (importSource === 'react-hook-form') {
456
+ const importedItems = node.specifiers
457
+ .filter(spec => spec.type === 'ImportSpecifier')
458
+ .map(spec => spec.imported.name);
459
+
460
+ const foundProblematic = importedItems.filter(item => problematicImports.includes(item));
461
+
462
+ // Allow useFormContext when Form is imported from pace-core
463
+ const isAcceptable = hasPaceCoreForm &&
464
+ foundProblematic.length === 1 &&
465
+ foundProblematic[0] === 'useFormContext';
466
+
467
+ if (foundProblematic.length > 0 && !isAcceptable) {
454
468
  context.report({
455
- node,
456
- messageId: 'directClientCreation',
469
+ node: node.source,
470
+ messageId: 'reactHookFormImport',
471
+ data: {
472
+ imports: foundProblematic.join(', ')
473
+ },
457
474
  suggest: [{
458
- desc: 'Use useSecureSupabase() hook instead',
475
+ desc: 'Use pace-core Form component instead',
459
476
  fix(fixer) {
460
- // This is complex to auto-fix, so we'll just report
461
- return null;
477
+ return fixer.remove(node);
462
478
  }
463
479
  }]
464
480
  });
465
481
  }
466
482
  }
467
-
468
- // Check for supabase.createClient() or similar patterns
469
- if (node.callee.type === 'MemberExpression' &&
470
- node.callee.property?.name === 'createClient') {
471
- if (!isConfigFile) {
483
+ },
484
+
485
+ JSXOpeningElement(node) {
486
+ // Check for plain <form> tags (lowercase, not Form component)
487
+ if (node.name.type === 'JSXIdentifier') {
488
+ const elementName = node.name.name;
489
+ // Check if it's lowercase 'form' (not 'Form' component)
490
+ if (elementName === 'form') {
472
491
  context.report({
473
492
  node,
474
- messageId: 'directClientCreation',
493
+ messageId: 'plainFormTag',
475
494
  suggest: [{
476
- desc: 'Use useSecureSupabase() hook instead',
495
+ desc: 'Replace with pace-core Form component',
477
496
  fix(fixer) {
497
+ // This is complex to auto-fix, so we'll just report
478
498
  return null;
479
499
  }
480
500
  }]
@@ -487,24 +507,3 @@ module.exports = {
487
507
  }
488
508
  }
489
509
  };
490
-
491
- // Helper function to get pace-core alternative for restricted imports
492
- function getPaceCoreAlternative(importSource) {
493
- const alternatives = {
494
- '@radix-ui/react-avatar': '/components',
495
- '@radix-ui/react-checkbox': '/components',
496
- '@radix-ui/react-dialog': '/components',
497
- '@radix-ui/react-label': '/components',
498
- '@radix-ui/react-switch': '/components',
499
- '@radix-ui/react-tabs': '/components',
500
- '@radix-ui/react-toast': '/components',
501
- '@radix-ui/react-tooltip': '/components',
502
- 'react-day-picker': '/components',
503
- '@tanstack/react-table': '/components',
504
- 'react-hook-form': '/components',
505
- 'zod': '/utils'
506
- };
507
-
508
- return alternatives[importSource] || '';
509
- }
510
-