@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,1102 @@
1
+ # Operations Standards
2
+
3
+ **🤖 Cursor Rule**: See [09-operations.mdc](../../cursor-rules/09-operations.mdc) for AI-optimized directives that automatically enforce error handling, performance, and CI/CD best practices.
4
+
5
+ ## Purpose
6
+
7
+ This standard defines error handling patterns, performance optimization strategies, and CI/CD integration patterns to ensure reliable, performant, and maintainable applications.
8
+
9
+ **Note:** RLS-specific performance requirements are covered in [Security & RBAC Standards](./6-security-rbac-standards.md). This document focuses on general application performance.
10
+
11
+ ---
12
+
13
+ ## Error Handling Patterns
14
+
15
+ ### Principles
16
+
17
+ 1. **Never expose internal details** in user-facing error messages
18
+ 2. **Always use type-safe error handling** (no `any` types)
19
+ 3. **Log errors appropriately** (with context, without sensitive data)
20
+ 4. **Provide recovery paths** when possible
21
+ 5. **Use consistent error shapes** across the application
22
+
23
+ ### Error Types
24
+
25
+ #### API Errors
26
+
27
+ **Shape:**
28
+ ```typescript
29
+ type ApiError = {
30
+ ok: false;
31
+ error: {
32
+ code: string; // Machine-readable error code
33
+ message: string; // User-friendly message
34
+ details?: object; // Optional additional context (non-sensitive)
35
+ };
36
+ };
37
+ ```
38
+
39
+ **Example:**
40
+ ```typescript
41
+ // ✅ CORRECT
42
+ const result = await apiCall();
43
+ if (!result.ok) {
44
+ // result.error.message is user-friendly
45
+ toast.error(result.error.message);
46
+ logger.error('API call failed', { code: result.error.code, details: result.error.details });
47
+ }
48
+
49
+ // ❌ WRONG - Exposing internal details
50
+ if (error.message.includes('SQL')) {
51
+ toast.error(error.message); // Exposes database internals
52
+ }
53
+ ```
54
+
55
+ #### Validation Errors
56
+
57
+ **Shape:**
58
+ ```typescript
59
+ type ValidationError = {
60
+ field: string;
61
+ message: string;
62
+ code?: string;
63
+ };
64
+ ```
65
+
66
+ **Example:**
67
+ ```typescript
68
+ // ✅ CORRECT - Using Zod validation
69
+ const schema = z.object({
70
+ email: z.string().email('Please enter a valid email address'),
71
+ name: z.string().min(1, 'Name is required'),
72
+ });
73
+
74
+ try {
75
+ const data = schema.parse(formData);
76
+ } catch (error) {
77
+ if (error instanceof z.ZodError) {
78
+ // User-friendly validation messages
79
+ error.errors.forEach(err => {
80
+ toast.error(err.message);
81
+ });
82
+ }
83
+ }
84
+ ```
85
+
86
+ #### Network Errors
87
+
88
+ **Pattern:**
89
+ ```typescript
90
+ // ✅ CORRECT - Handle network errors gracefully
91
+ try {
92
+ const data = await fetchData();
93
+ } catch (error) {
94
+ if (error instanceof TypeError && error.message.includes('fetch')) {
95
+ toast.error('Unable to connect. Please check your internet connection.');
96
+ logger.error('Network error', { error: error.message });
97
+ } else {
98
+ toast.error('An unexpected error occurred. Please try again.');
99
+ logger.error('Unexpected error', { error });
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Error Handling Patterns
105
+
106
+ #### Pattern 1: Try-Catch with Type Guards
107
+
108
+ **Use for:** Known error types that need different handling
109
+
110
+ ```typescript
111
+ // ✅ CORRECT
112
+ function isApiError(error: unknown): error is ApiError {
113
+ return (
114
+ typeof error === 'object' &&
115
+ error !== null &&
116
+ 'ok' in error &&
117
+ (error as ApiError).ok === false &&
118
+ 'error' in error
119
+ );
120
+ }
121
+
122
+ try {
123
+ const result = await apiCall();
124
+ if (!result.ok) {
125
+ handleApiError(result.error);
126
+ }
127
+ } catch (error) {
128
+ if (isApiError(error)) {
129
+ handleApiError(error.error);
130
+ } else {
131
+ handleUnknownError(error);
132
+ }
133
+ }
134
+ ```
135
+
136
+ #### Pattern 2: Result Types
137
+
138
+ **Use for:** Functions that can fail (preferred pattern)
139
+
140
+ ```typescript
141
+ // ✅ CORRECT - Using Result type
142
+ type Result<T, E = ApiError> =
143
+ | { ok: true; data: T }
144
+ | { ok: false; error: E };
145
+
146
+ async function fetchUser(id: string): Promise<Result<User>> {
147
+ try {
148
+ const { data, error } = await supabase.from('users').select('*').eq('id', id).single();
149
+ if (error) {
150
+ return { ok: false, error: { code: 'USER_NOT_FOUND', message: 'User not found' } };
151
+ }
152
+ return { ok: true, data };
153
+ } catch (error) {
154
+ return {
155
+ ok: false,
156
+ error: { code: 'UNKNOWN_ERROR', message: 'An unexpected error occurred' }
157
+ };
158
+ }
159
+ }
160
+
161
+ // Usage
162
+ const result = await fetchUser(userId);
163
+ if (result.ok) {
164
+ // TypeScript knows result.data exists
165
+ setUser(result.data);
166
+ } else {
167
+ // TypeScript knows result.error exists
168
+ toast.error(result.error.message);
169
+ }
170
+ ```
171
+
172
+ **Real-World Example: Form Submission with Validation**
173
+
174
+ ```typescript
175
+ // ✅ CORRECT - Real-world form submission with Result type
176
+ async function submitEventForm(formData: EventFormData): Promise<Result<Event>> {
177
+ // 1. Validate input
178
+ const validation = eventFormSchema.safeParse(formData);
179
+ if (!validation.success) {
180
+ return {
181
+ ok: false,
182
+ error: {
183
+ code: 'VALIDATION_ERROR',
184
+ message: 'Please check your input and try again',
185
+ details: validation.error.errors,
186
+ },
187
+ };
188
+ }
189
+
190
+ // 2. Check permissions
191
+ const { canCreate, isLoading } = useResourcePermissions(RESOURCE_NAMES.EVENTS);
192
+ if (isLoading) {
193
+ return {
194
+ ok: false,
195
+ error: {
196
+ code: 'PERMISSION_CHECK_IN_PROGRESS',
197
+ message: 'Please wait while we verify your permissions',
198
+ },
199
+ };
200
+ }
201
+ if (!canCreate({ organisationId: formData.organisationId })) {
202
+ return {
203
+ ok: false,
204
+ error: {
205
+ code: 'PERMISSION_DENIED',
206
+ message: 'You do not have permission to create events',
207
+ },
208
+ };
209
+ }
210
+
211
+ // 3. Submit to API
212
+ try {
213
+ const { data, error } = await secureSupabase.rpc('app_events_create', {
214
+ p_name: formData.name,
215
+ p_date: formData.date,
216
+ p_organisation_id: formData.organisationId,
217
+ });
218
+
219
+ if (error) {
220
+ // Map database errors to user-friendly messages
221
+ if (error.code === '23505') { // Unique constraint violation
222
+ return {
223
+ ok: false,
224
+ error: {
225
+ code: 'DUPLICATE_EVENT',
226
+ message: 'An event with this name already exists',
227
+ },
228
+ };
229
+ }
230
+ return {
231
+ ok: false,
232
+ error: {
233
+ code: 'CREATE_FAILED',
234
+ message: 'Unable to create event. Please try again.',
235
+ },
236
+ };
237
+ }
238
+
239
+ return { ok: true, data };
240
+ } catch (error) {
241
+ logger.error('Unexpected error creating event', { error, formData: { ...formData, password: '[REDACTED]' } });
242
+ return {
243
+ ok: false,
244
+ error: {
245
+ code: 'UNKNOWN_ERROR',
246
+ message: 'An unexpected error occurred. Please try again.',
247
+ },
248
+ };
249
+ }
250
+ }
251
+
252
+ // Usage in component
253
+ async function handleSubmit(formData: EventFormData) {
254
+ setIsSubmitting(true);
255
+ const result = await submitEventForm(formData);
256
+
257
+ if (result.ok) {
258
+ toast.success('Event created successfully');
259
+ navigate(`/events/${result.data.id}`);
260
+ } else {
261
+ // Handle different error types
262
+ if (result.error.code === 'VALIDATION_ERROR') {
263
+ // Show field-level errors
264
+ setFieldErrors(result.error.details);
265
+ } else {
266
+ // Show general error
267
+ toast.error(result.error.message);
268
+ }
269
+ }
270
+
271
+ setIsSubmitting(false);
272
+ }
273
+ ```
274
+
275
+ #### Pattern 3: Error Boundaries (React)
276
+
277
+ **Use for:** Catching React component errors
278
+
279
+ ```tsx
280
+ // ✅ CORRECT - Error boundary component
281
+ import { ErrorBoundary } from '@jmruthers/pace-core';
282
+
283
+ function App() {
284
+ return (
285
+ <ErrorBoundary
286
+ fallback={<ErrorFallback />}
287
+ onError={(error, errorInfo) => {
288
+ logger.error('React error boundary caught error', { error, errorInfo });
289
+ }}
290
+ >
291
+ <YourApp />
292
+ </ErrorBoundary>
293
+ );
294
+ }
295
+ ```
296
+
297
+ #### Pattern 4: Async Error Handling
298
+
299
+ **Use for:** Async operations with proper error handling
300
+
301
+ ```typescript
302
+ // ✅ CORRECT - Async with error handling
303
+ async function loadData() {
304
+ try {
305
+ setIsLoading(true);
306
+ const result = await fetchData();
307
+ if (!result.ok) {
308
+ throw new Error(result.error.message);
309
+ }
310
+ setData(result.data);
311
+ } catch (error) {
312
+ handleError(error);
313
+ } finally {
314
+ setIsLoading(false);
315
+ }
316
+ }
317
+ ```
318
+
319
+ ### Error Messages
320
+
321
+ #### User-Facing Messages
322
+
323
+ **MUST:**
324
+ - Be user-friendly and actionable
325
+ - Not expose internal details (SQL, stack traces, file paths)
326
+ - Provide context when helpful
327
+ - Be consistent in tone and style
328
+
329
+ **Examples:**
330
+ ```typescript
331
+ // ✅ CORRECT - User-friendly
332
+ 'Unable to save changes. Please try again.'
333
+ 'This field is required.'
334
+ 'Invalid email address format.'
335
+
336
+ // ❌ WRONG - Exposes internals
337
+ 'SQLSTATE[23000]: Integrity constraint violation'
338
+ 'Cannot read property "data" of undefined'
339
+ '/app/src/services/api.ts:123:45'
340
+ ```
341
+
342
+ #### Logging Messages
343
+
344
+ **MUST:**
345
+ - Include error context (user ID, operation, etc.)
346
+ - Not log sensitive data (passwords, tokens, PII)
347
+ - Use structured logging when possible
348
+ - Include error codes for correlation
349
+
350
+ **Examples:**
351
+ ```typescript
352
+ // ✅ CORRECT - Structured logging
353
+ logger.error('Failed to save user', {
354
+ userId: user.id,
355
+ operation: 'updateUser',
356
+ errorCode: error.code,
357
+ timestamp: new Date().toISOString(),
358
+ });
359
+
360
+ // ❌ WRONG - Logs sensitive data
361
+ logger.error('Failed to save user', {
362
+ password: user.password, // NEVER log passwords
363
+ token: authToken, // NEVER log tokens
364
+ ssn: user.ssn, // NEVER log PII
365
+ });
366
+ ```
367
+
368
+ ### Error Recovery
369
+
370
+ #### Retry Logic
371
+
372
+ **Use for:** Transient errors (network, timeouts)
373
+
374
+ ```typescript
375
+ // ✅ CORRECT - Retry with exponential backoff
376
+ async function fetchWithRetry<T>(
377
+ fn: () => Promise<Result<T>>,
378
+ maxRetries = 3
379
+ ): Promise<Result<T>> {
380
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
381
+ const result = await fn();
382
+ if (result.ok) {
383
+ return result;
384
+ }
385
+
386
+ // Don't retry on client errors (4xx)
387
+ if (result.error.code?.startsWith('4')) {
388
+ return result;
389
+ }
390
+
391
+ // Exponential backoff
392
+ await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
393
+ }
394
+
395
+ return { ok: false, error: { code: 'MAX_RETRIES', message: 'Operation failed after retries' } };
396
+ }
397
+ ```
398
+
399
+ **Real-World Example: File Upload with Retry**
400
+
401
+ ```typescript
402
+ // ✅ CORRECT - File upload with retry and progress tracking
403
+ async function uploadFileWithRetry(
404
+ file: File,
405
+ onProgress?: (progress: number) => void
406
+ ): Promise<Result<{ url: string }>> {
407
+ const maxRetries = 3;
408
+
409
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
410
+ try {
411
+ const formData = new FormData();
412
+ formData.append('file', file);
413
+
414
+ const xhr = new XMLHttpRequest();
415
+
416
+ // Track upload progress
417
+ xhr.upload.addEventListener('progress', (e) => {
418
+ if (e.lengthComputable && onProgress) {
419
+ const progress = (e.loaded / e.total) * 100;
420
+ onProgress(progress);
421
+ }
422
+ });
423
+
424
+ const result = await new Promise<Result<{ url: string }>>((resolve) => {
425
+ xhr.addEventListener('load', () => {
426
+ if (xhr.status === 200) {
427
+ resolve({ ok: true, data: JSON.parse(xhr.responseText) });
428
+ } else {
429
+ resolve({
430
+ ok: false,
431
+ error: {
432
+ code: `HTTP_${xhr.status}`,
433
+ message: 'File upload failed. Please try again.',
434
+ },
435
+ });
436
+ }
437
+ });
438
+
439
+ xhr.addEventListener('error', () => {
440
+ resolve({
441
+ ok: false,
442
+ error: {
443
+ code: 'NETWORK_ERROR',
444
+ message: 'Network error. Please check your connection.',
445
+ },
446
+ });
447
+ });
448
+
449
+ xhr.addEventListener('timeout', () => {
450
+ resolve({
451
+ ok: false,
452
+ error: {
453
+ code: 'TIMEOUT',
454
+ message: 'Upload timed out. Please try again.',
455
+ },
456
+ });
457
+ });
458
+
459
+ xhr.open('POST', '/api/upload');
460
+ xhr.send(formData);
461
+ });
462
+
463
+ if (result.ok) {
464
+ return result;
465
+ }
466
+
467
+ // Don't retry on client errors (4xx) or permission errors
468
+ if (result.error.code?.startsWith('HTTP_4') || result.error.code === 'PERMISSION_DENIED') {
469
+ return result;
470
+ }
471
+
472
+ // Exponential backoff before retry
473
+ if (attempt < maxRetries - 1) {
474
+ const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
475
+ await new Promise(resolve => setTimeout(resolve, delay));
476
+ }
477
+ } catch (error) {
478
+ logger.error('File upload error', { error, attempt, fileName: file.name });
479
+
480
+ if (attempt === maxRetries - 1) {
481
+ return {
482
+ ok: false,
483
+ error: {
484
+ code: 'UPLOAD_FAILED',
485
+ message: 'File upload failed after multiple attempts. Please try again later.',
486
+ },
487
+ };
488
+ }
489
+ }
490
+ }
491
+
492
+ return {
493
+ ok: false,
494
+ error: {
495
+ code: 'MAX_RETRIES',
496
+ message: 'File upload failed after multiple attempts.',
497
+ },
498
+ };
499
+ }
500
+
501
+ // Usage in component
502
+ function FileUploader() {
503
+ const [uploadProgress, setUploadProgress] = useState(0);
504
+ const [isUploading, setIsUploading] = useState(false);
505
+
506
+ async function handleFileSelect(file: File) {
507
+ setIsUploading(true);
508
+ setUploadProgress(0);
509
+
510
+ const result = await uploadFileWithRetry(file, (progress) => {
511
+ setUploadProgress(progress);
512
+ });
513
+
514
+ if (result.ok) {
515
+ toast.success('File uploaded successfully');
516
+ setFileUrl(result.data.url);
517
+ } else {
518
+ toast.error(result.error.message);
519
+ }
520
+
521
+ setIsUploading(false);
522
+ }
523
+
524
+ return (
525
+ <div>
526
+ <input type="file" onChange={(e) => e.target.files?.[0] && handleFileSelect(e.target.files[0])} />
527
+ {isUploading && <ProgressBar value={uploadProgress} />}
528
+ </div>
529
+ );
530
+ }
531
+ ```
532
+
533
+ #### Fallback Values
534
+
535
+ **Use for:** Non-critical data that can have defaults
536
+
537
+ ```typescript
538
+ // ✅ CORRECT - Fallback to default
539
+ const userPreferences = await fetchUserPreferences().catch(() => ({
540
+ theme: 'light',
541
+ language: 'en',
542
+ }));
543
+ ```
544
+
545
+ #### Graceful Degradation
546
+
547
+ **Use for:** Features that can work without optional data
548
+
549
+ ```typescript
550
+ // ✅ CORRECT - Graceful degradation
551
+ async function loadDashboard() {
552
+ const [events, stats, preferences] = await Promise.allSettled([
553
+ fetchEvents(),
554
+ fetchStats(),
555
+ fetchPreferences(),
556
+ ]);
557
+
558
+ // Use data that loaded successfully
559
+ if (events.status === 'fulfilled') {
560
+ setEvents(events.value);
561
+ } else {
562
+ logger.warn('Events failed to load', events.reason);
563
+ setEvents([]); // Fallback to empty array
564
+ }
565
+
566
+ // Similar for stats and preferences
567
+ }
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Performance Optimization
573
+
574
+ ### React Performance
575
+
576
+ #### Memoization
577
+
578
+ **Use `useMemo` for expensive computations:**
579
+
580
+ ```tsx
581
+ // ✅ CORRECT - Memoize expensive computation
582
+ const expensiveValue = useMemo(() => {
583
+ return computeExpensiveValue(data);
584
+ }, [data]);
585
+
586
+ // ❌ WRONG - Recomputes on every render
587
+ const expensiveValue = computeExpensiveValue(data);
588
+ ```
589
+
590
+ **Use `useCallback` for stable function references:**
591
+
592
+ ```tsx
593
+ // ✅ CORRECT - Stable callback reference
594
+ const handleClick = useCallback(() => {
595
+ doSomething(id);
596
+ }, [id]);
597
+
598
+ // ❌ WRONG - New function on every render
599
+ const handleClick = () => {
600
+ doSomething(id);
601
+ };
602
+ ```
603
+
604
+ **Use `React.memo` for expensive components:**
605
+
606
+ ```tsx
607
+ // ✅ CORRECT - Memoize component
608
+ const ExpensiveComponent = React.memo(({ data }) => {
609
+ return <div>{/* expensive rendering */}</div>;
610
+ });
611
+
612
+ // ❌ WRONG - Re-renders unnecessarily
613
+ function ExpensiveComponent({ data }) {
614
+ return <div>{/* expensive rendering */}</div>;
615
+ }
616
+ ```
617
+
618
+ #### Avoiding Unnecessary Re-renders
619
+
620
+ **Don't create new objects/arrays in render:**
621
+
622
+ ```tsx
623
+ // ❌ WRONG - New object on every render
624
+ function Component({ items }) {
625
+ const config = { items, enabled: true };
626
+ return <Child config={config} />;
627
+ }
628
+
629
+ // ✅ CORRECT - Memoize object
630
+ function Component({ items }) {
631
+ const config = useMemo(() => ({ items, enabled: true }), [items]);
632
+ return <Child config={config} />;
633
+ }
634
+ ```
635
+
636
+ #### Code Splitting
637
+
638
+ **Lazy load heavy components:**
639
+
640
+ ```tsx
641
+ // ✅ CORRECT - Lazy load
642
+ import { lazy, Suspense } from 'react';
643
+ const HeavyComponent = lazy(() => import('./HeavyComponent'));
644
+
645
+ function App() {
646
+ return (
647
+ <Suspense fallback={<Loading />}>
648
+ <HeavyComponent />
649
+ </Suspense>
650
+ );
651
+ }
652
+ ```
653
+
654
+ ### Database Performance
655
+
656
+ #### Query Optimization
657
+
658
+ **Use indexes for frequently queried columns:**
659
+
660
+ ```sql
661
+ -- ✅ CORRECT - Index on frequently queried column
662
+ CREATE INDEX idx_users_organisation_id ON users(organisation_id);
663
+ CREATE INDEX idx_events_organisation_id_event_id ON events(organisation_id, event_id);
664
+ ```
665
+
666
+ **Avoid N+1 queries:**
667
+
668
+ ```tsx
669
+ // ❌ WRONG - N+1 queries
670
+ const events = await fetchEvents();
671
+ for (const event of events) {
672
+ const users = await fetchEventUsers(event.id); // N queries!
673
+ }
674
+
675
+ // ✅ CORRECT - Single query with join
676
+ const eventsWithUsers = await fetchEventsWithUsers(); // 1 query
677
+ ```
678
+
679
+ **Use RPC functions for complex queries:**
680
+
681
+ ```tsx
682
+ // ✅ CORRECT - RPC for complex query
683
+ const { data } = await supabase.rpc('data_events_list', {
684
+ organisation_id: orgId
685
+ });
686
+
687
+ // ❌ AVOID - Complex client-side query
688
+ const { data } = await supabase
689
+ .from('events')
690
+ .select('*, users(*), organisations(*)')
691
+ .eq('organisation_id', orgId);
692
+ ```
693
+
694
+ ### Caching Strategies
695
+
696
+ #### TanStack Query Configuration
697
+
698
+ **Configure appropriate cache times:**
699
+
700
+ ```tsx
701
+ // ✅ CORRECT - Configure cache
702
+ const queryClient = new QueryClient({
703
+ defaultOptions: {
704
+ queries: {
705
+ staleTime: 5 * 60 * 1000, // 5 minutes
706
+ gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
707
+ retry: 1,
708
+ refetchOnWindowFocus: false, // Prevent unnecessary refetches
709
+ },
710
+ },
711
+ });
712
+ ```
713
+
714
+ **Use query keys effectively:**
715
+
716
+ ```tsx
717
+ // ✅ CORRECT - Specific query keys
718
+ const { data } = useQuery({
719
+ queryKey: ['events', organisationId, eventId],
720
+ queryFn: () => fetchEvent(organisationId, eventId),
721
+ });
722
+
723
+ // ❌ WRONG - Too generic
724
+ const { data } = useQuery({
725
+ queryKey: ['events'],
726
+ queryFn: () => fetchEvent(organisationId, eventId),
727
+ });
728
+ ```
729
+
730
+ #### React Query Optimizations
731
+
732
+ **Use `keepPreviousData` for pagination:**
733
+
734
+ ```tsx
735
+ // ✅ CORRECT - Keep previous data during pagination
736
+ const { data } = useQuery({
737
+ queryKey: ['events', page],
738
+ queryFn: () => fetchEvents(page),
739
+ keepPreviousData: true,
740
+ });
741
+ ```
742
+
743
+ **Use `select` to transform data efficiently:**
744
+
745
+ ```tsx
746
+ // ✅ CORRECT - Transform in select (only runs when data changes)
747
+ const { data: eventCount } = useQuery({
748
+ queryKey: ['events'],
749
+ queryFn: fetchEvents,
750
+ select: (data) => data.length,
751
+ });
752
+ ```
753
+
754
+ ### Bundle Size Optimization
755
+
756
+ #### Tree Shaking
757
+
758
+ **Use named imports:**
759
+
760
+ ```tsx
761
+ // ✅ CORRECT - Tree-shakeable
762
+ import { Button, Card } from '@jmruthers/pace-core';
763
+
764
+ // ❌ WRONG - Imports entire library
765
+ import * as PaceCore from '@jmruthers/pace-core';
766
+ ```
767
+
768
+ #### Dynamic Imports
769
+
770
+ **Lazy load heavy dependencies:**
771
+
772
+ ```tsx
773
+ // ✅ CORRECT - Dynamic import
774
+ const HeavyLibrary = lazy(() => import('heavy-library'));
775
+
776
+ // ❌ WRONG - Eager import
777
+ import HeavyLibrary from 'heavy-library';
778
+ ```
779
+
780
+ ### Performance Monitoring
781
+
782
+ #### Measuring Performance
783
+
784
+ **Use React DevTools Profiler:**
785
+
786
+ 1. Open React DevTools
787
+ 2. Go to Profiler tab
788
+ 3. Record a session
789
+ 4. Identify slow components
790
+ 5. Optimize based on findings
791
+
792
+ **Use browser DevTools:**
793
+
794
+ 1. Open Performance tab
795
+ 2. Record page load
796
+ 3. Identify bottlenecks
797
+ 4. Optimize critical rendering path
798
+
799
+ #### Performance Metrics
800
+
801
+ **Track these metrics:**
802
+
803
+ - **Time to First Byte (TTFB)** - < 200ms
804
+ - **First Contentful Paint (FCP)** - < 1.8s
805
+ - **Largest Contentful Paint (LCP)** - < 2.5s
806
+ - **Time to Interactive (TTI)** - < 3.8s
807
+ - **Cumulative Layout Shift (CLS)** - < 0.1
808
+
809
+ ---
810
+
811
+ ## CI/CD Integration
812
+
813
+ ### Principles
814
+
815
+ 1. **Automate everything** - Manual steps are error-prone
816
+ 2. **Fail fast** - Catch issues early in the pipeline
817
+ 3. **Test before deploy** - Never deploy untested code
818
+ 4. **Security first** - Scan for vulnerabilities
819
+ 5. **Consistent environments** - Dev, staging, production parity
820
+
821
+ ### CI/CD Pipeline Structure
822
+
823
+ #### Standard Pipeline Stages
824
+
825
+ ```
826
+ 1. Lint & Format Check
827
+ 2. Type Check
828
+ 3. Unit Tests
829
+ 4. Integration Tests
830
+ 5. Build
831
+ 6. Security Scan
832
+ 7. Deploy (staging/production)
833
+ ```
834
+
835
+ ### GitHub Actions Example
836
+
837
+ #### Basic Workflow
838
+
839
+ ```yaml
840
+ # .github/workflows/ci.yml
841
+ name: CI
842
+
843
+ on:
844
+ push:
845
+ branches: [main, develop]
846
+ pull_request:
847
+ branches: [main, develop]
848
+
849
+ jobs:
850
+ quality-checks:
851
+ runs-on: ubuntu-latest
852
+ steps:
853
+ - uses: actions/checkout@v4
854
+
855
+ - name: Setup Node.js
856
+ uses: actions/setup-node@v4
857
+ with:
858
+ node-version: '20'
859
+ cache: 'npm'
860
+
861
+ - name: Install dependencies
862
+ run: npm ci
863
+
864
+ - name: Lint
865
+ run: npm run lint
866
+
867
+ - name: Type check
868
+ run: npm run type-check
869
+
870
+ - name: Format check
871
+ run: npm run format:check
872
+
873
+ - name: Run tests
874
+ run: npm run test
875
+
876
+ - name: Build
877
+ run: npm run build
878
+
879
+ - name: Security scan
880
+ run: npm audit --audit-level=moderate
881
+ ```
882
+
883
+ ### Required CI Checks
884
+
885
+ **Every CI pipeline MUST include:**
886
+
887
+ 1. **Linting** - ESLint with pace-core rules
888
+ ```yaml
889
+ - name: Lint
890
+ run: npm run lint
891
+ ```
892
+
893
+ 2. **Type Checking** - TypeScript compilation
894
+ ```yaml
895
+ - name: Type check
896
+ run: npx tsc --noEmit
897
+ ```
898
+
899
+ 3. **Testing** - Unit and integration tests
900
+ ```yaml
901
+ - name: Run tests
902
+ run: npm run test
903
+ ```
904
+
905
+ 4. **Build** - Verify build succeeds
906
+ ```yaml
907
+ - name: Build
908
+ run: npm run build
909
+ ```
910
+
911
+ ### Package.json Scripts
912
+
913
+ **MUST have these scripts in `package.json`:**
914
+
915
+ ```json
916
+ {
917
+ "scripts": {
918
+ "lint": "eslint .",
919
+ "lint:fix": "eslint . --fix",
920
+ "type-check": "tsc --noEmit",
921
+ "test": "vitest run",
922
+ "test:watch": "vitest",
923
+ "test:coverage": "vitest run --coverage",
924
+ "build": "vite build",
925
+ "format": "prettier --write .",
926
+ "format:check": "prettier --check"
927
+ }
928
+ }
929
+ ```
930
+
931
+ ### Environment Variables
932
+
933
+ #### CI/CD Secrets
934
+
935
+ **Store sensitive values as secrets:**
936
+
937
+ ```yaml
938
+ # .github/workflows/deploy.yml
939
+ env:
940
+ SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
941
+ SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
942
+ VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
943
+ VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
944
+ ```
945
+
946
+ **Never commit secrets to repository:**
947
+ - Use GitHub Secrets
948
+ - Use environment-specific config files
949
+ - Use `.env.example` for documentation
950
+
951
+ ### Database Migrations in CI/CD
952
+
953
+ **For Supabase migrations:**
954
+
955
+ ```yaml
956
+ - name: Run migrations
957
+ run: |
958
+ npx supabase db push
959
+ # Or use Supabase CLI
960
+ supabase migration up
961
+ ```
962
+
963
+ **Best Practices:**
964
+ - Run migrations in staging first
965
+ - Test migrations in CI
966
+ - Never run destructive migrations automatically
967
+ - Use migration review process
968
+
969
+ ### Deployment Strategies
970
+
971
+ #### Staging Deployment
972
+
973
+ **Deploy to staging on every merge to `develop`:**
974
+
975
+ ```yaml
976
+ deploy-staging:
977
+ if: github.ref == 'refs/heads/develop'
978
+ environment: staging
979
+ steps:
980
+ - name: Deploy to Staging
981
+ run: |
982
+ # Deploy to staging environment
983
+ ```
984
+
985
+ #### Production Deployment
986
+
987
+ **Deploy to production only from `main` branch:**
988
+
989
+ ```yaml
990
+ deploy-production:
991
+ if: github.ref == 'refs/heads/main'
992
+ environment: production
993
+ steps:
994
+ - name: Deploy to Production
995
+ run: |
996
+ # Deploy to production environment
997
+ ```
998
+
999
+ ---
1000
+
1001
+ ## Checklists
1002
+
1003
+ ### Error Handling Checklist
1004
+
1005
+ Before committing code with error handling, verify:
1006
+
1007
+ - [ ] User-facing error messages are friendly and actionable
1008
+ - [ ] No internal details exposed in user messages
1009
+ - [ ] Type-safe error handling (no `any` types)
1010
+ - [ ] Errors are logged with context (no sensitive data)
1011
+ - [ ] Recovery paths provided when possible
1012
+ - [ ] Error shapes are consistent
1013
+ - [ ] Error boundaries used for React components
1014
+ - [ ] Async operations have proper error handling
1015
+ - [ ] Validation errors use Zod or similar
1016
+ - [ ] Network errors handled gracefully
1017
+
1018
+ ### Performance Checklist
1019
+
1020
+ Before committing performance-sensitive code, verify:
1021
+
1022
+ - [ ] Expensive computations memoized with `useMemo`
1023
+ - [ ] Callbacks stable with `useCallback`
1024
+ - [ ] Components memoized with `React.memo` when appropriate
1025
+ - [ ] No new objects/arrays created in render
1026
+ - [ ] Heavy components lazy loaded
1027
+ - [ ] Queries use indexes appropriately
1028
+ - [ ] No N+1 query patterns
1029
+ - [ ] TanStack Query configured with appropriate cache times
1030
+ - [ ] Bundle size optimized (tree shaking, code splitting)
1031
+ - [ ] Performance metrics measured and acceptable
1032
+
1033
+ ### CI/CD Checklist
1034
+
1035
+ Before setting up CI/CD, verify:
1036
+
1037
+ - [ ] Lint check configured
1038
+ - [ ] Type check configured
1039
+ - [ ] Tests run in CI
1040
+ - [ ] Build succeeds in CI
1041
+ - [ ] Security scan configured
1042
+ - [ ] Environment variables set as secrets
1043
+ - [ ] Staging deployment configured
1044
+ - [ ] Production deployment configured
1045
+ - [ ] Migration strategy defined
1046
+ - [ ] Rollback plan documented
1047
+
1048
+ ---
1049
+
1050
+ ## Common Mistakes to Avoid
1051
+
1052
+ ### Error Handling
1053
+
1054
+ ```typescript
1055
+ // ❌ WRONG - Expose internal details
1056
+ toast.error(error.message); // May contain SQL, stack traces, etc.
1057
+
1058
+ // ❌ WRONG - Use any for errors
1059
+ catch (error: any) {
1060
+ console.log(error.message);
1061
+ }
1062
+
1063
+ // ❌ WRONG - Log sensitive data
1064
+ logger.error('Login failed', { password, token });
1065
+
1066
+ // ❌ WRONG - Ignore errors
1067
+ try {
1068
+ await riskyOperation();
1069
+ } catch (error) {
1070
+ // Silent failure
1071
+ }
1072
+ ```
1073
+
1074
+ ### Performance
1075
+
1076
+ ```tsx
1077
+ // ❌ WRONG - Create new objects in render
1078
+ function Component({ items }) {
1079
+ return <Child config={{ items, enabled: true }} />;
1080
+ }
1081
+
1082
+ // ❌ WRONG - Use inline functions
1083
+ <Button onClick={() => handleClick(id)}>Click</Button>
1084
+
1085
+ // ❌ WRONG - Over-memoize
1086
+ const simpleValue = useMemo(() => items.length, [items]); // Unnecessary
1087
+ ```
1088
+
1089
+ ---
1090
+
1091
+ ## Related Documentation
1092
+
1093
+ - [Standards Overview](./0-standards-overview.md) - Standards system overview
1094
+ - [Security & RBAC](./6-security-rbac-standards.md) - RLS performance requirements
1095
+ - [Code Quality](./4-code-quality-standards.md) - React performance patterns
1096
+ - [API & Tech Stack](./7-api-tech-stack-standards.md) - TanStack Query configuration
1097
+
1098
+ ---
1099
+
1100
+ **Last Updated:** 2025-01-28
1101
+ **Version:** 2.0.0
1102
+ **Applies to:** All pace-core and consuming apps