@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,606 @@
1
+ # Architecture Standards
2
+
3
+ **🤖 Cursor Rule**: See [03-architecture.mdc](../../cursor-rules/03-architecture.mdc) for AI-optimized directives that automatically enforce SOLID principles and architectural patterns.
4
+
5
+ ## Purpose
6
+
7
+ Define the core architectural principles and SOLID expectations for pace-core and consuming apps so components, APIs, and utilities evolve consistently and sustainably.
8
+
9
+ ---
10
+
11
+ ## Architectural Principles
12
+
13
+ 1. **Composition over complexity** - Build complex features from simple, composable pieces
14
+ 2. **Separation of concerns** - Each module has a single, well-defined responsibility
15
+ 3. **Domain-agnostic core** - pace-core provides generic primitives, not business logic
16
+ 4. **Extensible, stable APIs** - APIs should be easy to extend without breaking existing code
17
+ 5. **Secure by default** - Security is built-in, not bolted on
18
+ 6. **Performance-conscious** - Consider performance implications in design decisions
19
+
20
+ ---
21
+
22
+ ## SOLID Principles
23
+
24
+ ### Single Responsibility Principle
25
+
26
+ **Each module has one reason to change; extract complex logic to hooks/services.**
27
+
28
+ ```tsx
29
+ // ❌ WRONG - Component doing too much
30
+ function EventCard({ eventId }: { eventId: string }) {
31
+ const [event, setEvent] = useState(null);
32
+ const [loading, setLoading] = useState(true);
33
+
34
+ useEffect(() => {
35
+ // Data fetching logic
36
+ fetchEvent(eventId).then(setEvent).finally(() => setLoading(false));
37
+ }, [eventId]);
38
+
39
+ // Formatting logic
40
+ const formattedDate = new Intl.DateTimeFormat('en-US').format(new Date(event.date));
41
+
42
+ // Permission checking logic
43
+ const canEdit = checkPermission('edit', event);
44
+
45
+ // Rendering
46
+ return <div>...</div>;
47
+ }
48
+
49
+ // ✅ CORRECT - Separated concerns
50
+ function EventCard({ eventId }: { eventId: string }) {
51
+ const { event, isLoading } = useEventData(eventId); // Hook handles data fetching
52
+ const formattedDate = formatDate(event?.date); // Utility handles formatting
53
+ const { canEdit } = useResourcePermissions(RESOURCE_NAMES.EVENTS); // Hook handles permissions
54
+
55
+ if (isLoading) return <Loading />;
56
+ return <div>...</div>;
57
+ }
58
+ ```
59
+
60
+ ### Open/Closed Principle
61
+
62
+ **Extend via composition/configuration, avoid modifying shared primitives.**
63
+
64
+ ```tsx
65
+ // ❌ WRONG - Modifying base component
66
+ function CustomButton({ children, ...props }) {
67
+ return (
68
+ <Button {...props} className="custom-style">
69
+ {children}
70
+ </Button>
71
+ );
72
+ }
73
+
74
+ // ✅ CORRECT - Extending via composition
75
+ function CustomButton({ children, variant = 'default', ...props }) {
76
+ return (
77
+ <Button variant={variant} {...props}>
78
+ {children}
79
+ </Button>
80
+ );
81
+ }
82
+
83
+ // ✅ CORRECT - Using configuration/props
84
+ <Button variant="custom" className="additional-styles">
85
+ Click me
86
+ </Button>
87
+ ```
88
+
89
+ ### Liskov Substitution Principle
90
+
91
+ **Derived types/components must satisfy the base contract.**
92
+
93
+ ```tsx
94
+ // ✅ CORRECT - Derived component satisfies base contract
95
+ interface BaseButtonProps {
96
+ onClick?: () => void;
97
+ disabled?: boolean;
98
+ children: React.ReactNode;
99
+ }
100
+
101
+ function PrimaryButton({ onClick, disabled, children }: BaseButtonProps) {
102
+ return (
103
+ <Button variant="primary" onClick={onClick} disabled={disabled}>
104
+ {children}
105
+ </Button>
106
+ );
107
+ }
108
+
109
+ // Can be used anywhere BaseButtonProps is expected
110
+ function ButtonGroup({ buttons }: { buttons: BaseButtonProps[] }) {
111
+ return buttons.map((props, i) => <PrimaryButton key={i} {...props} />);
112
+ }
113
+ ```
114
+
115
+ ### Interface Segregation Principle
116
+
117
+ **Prefer focused interfaces/props over catch-all configs.**
118
+
119
+ ```tsx
120
+ // ❌ WRONG - Catch-all config object
121
+ interface ComponentProps {
122
+ config: {
123
+ variant?: string;
124
+ size?: string;
125
+ color?: string;
126
+ disabled?: boolean;
127
+ onClick?: () => void;
128
+ // ... many more
129
+ };
130
+ }
131
+
132
+ // ✅ CORRECT - Focused, specific props
133
+ interface ComponentProps {
134
+ variant?: 'default' | 'primary' | 'secondary';
135
+ size?: 'sm' | 'md' | 'lg';
136
+ disabled?: boolean;
137
+ onClick?: () => void;
138
+ }
139
+ ```
140
+
141
+ ### Dependency Inversion Principle
142
+
143
+ **Depend on abstractions (types/interfaces); inject implementations.**
144
+
145
+ ```tsx
146
+ // ✅ CORRECT - Depend on interface, not implementation
147
+ interface DataService {
148
+ fetchEvent(id: string): Promise<Event>;
149
+ updateEvent(id: string, data: Partial<Event>): Promise<Event>;
150
+ }
151
+
152
+ function useEventData(eventId: string, service: DataService) {
153
+ const [event, setEvent] = useState<Event | null>(null);
154
+
155
+ useEffect(() => {
156
+ service.fetchEvent(eventId).then(setEvent);
157
+ }, [eventId, service]);
158
+
159
+ return { event };
160
+ }
161
+
162
+ // Can inject different implementations
163
+ const supabaseService: DataService = { /* ... */ };
164
+ const mockService: DataService = { /* ... */ };
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Component Design Principles
170
+
171
+ ### Stateless When Possible
172
+
173
+ **Keep components stateless and composable. Move state to hooks.**
174
+
175
+ ```tsx
176
+ // ❌ WRONG - Component manages state
177
+ function Counter() {
178
+ const [count, setCount] = useState(0);
179
+ return <button onClick={() => setCount(count + 1)}>{count}</button>;
180
+ }
181
+
182
+ // ✅ CORRECT - State in hook, component is presentational
183
+ function useCounter(initial = 0) {
184
+ const [count, setCount] = useState(initial);
185
+ const increment = () => setCount(c => c + 1);
186
+ return { count, increment };
187
+ }
188
+
189
+ function Counter() {
190
+ const { count, increment } = useCounter();
191
+ return <Button onClick={increment}>{count}</Button>;
192
+ }
193
+ ```
194
+
195
+ ### Accessible by Default
196
+
197
+ **Components must be accessible with correct roles, keyboard support, and visible focus.**
198
+
199
+ ```tsx
200
+ // ✅ CORRECT - Accessible component
201
+ function AccessibleButton({ children, onClick, disabled }: ButtonProps) {
202
+ return (
203
+ <button
204
+ onClick={onClick}
205
+ disabled={disabled}
206
+ role="button"
207
+ aria-disabled={disabled}
208
+ tabIndex={disabled ? -1 : 0}
209
+ className="focus:outline focus:outline-2 focus:outline-main-500"
210
+ >
211
+ {children}
212
+ </button>
213
+ );
214
+ }
215
+ ```
216
+
217
+ ### UI Primitives Only
218
+
219
+ **Never add domain logic or data fetching inside components. Use hooks/services instead.**
220
+
221
+ ```tsx
222
+ // ❌ WRONG - Domain logic in component
223
+ function EventCard({ eventId }: { eventId: string }) {
224
+ const [event, setEvent] = useState(null);
225
+
226
+ useEffect(() => {
227
+ // Domain-specific logic
228
+ if (eventId.startsWith('EVT-')) {
229
+ fetchEvent(eventId).then(setEvent);
230
+ }
231
+ }, [eventId]);
232
+
233
+ return <div>{event?.name}</div>;
234
+ }
235
+
236
+ // ✅ CORRECT - Logic in hook
237
+ function useEventData(eventId: string) {
238
+ const [event, setEvent] = useState(null);
239
+
240
+ useEffect(() => {
241
+ if (eventId.startsWith('EVT-')) {
242
+ fetchEvent(eventId).then(setEvent);
243
+ }
244
+ }, [eventId]);
245
+
246
+ return { event };
247
+ }
248
+
249
+ function EventCard({ eventId }: { eventId: string }) {
250
+ const { event } = useEventData(eventId);
251
+ return <div>{event?.name}</div>;
252
+ }
253
+ ```
254
+
255
+ ### Support Controlled + Uncontrolled Usage
256
+
257
+ **Components should work in both controlled and uncontrolled modes.**
258
+
259
+ ```tsx
260
+ // ✅ CORRECT - Supports both modes
261
+ interface InputProps {
262
+ value?: string; // Controlled
263
+ defaultValue?: string; // Uncontrolled
264
+ onChange?: (value: string) => void;
265
+ }
266
+
267
+ function Input({ value, defaultValue, onChange }: InputProps) {
268
+ const [internalValue, setInternalValue] = useState(defaultValue ?? '');
269
+ const isControlled = value !== undefined;
270
+ const currentValue = isControlled ? value : internalValue;
271
+
272
+ const handleChange = (newValue: string) => {
273
+ if (!isControlled) {
274
+ setInternalValue(newValue);
275
+ }
276
+ onChange?.(newValue);
277
+ };
278
+
279
+ return <input value={currentValue} onChange={e => handleChange(e.target.value)} />;
280
+ }
281
+ ```
282
+
283
+ **Real-World Example: Search Input Component**
284
+
285
+ ```tsx
286
+ // ✅ CORRECT - Real-world search input with debouncing
287
+ import { useDebounce } from '@jmruthers/pace-core';
288
+ import { Input } from '@jmruthers/pace-core';
289
+
290
+ interface SearchInputProps {
291
+ value?: string;
292
+ defaultValue?: string;
293
+ onSearch?: (query: string) => void;
294
+ placeholder?: string;
295
+ debounceMs?: number;
296
+ }
297
+
298
+ function SearchInput({
299
+ value,
300
+ defaultValue,
301
+ onSearch,
302
+ placeholder = 'Search...',
303
+ debounceMs = 300,
304
+ }: SearchInputProps) {
305
+ const [internalValue, setInternalValue] = useState(defaultValue ?? '');
306
+ const isControlled = value !== undefined;
307
+ const currentValue = isControlled ? value : internalValue;
308
+
309
+ // Debounce search calls
310
+ const debouncedValue = useDebounce(currentValue, debounceMs);
311
+
312
+ useEffect(() => {
313
+ if (debouncedValue && onSearch) {
314
+ onSearch(debouncedValue);
315
+ }
316
+ }, [debouncedValue, onSearch]);
317
+
318
+ const handleChange = (newValue: string) => {
319
+ if (!isControlled) {
320
+ setInternalValue(newValue);
321
+ }
322
+ };
323
+
324
+ return (
325
+ <Input
326
+ value={currentValue}
327
+ onChange={handleChange}
328
+ placeholder={placeholder}
329
+ type="search"
330
+ aria-label="Search"
331
+ />
332
+ );
333
+ }
334
+
335
+ // Usage examples:
336
+ // Controlled mode (parent manages state)
337
+ function ControlledSearch() {
338
+ const [query, setQuery] = useState('');
339
+
340
+ return (
341
+ <SearchInput
342
+ value={query}
343
+ onSearch={(q) => {
344
+ setQuery(q);
345
+ // Trigger search API call
346
+ }}
347
+ />
348
+ );
349
+ }
350
+
351
+ // Uncontrolled mode (component manages its own state)
352
+ function UncontrolledSearch() {
353
+ return (
354
+ <SearchInput
355
+ defaultValue=""
356
+ onSearch={(q) => {
357
+ // Trigger search API call
358
+ }}
359
+ />
360
+ );
361
+ }
362
+ ```
363
+
364
+ ### Small Surface Area
365
+
366
+ **Keep component APIs small and focused. Prefer composition over configuration.**
367
+
368
+ ```tsx
369
+ // ❌ WRONG - Too many props, complex API
370
+ interface ComplexComponentProps {
371
+ variant: 'a' | 'b' | 'c' | 'd' | 'e';
372
+ size: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
373
+ color: 'red' | 'blue' | 'green' | 'yellow';
374
+ position: 'top' | 'bottom' | 'left' | 'right';
375
+ animation: 'fade' | 'slide' | 'zoom';
376
+ // ... 20 more props
377
+ }
378
+
379
+ // ✅ CORRECT - Small, focused API
380
+ interface SimpleComponentProps {
381
+ variant?: 'default' | 'primary';
382
+ children: React.ReactNode;
383
+ }
384
+
385
+ // Complex behavior via composition
386
+ function ComplexLayout() {
387
+ return (
388
+ <Container>
389
+ <Header variant="primary" />
390
+ <Content />
391
+ <Footer />
392
+ </Container>
393
+ );
394
+ }
395
+ ```
396
+
397
+ ---
398
+
399
+ ## API Design Principles
400
+
401
+ ### Consistent Naming Conventions
402
+
403
+ **Follow standard naming patterns for RPCs and APIs.**
404
+
405
+ ```typescript
406
+ // ✅ CORRECT - Consistent naming
407
+ // Format: <family>_<domain>_<verb>
408
+ data_events_list
409
+ data_events_get
410
+ app_events_create
411
+ app_events_update
412
+ app_events_delete
413
+ app_events_bulk_create // Bulk operations use _bulk_ prefix
414
+ ```
415
+
416
+ ### Type-Safe Results
417
+
418
+ **Always use type-safe result types for APIs.**
419
+
420
+ ```typescript
421
+ // ✅ CORRECT - Type-safe result type
422
+ type ApiResult<T> =
423
+ | { ok: true; data: T }
424
+ | { ok: false; error: ApiError };
425
+
426
+ async function fetchEvent(id: string): Promise<ApiResult<Event>> {
427
+ try {
428
+ const { data, error } = await supabase.from('events').select('*').eq('id', id).single();
429
+ if (error) {
430
+ return { ok: false, error: { code: 'NOT_FOUND', message: 'Event not found' } };
431
+ }
432
+ return { ok: true, data };
433
+ } catch (error) {
434
+ return { ok: false, error: { code: 'UNKNOWN', message: 'An error occurred' } };
435
+ }
436
+ }
437
+ ```
438
+
439
+ ### Idempotent Writes
440
+
441
+ **Write operations should be idempotent when possible.**
442
+
443
+ ```typescript
444
+ // ✅ CORRECT - Idempotent update
445
+ async function updateEvent(id: string, data: Partial<Event>): Promise<ApiResult<Event>> {
446
+ // Using upsert makes this idempotent
447
+ const { data: event, error } = await supabase
448
+ .from('events')
449
+ .upsert({ id, ...data }, { onConflict: 'id' })
450
+ .select()
451
+ .single();
452
+
453
+ if (error) {
454
+ return { ok: false, error: { code: 'UPDATE_FAILED', message: error.message } };
455
+ }
456
+ return { ok: true, data: event };
457
+ }
458
+ ```
459
+
460
+ ### Read RPCs Never Mutate
461
+
462
+ **Read operations must never have side effects.**
463
+
464
+ ```typescript
465
+ // ❌ WRONG - Read operation with side effects
466
+ async function getEvent(id: string): Promise<Event> {
467
+ // Side effect: updates last_accessed
468
+ await supabase.from('events').update({ last_accessed: new Date() }).eq('id', id);
469
+ const { data } = await supabase.from('events').select('*').eq('id', id).single();
470
+ return data;
471
+ }
472
+
473
+ // ✅ CORRECT - Pure read operation
474
+ async function getEvent(id: string): Promise<ApiResult<Event>> {
475
+ const { data, error } = await supabase.from('events').select('*').eq('id', id).single();
476
+ if (error) {
477
+ return { ok: false, error: { code: 'NOT_FOUND', message: 'Event not found' } };
478
+ }
479
+ return { ok: true, data };
480
+ }
481
+ ```
482
+
483
+ ### Never Accept Dynamic SQL
484
+
485
+ **Never accept SQL strings as parameters. Use parameterized queries or RPCs.**
486
+
487
+ ```typescript
488
+ // ❌ WRONG - Dynamic SQL injection risk
489
+ async function executeQuery(sql: string): Promise<any> {
490
+ return supabase.rpc('execute_sql', { sql });
491
+ }
492
+
493
+ // ✅ CORRECT - Parameterized query
494
+ async function getEvents(filters: EventFilters): Promise<ApiResult<Event[]>> {
495
+ let query = supabase.from('events').select('*');
496
+
497
+ if (filters.organisationId) {
498
+ query = query.eq('organisation_id', filters.organisationId);
499
+ }
500
+
501
+ if (filters.status) {
502
+ query = query.eq('status', filters.status);
503
+ }
504
+
505
+ const { data, error } = await query;
506
+ // ...
507
+ }
508
+ ```
509
+
510
+ ---
511
+
512
+ ## Performance & RLS Boundaries
513
+
514
+ **Note:** Detailed RLS performance requirements are covered in [Security & RBAC Standards](./6-security-rbac-standards.md). This section provides a brief overview.
515
+
516
+ ### RLS Helper Functions
517
+
518
+ **Policies must rely on helper functions (no subqueries) to avoid N+1/per-row overhead.**
519
+
520
+ See [Security & RBAC Standards](./6-security-rbac-standards.md) for detailed RLS policy patterns and helper function requirements.
521
+
522
+ ### Test Migrations
523
+
524
+ **Verify DB migrations for performance regressions and timeouts.**
525
+
526
+ ```bash
527
+ # Run migrations with timeout
528
+ timeout 120 npm run test:db
529
+
530
+ # Check for performance issues
531
+ supabase advisors performance
532
+ ```
533
+
534
+ ### Monitor Queries
535
+
536
+ **Use EXPLAIN/Advisors to ensure policies don't introduce InitPlan nodes.**
537
+
538
+ See [Security & RBAC Standards](./6-security-rbac-standards.md) for detailed RLS performance monitoring requirements.
539
+
540
+ ---
541
+
542
+ ## In/Out of Scope
543
+
544
+ ### In Scope
545
+
546
+ pace-core provides:
547
+
548
+ - **UI primitives** - Buttons, inputs, cards, dialogs, etc.
549
+ - **Generic hooks** - `useDebounce`, `useToast`, `useUnifiedAuth`, etc.
550
+ - **Shared API patterns** - Result types, error handling, RPC conventions
551
+ - **Error-handling conventions** - Consistent error shapes and recovery
552
+ - **RPC shape conventions** - Naming, parameterization, idempotency
553
+
554
+ ### Out of Scope
555
+
556
+ pace-core does NOT provide:
557
+
558
+ - **App/domain-specific logic** - Business rules, workflows, domain models
559
+ - **App-specific styling** - Custom themes, brand colors (apps define these)
560
+ - **Business workflows** - Order processing, user onboarding, etc.
561
+
562
+ ---
563
+
564
+ ## Precedence
565
+
566
+ When architectural decisions conflict, apply this precedence:
567
+
568
+ 1. **Security** - Security requirements override all others
569
+ 2. **API/RPC** - API contracts must be stable and consistent
570
+ 3. **Components** - Component APIs should be simple and composable
571
+ 4. **Code Style** - Code style and patterns
572
+ 5. **Testing** - Testing requirements
573
+ 6. **Documentation** - Documentation requirements
574
+
575
+ ---
576
+
577
+ ## Cursor Checklist
578
+
579
+ When making architectural changes, verify:
580
+
581
+ - [ ] Changes fit boundaries (no domain logic in core primitives)
582
+ - [ ] Follow SOLID guidance above
583
+ - [ ] Prefer additive changes; avoid breaking contracts
584
+ - [ ] Keep helpers small, pure, and typed
585
+ - [ ] Components are stateless when possible
586
+ - [ ] Components are accessible by default
587
+ - [ ] APIs use type-safe result types
588
+ - [ ] RPCs follow naming conventions
589
+ - [ ] Write operations are idempotent when possible
590
+ - [ ] No dynamic SQL in APIs
591
+
592
+ ---
593
+
594
+ ## Related Documentation
595
+
596
+ - [Standards Overview](./0-standards-overview.md) - Standards system overview
597
+ - [Code Quality](./4-code-quality-standards.md) - TypeScript and code patterns
598
+ - [API & Tech Stack](./7-api-tech-stack-standards.md) - API and RPC standards
599
+ - [Security & RBAC](./6-security-rbac-standards.md) - Security and RLS patterns
600
+ - [Styling](./5-styling-standards.md) - Component styling patterns
601
+
602
+ ---
603
+
604
+ **Last Updated:** 2025-01-28
605
+ **Version:** 2.0.0
606
+ **Applies to:** All pace-core and consuming apps