@jmruthers/pace-core 0.6.6 → 0.6.8

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 (292) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +227 -22
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +240 -0
  4. package/audit-tool/audits/03-architecture.cjs +224 -0
  5. package/audit-tool/audits/04-code-quality.cjs +149 -0
  6. package/audit-tool/audits/05-styling.cjs +224 -0
  7. package/audit-tool/audits/06-security-rbac.cjs +554 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +355 -0
  9. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  10. package/audit-tool/audits/09-operations.cjs +208 -0
  11. package/audit-tool/index.cjs +295 -0
  12. package/audit-tool/utils/code-utils.cjs +218 -0
  13. package/audit-tool/utils/file-utils.cjs +230 -0
  14. package/audit-tool/utils/report-utils.cjs +380 -0
  15. package/cursor-rules/00-standards-overview.mdc +156 -0
  16. package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
  17. package/cursor-rules/02-project-structure.mdc +37 -5
  18. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
  19. package/cursor-rules/04-code-quality.mdc +419 -0
  20. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
  21. package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
  22. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  23. package/cursor-rules/08-testing-documentation.mdc +324 -0
  24. package/cursor-rules/09-operations.mdc +365 -0
  25. package/dist/DataTable-6RMSCQJ6.js +15 -0
  26. package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  27. package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-CIGSujI2.d.ts} +40 -24
  28. package/dist/{UnifiedAuthProvider-ZT6TIGM7.js → UnifiedAuthProvider-7SNDOWYD.js} +2 -2
  29. package/dist/{api-Y4MQWOFW.js → api-7P7DI652.js} +1 -1
  30. package/dist/{chunk-MAGBIDNS.js → chunk-4DDCYDQ3.js} +8 -7
  31. package/dist/{chunk-BVP2BCJF.js → chunk-5W2A3DRC.js} +10 -9
  32. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  33. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  34. package/dist/{chunk-3O3WHILE.js → chunk-EF2UGZWY.js} +239 -63
  35. package/dist/{chunk-LAZMKTTF.js → chunk-EURB7QFZ.js} +341 -337
  36. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  37. package/dist/{chunk-7TYHROIV.js → chunk-GS5672WG.js} +55 -13
  38. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  39. package/dist/{chunk-ZFYPMX46.js → chunk-LX6U42O3.js} +1 -1
  40. package/dist/{chunk-FENMYN2U.js → chunk-MPBLMWVR.js} +3 -3
  41. package/dist/{chunk-ZS5VO5JB.js → chunk-NKHKXPI4.js} +408 -453
  42. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  43. package/dist/{chunk-4T7OBVTU.js → chunk-S6ZQKDY6.js} +1 -1
  44. package/dist/{chunk-FTCRZOG2.js → chunk-T5CVK4R3.js} +5 -5
  45. package/dist/{chunk-OHIK3MIO.js → chunk-Z2FNRKF3.js} +13 -13
  46. package/dist/components.d.ts +5 -4
  47. package/dist/components.js +29 -34
  48. package/dist/eslint-rules/index.cjs +22 -9
  49. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  50. package/dist/eslint-rules/rules/04-code-quality.cjs +346 -0
  51. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  52. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +34 -13
  53. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +385 -0
  54. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  55. package/dist/{functions-DHebl8-F.d.ts → functions-lBy5L2ry.d.ts} +1 -1
  56. package/dist/hooks.d.ts +5 -5
  57. package/dist/hooks.js +8 -8
  58. package/dist/index.d.ts +7 -7
  59. package/dist/index.js +21 -20
  60. package/dist/providers.js +2 -2
  61. package/dist/rbac/index.d.ts +1 -1
  62. package/dist/rbac/index.js +8 -8
  63. package/dist/theming/runtime.d.ts +61 -1
  64. package/dist/theming/runtime.js +1 -1
  65. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  66. package/dist/types.d.ts +2 -2
  67. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  68. package/dist/utils.d.ts +2 -2
  69. package/dist/utils.js +8 -8
  70. package/docs/README.md +1 -1
  71. package/docs/api/modules.md +106 -41
  72. package/docs/api-reference/components.md +18 -20
  73. package/docs/api-reference/hooks.md +80 -80
  74. package/docs/api-reference/types.md +1 -1
  75. package/docs/api-reference/utilities.md +1 -1
  76. package/docs/architecture/README.md +1 -1
  77. package/docs/core-concepts/events.md +3 -3
  78. package/docs/core-concepts/organisations.md +6 -6
  79. package/docs/core-concepts/permissions.md +6 -6
  80. package/docs/documentation-index.md +12 -18
  81. package/docs/getting-started/dependencies.md +23 -0
  82. package/docs/getting-started/documentation-index.md +1 -1
  83. package/docs/getting-started/examples/README.md +4 -4
  84. package/docs/getting-started/examples/full-featured-app.md +1 -1
  85. package/docs/getting-started/faq.md +2 -2
  86. package/docs/getting-started/quick-reference.md +4 -4
  87. package/docs/implementation-guides/app-layout.md +1 -1
  88. package/docs/implementation-guides/authentication.md +15 -15
  89. package/docs/implementation-guides/component-styling.md +1 -1
  90. package/docs/implementation-guides/data-tables.md +127 -34
  91. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  92. package/docs/implementation-guides/dynamic-colors.md +3 -3
  93. package/docs/implementation-guides/file-upload-storage.md +2 -2
  94. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  95. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  96. package/docs/implementation-guides/large-datasets.md +3 -2
  97. package/docs/implementation-guides/organisation-security.md +2 -2
  98. package/docs/implementation-guides/performance.md +2 -2
  99. package/docs/implementation-guides/permission-enforcement.md +1 -1
  100. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  101. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  102. package/docs/rbac/README.md +5 -5
  103. package/docs/rbac/advanced-patterns.md +6 -6
  104. package/docs/rbac/api-reference.md +20 -20
  105. package/docs/rbac/event-based-apps.md +3 -3
  106. package/docs/rbac/examples.md +41 -41
  107. package/docs/rbac/getting-started.md +37 -37
  108. package/docs/rbac/performance.md +1 -1
  109. package/docs/rbac/quick-start.md +52 -52
  110. package/docs/rbac/secure-client-protection.md +1 -1
  111. package/docs/rbac/troubleshooting.md +1 -1
  112. package/docs/security/README.md +5 -5
  113. package/docs/standards/0-standards-overview.md +220 -0
  114. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +241 -185
  115. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  116. package/docs/standards/3-architecture-standards.md +606 -0
  117. package/docs/standards/4-code-quality-standards.md +728 -0
  118. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  119. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  120. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  121. package/docs/standards/8-testing-documentation-standards.md +401 -0
  122. package/docs/standards/9-operations-standards.md +1102 -0
  123. package/docs/standards/README.md +203 -104
  124. package/docs/troubleshooting/README.md +4 -4
  125. package/docs/troubleshooting/common-issues.md +2 -2
  126. package/docs/troubleshooting/debugging.md +9 -9
  127. package/docs/troubleshooting/migration.md +4 -4
  128. package/eslint-config-pace-core.cjs +50 -20
  129. package/package.json +50 -19
  130. package/scripts/eslint-audit.cjs +123 -0
  131. package/scripts/install-cursor-rules.cjs +11 -243
  132. package/scripts/install-eslint-config.cjs +349 -0
  133. package/scripts/validate-dependencies.cjs +248 -0
  134. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  135. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  136. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +30 -18
  137. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  138. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  139. package/src/__tests__/templates/accessibility.test.template.tsx +10 -9
  140. package/src/__tests__/templates/component.test.template.tsx +18 -15
  141. package/src/components/AddressField/AddressField.tsx +26 -1
  142. package/src/components/Alert/Alert.test.tsx +86 -22
  143. package/src/components/Alert/Alert.tsx +19 -11
  144. package/src/components/Badge/Badge.tsx +1 -1
  145. package/src/components/Calendar/Calendar.tsx +201 -47
  146. package/src/components/Checkbox/Checkbox.test.tsx +2 -1
  147. package/src/components/ContextSelector/ContextSelector.tsx +108 -126
  148. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  149. package/src/components/DataTable/DataTable.tsx +1 -19
  150. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +6 -2
  151. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +21 -6
  152. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +3 -2
  153. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  154. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  155. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  156. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  157. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  158. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  159. package/src/components/DataTable/components/EmptyState.tsx +11 -10
  160. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  161. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  162. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  163. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  164. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  165. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  166. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  167. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  168. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  169. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +45 -27
  170. package/src/components/DataTable/components/index.ts +2 -1
  171. package/src/components/DataTable/types.ts +0 -18
  172. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  173. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
  174. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  175. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  176. package/src/components/Dialog/Dialog.test.tsx +1 -0
  177. package/src/components/Dialog/Dialog.tsx +25 -8
  178. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  179. package/src/components/FileUpload/FileUpload.test.tsx +45 -16
  180. package/src/components/FileUpload/FileUpload.tsx +141 -130
  181. package/src/components/NavigationMenu/NavigationMenu.test.tsx +48 -12
  182. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +9 -9
  183. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +30 -30
  184. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -4
  185. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +7 -1
  186. package/src/components/Progress/Progress.tsx +2 -4
  187. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  188. package/src/components/Select/Select.tsx +86 -77
  189. package/src/components/Select/types.ts +3 -0
  190. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  191. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  192. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +8 -5
  193. package/src/hooks/__tests__/useFileUrl.unit.test.ts +4 -0
  194. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +99 -99
  195. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +45 -8
  196. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +22 -2
  197. package/src/hooks/public/usePublicEvent.ts +5 -5
  198. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  199. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  200. package/src/hooks/public/usePublicRouteParams.ts +13 -9
  201. package/src/hooks/useAddressAutocomplete.test.ts +18 -18
  202. package/src/hooks/useAppConfig.ts +2 -2
  203. package/src/hooks/useEventTheme.test.ts +7 -7
  204. package/src/hooks/useEventTheme.ts +2 -1
  205. package/src/hooks/useFileDisplay.ts +2 -2
  206. package/src/hooks/useFileUrl.ts +52 -8
  207. package/src/hooks/useOrganisationSecurity.test.ts +2 -1
  208. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  209. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  210. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  211. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  212. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  213. package/src/providers/__tests__/ProviderLifecycle.test.tsx +38 -38
  214. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  215. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  216. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  217. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +15 -6
  218. package/src/rbac/__tests__/rbac-functions.test.ts +3 -3
  219. package/src/rbac/api.test.ts +104 -0
  220. package/src/rbac/engine.ts +1 -1
  221. package/src/rbac/hooks/useCan.test.ts +2 -2
  222. package/src/rbac/secureClient.ts +1 -1
  223. package/src/rbac/types/functions.ts +1 -1
  224. package/src/styles/core.css +7 -0
  225. package/src/theming/__tests__/parseEventColours.test.ts +118 -3
  226. package/src/theming/parseEventColours.ts +77 -11
  227. package/src/types/supabase.ts +2 -3
  228. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +9 -9
  229. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  230. package/src/utils/file-reference/__tests__/file-reference.test.ts +4 -0
  231. package/src/utils/formatting/formatDate.test.ts +3 -2
  232. package/src/utils/formatting/formatDateTime.test.ts +2 -2
  233. package/src/utils/google-places/googlePlacesUtils.test.ts +36 -24
  234. package/src/utils/storage/README.md +1 -1
  235. package/src/utils/storage/__tests__/helpers.unit.test.ts +19 -12
  236. package/src/utils/storage/helpers.test.ts +69 -3
  237. package/cursor-rules/01-standards-compliance.mdc +0 -285
  238. package/cursor-rules/04-testing-standards.mdc +0 -270
  239. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  240. package/cursor-rules/06-code-quality.mdc +0 -311
  241. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  242. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  243. package/cursor-rules/11-performance-optimization.mdc +0 -169
  244. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  245. package/dist/DataTable-LRJL4IRV.js +0 -15
  246. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  247. package/dist/eslint-rules/rules/components.cjs +0 -113
  248. package/dist/eslint-rules/rules/imports.cjs +0 -102
  249. package/docs/best-practices/README.md +0 -472
  250. package/docs/best-practices/accessibility.md +0 -604
  251. package/docs/best-practices/common-patterns.md +0 -516
  252. package/docs/best-practices/deployment.md +0 -1103
  253. package/docs/best-practices/performance.md +0 -1328
  254. package/docs/best-practices/security.md +0 -940
  255. package/docs/best-practices/testing.md +0 -1034
  256. package/docs/rbac/compliance/compliance-guide.md +0 -544
  257. package/docs/standards/01-standards-compliance.md +0 -188
  258. package/docs/standards/03-solid-principles.md +0 -39
  259. package/docs/standards/04-testing-standards.md +0 -36
  260. package/docs/standards/05-bug-reports-and-features.md +0 -27
  261. package/docs/standards/06-code-quality.md +0 -34
  262. package/docs/standards/07-tech-stack-compliance.md +0 -30
  263. package/docs/standards/10-error-handling-patterns.md +0 -401
  264. package/docs/standards/11-performance-optimization.md +0 -348
  265. package/docs/standards/12-ci-cd-integration.md +0 -370
  266. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  267. package/scripts/audit/audit-compliance.cjs +0 -1295
  268. package/scripts/audit/audit-components.cjs +0 -260
  269. package/scripts/audit/audit-rbac.cjs +0 -954
  270. package/scripts/audit/audit-standards.cjs +0 -1268
  271. package/scripts/audit/index.cjs +0 -1927
  272. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  273. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  274. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  275. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  276. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  277. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  278. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  279. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  280. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  281. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  282. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  283. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  284. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  285. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  286. package/src/components/DataTable/utils/debugTools.ts +0 -514
  287. package/src/eslint-rules/index.cjs +0 -22
  288. package/src/eslint-rules/rules/components.cjs +0 -113
  289. package/src/eslint-rules/rules/imports.cjs +0 -102
  290. package/src/eslint-rules/rules/rbac.cjs +0 -790
  291. package/src/eslint-rules/utils/helpers.cjs +0 -42
  292. package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Code Quality Rules (Standard 4)
3
+ * @package @jmruthers/pace-core
4
+ * @module ESLintRules/rules/code-quality
5
+ *
6
+ * Enforces code quality patterns from Standard 4:
7
+ * - Naming conventions (hooks: use*, providers: *Provider)
8
+ * - TypeScript best practices
9
+ *
10
+ * Reference: packages/core/docs/standards/4-code-quality-standards.md
11
+ */
12
+
13
+ const { isPaceCoreSourceFile } = require('../utils/helpers.cjs');
14
+
15
+ module.exports = {
16
+ rules: {
17
+ /**
18
+ * Enforce naming conventions for hooks and providers
19
+ */
20
+ 'naming-convention': {
21
+ meta: {
22
+ type: 'suggestion',
23
+ docs: {
24
+ description: 'Enforce naming conventions: hooks must start with "use", providers must end with "Provider"',
25
+ category: 'Best Practices',
26
+ recommended: true,
27
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/4-code-quality-standards.md'
28
+ },
29
+ messages: {
30
+ hookNaming: "Hook '{{name}}' must start with 'use' (e.g., useEventData, useUserProfile)",
31
+ providerNaming: "Provider '{{name}}' must end with 'Provider' (e.g., AuthProvider, OrganisationProvider)"
32
+ },
33
+ hasSuggestions: true
34
+ },
35
+ create(context) {
36
+ const filename = context.getFilename();
37
+
38
+ // Exclude pace-core source files - these rules are for consuming apps
39
+ if (isPaceCoreSourceFile(filename)) {
40
+ return {};
41
+ }
42
+
43
+ return {
44
+ FunctionDeclaration(node) {
45
+ if (!node.id) return;
46
+
47
+ const functionName = node.id.name;
48
+ if (!functionName) return;
49
+
50
+ const sourceCode = context.getSourceCode();
51
+ const functionText = sourceCode.getText(node.body);
52
+
53
+ // Check if function returns JSX (is a component)
54
+ const returnsJSX = functionText.includes('return') && (functionText.includes('<') || functionText.includes('createElement'));
55
+
56
+ // Check for hooks (functions that use React hooks but are NOT components)
57
+ const usesReactHooks = /use(State|Effect|Callback|Memo|Ref|Context|Reducer|LayoutEffect|ImperativeHandle|DebugValue)/.test(functionText);
58
+
59
+ // Only flag as hook if:
60
+ // 1. It uses React hooks AND doesn't return JSX (not a component)
61
+ // 2. OR it starts with 'use' but doesn't return JSX (likely a hook)
62
+ // Components that use hooks are NOT hooks themselves
63
+ if (usesReactHooks && !returnsJSX) {
64
+ if (!functionName.startsWith('use')) {
65
+ context.report({
66
+ node: node.id,
67
+ messageId: 'hookNaming',
68
+ data: { name: functionName },
69
+ suggest: [{
70
+ desc: `Rename to use${functionName.charAt(0).toUpperCase() + functionName.slice(1)}`,
71
+ fix(fixer) {
72
+ return null; // Complex rename
73
+ }
74
+ }]
75
+ });
76
+ }
77
+ }
78
+
79
+ // Check for providers (components that CREATE a Context.Provider, not just use/wrap one)
80
+ // A provider component must:
81
+ // 1. Use createContext (creates a context)
82
+ // 2. Return <ContextName.Provider> (provides the context)
83
+ // We don't flag components that just wrap other providers (like AppProviders wrapping QueryClientProvider)
84
+ const createsContext = /createContext\s*\(/.test(functionText);
85
+ const returnsContextProvider = /<[A-Z][a-zA-Z0-9]*\.Provider/.test(functionText) ||
86
+ /\.Provider\s*>/.test(functionText);
87
+
88
+ // Only flag if it actually creates a context AND returns a Context.Provider
89
+ // AND doesn't already end with 'Provider'
90
+ if (returnsJSX && createsContext && returnsContextProvider && !functionName.endsWith('Provider')) {
91
+ context.report({
92
+ node: node.id,
93
+ messageId: 'providerNaming',
94
+ data: { name: functionName },
95
+ suggest: [{
96
+ desc: `Rename to ${functionName}Provider`,
97
+ fix(fixer) {
98
+ return null; // Complex rename
99
+ }
100
+ }]
101
+ });
102
+ }
103
+ },
104
+
105
+ VariableDeclarator(node) {
106
+ if (!node.id || node.id.type !== 'Identifier') return;
107
+ if (!node.init) return;
108
+
109
+ const varName = node.id.name;
110
+ if (!varName) return;
111
+
112
+ // Check for hook variables (arrow functions that use React hooks)
113
+ if (node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression') {
114
+ const sourceCode = context.getSourceCode();
115
+ const functionText = sourceCode.getText(node.init);
116
+
117
+ // Check if it returns JSX (is a component)
118
+ const returnsJSX = functionText.includes('return') && (functionText.includes('<') || functionText.includes('createElement'));
119
+ const usesReactHooks = /use(State|Effect|Callback|Memo|Ref|Context|Reducer|LayoutEffect|ImperativeHandle|DebugValue)/.test(functionText);
120
+
121
+ // Only flag as hook if it uses React hooks but doesn't return JSX (not a component)
122
+ // Components that use hooks are NOT hooks themselves
123
+ if (usesReactHooks && !returnsJSX && !varName.startsWith('use')) {
124
+ context.report({
125
+ node: node.id,
126
+ messageId: 'hookNaming',
127
+ data: { name: varName },
128
+ suggest: [{
129
+ desc: `Rename to use${varName.charAt(0).toUpperCase() + varName.slice(1)}`,
130
+ fix(fixer) {
131
+ return null; // Complex rename
132
+ }
133
+ }]
134
+ });
135
+ }
136
+ }
137
+ }
138
+ };
139
+ }
140
+ },
141
+
142
+ /**
143
+ * Enforce component naming: PascalCase
144
+ */
145
+ 'component-naming': {
146
+ meta: {
147
+ type: 'suggestion',
148
+ docs: {
149
+ description: 'Enforce component naming: components must use PascalCase',
150
+ category: 'Best Practices',
151
+ recommended: true,
152
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/4-code-quality-standards.md'
153
+ },
154
+ messages: {
155
+ componentNaming: "Component '{{name}}' must use PascalCase (e.g., EventCard, UserProfile)"
156
+ },
157
+ hasSuggestions: true
158
+ },
159
+ create(context) {
160
+ const filename = context.getFilename();
161
+
162
+ // Exclude pace-core source files - these rules are for consuming apps
163
+ if (isPaceCoreSourceFile(filename)) {
164
+ return {};
165
+ }
166
+
167
+ const isComponentFile = filename.match(/(components|Components)\//);
168
+ if (!isComponentFile) {
169
+ return {};
170
+ }
171
+
172
+ return {
173
+ FunctionDeclaration(node) {
174
+ if (!node.id) return;
175
+
176
+ const functionName = node.id.name;
177
+ if (!functionName) return;
178
+
179
+ // Skip event handlers and utility functions (these are not components)
180
+ const isEventHandler = functionName.startsWith('handle') ||
181
+ functionName.startsWith('on') ||
182
+ functionName.startsWith('get') ||
183
+ functionName.startsWith('set') ||
184
+ functionName.startsWith('load') ||
185
+ functionName.startsWith('read') ||
186
+ functionName.startsWith('create') ||
187
+ functionName.startsWith('update') ||
188
+ functionName.startsWith('delete');
189
+
190
+ if (isEventHandler) return;
191
+
192
+ // Only check exported components (not internal functions)
193
+ const isExported = node.parent?.type === 'ExportNamedDeclaration' ||
194
+ node.parent?.type === 'ExportDefaultDeclaration' ||
195
+ (node.parent?.parent?.type === 'ExportNamedDeclaration');
196
+
197
+ if (!isExported) return;
198
+
199
+ // Check if it's a component (returns JSX)
200
+ const sourceCode = context.getSourceCode();
201
+ const functionText = sourceCode.getText(node.body);
202
+ const returnsJSX = functionText.includes('return') && (functionText.includes('<') || functionText.includes('createElement'));
203
+
204
+ if (returnsJSX) {
205
+ // Check if it's PascalCase
206
+ const isPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(functionName);
207
+ if (!isPascalCase) {
208
+ context.report({
209
+ node: node.id,
210
+ messageId: 'componentNaming',
211
+ data: { name: functionName },
212
+ suggest: [{
213
+ desc: `Rename to ${functionName.charAt(0).toUpperCase() + functionName.slice(1).replace(/[^a-zA-Z0-9]/g, '')}`,
214
+ fix(fixer) {
215
+ return null; // Complex rename
216
+ }
217
+ }]
218
+ });
219
+ }
220
+ }
221
+ },
222
+
223
+ VariableDeclarator(node) {
224
+ if (!node.id || node.id.type !== 'Identifier') return;
225
+ if (!node.init) return;
226
+
227
+ const varName = node.id.name;
228
+ if (!varName) return;
229
+
230
+ // Skip event handlers and utility functions (these are not components)
231
+ const isEventHandler = varName.startsWith('handle') ||
232
+ varName.startsWith('on') ||
233
+ varName.startsWith('get') ||
234
+ varName.startsWith('set') ||
235
+ varName.startsWith('load') ||
236
+ varName.startsWith('read') ||
237
+ varName.startsWith('create') ||
238
+ varName.startsWith('update') ||
239
+ varName.startsWith('delete');
240
+
241
+ if (isEventHandler) return;
242
+
243
+ // Only check exported components (not internal functions)
244
+ const isExported = node.parent?.parent?.type === 'ExportNamedDeclaration' ||
245
+ node.parent?.parent?.type === 'ExportDefaultDeclaration';
246
+
247
+ if (!isExported) return;
248
+
249
+ // Check for component variables (arrow functions that return JSX)
250
+ if (node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression') {
251
+ const sourceCode = context.getSourceCode();
252
+ const functionText = sourceCode.getText(node.init);
253
+ const returnsJSX = functionText.includes('return') && (functionText.includes('<') || functionText.includes('createElement'));
254
+
255
+ if (returnsJSX) {
256
+ const isPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(varName);
257
+ if (!isPascalCase) {
258
+ context.report({
259
+ node: node.id,
260
+ messageId: 'componentNaming',
261
+ data: { name: varName },
262
+ suggest: [{
263
+ desc: `Rename to ${varName.charAt(0).toUpperCase() + varName.slice(1).replace(/[^a-zA-Z0-9]/g, '')}`,
264
+ fix(fixer) {
265
+ return null; // Complex rename
266
+ }
267
+ }]
268
+ });
269
+ }
270
+ }
271
+ }
272
+ }
273
+ };
274
+ }
275
+ },
276
+
277
+ /**
278
+ * Enforce type/interface naming: PascalCase
279
+ */
280
+ 'type-naming': {
281
+ meta: {
282
+ type: 'suggestion',
283
+ docs: {
284
+ description: 'Enforce type/interface naming: types and interfaces must use PascalCase',
285
+ category: 'Best Practices',
286
+ recommended: true,
287
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/4-code-quality-standards.md'
288
+ },
289
+ messages: {
290
+ typeNaming: "Type/interface '{{name}}' must use PascalCase (e.g., EventStatus, UserProfile)"
291
+ },
292
+ hasSuggestions: true
293
+ },
294
+ create(context) {
295
+ const filename = context.getFilename();
296
+
297
+ // Exclude pace-core source files - these rules are for consuming apps
298
+ if (isPaceCoreSourceFile(filename)) {
299
+ return {};
300
+ }
301
+
302
+ return {
303
+ TSInterfaceDeclaration(node) {
304
+ const interfaceName = node.id.name;
305
+ if (!interfaceName) return;
306
+
307
+ const isPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(interfaceName);
308
+ if (!isPascalCase) {
309
+ context.report({
310
+ node: node.id,
311
+ messageId: 'typeNaming',
312
+ data: { name: interfaceName },
313
+ suggest: [{
314
+ desc: `Rename to ${interfaceName.charAt(0).toUpperCase() + interfaceName.slice(1).replace(/[^a-zA-Z0-9]/g, '')}`,
315
+ fix(fixer) {
316
+ return null; // Complex rename
317
+ }
318
+ }]
319
+ });
320
+ }
321
+ },
322
+
323
+ TSTypeAliasDeclaration(node) {
324
+ const typeName = node.id.name;
325
+ if (!typeName) return;
326
+
327
+ const isPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(typeName);
328
+ if (!isPascalCase) {
329
+ context.report({
330
+ node: node.id,
331
+ messageId: 'typeNaming',
332
+ data: { name: typeName },
333
+ suggest: [{
334
+ desc: `Rename to ${typeName.charAt(0).toUpperCase() + typeName.slice(1).replace(/[^a-zA-Z0-9]/g, '')}`,
335
+ fix(fixer) {
336
+ return null; // Complex rename
337
+ }
338
+ }]
339
+ });
340
+ }
341
+ }
342
+ };
343
+ }
344
+ }
345
+ }
346
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Styling Rules (Standard 5)
3
+ * @package @jmruthers/pace-core
4
+ * @module ESLintRules/rules/styling
5
+ *
6
+ * Enforces styling patterns from Standard 5:
7
+ * - No inline styles (use pace-core components or Tailwind classes)
8
+ *
9
+ * Reference: packages/core/docs/standards/5-styling-standards.md
10
+ */
11
+
12
+ const { isPaceCoreSourceFile } = require('../utils/helpers.cjs');
13
+
14
+ module.exports = {
15
+ rules: {
16
+ /**
17
+ * Disallow inline styles
18
+ */
19
+ 'no-inline-styles': {
20
+ meta: {
21
+ type: 'problem',
22
+ docs: {
23
+ description: 'Disallow inline styles. Use pace-core components or Tailwind classes instead.',
24
+ category: 'Best Practices',
25
+ recommended: true,
26
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/5-styling-standards.md'
27
+ },
28
+ messages: {
29
+ inlineStyle: 'Inline style detected. Use pace-core components or Tailwind classes instead.'
30
+ },
31
+ hasSuggestions: true
32
+ },
33
+ create(context) {
34
+ const filename = context.getFilename();
35
+
36
+ // Exclude pace-core source files - these rules are for consuming apps
37
+ if (isPaceCoreSourceFile(filename)) {
38
+ return {};
39
+ }
40
+
41
+ return {
42
+ JSXAttribute(node) {
43
+ if (node.name && node.name.name === 'style') {
44
+ context.report({
45
+ node,
46
+ messageId: 'inlineStyle',
47
+ suggest: [{
48
+ desc: 'Remove inline style and use pace-core component styling or Tailwind utility classes',
49
+ fix(fixer) {
50
+ // Remove the style attribute
51
+ return fixer.remove(node);
52
+ }
53
+ }]
54
+ });
55
+ }
56
+ }
57
+ };
58
+ }
59
+ }
60
+ }
61
+ };
@@ -1,7 +1,15 @@
1
1
  /**
2
- * RBAC-specific rules
2
+ * Security & RBAC Rules (Standard 6)
3
3
  * @package @jmruthers/pace-core
4
- * @module ESLintRules/rules/rbac
4
+ * @module ESLintRules/rules/security-rbac
5
+ *
6
+ * Enforces security and RBAC patterns from Standard 6:
7
+ * - Use secure Supabase client (useSecureSupabase)
8
+ * - Use RESOURCE_NAMES constants
9
+ * - No wrapper components/functions around RBAC hooks
10
+ * - Proper permission loading state handling
11
+ *
12
+ * Reference: packages/core/docs/standards/6-security-rbac-standards.md
5
13
  */
6
14
 
7
15
  const { isPaceCoreSourceFile } = require('../utils/helpers.cjs');
@@ -17,7 +25,8 @@ module.exports = {
17
25
  docs: {
18
26
  description: 'Disallow direct createClient calls from @supabase/supabase-js. Use useSecureSupabase() from pace-core instead to ensure organisation context and RLS policies are enforced.',
19
27
  category: 'Security',
20
- recommended: true
28
+ recommended: true,
29
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
21
30
  },
22
31
  messages: {
23
32
  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.",
@@ -33,6 +42,11 @@ module.exports = {
33
42
  return {};
34
43
  }
35
44
 
45
+ // Exclude Edge Functions - they run in Deno and cannot use React hooks
46
+ // Edge Functions must use createClient() directly with service role keys
47
+ const isEdgeFunction = filename.includes('supabase/functions/') ||
48
+ filename.includes('supabase\\functions\\');
49
+
36
50
  // Allow createClient in specific config files (supabaseClient.ts/js, etc.)
37
51
  const isConfigFile = /(supabase|client)\.(ts|js|tsx|jsx)$/i.test(filename) &&
38
52
  (filename.includes('supabase') || filename.includes('client'));
@@ -54,7 +68,7 @@ module.exports = {
54
68
  return false;
55
69
  });
56
70
 
57
- if (hasCreateClient && !isConfigFile) {
71
+ if (hasCreateClient && !isConfigFile && !isEdgeFunction) {
58
72
  context.report({
59
73
  node: node.source,
60
74
  messageId: 'directClientImport',
@@ -72,7 +86,7 @@ module.exports = {
72
86
  CallExpression(node) {
73
87
  // Check for createClient() calls
74
88
  if (node.callee.type === 'Identifier' && node.callee.name === 'createClient') {
75
- if (!isConfigFile) {
89
+ if (!isConfigFile && !isEdgeFunction) {
76
90
  context.report({
77
91
  node,
78
92
  messageId: 'directClientCreation',
@@ -89,7 +103,7 @@ module.exports = {
89
103
  // Check for supabase.createClient() or similar patterns
90
104
  if (node.callee.type === 'MemberExpression' &&
91
105
  node.callee.property?.name === 'createClient') {
92
- if (!isConfigFile) {
106
+ if (!isConfigFile && !isEdgeFunction) {
93
107
  context.report({
94
108
  node,
95
109
  messageId: 'directClientCreation',
@@ -116,7 +130,8 @@ module.exports = {
116
130
  docs: {
117
131
  description: 'Require isLoading extraction from useResourcePermissions and check it before permission calls in mutations.',
118
132
  category: 'Security',
119
- recommended: true
133
+ recommended: true,
134
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
120
135
  },
121
136
  messages: {
122
137
  missingIsLoading: "useResourcePermissions is used but 'isLoading' is not extracted. Permission checks may fail if scope resolution is still in progress.",
@@ -237,7 +252,8 @@ module.exports = {
237
252
  docs: {
238
253
  description: 'Disallow direct calls to RBAC RPC functions. Use pace-core RBAC hooks instead.',
239
254
  category: 'Security',
240
- recommended: true
255
+ recommended: true,
256
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
241
257
  },
242
258
  messages: {
243
259
  directRbacRpc: "Direct RPC call to '{{rpcName}}' detected. Use pace-core RBAC hooks from '@jmruthers/pace-core/rbac' instead."
@@ -296,7 +312,8 @@ module.exports = {
296
312
  docs: {
297
313
  description: 'Disallow direct queries to RBAC tables. Use pace-core RBAC API functions or useSecureSupabase instead.',
298
314
  category: 'Security',
299
- recommended: true
315
+ recommended: true,
316
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
300
317
  },
301
318
  messages: {
302
319
  directRbacTable: "Direct query to RBAC table '{{tableName}}' detected. Use pace-core RBAC API functions from '@jmruthers/pace-core/rbac' or useSecureSupabase hook instead."
@@ -402,7 +419,8 @@ module.exports = {
402
419
  docs: {
403
420
  description: 'Disallow hardcoded role checks. Use useAccessLevel hook or getRoleContext API from pace-core instead.',
404
421
  category: 'Security',
405
- recommended: true
422
+ recommended: true,
423
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
406
424
  },
407
425
  messages: {
408
426
  hardcodedRoleCheck: "Hardcoded role check detected. Use useAccessLevel hook or getRoleContext API from '@jmruthers/pace-core/rbac' instead."
@@ -471,7 +489,8 @@ module.exports = {
471
489
  docs: {
472
490
  description: 'Require RESOURCE_NAMES constants instead of string literals in useResourcePermissions calls.',
473
491
  category: 'Best Practices',
474
- recommended: true
492
+ recommended: true,
493
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
475
494
  },
476
495
  messages: {
477
496
  resourcePermissionStringLiteral: "Resource permission string literal detected. Use RESOURCE_NAMES constant object instead (e.g., RESOURCE_NAMES.ORGANISATIONS, RESOURCE_NAMES.EVENTS)."
@@ -536,7 +555,8 @@ module.exports = {
536
555
  docs: {
537
556
  description: 'Disallow wrapper components around PagePermissionGuard. Use PagePermissionGuard directly.',
538
557
  category: 'Security',
539
- recommended: true
558
+ recommended: true,
559
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
540
560
  },
541
561
  messages: {
542
562
  wrapperComponent: "Wrapper component '{{componentName}}' detected around PagePermissionGuard. Must use PagePermissionGuard directly, not through wrappers."
@@ -643,7 +663,8 @@ module.exports = {
643
663
  docs: {
644
664
  description: 'Disallow wrapper functions around pace-core permission hooks. Use hooks directly in components.',
645
665
  category: 'Security',
646
- recommended: true
666
+ recommended: true,
667
+ url: 'https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/standards/6-security-rbac-standards.md'
647
668
  },
648
669
  messages: {
649
670
  wrapperFunction: "Permission wrapper function '{{functionName}}' detected. Use pace-core hooks (useCan, useResourcePermissions) directly in components instead of wrapping them."