@jmruthers/pace-core 0.6.1 → 0.6.3

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 (549) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +46 -87
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
  17. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
  18. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
  19. package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
  20. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  21. package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
  22. package/dist/chunk-2T2IG7T7.js.map +1 -0
  23. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  24. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  25. package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
  26. package/dist/chunk-6Z7LTB3D.js.map +1 -0
  27. package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
  28. package/dist/chunk-CNCQDFLN.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
  31. package/dist/chunk-DWUBLJJM.js.map +1 -0
  32. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  33. package/dist/chunk-FFQEQTNW.js.map +1 -0
  34. package/dist/chunk-FMUCXFII.js +76 -0
  35. package/dist/chunk-FMUCXFII.js.map +1 -0
  36. package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
  37. package/dist/chunk-HFZBI76P.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
  43. package/dist/chunk-M7MPQISP.js.map +1 -0
  44. package/dist/chunk-PQBSKX33.js +7793 -0
  45. package/dist/chunk-PQBSKX33.js.map +1 -0
  46. package/dist/chunk-QRPVRXYT.js +226 -0
  47. package/dist/chunk-QRPVRXYT.js.map +1 -0
  48. package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
  49. package/dist/chunk-RWEBCB47.js.map +1 -0
  50. package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
  51. package/dist/chunk-YDQHOZNA.js.map +1 -0
  52. package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
  53. package/dist/chunk-ZNIWI3UC.js.map +1 -0
  54. package/dist/components.d.ts +5 -5
  55. package/dist/components.js +18 -16
  56. package/dist/components.js.map +1 -1
  57. package/dist/contextValidator-3JNZKUTX.js +9 -0
  58. package/dist/contextValidator-3JNZKUTX.js.map +1 -0
  59. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  60. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  61. package/dist/hooks.d.ts +55 -122
  62. package/dist/hooks.js +10 -13
  63. package/dist/hooks.js.map +1 -1
  64. package/dist/index.d.ts +60 -13
  65. package/dist/index.js +30 -25
  66. package/dist/index.js.map +1 -1
  67. package/dist/providers.d.ts +21 -3
  68. package/dist/providers.js +4 -3
  69. package/dist/rbac/index.d.ts +210 -139
  70. package/dist/rbac/index.js +17 -13
  71. package/dist/styles/index.js +1 -1
  72. package/dist/theming/runtime.d.ts +1 -13
  73. package/dist/theming/runtime.js +2 -2
  74. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  75. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  76. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  77. package/dist/types.d.ts +2 -2
  78. package/dist/types.js +1 -1
  79. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
  80. package/dist/utils.d.ts +4 -5
  81. package/dist/utils.js +17 -19
  82. package/dist/utils.js.map +1 -1
  83. package/docs/api/README.md +21 -17
  84. package/docs/api/modules.md +4191 -2967
  85. package/docs/architecture/database-schema-requirements.md +161 -0
  86. package/docs/components/context-selector.md +126 -0
  87. package/docs/core-concepts/rbac-system.md +3 -3
  88. package/docs/documentation-index.md +2 -4
  89. package/docs/getting-started/cursor-rules.md +2 -1
  90. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  91. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  92. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  93. package/docs/migration/README.md +52 -6
  94. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  95. package/docs/migration/database-changes-december-2025.md +3 -3
  96. package/docs/pace-mint-fix-auto-selection.md +218 -0
  97. package/docs/pace-mint-rbac-setup.md +391 -0
  98. package/docs/rbac/event-based-apps.md +1 -1
  99. package/docs/rbac/getting-started.md +1 -1
  100. package/docs/rbac/quick-start.md +1 -1
  101. package/docs/rbac/secure-client-protection.md +330 -0
  102. package/docs/standards/README.md +1 -0
  103. package/package.json +4 -3
  104. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  105. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  106. package/scripts/audit/core/checks/bundle.cjs +142 -0
  107. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
  108. package/scripts/audit/core/checks/config.cjs +54 -0
  109. package/scripts/audit/core/checks/coverage.cjs +84 -0
  110. package/scripts/audit/core/checks/dependencies.cjs +985 -0
  111. package/scripts/audit/core/checks/documentation.cjs +268 -0
  112. package/scripts/audit/core/checks/environment.cjs +116 -0
  113. package/scripts/audit/core/checks/error-handling.cjs +340 -0
  114. package/scripts/audit/core/checks/forms.cjs +172 -0
  115. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  116. package/scripts/audit/core/checks/hooks.cjs +334 -0
  117. package/scripts/audit/core/checks/imports.cjs +244 -0
  118. package/scripts/audit/core/checks/performance.cjs +325 -0
  119. package/scripts/audit/core/checks/routes.cjs +117 -0
  120. package/scripts/audit/core/checks/state.cjs +130 -0
  121. package/scripts/audit/core/checks/structure.cjs +65 -0
  122. package/scripts/audit/core/checks/style.cjs +584 -0
  123. package/scripts/audit/core/checks/testing.cjs +122 -0
  124. package/scripts/audit/core/checks/typescript.cjs +61 -0
  125. package/scripts/audit/core/scanner.cjs +199 -0
  126. package/scripts/audit/core/utils.cjs +137 -0
  127. package/scripts/audit/index.cjs +223 -0
  128. package/scripts/audit/reporters/console.cjs +151 -0
  129. package/scripts/audit/reporters/json.cjs +54 -0
  130. package/scripts/audit/reporters/markdown.cjs +124 -0
  131. package/scripts/audit-consuming-app.cjs +61 -936
  132. package/scripts/build-docs/build-decision.js +240 -0
  133. package/scripts/build-docs/cache-utils.js +105 -0
  134. package/scripts/build-docs/content-normalization.js +150 -0
  135. package/scripts/build-docs/file-utils.js +105 -0
  136. package/scripts/build-docs/git-utils.js +86 -0
  137. package/scripts/build-docs/hash-utils.js +116 -0
  138. package/scripts/build-docs/typedoc-runner.js +220 -0
  139. package/scripts/build-docs-incremental.js +77 -913
  140. package/scripts/utils/command-runner.js +16 -11
  141. package/scripts/validate-formats.js +61 -56
  142. package/scripts/validate-master.js +74 -69
  143. package/scripts/validate-pre-publish.js +70 -65
  144. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  145. package/src/components/Alert/Alert.test.tsx +12 -18
  146. package/src/components/Alert/Alert.tsx +5 -7
  147. package/src/components/Avatar/Avatar.test.tsx +4 -4
  148. package/src/components/Badge/Badge.tsx +14 -0
  149. package/src/components/Button/Button.tsx +22 -0
  150. package/src/components/Calendar/Calendar.tsx +8 -2
  151. package/src/components/Card/Card.tsx +4 -0
  152. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  153. package/src/components/Checkbox/Checkbox.tsx +2 -2
  154. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  155. package/src/components/ContextSelector/index.ts +3 -0
  156. package/src/components/DataTable/DataTable.tsx +38 -4
  157. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  158. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  159. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  160. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  161. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  162. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  163. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  164. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  165. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  166. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  167. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  168. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  169. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  170. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  171. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  172. package/src/components/DataTable/components/EditFields.tsx +307 -0
  173. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  174. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  176. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  177. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  178. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  179. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  180. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  181. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  182. package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
  183. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  184. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  185. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  186. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  187. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  188. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  189. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  190. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  191. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  192. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  193. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  194. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  195. package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
  196. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  197. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  198. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  199. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  200. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  201. package/src/components/DataTable/styles.ts +6 -6
  202. package/src/components/DataTable/types.ts +6 -10
  203. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  204. package/src/components/DataTable/utils/debugTools.ts +18 -113
  205. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  206. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  207. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  208. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  209. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  210. package/src/components/Dialog/Dialog.tsx +31 -3
  211. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  212. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  213. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  214. package/src/components/ErrorBoundary/index.ts +27 -2
  215. package/src/components/FileDisplay/FileDisplay.tsx +74 -28
  216. package/src/components/FileUpload/FileUpload.tsx +22 -2
  217. package/src/components/Footer/Footer.test.tsx +16 -16
  218. package/src/components/Footer/Footer.tsx +14 -11
  219. package/src/components/Form/Form.tsx +1 -0
  220. package/src/components/Header/Header.test.tsx +43 -73
  221. package/src/components/Header/Header.tsx +59 -49
  222. package/src/components/Input/Input.test.tsx +2 -2
  223. package/src/components/Input/Input.tsx +8 -4
  224. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  225. package/src/components/LoginForm/LoginForm.tsx +4 -0
  226. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  227. package/src/components/NavigationMenu/types.ts +56 -0
  228. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  229. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  230. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  231. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  232. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
  233. package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
  234. package/src/components/PaceAppLayout/README.md +14 -17
  235. package/src/components/PaceAppLayout/test-setup.tsx +3 -4
  236. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  237. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  238. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  239. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  240. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  241. package/src/components/Select/Select.tsx +80 -434
  242. package/src/components/Select/context.ts +23 -0
  243. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  244. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  245. package/src/components/Select/hooks/useSelectState.ts +104 -0
  246. package/src/components/Select/index.ts +9 -1
  247. package/src/components/Select/types.ts +123 -0
  248. package/src/components/Select/utils/text.ts +26 -0
  249. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  250. package/src/components/Switch/Switch.tsx +4 -4
  251. package/src/components/Tabs/Tabs.tsx +1 -1
  252. package/src/components/Toast/Toast.tsx +4 -0
  253. package/src/components/Tooltip/Tooltip.tsx +2 -2
  254. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  255. package/src/components/UserMenu/UserMenu.tsx +21 -18
  256. package/src/components/index.ts +7 -7
  257. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  258. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  259. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  260. package/src/hooks/index.ts +1 -2
  261. package/src/hooks/public/usePublicEvent.ts +4 -0
  262. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  263. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  264. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  265. package/src/hooks/services/useAuth.ts +32 -0
  266. package/src/hooks/services/useCurrentEvent.ts +6 -0
  267. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  268. package/src/hooks/useAppConfig.ts +15 -30
  269. package/src/hooks/useDebounce.ts +9 -0
  270. package/src/hooks/useEventTheme.ts +6 -0
  271. package/src/hooks/useFileDisplay.ts +81 -50
  272. package/src/hooks/useFileReference.ts +25 -7
  273. package/src/hooks/useFileUrl.ts +11 -1
  274. package/src/hooks/useFocusManagement.ts +14 -0
  275. package/src/hooks/useFocusTrap.ts +3 -0
  276. package/src/hooks/useInactivityTracker.ts +3 -0
  277. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  278. package/src/hooks/useOrganisationPermissions.ts +4 -0
  279. package/src/hooks/useOrganisationSecurity.ts +4 -0
  280. package/src/hooks/usePerformanceMonitor.ts +4 -0
  281. package/src/hooks/usePermissionCache.ts +7 -0
  282. package/src/hooks/useQueryCache.ts +12 -1
  283. package/src/hooks/useSessionRestoration.ts +4 -0
  284. package/src/hooks/useStorage.ts +4 -0
  285. package/src/hooks/useToast.ts +1 -1
  286. package/src/index.ts +6 -6
  287. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  288. package/src/providers/services/AuthServiceProvider.tsx +35 -7
  289. package/src/providers/services/EventServiceProvider.tsx +51 -5
  290. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  291. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  292. package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
  293. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  294. package/src/rbac/README.md +1 -1
  295. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  296. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  297. package/src/rbac/adapters.tsx +12 -3
  298. package/src/rbac/api.test.ts +59 -51
  299. package/src/rbac/api.ts +246 -167
  300. package/src/rbac/components/NavigationProvider.tsx +4 -1
  301. package/src/rbac/components/PagePermissionGuard.tsx +185 -17
  302. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  303. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  304. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  305. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  306. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  307. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  308. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  309. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  310. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  311. package/src/rbac/engine.ts +38 -14
  312. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  313. package/src/rbac/hooks/permissions/index.ts +7 -0
  314. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  315. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  316. package/src/rbac/hooks/permissions/useCan.ts +377 -0
  317. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  318. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  319. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  320. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  321. package/src/rbac/hooks/useCan.test.ts +64 -66
  322. package/src/rbac/hooks/usePermissions.ts +14 -995
  323. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  324. package/src/rbac/hooks/useRBAC.ts +36 -37
  325. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  326. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  327. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  328. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  329. package/src/rbac/hooks/useSecureSupabase.ts +27 -7
  330. package/src/rbac/index.ts +7 -0
  331. package/src/rbac/permissions.ts +0 -30
  332. package/src/rbac/secureClient.test.ts +22 -18
  333. package/src/rbac/secureClient.ts +294 -68
  334. package/src/rbac/security.ts +0 -17
  335. package/src/rbac/types.ts +9 -0
  336. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  337. package/src/rbac/utils/clientSecurity.ts +93 -0
  338. package/src/rbac/utils/contextValidator.ts +77 -168
  339. package/src/services/AuthService.ts +39 -7
  340. package/src/services/EventService.ts +186 -54
  341. package/src/services/OrganisationService.ts +81 -14
  342. package/src/services/__tests__/EventService.test.ts +1 -2
  343. package/src/services/base/BaseService.ts +3 -0
  344. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  345. package/src/theming/parseEventColours.ts +5 -19
  346. package/src/types/vitest-globals.d.ts +51 -26
  347. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  348. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  349. package/src/utils/__tests__/index.unit.test.ts +2 -2
  350. package/src/utils/audit/audit.ts +0 -3
  351. package/src/utils/core/cn.ts +1 -1
  352. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  353. package/src/utils/file-reference/index.ts +53 -1
  354. package/src/utils/formatting/formatting.ts +8 -18
  355. package/src/utils/index.ts +0 -1
  356. package/dist/chunk-3QRJFVBR.js.map +0 -1
  357. package/dist/chunk-3XTALGJF.js.map +0 -1
  358. package/dist/chunk-4N5C5XZU.js.map +0 -1
  359. package/dist/chunk-4ZC4GX36.js.map +0 -1
  360. package/dist/chunk-7D4SUZUM.js +0 -38
  361. package/dist/chunk-BYFSK72L.js.map +0 -1
  362. package/dist/chunk-EXUD6RNJ.js +0 -451
  363. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  364. package/dist/chunk-GLK6VM3F.js.map +0 -1
  365. package/dist/chunk-I7PSE6JW.js.map +0 -1
  366. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  367. package/dist/chunk-KNC55RTG.js.map +0 -1
  368. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  369. package/dist/chunk-R77UEZ4E.js.map +0 -1
  370. package/dist/chunk-SQGMNID3.js.map +0 -1
  371. package/dist/chunk-T33XF5ZC.js +0 -12922
  372. package/dist/chunk-T33XF5ZC.js.map +0 -1
  373. package/dist/chunk-XM25TVIE.js.map +0 -1
  374. package/docs/api/classes/ColumnFactory.md +0 -243
  375. package/docs/api/classes/ErrorBoundary.md +0 -144
  376. package/docs/api/classes/InvalidScopeError.md +0 -73
  377. package/docs/api/classes/Logger.md +0 -178
  378. package/docs/api/classes/MissingUserContextError.md +0 -66
  379. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  380. package/docs/api/classes/PermissionDeniedError.md +0 -73
  381. package/docs/api/classes/RBACAuditManager.md +0 -297
  382. package/docs/api/classes/RBACCache.md +0 -322
  383. package/docs/api/classes/RBACEngine.md +0 -171
  384. package/docs/api/classes/RBACError.md +0 -76
  385. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  386. package/docs/api/classes/SecureSupabaseClient.md +0 -160
  387. package/docs/api/classes/StorageUtils.md +0 -328
  388. package/docs/api/enums/FileCategory.md +0 -184
  389. package/docs/api/enums/LogLevel.md +0 -54
  390. package/docs/api/enums/RBACErrorCode.md +0 -228
  391. package/docs/api/enums/RPCFunction.md +0 -118
  392. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  393. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  394. package/docs/api/interfaces/AggregateConfig.md +0 -43
  395. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  396. package/docs/api/interfaces/AvatarProps.md +0 -128
  397. package/docs/api/interfaces/BadgeProps.md +0 -27
  398. package/docs/api/interfaces/ButtonProps.md +0 -53
  399. package/docs/api/interfaces/CalendarProps.md +0 -70
  400. package/docs/api/interfaces/CardProps.md +0 -66
  401. package/docs/api/interfaces/ColorPalette.md +0 -7
  402. package/docs/api/interfaces/ColorShade.md +0 -66
  403. package/docs/api/interfaces/ComplianceResult.md +0 -30
  404. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  405. package/docs/api/interfaces/DataRecord.md +0 -11
  406. package/docs/api/interfaces/DataTableAction.md +0 -249
  407. package/docs/api/interfaces/DataTableColumn.md +0 -504
  408. package/docs/api/interfaces/DataTableProps.md +0 -625
  409. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  410. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  411. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  412. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  413. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  414. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  415. package/docs/api/interfaces/ExportColumn.md +0 -90
  416. package/docs/api/interfaces/ExportOptions.md +0 -126
  417. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  418. package/docs/api/interfaces/FileMetadata.md +0 -129
  419. package/docs/api/interfaces/FileReference.md +0 -118
  420. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  421. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  422. package/docs/api/interfaces/FileUploadProps.md +0 -293
  423. package/docs/api/interfaces/FooterProps.md +0 -105
  424. package/docs/api/interfaces/FormFieldProps.md +0 -166
  425. package/docs/api/interfaces/FormProps.md +0 -113
  426. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  427. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  428. package/docs/api/interfaces/InputProps.md +0 -53
  429. package/docs/api/interfaces/LabelProps.md +0 -107
  430. package/docs/api/interfaces/LoggerConfig.md +0 -62
  431. package/docs/api/interfaces/LoginFormProps.md +0 -184
  432. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  433. package/docs/api/interfaces/NavigationContextType.md +0 -164
  434. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  435. package/docs/api/interfaces/NavigationItem.md +0 -120
  436. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  437. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  438. package/docs/api/interfaces/Organisation.md +0 -140
  439. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  440. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  441. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  442. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  443. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
  444. package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
  445. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  446. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  447. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  448. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  449. package/docs/api/interfaces/PaletteData.md +0 -41
  450. package/docs/api/interfaces/ParsedAddress.md +0 -120
  451. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  452. package/docs/api/interfaces/ProgressProps.md +0 -42
  453. package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
  454. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  455. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  456. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
  457. package/docs/api/interfaces/QuickFix.md +0 -52
  458. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  459. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  460. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  461. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  462. package/docs/api/interfaces/RBACConfig.md +0 -133
  463. package/docs/api/interfaces/RBACContext.md +0 -52
  464. package/docs/api/interfaces/RBACLogger.md +0 -112
  465. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  466. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  467. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  468. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  469. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  470. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  471. package/docs/api/interfaces/RBACResult.md +0 -58
  472. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  473. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  474. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  475. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  476. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  477. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  478. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  479. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  480. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  481. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  482. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  483. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  484. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  485. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  486. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  487. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  488. package/docs/api/interfaces/RouteConfig.md +0 -134
  489. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  490. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  491. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  492. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  493. package/docs/api/interfaces/SetupIssue.md +0 -41
  494. package/docs/api/interfaces/StorageConfig.md +0 -41
  495. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  496. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  497. package/docs/api/interfaces/StorageListOptions.md +0 -99
  498. package/docs/api/interfaces/StorageListResult.md +0 -41
  499. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  500. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  501. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  502. package/docs/api/interfaces/StyleImport.md +0 -19
  503. package/docs/api/interfaces/SwitchProps.md +0 -34
  504. package/docs/api/interfaces/TabsContentProps.md +0 -9
  505. package/docs/api/interfaces/TabsListProps.md +0 -9
  506. package/docs/api/interfaces/TabsProps.md +0 -9
  507. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  508. package/docs/api/interfaces/TextareaProps.md +0 -53
  509. package/docs/api/interfaces/ToastActionElement.md +0 -9
  510. package/docs/api/interfaces/ToastProps.md +0 -9
  511. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
  512. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
  513. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  514. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  515. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
  516. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  517. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  518. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
  519. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  520. package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
  521. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  522. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
  523. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
  524. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  525. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  526. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  527. package/docs/api/interfaces/UserEventAccess.md +0 -118
  528. package/docs/api/interfaces/UserMenuProps.md +0 -86
  529. package/docs/api/interfaces/UserProfile.md +0 -63
  530. package/docs/migration/quick-migration-guide.md +0 -356
  531. package/docs/migration/service-architecture.md +0 -281
  532. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  533. package/src/components/EventSelector/EventSelector.tsx +0 -420
  534. package/src/components/EventSelector/index.ts +0 -3
  535. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  536. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
  537. package/src/components/OrganisationSelector/index.ts +0 -9
  538. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  539. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  540. package/src/hooks/useSecureDataAccess.ts +0 -681
  541. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
  542. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
  543. /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
  544. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  545. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  546. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  547. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  548. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  549. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -0,0 +1,340 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Error Handling Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/ErrorHandling
7
+ *
8
+ * Checks for:
9
+ * - Missing error boundaries
10
+ * - Unhandled promise rejections
11
+ * - Missing error handling in async functions
12
+ * - Silent error swallowing
13
+ * - Missing error states in components
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const { getRelativePath, getLineNumber } = require('../utils.cjs');
19
+
20
+ const errorHandlingCheck = {
21
+ name: 'error-handling',
22
+ description: 'Error handling patterns (error boundaries, async error handling)',
23
+ severity: 'warning',
24
+
25
+ async run(context) {
26
+ const { projectRoot, files } = context;
27
+ const issues = [];
28
+ const warnings = [];
29
+ const suggestions = [];
30
+
31
+ if (!files || files.length === 0) {
32
+ return { issues, warnings, suggestions };
33
+ }
34
+
35
+ // Skip if this is the pace-core repository itself
36
+ // Detect pace-core repository by checking if packages/core exists
37
+ const packagesCorePath = path.join(projectRoot, 'packages', 'core');
38
+ const isPaceCoreRepository = fs.existsSync(packagesCorePath);
39
+
40
+ // Check for ErrorBoundary usage in main app file
41
+ let hasErrorBoundary = false;
42
+ const mainFiles = ['main.tsx', 'main.ts', 'App.tsx', 'App.ts', 'index.tsx', 'index.ts'];
43
+
44
+ // First, specifically check the main app file for ErrorBoundary usage
45
+ // This must be done before the general file loop to ensure we check the actual main file
46
+ // Previously, the check would skip src/ files (thinking they were demo apps) and only
47
+ // check if ErrorBoundary appeared anywhere in the codebase. Now we specifically check
48
+ // the main.tsx file for proper ErrorBoundary import and JSX usage patterns.
49
+ if (!isPaceCoreRepository) {
50
+ const mainFile = mainFiles.find(file => {
51
+ const filePath = path.join(projectRoot, 'src', file);
52
+ return fs.existsSync(filePath);
53
+ });
54
+
55
+ if (mainFile) {
56
+ const mainFilePath = path.join(projectRoot, 'src', mainFile);
57
+ try {
58
+ const mainContent = fs.readFileSync(mainFilePath, 'utf8');
59
+
60
+ // Check for ErrorBoundary import from pace-core
61
+ // Matches: import { ErrorBoundary } from '@jmruthers/pace-core'
62
+ // Matches: import { ..., ErrorBoundary, ... } from '@jmruthers/pace-core'
63
+ // Matches: import { ErrorBoundary } from '@jmruthers/pace-core/components'
64
+ const hasErrorBoundaryImport = /import\s+[^'"]*ErrorBoundary[^'"]*from\s+['"]@jmruthers\/pace-core/.test(mainContent);
65
+
66
+ // Check for ErrorBoundary JSX usage
67
+ // Matches: <ErrorBoundary ...> or <ErrorBoundary>
68
+ const hasErrorBoundaryJSX = /<ErrorBoundary[\s>]/.test(mainContent);
69
+
70
+ // Check if ErrorBoundary wraps the render call or app component
71
+ // Pattern 1: createRoot(...).render(<ErrorBoundary ...)
72
+ // Pattern 2: render(<ErrorBoundary ...)
73
+ // Pattern 3: <ErrorBoundary ...> wrapping App component or createRoot call
74
+ const hasErrorBoundaryInRender = /createRoot\s*\([^)]*\)\s*\.\s*render\s*\(\s*<ErrorBoundary/.test(mainContent) ||
75
+ /\.render\s*\(\s*<ErrorBoundary/.test(mainContent) ||
76
+ /<ErrorBoundary[\s\S]{0,500}?<App[\s>]/.test(mainContent) ||
77
+ /<ErrorBoundary[\s\S]{0,500}?createRoot/.test(mainContent);
78
+
79
+ // ErrorBoundary is present if it's imported AND used in JSX
80
+ // We check both JSX usage and render wrapping to catch all valid patterns
81
+ if (hasErrorBoundaryImport && (hasErrorBoundaryJSX || hasErrorBoundaryInRender)) {
82
+ hasErrorBoundary = true;
83
+ }
84
+ } catch (error) {
85
+ // If we can't read the main file, continue with other checks
86
+ }
87
+ }
88
+ }
89
+
90
+ for (const filePath of files) {
91
+ try {
92
+ const content = fs.readFileSync(filePath, 'utf8');
93
+ const relativePath = getRelativePath(filePath, projectRoot);
94
+ const normalizedPath = relativePath.replace(/\\/g, '/');
95
+ const fileName = path.basename(relativePath);
96
+
97
+ // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
98
+ // Note: We DO check packages/core/ files because error handling issues (missing try/catch, missing .catch()) are real issues that should be fixed
99
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
100
+ if (isRootSrc) {
101
+ continue; // Skip demo app files
102
+ }
103
+
104
+ // Skip scripts directory - utility scripts don't need error handling validation
105
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
106
+ if (isScript) {
107
+ continue; // Skip script files
108
+ }
109
+
110
+ // Check for unhandled promise rejections
111
+ const asyncFunctionPattern = /(async\s+function|const\s+\w+\s*=\s*async|export\s+async\s+function)/g;
112
+ let asyncMatch;
113
+ while ((asyncMatch = asyncFunctionPattern.exec(content)) !== null) {
114
+ const functionStart = asyncMatch.index;
115
+ const beforeAsync = content.substring(Math.max(0, functionStart - 100), functionStart);
116
+ const afterAsync = content.substring(functionStart, Math.min(content.length, functionStart + 1000));
117
+
118
+ // Check if function has try/catch
119
+ const hasTryCatch = /try\s*\{/.test(afterAsync);
120
+
121
+ // Check for await calls
122
+ const hasAwait = /await\s+/.test(afterAsync);
123
+
124
+ // Check if this is an exported library function (designed to throw errors for callers to handle)
125
+ // Look for "export" before the async function declaration
126
+ const isExportedLibraryFunction = /export\s+(async\s+function|function)/.test(beforeAsync + asyncMatch[0]) ||
127
+ asyncMatch[0].includes('export');
128
+
129
+ // Skip pace-core files - library code has different error handling patterns
130
+ // Exported functions are designed to throw errors for callers to handle
131
+ // Internal utility functions may also intentionally propagate errors
132
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
133
+
134
+ if (hasAwait && !hasTryCatch && !isExportedLibraryFunction && !isPaceCorePackage) {
135
+ warnings.push({
136
+ type: 'unhandled-async',
137
+ file: relativePath,
138
+ line: getLineNumber(content, asyncMatch.index),
139
+ message: 'Async function with await calls but no try/catch error handling',
140
+ recommendation: 'Wrap await calls in try/catch blocks to handle errors gracefully'
141
+ });
142
+ }
143
+ }
144
+
145
+ // Check for .then() without .catch()
146
+ // Look for .then() and check if there's a .catch() later in the chain (not just immediately after)
147
+ const thenPattern = /\.then\s*\([^)]*\)/g;
148
+ let thenMatch;
149
+ while ((thenMatch = thenPattern.exec(content)) !== null) {
150
+ // Check if there's a .catch() after this .then() in the promise chain
151
+ // Look ahead up to 500 chars (reasonable for a promise chain)
152
+ const afterThen = content.substring(thenMatch.index, Math.min(content.length, thenMatch.index + 500));
153
+
154
+ // Check if there's a .catch() in the chain
155
+ // Also check for .then().catch() pattern
156
+ const hasCatch = /\.catch\s*\(/.test(afterThen);
157
+
158
+ // Check if the .then() handler has try/catch inside it (valid pattern: .then(async () => { try { ... } catch { ... } }))
159
+ const hasTryCatchInHandler = /\.then\s*\(\s*async\s*\([^)]*\)\s*=>\s*\{[^}]*try\s*\{/.test(afterThen);
160
+
161
+ // Also check if the .then() itself handles errors (returns error object)
162
+ const thenBody = thenMatch[0];
163
+ const handlesErrorInThen = /error|Error|catch/.test(thenBody);
164
+
165
+ // Skip pace-core files - library code has complex patterns that are hard to detect accurately
166
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
167
+
168
+ if (!hasCatch && !handlesErrorInThen && !hasTryCatchInHandler && !isPaceCorePackage) {
169
+ warnings.push({
170
+ type: 'unhandled-promise',
171
+ file: relativePath,
172
+ line: getLineNumber(content, thenMatch.index),
173
+ message: 'Promise chain with .then() but no .catch() handler',
174
+ recommendation: 'Add .catch() handler to handle promise rejections. The .catch() can come after the .then() in the chain.'
175
+ });
176
+ }
177
+ }
178
+
179
+ // Check for console.error without proper error handling
180
+ const consoleErrorPattern = /console\.error\s*\([^)]*\)/g;
181
+ let consoleMatch;
182
+ while ((consoleMatch = consoleErrorPattern.exec(content)) !== null) {
183
+ // Skip console.error in comments/JSDoc (example code)
184
+ const beforeMatch = content.substring(Math.max(0, consoleMatch.index - 500), consoleMatch.index);
185
+ const afterMatch = content.substring(consoleMatch.index, Math.min(content.length, consoleMatch.index + 50));
186
+
187
+ // Get the line number to check if we're in a comment block
188
+ const errorLineNum = getLineNumber(content, consoleMatch.index);
189
+ const lines = content.split('\n');
190
+ const errorLine = lines[errorLineNum - 1]; // Line numbers are 1-indexed
191
+
192
+ // Check if the line starts with * (JSDoc comment block) or // (single line comment)
193
+ const isInCommentLine = /^\s*\*|^\s*\/\//.test(errorLine);
194
+
195
+ // Check if we're inside a /* ... */ or /** ... */ block
196
+ // Find the last /* or /** before this position, and check if */ comes after
197
+ const lastCommentStart = beforeMatch.lastIndexOf('/*');
198
+ const lastCommentEndBefore = beforeMatch.lastIndexOf('*/');
199
+ const firstCommentEndAfter = afterMatch.indexOf('*/');
200
+ // We're in a comment block if there's a /* before and no */ before it (or */ is after)
201
+ const isInCommentBlock = lastCommentStart !== -1 &&
202
+ (lastCommentStart > lastCommentEndBefore || firstCommentEndAfter !== -1);
203
+
204
+ // Check if it's in a comment (JSDoc example or comment block)
205
+ if (isInCommentLine || isInCommentBlock) {
206
+ continue; // Skip JSDoc examples and comment blocks
207
+ }
208
+
209
+ // Skip example files - these are demonstration code, not production code
210
+ const isExampleFile = normalizedPath.includes('/examples/') || normalizedPath.includes('Example.tsx') || normalizedPath.includes('Example.ts');
211
+ if (isExampleFile) {
212
+ continue; // Skip example files
213
+ }
214
+
215
+ // Skip logger utility files - these ARE the error handling mechanism
216
+ const isLoggerUtility = normalizedPath.includes('logger') ||
217
+ normalizedPath.includes('debugLogger') ||
218
+ normalizedPath.includes('rbac/config.ts'); // RBAC config is a logger setup
219
+ if (isLoggerUtility) {
220
+ continue; // Skip logger utilities
221
+ }
222
+
223
+ // Skip ESLint rule files - these are tooling, not application code
224
+ const isEslintRule = normalizedPath.includes('eslint-rules') || normalizedPath.includes('eslint.config');
225
+ if (isEslintRule) {
226
+ continue; // Skip ESLint rules
227
+ }
228
+
229
+ // Skip performance monitoring utilities - these are informational logging
230
+ const isPerformanceUtility = normalizedPath.includes('performance') &&
231
+ (normalizedPath.includes('performanceBudgets') || normalizedPath.includes('performanceBenchmark'));
232
+ if (isPerformanceUtility) {
233
+ continue; // Skip performance monitoring
234
+ }
235
+
236
+ // Check if it's just logging without handling
237
+ // Also check after the console.error to see if error state is set or error is returned
238
+ const afterError = content.substring(consoleMatch.index, Math.min(content.length, consoleMatch.index + 200));
239
+ const hasErrorHandling = beforeMatch.includes('throw') ||
240
+ beforeMatch.includes('return') ||
241
+ beforeMatch.includes('catch') ||
242
+ afterError.includes('throw') || // Check after as well (e.g., console.error then throw)
243
+ afterError.includes('setError') ||
244
+ afterError.includes('error =') ||
245
+ afterError.includes('onError') ||
246
+ afterError.includes('onUploadError');
247
+
248
+ if (!hasErrorHandling) {
249
+ suggestions.push({
250
+ type: 'error-logging-only',
251
+ file: relativePath,
252
+ line: getLineNumber(content, consoleMatch.index),
253
+ message: 'Error is logged but may not be handled properly',
254
+ recommendation: 'Ensure errors are properly handled (thrown, returned, or displayed to users)'
255
+ });
256
+ }
257
+ }
258
+
259
+ // Check for missing error states in components using pace-core hooks
260
+ // Only check React component files
261
+ if (filePath.match(/\.(tsx|jsx)$/)) {
262
+ const paceCoreHooks = ['useUnifiedAuth', 'useOrganisations', 'useEvents', 'useSecureSupabase'];
263
+ paceCoreHooks.forEach(hookName => {
264
+ if (content.includes(hookName)) {
265
+ const hookPattern = new RegExp(`const\\s+[^=]+=\\s+${hookName}\\s*\\(`, 'g');
266
+ let hookMatch;
267
+ while ((hookMatch = hookPattern.exec(content)) !== null) {
268
+ // Check the entire component, not just 500 chars after hook
269
+ // React's Rules of Hooks require all hooks to be called before conditional returns,
270
+ // so error handling may occur later in the component
271
+ const hookLine = getLineNumber(content, hookMatch.index);
272
+ const lines = content.split('\n');
273
+ const hookLineIndex = hookLine - 1;
274
+
275
+ // Get the rest of the component after this hook
276
+ const afterHookContent = lines.slice(hookLineIndex).join('\n');
277
+
278
+ // Check if error is destructured from the hook
279
+ const hookCallLine = lines[hookLineIndex];
280
+ const hasErrorDestructured = /(error|Error|authError|orgError)/.test(hookCallLine);
281
+
282
+ // For useSecureSupabase, check for null checks (it returns null on error)
283
+ const isSecureSupabase = hookName === 'useSecureSupabase';
284
+ const hasNullCheck = isSecureSupabase && /secureSupabase\s*===?\s*null|!secureSupabase/.test(afterHookContent);
285
+
286
+ // Check for error handling patterns in the entire component:
287
+ // 1. Component-level error checks with early returns: if (error) return <ErrorUI />
288
+ // 2. useEffect error handlers: useEffect(() => { if (error) ... }, [error])
289
+ // 3. Null checks for useSecureSupabase: if (secureSupabase === null) ...
290
+ // 4. Error passed to error handling utilities
291
+
292
+ const hasComponentLevelErrorCheck = /if\s*\(\s*(error|authError|orgError|rbacError)/.test(afterHookContent);
293
+ const hasUseEffectErrorHandler = /useEffect\s*\([^)]*\([^)]*\)\s*=>\s*\{[^}]*\b(error|authError|orgError|rbacError)\b[^}]*\}/.test(afterHookContent);
294
+ const hasErrorInDeps = /useEffect\s*\([^)]*,\s*\[[^\]]*\b(error|authError|orgError|rbacError)\b/.test(afterHookContent);
295
+ const hasErrorHandling = /if\s*\(\s*(error|authError|orgError|rbacError)|(error|authError|orgError|rbacError)\s*\?|catch/.test(afterHookContent);
296
+ const hasErrorUtility = /handleSupabaseError|handleError|toast.*error|showError/.test(afterHookContent);
297
+
298
+ // Only flag if error is destructured but not handled anywhere
299
+ if (hasErrorDestructured && !hasComponentLevelErrorCheck && !hasUseEffectErrorHandler &&
300
+ !hasErrorInDeps && !hasErrorHandling && !hasErrorUtility && !hasNullCheck) {
301
+ warnings.push({
302
+ type: 'unhandled-hook-error',
303
+ file: relativePath,
304
+ line: hookLine,
305
+ message: `${hookName} may return an error that is not handled`,
306
+ recommendation: 'Check for error state and handle it appropriately. You can handle errors via: (1) Component-level checks after all hooks: if (error) return <ErrorUI />, (2) useEffect hooks that monitor error states, or (3) For useSecureSupabase, null checks: if (secureSupabase === null) ...'
307
+ });
308
+ }
309
+ }
310
+ }
311
+ });
312
+ }
313
+ } catch (error) {
314
+ // Skip files with errors
315
+ }
316
+ }
317
+
318
+ // Check if ErrorBoundary is used in main app file
319
+ // Skip this check if this is the pace-core repository (root src/ is a demo app)
320
+ if (!hasErrorBoundary && !isPaceCoreRepository) {
321
+ const mainFile = mainFiles.find(file => {
322
+ const filePath = path.join(projectRoot, 'src', file);
323
+ return fs.existsSync(filePath);
324
+ });
325
+
326
+ if (mainFile) {
327
+ suggestions.push({
328
+ type: 'missing-error-boundary',
329
+ file: `src/${mainFile}`,
330
+ message: 'No ErrorBoundary found in main app file',
331
+ recommendation: 'Wrap your app with ErrorBoundary from @jmruthers/pace-core to catch and handle React errors gracefully'
332
+ });
333
+ }
334
+ }
335
+
336
+ return { issues, warnings, suggestions };
337
+ }
338
+ };
339
+
340
+ module.exports = errorHandlingCheck;
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Form Validation Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Forms
7
+ *
8
+ * Checks for:
9
+ * - Missing form validation
10
+ * - Incorrect useZodForm usage
11
+ * - Missing error messages
12
+ * - Form submission without loading states
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const { getRelativePath, getLineNumber } = require('../utils.cjs');
17
+
18
+ const formsCheck = {
19
+ name: 'forms',
20
+ description: 'Form validation patterns (useZodForm, error handling)',
21
+ severity: 'warning',
22
+
23
+ async run(context) {
24
+ const { projectRoot, files } = context;
25
+ const issues = [];
26
+ const warnings = [];
27
+ const suggestions = [];
28
+
29
+ if (!files || files.length === 0) {
30
+ return { issues, warnings, suggestions };
31
+ }
32
+
33
+ for (const filePath of files) {
34
+ try {
35
+ // Only check React component files
36
+ if (!filePath.match(/\.(tsx|jsx)$/)) {
37
+ continue;
38
+ }
39
+
40
+ const content = fs.readFileSync(filePath, 'utf8');
41
+ const relativePath = getRelativePath(filePath, projectRoot);
42
+ const normalizedPath = relativePath.replace(/\\/g, '/');
43
+
44
+ // Skip pace-core package files - forms check is for consuming applications, not the library itself
45
+ // Note: Library components (like Form.tsx, LoginForm.tsx, Select.tsx) ARE the Form components
46
+ // They don't need to use themselves - they provide the form functionality
47
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
48
+ if (isPaceCorePackage) {
49
+ continue; // Skip library files (including examples)
50
+ }
51
+
52
+ // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
53
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
54
+ if (isRootSrc) {
55
+ continue; // Skip demo app files
56
+ }
57
+
58
+ // Skip scripts directory - utility scripts don't need form validation
59
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
60
+ if (isScript) {
61
+ continue; // Skip script files
62
+ }
63
+
64
+ // Check for form elements
65
+ const hasForm = /<form|Form|useZodForm/.test(content);
66
+ if (!hasForm) {
67
+ continue;
68
+ }
69
+
70
+ // Check for useZodForm usage
71
+ if (content.includes('useZodForm')) {
72
+ const zodFormPattern = /const\s+[^=]+=\s+useZodForm\s*\(/g;
73
+ let formMatch;
74
+ while ((formMatch = zodFormPattern.exec(content)) !== null) {
75
+ const afterForm = content.substring(formMatch.index, Math.min(content.length, formMatch.index + 500));
76
+
77
+ // Check if schema is provided
78
+ if (!afterForm.includes('schema:') && !afterForm.includes('schema =')) {
79
+ warnings.push({
80
+ type: 'missing-form-schema',
81
+ file: relativePath,
82
+ line: getLineNumber(content, formMatch.index),
83
+ message: 'useZodForm called without schema',
84
+ recommendation: 'Provide a Zod schema to useZodForm for form validation'
85
+ });
86
+ }
87
+
88
+ // Check if form errors are displayed
89
+ const hasErrorDisplay = /formState\.errors|errors\[|\.error/.test(content);
90
+ if (!hasErrorDisplay) {
91
+ suggestions.push({
92
+ type: 'missing-form-errors',
93
+ file: relativePath,
94
+ line: getLineNumber(content, formMatch.index),
95
+ message: 'Form may not be displaying validation errors',
96
+ recommendation: 'Display form errors to users using formState.errors or FormField error prop'
97
+ });
98
+ }
99
+ }
100
+ }
101
+
102
+ // Check for form submission without loading state
103
+ const onSubmitPattern = /onSubmit\s*=\s*\{[^}]*async/g;
104
+ let submitMatch;
105
+ while ((submitMatch = onSubmitPattern.exec(content)) !== null) {
106
+ const afterSubmit = content.substring(submitMatch.index, Math.min(content.length, submitMatch.index + 1000));
107
+
108
+ // Check if there's a loading state
109
+ const hasLoadingState = /isLoading|loading|isSubmitting|submitting/.test(afterSubmit);
110
+ const hasDisabled = /disabled\s*=\s*\{/.test(content);
111
+
112
+ if (!hasLoadingState && !hasDisabled) {
113
+ suggestions.push({
114
+ type: 'missing-submit-loading',
115
+ file: relativePath,
116
+ line: getLineNumber(content, submitMatch.index),
117
+ message: 'Form submission without loading state',
118
+ recommendation: 'Add loading state during form submission to prevent double-submission and provide user feedback'
119
+ });
120
+ }
121
+ }
122
+
123
+ // Check for native form elements that should use pace-core Form
124
+ const nativeFormPattern = /<form[^>]*>/g;
125
+ let nativeMatch;
126
+ while ((nativeMatch = nativeFormPattern.exec(content)) !== null) {
127
+ const beforeMatch = content.substring(Math.max(0, nativeMatch.index - 100), nativeMatch.index);
128
+ const usesPaceCoreForm = beforeMatch.includes('from \'@jmruthers/pace-core\'') ||
129
+ content.includes('<Form');
130
+
131
+ if (!usesPaceCoreForm) {
132
+ suggestions.push({
133
+ type: 'native-form-element',
134
+ file: relativePath,
135
+ line: getLineNumber(content, nativeMatch.index),
136
+ message: 'Native <form> element detected',
137
+ recommendation: 'Use Form component from @jmruthers/pace-core for consistent styling and validation'
138
+ });
139
+ }
140
+ }
141
+
142
+ // Check for input elements without validation
143
+ const inputPattern = /<input[^>]*>/g;
144
+ let inputMatch;
145
+ while ((inputMatch = inputPattern.exec(content)) !== null) {
146
+ const inputTag = inputMatch[0];
147
+ const hasFormField = content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index).includes('FormField');
148
+ const hasValidation = inputTag.includes('required') ||
149
+ inputTag.includes('pattern') ||
150
+ content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index).includes('register');
151
+
152
+ if (!hasFormField && !hasValidation && !inputTag.includes('type="hidden"')) {
153
+ suggestions.push({
154
+ type: 'unvalidated-input',
155
+ file: relativePath,
156
+ line: getLineNumber(content, inputMatch.index),
157
+ message: 'Input element without validation',
158
+ recommendation: 'Use FormField from @jmruthers/pace-core or add validation to form inputs'
159
+ });
160
+ }
161
+ }
162
+
163
+ } catch (error) {
164
+ // Skip files with errors
165
+ }
166
+ }
167
+
168
+ return { issues, warnings, suggestions };
169
+ }
170
+ };
171
+
172
+ module.exports = formsCheck;
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Heuristic Checks Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Heuristics
7
+ *
8
+ * Code quality heuristics including:
9
+ * - Large files
10
+ * - God objects (files with many exports)
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const heuristicsCheck = {
17
+ name: 'heuristics',
18
+ description: 'Code quality heuristics (large files, god objects)',
19
+ severity: 'suggestion',
20
+
21
+ async run(context) {
22
+ const { projectRoot, files } = context;
23
+ const suggestions = [];
24
+
25
+ if (!files || files.length === 0) {
26
+ return { issues: [], warnings: [], suggestions: [] };
27
+ }
28
+
29
+ const maxLines = 1000;
30
+
31
+ // Check for large files
32
+ for (const filePath of files) {
33
+ try {
34
+ const content = fs.readFileSync(filePath, 'utf8');
35
+ const lines = content.split('\n').length;
36
+
37
+ if (lines > maxLines) {
38
+ const relativePath = path.relative(projectRoot, filePath);
39
+ suggestions.push({
40
+ type: 'large-file',
41
+ file: relativePath,
42
+ message: `File has ${lines} lines, consider splitting into smaller modules`,
43
+ recommendation: `Split this file into smaller, focused modules (target: <${maxLines} lines)`
44
+ });
45
+ }
46
+
47
+ // Check for god objects (files with many exports)
48
+ const exportCount = (content.match(/export\s+(const|function|class|interface|type)/g) || []).length;
49
+
50
+ if (exportCount > 10) {
51
+ const relativePath = path.relative(projectRoot, filePath);
52
+ suggestions.push({
53
+ type: 'god-object',
54
+ file: relativePath,
55
+ message: `File exports ${exportCount} items, consider splitting into smaller modules`,
56
+ recommendation: `Split this file into smaller modules, each with a focused responsibility (target: <10 exports per file)`
57
+ });
58
+ }
59
+ } catch (error) {
60
+ // Skip files we can't read
61
+ }
62
+ }
63
+
64
+ return { issues: [], warnings: [], suggestions };
65
+ }
66
+ };
67
+
68
+ module.exports = heuristicsCheck;