@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,728 @@
1
+ # Code Quality Standards
2
+
3
+ **🤖 Cursor Rule**: See [04-code-quality.mdc](../../cursor-rules/04-code-quality.mdc) for AI-optimized directives that automatically enforce code quality standards.
4
+
5
+ **🔧 ESLint Rules**: See [04-code-quality.cjs](../../eslint-rules/rules/04-code-quality.cjs) for mechanically checkable code quality rules.
6
+
7
+ ## Purpose
8
+
9
+ This standard defines TypeScript rules, naming conventions, and code style patterns to ensure consistent, maintainable, and type-safe code across pace-core and consuming apps.
10
+
11
+ ---
12
+
13
+ ## TypeScript Rules
14
+
15
+ ### No `any` Type
16
+
17
+ **Never use `any`. Use `unknown` for truly unknown types, then narrow with type guards.**
18
+
19
+ ```typescript
20
+ // ❌ WRONG - Using any
21
+ function processData(data: any) {
22
+ return data.value;
23
+ }
24
+
25
+ // ✅ CORRECT - Using unknown with type guard
26
+ function isDataObject(value: unknown): value is { value: string } {
27
+ return (
28
+ typeof value === 'object' &&
29
+ value !== null &&
30
+ 'value' in value &&
31
+ typeof (value as { value: unknown }).value === 'string'
32
+ );
33
+ }
34
+
35
+ function processData(data: unknown) {
36
+ if (isDataObject(data)) {
37
+ return data.value; // TypeScript knows data.value is string
38
+ }
39
+ throw new Error('Invalid data');
40
+ }
41
+ ```
42
+
43
+ ### Prefer Discriminated Unions
44
+
45
+ **Use discriminated unions instead of boolean mode flags.**
46
+
47
+ ```typescript
48
+ // ❌ WRONG - Boolean flag
49
+ interface ComponentProps {
50
+ mode: 'create' | 'edit';
51
+ isEditing: boolean; // Redundant with mode
52
+ }
53
+
54
+ // ✅ CORRECT - Discriminated union
55
+ type ComponentProps =
56
+ | { mode: 'create'; initialData?: never }
57
+ | { mode: 'edit'; initialData: Event };
58
+
59
+ function Component(props: ComponentProps) {
60
+ if (props.mode === 'create') {
61
+ // TypeScript knows initialData doesn't exist
62
+ return <CreateForm />;
63
+ }
64
+ // TypeScript knows initialData exists
65
+ return <EditForm data={props.initialData} />;
66
+ }
67
+ ```
68
+
69
+ ### Avoid Type Assertions
70
+
71
+ **Avoid type assertions unless in escape hatches. Prefer type guards.**
72
+
73
+ ```typescript
74
+ // ❌ WRONG - Type assertion
75
+ function getValue(data: unknown): string {
76
+ return (data as { value: string }).value; // Unsafe
77
+ }
78
+
79
+ // ✅ CORRECT - Type guard
80
+ function isValueObject(data: unknown): data is { value: string } {
81
+ return (
82
+ typeof data === 'object' &&
83
+ data !== null &&
84
+ 'value' in data &&
85
+ typeof (data as { value: unknown }).value === 'string'
86
+ );
87
+ }
88
+
89
+ function getValue(data: unknown): string {
90
+ if (isValueObject(data)) {
91
+ return data.value; // Type-safe
92
+ }
93
+ throw new Error('Invalid data');
94
+ }
95
+ ```
96
+
97
+ ### Use ReadonlyArray Where Possible
98
+
99
+ **Prefer `ReadonlyArray` for arrays that shouldn't be mutated.**
100
+
101
+ ```typescript
102
+ // ❌ WRONG - Mutable array
103
+ function processItems(items: string[]) {
104
+ items.push('new'); // Mutates input
105
+ return items;
106
+ }
107
+
108
+ // ✅ CORRECT - Readonly array
109
+ function processItems(items: ReadonlyArray<string>): string[] {
110
+ return [...items, 'new']; // Creates new array
111
+ }
112
+ ```
113
+
114
+ ### Avoid Implicit `any`
115
+
116
+ **Always provide explicit types. Enable `strict` mode in TypeScript.**
117
+
118
+ ```json
119
+ // tsconfig.json
120
+ {
121
+ "compilerOptions": {
122
+ "strict": true,
123
+ "noImplicitAny": true,
124
+ "strictNullChecks": true
125
+ }
126
+ }
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Naming Conventions
132
+
133
+ ### Hooks
134
+
135
+ **Hooks must start with `use` prefix and use camelCase.**
136
+
137
+ ```typescript
138
+ // ✅ CORRECT
139
+ export function useEventData() { }
140
+ export function useUserProfile() { }
141
+ export function useDebounce() { }
142
+
143
+ // ❌ WRONG
144
+ export function getEventData() { } // Not a hook
145
+ export function UseEventData() { } // Wrong case
146
+ ```
147
+
148
+ ### Providers
149
+
150
+ **Providers must end with `Provider` suffix and use PascalCase.**
151
+
152
+ ```typescript
153
+ // ✅ CORRECT
154
+ export function UnifiedAuthProvider() { }
155
+ export function QueryProvider() { }
156
+ export function ThemeProvider() { }
157
+
158
+ // ❌ WRONG
159
+ export function unifiedAuthProvider() { } // Wrong case
160
+ export function UnifiedAuth() { } // Missing Provider suffix
161
+ ```
162
+
163
+ ### Utilities
164
+
165
+ **Utilities use camelCase and should be pure functions when possible.**
166
+
167
+ ```typescript
168
+ // ✅ CORRECT
169
+ export function formatDate(date: Date): string { }
170
+ export function validateEmail(email: string): boolean { }
171
+ export function calculateTotal(items: Item[]): number { }
172
+
173
+ // ❌ WRONG
174
+ export function FormatDate() { } // Wrong case
175
+ export function format_date() { } // Wrong case
176
+ ```
177
+
178
+ ### Components
179
+
180
+ **Components use PascalCase.**
181
+
182
+ ```typescript
183
+ // ✅ CORRECT
184
+ export function EventCard() { }
185
+ export function UserProfile() { }
186
+ export const Button = () => { };
187
+
188
+ // ❌ WRONG
189
+ export function eventCard() { } // Wrong case
190
+ export function event_card() { } // Wrong case
191
+ ```
192
+
193
+ ### Types and Interfaces
194
+
195
+ **Types and interfaces use PascalCase. Prefer `type` for unions/intersections, `interface` for objects.**
196
+
197
+ ```typescript
198
+ // ✅ CORRECT
199
+ export type EventStatus = 'draft' | 'published' | 'archived';
200
+ export interface Event {
201
+ id: string;
202
+ title: string;
203
+ }
204
+
205
+ // ❌ WRONG
206
+ export type eventStatus = 'draft' | 'published'; // Wrong case
207
+ export interface event { } // Wrong case
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Preferred Patterns
213
+
214
+ ### Pure Functions
215
+
216
+ **Prefer pure functions that don't have side effects.**
217
+
218
+ ```typescript
219
+ // ✅ CORRECT - Pure function
220
+ function calculateTotal(items: ReadonlyArray<Item>): number {
221
+ return items.reduce((sum, item) => sum + item.price, 0);
222
+ }
223
+
224
+ // ❌ WRONG - Side effect
225
+ function calculateTotal(items: Item[]): number {
226
+ let total = 0;
227
+ items.forEach(item => {
228
+ total += item.price;
229
+ console.log(item); // Side effect
230
+ });
231
+ return total;
232
+ }
233
+ ```
234
+
235
+ ### Composition Over Inheritance
236
+
237
+ **Prefer composition and functional patterns over class inheritance.**
238
+
239
+ ```typescript
240
+ // ❌ WRONG - Class inheritance
241
+ class BaseService {
242
+ protected baseUrl: string;
243
+ constructor(baseUrl: string) {
244
+ this.baseUrl = baseUrl;
245
+ }
246
+ }
247
+
248
+ class EventService extends BaseService {
249
+ getEvents() {
250
+ return fetch(`${this.baseUrl}/events`);
251
+ }
252
+ }
253
+
254
+ // ✅ CORRECT - Composition
255
+ function createApiClient(baseUrl: string) {
256
+ return {
257
+ get: (path: string) => fetch(`${baseUrl}${path}`),
258
+ post: (path: string, data: unknown) => fetch(`${baseUrl}${path}`, {
259
+ method: 'POST',
260
+ body: JSON.stringify(data),
261
+ }),
262
+ };
263
+ }
264
+
265
+ function createEventService(client: ReturnType<typeof createApiClient>) {
266
+ return {
267
+ getEvents: () => client.get('/events'),
268
+ createEvent: (data: Event) => client.post('/events', data),
269
+ };
270
+ }
271
+ ```
272
+
273
+ ### Early Returns
274
+
275
+ **Use early returns to reduce nesting and improve readability.**
276
+
277
+ ```typescript
278
+ // ❌ WRONG - Deep nesting
279
+ function processEvent(event: Event | null): string {
280
+ if (event !== null) {
281
+ if (event.status === 'published') {
282
+ if (event.date > new Date()) {
283
+ return 'Upcoming event';
284
+ } else {
285
+ return 'Past event';
286
+ }
287
+ } else {
288
+ return 'Draft event';
289
+ }
290
+ } else {
291
+ return 'No event';
292
+ }
293
+ }
294
+
295
+ // ✅ CORRECT - Early returns
296
+ function processEvent(event: Event | null): string {
297
+ if (event === null) {
298
+ return 'No event';
299
+ }
300
+
301
+ if (event.status !== 'published') {
302
+ return 'Draft event';
303
+ }
304
+
305
+ if (event.date > new Date()) {
306
+ return 'Upcoming event';
307
+ }
308
+
309
+ return 'Past event';
310
+ }
311
+ ```
312
+
313
+ **Real-World Example: Permission Check with Early Returns**
314
+
315
+ ```typescript
316
+ // ✅ CORRECT - Real-world permission check with early returns
317
+ async function canUserEditEvent(
318
+ userId: string,
319
+ eventId: string,
320
+ secureSupabase: SupabaseClient
321
+ ): Promise<boolean> {
322
+ // Early return: No user ID
323
+ if (!userId) {
324
+ return false;
325
+ }
326
+
327
+ // Early return: No event ID
328
+ if (!eventId) {
329
+ return false;
330
+ }
331
+
332
+ // Get event
333
+ const { data: event, error } = await secureSupabase
334
+ .from('events')
335
+ .select('organisation_id, status, created_by')
336
+ .eq('id', eventId)
337
+ .single();
338
+
339
+ // Early return: Event not found
340
+ if (error || !event) {
341
+ return false;
342
+ }
343
+
344
+ // Early return: Event is archived
345
+ if (event.status === 'archived') {
346
+ return false;
347
+ }
348
+
349
+ // Check permissions
350
+ const { canUpdate, isLoading } = useResourcePermissions(RESOURCE_NAMES.EVENTS);
351
+
352
+ // Early return: Permissions still loading
353
+ if (isLoading) {
354
+ return false;
355
+ }
356
+
357
+ // Check if user created the event (always allow edit for creator)
358
+ if (event.created_by === userId) {
359
+ return true;
360
+ }
361
+
362
+ // Check RBAC permissions
363
+ return canUpdate({ organisationId: event.organisation_id });
364
+ }
365
+
366
+ // Usage
367
+ async function handleEditClick(eventId: string) {
368
+ const { user } = await secureSupabase.auth.getUser();
369
+
370
+ if (!user) {
371
+ toast.error('Please log in to edit events');
372
+ return;
373
+ }
374
+
375
+ const canEdit = await canUserEditEvent(user.id, eventId, secureSupabase);
376
+
377
+ if (!canEdit) {
378
+ toast.error('You do not have permission to edit this event');
379
+ return;
380
+ }
381
+
382
+ navigate(`/events/${eventId}/edit`);
383
+ }
384
+ ```
385
+
386
+ ### Small Private Helpers
387
+
388
+ **Extract complex logic into small, focused helper functions.**
389
+
390
+ ```typescript
391
+ // ❌ WRONG - Complex inline logic
392
+ function formatEventDisplay(event: Event): string {
393
+ const date = new Date(event.date);
394
+ const month = date.toLocaleString('en-US', { month: 'short' });
395
+ const day = date.getDate();
396
+ const year = date.getFullYear();
397
+ const time = date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
398
+ return `${event.title} - ${month} ${day}, ${year} at ${time}`;
399
+ }
400
+
401
+ // ✅ CORRECT - Extracted helpers
402
+ function formatDate(date: Date): string {
403
+ return date.toLocaleString('en-US', {
404
+ month: 'short',
405
+ day: 'numeric',
406
+ year: 'numeric'
407
+ });
408
+ }
409
+
410
+ function formatTime(date: Date): string {
411
+ return date.toLocaleTimeString('en-US', {
412
+ hour: '2-digit',
413
+ minute: '2-digit'
414
+ });
415
+ }
416
+
417
+ function formatEventDisplay(event: Event): string {
418
+ const date = new Date(event.date);
419
+ return `${event.title} - ${formatDate(date)} at ${formatTime(date)}`;
420
+ }
421
+ ```
422
+
423
+ ---
424
+
425
+ ## Forbidden Patterns
426
+
427
+ ### Implicit `any`
428
+
429
+ **Never use implicit `any`. Always provide explicit types.**
430
+
431
+ ```typescript
432
+ // ❌ WRONG
433
+ function process(data) { // Implicit any
434
+ return data.value;
435
+ }
436
+
437
+ // ✅ CORRECT
438
+ function process(data: { value: string }): string {
439
+ return data.value;
440
+ }
441
+ ```
442
+
443
+ ### Bloated Components
444
+
445
+ **Components should be small and focused. Extract logic to hooks/services.**
446
+
447
+ ```tsx
448
+ // ❌ WRONG - Bloated component
449
+ function EventPage({ eventId }: { eventId: string }) {
450
+ const [event, setEvent] = useState(null);
451
+ const [loading, setLoading] = useState(true);
452
+ const [error, setError] = useState(null);
453
+ const [permissions, setPermissions] = useState(null);
454
+
455
+ useEffect(() => {
456
+ // 50+ lines of logic
457
+ }, [eventId]);
458
+
459
+ // 200+ lines of JSX
460
+ return <div>...</div>;
461
+ }
462
+
463
+ // ✅ CORRECT - Focused component
464
+ function EventPage({ eventId }: { eventId: string }) {
465
+ const { event, isLoading, error } = useEventData(eventId);
466
+ const { permissions } = useResourcePermissions(RESOURCE_NAMES.EVENTS);
467
+
468
+ if (isLoading) return <Loading />;
469
+ if (error) return <Error error={error} />;
470
+
471
+ return <EventDetails event={event} permissions={permissions} />;
472
+ }
473
+ ```
474
+
475
+ ### Domain-Specific Types in pace-core
476
+
477
+ **pace-core must not contain domain-specific types. These belong in consuming apps.**
478
+
479
+ ```typescript
480
+ // ❌ WRONG - Domain type in pace-core
481
+ // packages/core/src/types/event.ts
482
+ export interface Event {
483
+ id: string;
484
+ name: string;
485
+ date: Date;
486
+ }
487
+
488
+ // ✅ CORRECT - Generic type in pace-core
489
+ // packages/core/src/types/common.ts
490
+ export interface BaseEntity {
491
+ id: string;
492
+ created_at: string;
493
+ updated_at: string;
494
+ }
495
+
496
+ // ✅ CORRECT - Domain type in consuming app
497
+ // src/types/event.ts
498
+ import type { BaseEntity } from '@jmruthers/pace-core';
499
+
500
+ export interface Event extends BaseEntity {
501
+ name: string;
502
+ date: Date;
503
+ }
504
+ ```
505
+
506
+ ---
507
+
508
+ ## React Patterns
509
+
510
+ ### Functional Components Only
511
+
512
+ **Use functional components with hooks. No class components.**
513
+
514
+ ```tsx
515
+ // ❌ WRONG - Class component
516
+ class EventCard extends React.Component<{ event: Event }> {
517
+ render() {
518
+ return <div>{this.props.event.name}</div>;
519
+ }
520
+ }
521
+
522
+ // ✅ CORRECT - Functional component
523
+ function EventCard({ event }: { event: Event }) {
524
+ return <div>{event.name}</div>;
525
+ }
526
+ ```
527
+
528
+ ### Proper Hook Dependencies
529
+
530
+ **Always include all dependencies in hook dependency arrays.**
531
+
532
+ ```tsx
533
+ // ❌ WRONG - Missing dependencies
534
+ function Component({ id }: { id: string }) {
535
+ const [data, setData] = useState(null);
536
+
537
+ useEffect(() => {
538
+ fetchData(id).then(setData);
539
+ }, []); // Missing id dependency
540
+
541
+ return <div>{data?.name}</div>;
542
+ }
543
+
544
+ // ✅ CORRECT - All dependencies included
545
+ function Component({ id }: { id: string }) {
546
+ const [data, setData] = useState(null);
547
+
548
+ useEffect(() => {
549
+ fetchData(id).then(setData);
550
+ }, [id]); // All dependencies included
551
+
552
+ return <div>{data?.name}</div>;
553
+ }
554
+ ```
555
+
556
+ ### Memoization When Appropriate
557
+
558
+ **Use `useMemo` and `useCallback` when values/functions are expensive or passed to memoized children.**
559
+
560
+ ```tsx
561
+ // ✅ CORRECT - Memoized expensive computation
562
+ function Component({ items }: { items: ReadonlyArray<Item> }) {
563
+ const total = useMemo(() => {
564
+ return items.reduce((sum, item) => sum + item.price, 0);
565
+ }, [items]);
566
+
567
+ return <div>Total: {total}</div>;
568
+ }
569
+
570
+ // ✅ CORRECT - Memoized callback for memoized child
571
+ const MemoizedChild = React.memo(({ onClick }: { onClick: () => void }) => {
572
+ return <button onClick={onClick}>Click</button>;
573
+ });
574
+
575
+ function Component() {
576
+ const handleClick = useCallback(() => {
577
+ console.log('clicked');
578
+ }, []);
579
+
580
+ return <MemoizedChild onClick={handleClick} />;
581
+ }
582
+ ```
583
+
584
+ ---
585
+
586
+ ## Accessibility Requirements
587
+
588
+ **MUST** ensure all components and pages meet WCAG 2.1 Level AA standards.
589
+
590
+ ### Core Principles
591
+
592
+ 1. **Semantic HTML** - Use semantic elements (`<main>`, `<section>`, `<article>`, `<nav>`, etc.) instead of `<div>` wrappers
593
+ 2. **Keyboard Navigation** - All interactive elements must be accessible via keyboard
594
+ 3. **ARIA Labels** - Provide clear labels for screen readers when visible text isn't sufficient
595
+ 4. **Focus Management** - Visible focus indicators on all interactive elements
596
+ 5. **Color Contrast** - Text must meet 4.5:1 contrast ratio (WCAG AA)
597
+
598
+ ### Implementation Requirements
599
+
600
+ ```tsx
601
+ // ✅ CORRECT - Semantic HTML with ARIA
602
+ <main>
603
+ <h1>Page Title</h1>
604
+ <section aria-labelledby="section-title">
605
+ <h2 id="section-title">Section Title</h2>
606
+ <Button
607
+ aria-label="Delete user"
608
+ aria-describedby="delete-help"
609
+ >
610
+ Delete
611
+ </Button>
612
+ <span id="delete-help" className="sr-only">
613
+ Permanently removes the user account
614
+ </span>
615
+ </section>
616
+ </main>
617
+
618
+ // ❌ WRONG - Non-semantic structure
619
+ <div>
620
+ <div className="title">Page Title</div>
621
+ <div>
622
+ <button>Delete</button>
623
+ </div>
624
+ </div>
625
+ ```
626
+
627
+ ### Keyboard Navigation
628
+
629
+ **MUST** ensure all interactive elements are keyboard accessible:
630
+
631
+ ```tsx
632
+ // ✅ CORRECT - pace-core components handle keyboard navigation automatically
633
+ import { Button, DataTable } from '@jmruthers/pace-core';
634
+
635
+ <Button onClick={handleClick}>Click me</Button>
636
+ <DataTable data={data} columns={columns} />
637
+ ```
638
+
639
+ ### Screen Reader Support
640
+
641
+ **MUST** provide appropriate ARIA attributes:
642
+
643
+ ```tsx
644
+ // ✅ CORRECT - ARIA labels for icons
645
+ <Button aria-label="Close dialog">
646
+ <Icon name="x" aria-hidden="true" />
647
+ </Button>
648
+
649
+ // ✅ CORRECT - Error messages with role="alert"
650
+ {error && (
651
+ <div role="alert" aria-live="polite">
652
+ {error.message}
653
+ </div>
654
+ )}
655
+
656
+ // ✅ CORRECT - Loading states
657
+ <div role="status" aria-live="polite" aria-busy={loading}>
658
+ {loading && <span className="sr-only">Loading data...</span>}
659
+ {loading ? <Spinner /> : <Content />}
660
+ </div>
661
+ ```
662
+
663
+ ### Testing Accessibility
664
+
665
+ **MUST** test with:
666
+ - Keyboard navigation (Tab, Enter, Space, Arrow keys)
667
+ - Screen readers (NVDA, JAWS, VoiceOver)
668
+ - Automated tools (axe DevTools, WAVE, Lighthouse)
669
+
670
+ **Accessibility Checklist:**
671
+ - [ ] All interactive elements keyboard accessible
672
+ - [ ] Visible focus indicators on all interactive elements
673
+ - [ ] ARIA labels provided for icons and images
674
+ - [ ] Semantic HTML used appropriately
675
+ - [ ] Error messages properly associated with form fields
676
+ - [ ] Color contrast meets WCAG AA (4.5:1 for text)
677
+ - [ ] Screen reader announcements work correctly
678
+ - [ ] Focus management in modals and dynamic content
679
+
680
+ ## Code Quality Checklist
681
+
682
+ Before committing code, verify:
683
+
684
+ - [ ] No `any` types (use `unknown` with type guards)
685
+ - [ ] No implicit `any` (TypeScript `strict` mode enabled)
686
+ - [ ] Discriminated unions used instead of boolean flags
687
+ - [ ] Type assertions avoided (use type guards)
688
+ - [ ] `ReadonlyArray` used for immutable arrays
689
+ - [ ] Naming conventions followed (hooks: `use*`, providers: `*Provider`, etc.)
690
+ - [ ] Pure functions preferred (no side effects)
691
+ - [ ] Early returns used to reduce nesting
692
+ - [ ] Complex logic extracted to helpers
693
+ - [ ] Components are small and focused
694
+ - [ ] No domain-specific types in pace-core
695
+ - [ ] Functional components only (no class components)
696
+ - [ ] Hook dependencies are complete
697
+ - [ ] Memoization used when appropriate
698
+ - [ ] Accessibility requirements met (WCAG 2.1 AA)
699
+ - [ ] Keyboard navigation supported
700
+ - [ ] ARIA labels provided where needed
701
+ - [ ] Semantic HTML used appropriately
702
+
703
+ ---
704
+
705
+ ## ESLint Rules
706
+
707
+ The following ESLint rules enforce code quality standards:
708
+
709
+ ### Naming Conventions
710
+
711
+ - **`naming-convention`** - Enforces hook naming (`use*`) and provider naming (`*Provider`)
712
+
713
+ These rules are part of the `pace-core-compliance` plugin and are automatically enabled when you extend `@jmruthers/pace-core/eslint-config`.
714
+
715
+ **Setup**: Run `node node_modules/@jmruthers/pace-core/scripts/install-eslint-config.cjs` to configure ESLint in your consuming app.
716
+
717
+ ## Related Documentation
718
+
719
+ - [Standards Overview](./0-standards-overview.md) - Standards system overview
720
+ - [Architecture](./3-architecture-standards.md) - SOLID principles and architecture
721
+ - [API & Tech Stack](./7-api-tech-stack-standards.md) - TypeScript configuration
722
+ - [Testing & Documentation](./8-testing-documentation-standards.md) - Testing patterns
723
+
724
+ ---
725
+
726
+ **Last Updated:** 2025-01-28
727
+ **Version:** 2.0.0
728
+ **Applies to:** All pace-core and consuming apps