@jmruthers/pace-core 0.5.193 → 0.6.2

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 (577) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +299 -0
  4. package/cursor-rules/01-standards-compliance.mdc +244 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +222 -0
  7. package/cursor-rules/04-testing-standards.mdc +268 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +309 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
  11. package/cursor-rules/08-markup-quality.mdc +452 -0
  12. package/cursor-rules/CHANGELOG.md +119 -0
  13. package/cursor-rules/README.md +192 -0
  14. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  15. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
  16. package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
  17. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
  18. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  19. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  20. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  21. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  22. package/dist/chunk-24UVZUZG.js.map +1 -0
  23. package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
  24. package/dist/chunk-2UOI2FG5.js.map +1 -0
  25. package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
  26. package/dist/chunk-3XC4CPTD.js.map +1 -0
  27. package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
  28. package/dist/chunk-6J4GEEJR.js.map +1 -0
  29. package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
  30. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  31. package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
  32. package/dist/chunk-EHMR7VYL.js.map +1 -0
  33. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  34. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  35. package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
  36. package/dist/chunk-FFQEQTNW.js.map +1 -0
  37. package/dist/chunk-FMUCXFII.js +76 -0
  38. package/dist/chunk-FMUCXFII.js.map +1 -0
  39. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  40. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  41. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  42. package/dist/chunk-L4OXEN46.js.map +1 -0
  43. package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
  44. package/dist/chunk-M43Y4SSO.js.map +1 -0
  45. package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
  46. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  47. package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
  48. package/dist/chunk-NECFR5MM.js.map +1 -0
  49. package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
  50. package/dist/chunk-SFZUDBL5.js.map +1 -0
  51. package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
  52. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  53. package/dist/components.d.ts +6 -6
  54. package/dist/components.js +15 -12
  55. package/dist/components.js.map +1 -1
  56. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  57. package/dist/hooks.d.ts +59 -126
  58. package/dist/hooks.js +19 -28
  59. package/dist/hooks.js.map +1 -1
  60. package/dist/index.d.ts +63 -16
  61. package/dist/index.js +23 -24
  62. package/dist/index.js.map +1 -1
  63. package/dist/providers.d.ts +21 -3
  64. package/dist/providers.js +2 -2
  65. package/dist/rbac/index.d.ts +146 -115
  66. package/dist/rbac/index.js +8 -11
  67. package/dist/theming/runtime.d.ts +1 -13
  68. package/dist/theming/runtime.js +1 -1
  69. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  70. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  71. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
  74. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  75. package/dist/utils.d.ts +4 -5
  76. package/dist/utils.js +15 -15
  77. package/dist/utils.js.map +1 -1
  78. package/docs/api/README.md +7 -1
  79. package/docs/api/classes/ColumnFactory.md +8 -8
  80. package/docs/api/classes/InvalidScopeError.md +4 -4
  81. package/docs/api/classes/Logger.md +1 -1
  82. package/docs/api/classes/MissingUserContextError.md +4 -4
  83. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  84. package/docs/api/classes/PermissionDeniedError.md +4 -4
  85. package/docs/api/classes/RBACAuditManager.md +1 -1
  86. package/docs/api/classes/RBACCache.md +1 -1
  87. package/docs/api/classes/RBACEngine.md +1 -1
  88. package/docs/api/classes/RBACError.md +4 -4
  89. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  90. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  91. package/docs/api/classes/StorageUtils.md +1 -1
  92. package/docs/api/enums/FileCategory.md +1 -1
  93. package/docs/api/enums/LogLevel.md +1 -1
  94. package/docs/api/enums/RBACErrorCode.md +1 -1
  95. package/docs/api/enums/RPCFunction.md +1 -1
  96. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  97. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  98. package/docs/api/interfaces/AggregateConfig.md +4 -4
  99. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  100. package/docs/api/interfaces/AvatarProps.md +1 -1
  101. package/docs/api/interfaces/BadgeProps.md +9 -2
  102. package/docs/api/interfaces/ButtonProps.md +7 -4
  103. package/docs/api/interfaces/CalendarProps.md +8 -5
  104. package/docs/api/interfaces/CardProps.md +8 -5
  105. package/docs/api/interfaces/ColorPalette.md +1 -1
  106. package/docs/api/interfaces/ColorShade.md +1 -1
  107. package/docs/api/interfaces/ComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  109. package/docs/api/interfaces/DataRecord.md +1 -1
  110. package/docs/api/interfaces/DataTableAction.md +24 -21
  111. package/docs/api/interfaces/DataTableColumn.md +31 -31
  112. package/docs/api/interfaces/DataTableProps.md +1 -1
  113. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  114. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  115. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  116. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  117. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  118. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  119. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  120. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  121. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  122. package/docs/api/interfaces/ExportColumn.md +1 -1
  123. package/docs/api/interfaces/ExportOptions.md +8 -8
  124. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  125. package/docs/api/interfaces/FileMetadata.md +1 -1
  126. package/docs/api/interfaces/FileReference.md +1 -1
  127. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  128. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  129. package/docs/api/interfaces/FileUploadProps.md +26 -23
  130. package/docs/api/interfaces/FooterProps.md +10 -8
  131. package/docs/api/interfaces/FormFieldProps.md +10 -10
  132. package/docs/api/interfaces/FormProps.md +1 -1
  133. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  134. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  135. package/docs/api/interfaces/InputProps.md +7 -4
  136. package/docs/api/interfaces/LabelProps.md +1 -1
  137. package/docs/api/interfaces/LoggerConfig.md +1 -1
  138. package/docs/api/interfaces/LoginFormProps.md +14 -11
  139. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  140. package/docs/api/interfaces/NavigationContextType.md +1 -1
  141. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  142. package/docs/api/interfaces/NavigationItem.md +11 -11
  143. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  144. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  145. package/docs/api/interfaces/Organisation.md +1 -1
  146. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  147. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  148. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  149. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  150. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  151. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  152. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  153. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  154. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  155. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  156. package/docs/api/interfaces/PaletteData.md +1 -1
  157. package/docs/api/interfaces/ParsedAddress.md +1 -1
  158. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  159. package/docs/api/interfaces/ProgressProps.md +1 -1
  160. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  161. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  162. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  163. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  164. package/docs/api/interfaces/QuickFix.md +1 -1
  165. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  166. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  167. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  168. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  169. package/docs/api/interfaces/RBACConfig.md +1 -1
  170. package/docs/api/interfaces/RBACContext.md +1 -1
  171. package/docs/api/interfaces/RBACLogger.md +1 -1
  172. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  173. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  174. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  175. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  176. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  177. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  178. package/docs/api/interfaces/RBACResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  181. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  182. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  183. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  184. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  185. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  186. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  187. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  188. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  189. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  190. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  191. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  192. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  193. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  194. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  195. package/docs/api/interfaces/RouteConfig.md +1 -1
  196. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  197. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  198. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  199. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  200. package/docs/api/interfaces/SetupIssue.md +1 -1
  201. package/docs/api/interfaces/StorageConfig.md +1 -1
  202. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  203. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  204. package/docs/api/interfaces/StorageListOptions.md +1 -1
  205. package/docs/api/interfaces/StorageListResult.md +1 -1
  206. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  207. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  208. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  209. package/docs/api/interfaces/StyleImport.md +1 -1
  210. package/docs/api/interfaces/SwitchProps.md +1 -1
  211. package/docs/api/interfaces/TabsContentProps.md +1 -1
  212. package/docs/api/interfaces/TabsListProps.md +1 -1
  213. package/docs/api/interfaces/TabsProps.md +1 -1
  214. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  215. package/docs/api/interfaces/TextareaProps.md +1 -1
  216. package/docs/api/interfaces/ToastActionElement.md +4 -1
  217. package/docs/api/interfaces/ToastProps.md +1 -1
  218. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  219. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  220. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  221. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  222. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  223. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  224. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  225. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  226. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  227. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  228. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  229. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  230. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  231. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  232. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  233. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  234. package/docs/api/interfaces/UserEventAccess.md +14 -11
  235. package/docs/api/interfaces/UserMenuProps.md +8 -6
  236. package/docs/api/interfaces/UserProfile.md +1 -1
  237. package/docs/api/modules.md +575 -634
  238. package/docs/architecture/database-schema-requirements.md +161 -0
  239. package/docs/core-concepts/rbac-system.md +3 -3
  240. package/docs/documentation-index.md +2 -4
  241. package/docs/getting-started/cursor-rules.md +263 -0
  242. package/docs/getting-started/installation-guide.md +6 -1
  243. package/docs/getting-started/quick-start.md +6 -1
  244. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  245. package/docs/migration/MIGRATION_GUIDE.md +6 -28
  246. package/docs/migration/README.md +52 -6
  247. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  248. package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
  249. package/docs/migration/database-changes-december-2025.md +3 -3
  250. package/docs/rbac/event-based-apps.md +1 -1
  251. package/docs/rbac/getting-started.md +1 -1
  252. package/docs/rbac/quick-start.md +1 -1
  253. package/docs/standards/README.md +40 -0
  254. package/docs/troubleshooting/migration.md +4 -4
  255. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  256. package/package.json +12 -6
  257. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  258. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  259. package/scripts/audit/core/checks/bundle.cjs +142 -0
  260. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
  261. package/scripts/audit/core/checks/config.cjs +54 -0
  262. package/scripts/audit/core/checks/coverage.cjs +84 -0
  263. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  264. package/scripts/audit/core/checks/documentation.cjs +203 -0
  265. package/scripts/audit/core/checks/environment.cjs +128 -0
  266. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  267. package/scripts/audit/core/checks/forms.cjs +172 -0
  268. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  269. package/scripts/audit/core/checks/hooks.cjs +334 -0
  270. package/scripts/audit/core/checks/imports.cjs +244 -0
  271. package/scripts/audit/core/checks/performance.cjs +325 -0
  272. package/scripts/audit/core/checks/routes.cjs +117 -0
  273. package/scripts/audit/core/checks/state.cjs +130 -0
  274. package/scripts/audit/core/checks/structure.cjs +65 -0
  275. package/scripts/audit/core/checks/style.cjs +584 -0
  276. package/scripts/audit/core/checks/testing.cjs +122 -0
  277. package/scripts/audit/core/checks/typescript.cjs +61 -0
  278. package/scripts/audit/core/scanner.cjs +199 -0
  279. package/scripts/audit/core/utils.cjs +137 -0
  280. package/scripts/audit/index.cjs +223 -0
  281. package/scripts/audit/reporters/console.cjs +151 -0
  282. package/scripts/audit/reporters/json.cjs +54 -0
  283. package/scripts/audit/reporters/markdown.cjs +124 -0
  284. package/scripts/audit-consuming-app.cjs +86 -0
  285. package/scripts/build-docs/build-decision.js +240 -0
  286. package/scripts/build-docs/cache-utils.js +105 -0
  287. package/scripts/build-docs/content-normalization.js +150 -0
  288. package/scripts/build-docs/file-utils.js +105 -0
  289. package/scripts/build-docs/git-utils.js +86 -0
  290. package/scripts/build-docs/hash-utils.js +116 -0
  291. package/scripts/build-docs/typedoc-runner.js +220 -0
  292. package/scripts/build-docs-incremental.js +77 -913
  293. package/scripts/install-cursor-rules.cjs +236 -0
  294. package/scripts/utils/command-runner.js +16 -11
  295. package/scripts/validate-formats.js +61 -56
  296. package/scripts/validate-master.js +74 -69
  297. package/scripts/validate-pre-publish.js +70 -65
  298. package/src/__tests__/helpers/test-providers.tsx +1 -1
  299. package/src/__tests__/helpers/test-utils.tsx +1 -1
  300. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  301. package/src/components/Alert/Alert.test.tsx +12 -18
  302. package/src/components/Alert/Alert.tsx +5 -7
  303. package/src/components/Avatar/Avatar.test.tsx +4 -4
  304. package/src/components/Badge/Badge.tsx +16 -4
  305. package/src/components/Button/Button.tsx +27 -4
  306. package/src/components/Calendar/Calendar.tsx +9 -3
  307. package/src/components/Card/Card.tsx +4 -0
  308. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  309. package/src/components/Checkbox/Checkbox.tsx +2 -2
  310. package/src/components/DataTable/DataTable.test.tsx +57 -93
  311. package/src/components/DataTable/DataTable.tsx +40 -6
  312. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  313. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
  314. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  315. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  316. package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
  317. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  318. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  319. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  320. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  321. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  322. package/src/components/DataTable/components/DataTableCore.tsx +200 -561
  323. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  324. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  325. package/src/components/DataTable/components/DataTableModals.tsx +9 -1
  326. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  327. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  328. package/src/components/DataTable/components/EditFields.tsx +307 -0
  329. package/src/components/DataTable/components/EditableRow.tsx +9 -1
  330. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  331. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  332. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  333. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  334. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  335. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  336. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  337. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  338. package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
  339. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  340. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  341. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  342. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  343. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  344. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  345. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  346. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  347. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  348. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  349. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  350. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  351. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  352. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  353. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  354. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  355. package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
  356. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  357. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  358. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  359. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  360. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  361. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  362. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  363. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  364. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  365. package/src/components/DataTable/styles.ts +6 -6
  366. package/src/components/DataTable/types.ts +6 -10
  367. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  368. package/src/components/DataTable/utils/debugTools.ts +18 -113
  369. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  370. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  371. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  372. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  373. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  374. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  375. package/src/components/Dialog/Dialog.tsx +8 -7
  376. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  377. package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
  378. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  379. package/src/components/ErrorBoundary/index.ts +27 -2
  380. package/src/components/EventSelector/EventSelector.tsx +4 -1
  381. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  382. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  383. package/src/components/FileUpload/FileUpload.tsx +22 -2
  384. package/src/components/Footer/Footer.test.tsx +16 -16
  385. package/src/components/Footer/Footer.tsx +15 -12
  386. package/src/components/Form/Form.test.tsx +36 -15
  387. package/src/components/Form/Form.tsx +31 -26
  388. package/src/components/Header/Header.tsx +22 -11
  389. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  390. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  391. package/src/components/Input/Input.test.tsx +2 -2
  392. package/src/components/Input/Input.tsx +36 -34
  393. package/src/components/Label/Label.tsx +1 -1
  394. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  395. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  396. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  397. package/src/components/LoginForm/LoginForm.tsx +12 -8
  398. package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
  399. package/src/components/NavigationMenu/types.ts +56 -0
  400. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  401. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  402. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  403. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
  404. package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
  405. package/src/components/PaceAppLayout/README.md +1 -1
  406. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  407. package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
  408. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  409. package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
  410. package/src/components/Progress/Progress.tsx +1 -1
  411. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  412. package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
  413. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  414. package/src/components/Select/Select.tsx +95 -438
  415. package/src/components/Select/context.ts +23 -0
  416. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  417. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  418. package/src/components/Select/hooks/useSelectState.ts +104 -0
  419. package/src/components/Select/index.ts +9 -1
  420. package/src/components/Select/types.ts +123 -0
  421. package/src/components/Select/utils/text.ts +26 -0
  422. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
  423. package/src/components/Switch/Switch.tsx +4 -4
  424. package/src/components/Table/Table.tsx +1 -1
  425. package/src/components/Tabs/Tabs.tsx +1 -1
  426. package/src/components/Textarea/Textarea.tsx +27 -29
  427. package/src/components/Toast/Toast.tsx +5 -1
  428. package/src/components/Tooltip/Tooltip.tsx +3 -3
  429. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  430. package/src/components/UserMenu/UserMenu.tsx +22 -19
  431. package/src/components/index.ts +2 -2
  432. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  433. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  434. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  435. package/src/hooks/index.ts +1 -2
  436. package/src/hooks/public/usePublicEvent.ts +5 -1
  437. package/src/hooks/public/usePublicEventLogo.ts +5 -1
  438. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  439. package/src/hooks/public/usePublicRouteParams.ts +5 -1
  440. package/src/hooks/services/useAuth.ts +32 -0
  441. package/src/hooks/services/useCurrentEvent.ts +6 -0
  442. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  443. package/src/hooks/useDataTableState.ts +8 -18
  444. package/src/hooks/useDebounce.ts +9 -0
  445. package/src/hooks/useEventTheme.ts +6 -0
  446. package/src/hooks/useFileDisplay.ts +4 -0
  447. package/src/hooks/useFileReference.ts +25 -7
  448. package/src/hooks/useFileUrl.ts +11 -1
  449. package/src/hooks/useFocusManagement.ts +16 -2
  450. package/src/hooks/useFocusTrap.ts +7 -4
  451. package/src/hooks/useFormDialog.ts +8 -7
  452. package/src/hooks/useInactivityTracker.ts +4 -1
  453. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  454. package/src/hooks/useOrganisationPermissions.ts +4 -0
  455. package/src/hooks/useOrganisationSecurity.ts +4 -0
  456. package/src/hooks/usePerformanceMonitor.ts +4 -0
  457. package/src/hooks/usePermissionCache.ts +8 -1
  458. package/src/hooks/useQueryCache.ts +12 -1
  459. package/src/hooks/useSessionRestoration.ts +4 -0
  460. package/src/hooks/useStorage.ts +4 -0
  461. package/src/hooks/useToast.ts +3 -3
  462. package/src/index.ts +2 -1
  463. package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
  464. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  465. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  466. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  467. package/src/providers/services/EventServiceProvider.tsx +18 -0
  468. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  469. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  470. package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
  471. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
  472. package/src/rbac/README.md +1 -1
  473. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
  474. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  475. package/src/rbac/adapters.tsx +14 -5
  476. package/src/rbac/api.ts +100 -67
  477. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  478. package/src/rbac/components/NavigationGuard.tsx +1 -1
  479. package/src/rbac/components/NavigationProvider.tsx +5 -2
  480. package/src/rbac/components/PagePermissionGuard.tsx +158 -18
  481. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  482. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  483. package/src/rbac/components/RoleBasedRouter.tsx +6 -2
  484. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  485. package/src/rbac/components/SecureDataProvider.tsx +21 -6
  486. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  487. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  488. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  489. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  490. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  491. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  492. package/src/rbac/engine.ts +38 -14
  493. package/src/rbac/hooks/permissions/index.ts +7 -0
  494. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  495. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  496. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  497. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  498. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  499. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  500. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  501. package/src/rbac/hooks/useCan.test.ts +71 -64
  502. package/src/rbac/hooks/usePermissions.ts +14 -995
  503. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  504. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  505. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  506. package/src/rbac/permissions.ts +0 -30
  507. package/src/rbac/secureClient.ts +212 -61
  508. package/src/rbac/types.ts +8 -0
  509. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  510. package/src/theming/parseEventColours.ts +5 -19
  511. package/src/types/vitest-globals.d.ts +51 -26
  512. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  513. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  514. package/src/utils/__tests__/index.unit.test.ts +2 -2
  515. package/src/utils/audit/audit.ts +0 -3
  516. package/src/utils/core/cn.ts +1 -1
  517. package/src/utils/file-reference/index.ts +53 -1
  518. package/src/utils/formatting/formatting.ts +8 -18
  519. package/src/utils/index.ts +0 -1
  520. package/src/utils/security/secureDataAccess.test.ts +31 -20
  521. package/src/utils/security/secureDataAccess.ts +4 -3
  522. package/dist/chunk-6C4YBBJM.js +0 -628
  523. package/dist/chunk-6C4YBBJM.js.map +0 -1
  524. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  525. package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
  526. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  527. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  528. package/dist/chunk-7FLMSG37.js.map +0 -1
  529. package/dist/chunk-BC4IJKSL.js.map +0 -1
  530. package/dist/chunk-E3SPN4VZ.js +0 -12917
  531. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  532. package/dist/chunk-E66EQZE6 5.js +0 -37
  533. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  534. package/dist/chunk-HWIIPPNI.js.map +0 -1
  535. package/dist/chunk-I7PSE6JW 5.js +0 -191
  536. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  537. package/dist/chunk-I7PSE6JW.js.map +0 -1
  538. package/dist/chunk-IIELH4DL.js.map +0 -1
  539. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  540. package/dist/chunk-KNC55RTG.js.map +0 -1
  541. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  542. package/dist/chunk-LFNCN2SP.js +0 -412
  543. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  544. package/dist/chunk-LFNCN2SP.js.map +0 -1
  545. package/dist/chunk-LMC26NLJ 2.js +0 -84
  546. package/dist/chunk-NOAYCWCX.js +0 -4993
  547. package/dist/chunk-NOAYCWCX.js.map +0 -1
  548. package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
  549. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  550. package/dist/chunk-QXHPKYJV 3.js +0 -113
  551. package/dist/chunk-R77UEZ4E.js +0 -68
  552. package/dist/chunk-R77UEZ4E.js.map +0 -1
  553. package/dist/chunk-SQGMNID3.js.map +0 -1
  554. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  555. package/dist/chunk-XNXXZ43G.js.map +0 -1
  556. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  557. package/dist/components.js 5.map +0 -1
  558. package/dist/styles/index 2.js +0 -12
  559. package/dist/styles/index.js 5.map +0 -1
  560. package/dist/theming/runtime 5.js +0 -19
  561. package/dist/theming/runtime.js 5.map +0 -1
  562. package/docs/api/classes/ErrorBoundary.md +0 -144
  563. package/docs/migration/quick-migration-guide.md +0 -356
  564. package/docs/migration/service-architecture.md +0 -281
  565. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  566. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  567. package/src/hooks/useSecureDataAccess.ts +0 -666
  568. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  569. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  570. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  571. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  572. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  573. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  574. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  575. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  576. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  577. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -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;
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * React Hooks Compliance Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Hooks
7
+ *
8
+ * Checks for:
9
+ * - Hooks called conditionally or after early returns
10
+ * - Hooks called in loops
11
+ * - Missing dependencies in useEffect/useMemo/useCallback
12
+ * - Hooks called in wrong order
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const { getRelativePath, getLineNumber } = require('../utils.cjs');
17
+
18
+ const hooksCheck = {
19
+ name: 'hooks',
20
+ description: 'React hooks compliance (conditional calls, missing dependencies, etc.)',
21
+ severity: 'error',
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
+ // React hooks that must follow rules
34
+ // Separate actual React hooks from custom hooks
35
+ const actualReactHooks = [
36
+ 'useState', 'useEffect', 'useContext', 'useReducer', 'useCallback',
37
+ 'useMemo', 'useRef', 'useImperativeHandle', 'useLayoutEffect',
38
+ 'useDebugValue'
39
+ ];
40
+
41
+ const customHooks = [
42
+ 'useUnifiedAuth', 'useOrganisations', 'useEvents',
43
+ 'usePermissions', 'useCan', 'useSecureSupabase', 'useToast',
44
+ 'useDebounce', 'useZodForm', 'useFileReference', 'useRBAC'
45
+ ];
46
+
47
+ // Combined list for pattern matching
48
+ const hookNames = [...actualReactHooks, ...customHooks];
49
+
50
+ const hookPattern = new RegExp(`\\b(${hookNames.join('|')})\\s*\\(`, 'g');
51
+
52
+ for (const filePath of files) {
53
+ try {
54
+ // Only check React component files
55
+ if (!filePath.match(/\.(tsx|jsx)$/)) {
56
+ continue;
57
+ }
58
+
59
+ const content = fs.readFileSync(filePath, 'utf8');
60
+ const relativePath = getRelativePath(filePath, projectRoot);
61
+ const normalizedPath = relativePath.replace(/\\/g, '/');
62
+
63
+ // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
64
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
65
+ if (isRootSrc) {
66
+ continue; // Skip demo app files
67
+ }
68
+
69
+ // Skip scripts directory - utility scripts don't need hooks validation
70
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
71
+ if (isScript) {
72
+ continue; // Skip script files
73
+ }
74
+
75
+ // Check if this is a pace-core package file
76
+ // Skip "hook-after-return" check for pace-core files - these are false positives.
77
+ // The detection logic has issues with complex nested structures in library code.
78
+ // Other hook checks (conditional calls, missing dependencies) still apply to pace-core.
79
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
80
+
81
+ // Check for hooks
82
+ if (!hookPattern.test(content)) {
83
+ continue; // No hooks in this file
84
+ }
85
+
86
+ // Find all hook calls
87
+ const hookCalls = [];
88
+ let match;
89
+ const regex = new RegExp(`\\b(${hookNames.join('|')})\\s*\\(`, 'g');
90
+ while ((match = regex.exec(content)) !== null) {
91
+ hookCalls.push({
92
+ name: match[1],
93
+ index: match.index,
94
+ line: getLineNumber(content, match.index)
95
+ });
96
+ }
97
+
98
+ if (hookCalls.length === 0) {
99
+ continue;
100
+ }
101
+
102
+ // Check for hooks called conditionally
103
+ for (const hookCall of hookCalls) {
104
+ const beforeHook = content.substring(0, hookCall.index);
105
+ const linesBefore = beforeHook.split('\n');
106
+ const currentLine = linesBefore[linesBefore.length - 1];
107
+
108
+ // Check if hook is in a conditional
109
+ const isInConditional = /if\s*\(|else\s*\{|switch\s*\(|case\s+.*:|for\s*\(|while\s*\(|\.map\s*\(/.test(currentLine);
110
+
111
+ if (isInConditional) {
112
+ issues.push({
113
+ type: 'hook-in-conditional',
114
+ file: relativePath,
115
+ line: hookCall.line,
116
+ message: `Hook '${hookCall.name}' is called conditionally or in a loop`,
117
+ recommendation: 'Hooks must be called at the top level of the component, not conditionally or in loops'
118
+ });
119
+ }
120
+
121
+ // Check if there's an early return before this hook at the component level
122
+ // Only flag returns at the component function level, not inside nested functions
123
+ const functionStart = beforeHook.lastIndexOf('function') > beforeHook.lastIndexOf('=>') ?
124
+ beforeHook.lastIndexOf('function') : beforeHook.lastIndexOf('=>');
125
+
126
+ if (functionStart !== -1) {
127
+ const functionBody = content.substring(functionStart, hookCall.index);
128
+
129
+ // Find all return statements and check if they're at component level (not in nested functions)
130
+ // Only flag CONDITIONAL early returns (guard clauses), not the final return statement
131
+ const returnPattern = /\breturn\s+[^;]+;|\breturn\s*;|\breturn\s+\(/g;
132
+ let returnMatch;
133
+ let hasComponentLevelReturn = false;
134
+
135
+ while ((returnMatch = returnPattern.exec(functionBody)) !== null) {
136
+ const returnIndex = returnMatch.index;
137
+ const beforeReturn = functionBody.substring(Math.max(0, returnIndex - 200), returnIndex);
138
+ const afterReturn = functionBody.substring(returnIndex, Math.min(functionBody.length, returnIndex + 100));
139
+
140
+ // Check if this is a CONDITIONAL early return (guard clause)
141
+ // Pattern: if (...) return ...; or if (...) { return ...; }
142
+ // NOT the final return statement of the component
143
+ const isConditionalReturn = /\bif\s*\([^)]+\)\s*(return|{[\s\S]*?return)/.test(beforeReturn) ||
144
+ /\belse\s+if\s*\([^)]+\)\s*(return|{[\s\S]*?return)/.test(beforeReturn) ||
145
+ /\belse\s*{\s*return/.test(beforeReturn) ||
146
+ /\?\s*\([^)]*\)\s*=>\s*{?\s*return/.test(beforeReturn); // Ternary operator
147
+
148
+ // If it's not a conditional return, it's likely the final return - skip it
149
+ if (!isConditionalReturn) {
150
+ continue;
151
+ }
152
+
153
+ // Check if return is inside a nested function by looking for patterns
154
+ const isInHookInitializer = /useState\s*\(\s*\([^)]*\)\s*=>/.test(beforeReturn);
155
+ const isInEffectCallback = /useEffect\s*\(\s*\([^)]*\)\s*=>/.test(beforeReturn);
156
+ const isInQueryFn = /queryFn\s*:\s*\([^)]*\)\s*=>|useQuery\s*\(\s*\{[^}]*queryFn/.test(beforeReturn);
157
+ const isInArrayMethod = /\.(map|filter|find|reduce|forEach|some|every)\s*\(\s*\([^)]*\)\s*=>/.test(beforeReturn);
158
+ const isInIIFE = /\(\s*\([^)]*\)\s*=>/.test(beforeReturn);
159
+ const isInFunctionExpr = /\bfunction\s*\([^)]*\)\s*\{/.test(beforeReturn);
160
+ const isInUseMemo = /useMemo\s*\(\s*\([^)]*\)\s*=>/.test(beforeReturn);
161
+ const isInUseCallback = /useCallback\s*\(\s*\([^)]*\)\s*=>/.test(beforeReturn);
162
+
163
+ // Check if return is inside a switch case (also nested)
164
+ const isInSwitchCase = /\bcase\s+[^:]+:\s*[\s\S]*?return/.test(beforeReturn);
165
+
166
+ // If return is NOT in any nested function pattern, it's a component-level conditional return
167
+ if (!isInHookInitializer && !isInEffectCallback && !isInQueryFn &&
168
+ !isInArrayMethod && !isInIIFE && !isInFunctionExpr &&
169
+ !isInUseMemo && !isInUseCallback && !isInSwitchCase) {
170
+ hasComponentLevelReturn = true;
171
+ break;
172
+ }
173
+ }
174
+
175
+ if (hasComponentLevelReturn && !isPaceCorePackage) {
176
+ // Skip this check for pace-core files - false positives due to complex nested structures
177
+ issues.push({
178
+ type: 'hook-after-return',
179
+ file: relativePath,
180
+ line: hookCall.line,
181
+ message: `Hook '${hookCall.name}' is called after an early return`,
182
+ recommendation: 'All hooks must be called before any conditional returns at the component level. Move hooks to the top of the component, before any component-level conditional returns. Returns inside nested functions (useState initializers, useEffect callbacks, etc.) are fine. If you have guard clauses (if (loading) return <Loading />), move all hooks above these guard clauses.'
183
+ });
184
+ }
185
+ }
186
+ }
187
+
188
+ // Check for missing dependencies in useEffect, useMemo, useCallback
189
+ const dependencyHooks = ['useEffect', 'useMemo', 'useCallback', 'useLayoutEffect'];
190
+ dependencyHooks.forEach(hookName => {
191
+ // Match hook with dependency array - handle multiline and various formats
192
+ const hookRegex = new RegExp(`${hookName}\\s*\\(([^)]*)\\)\\s*,\\s*\\[([^\\]]*)\\]`, 'gs');
193
+ let depMatch;
194
+ while ((depMatch = hookRegex.exec(content)) !== null) {
195
+ const hookBody = depMatch[1]; // The function body or callback
196
+ const deps = depMatch[2].trim();
197
+
198
+ // If dependency array is empty, check if hook only uses stable globals
199
+ if (deps === '') {
200
+ // Extract the actual callback body (handle arrow functions and regular functions)
201
+ let callbackBody = hookBody;
202
+
203
+ // Check if it's an arrow function: () => { ... } or () => ...
204
+ if (hookBody.includes('=>')) {
205
+ const arrowMatch = hookBody.match(/=>\s*\{?([^}]*)\}?$/);
206
+ if (arrowMatch) {
207
+ callbackBody = arrowMatch[1];
208
+ }
209
+ }
210
+
211
+ // Check if callback only uses stable global APIs or imported module-level constants
212
+ // Stable globals that don't need dependencies:
213
+ const stableGlobals = [
214
+ /window\.(location|document|localStorage|sessionStorage|navigator)/,
215
+ /document\.(documentElement|body|getElementById|querySelector)/,
216
+ /localStorage\.(getItem|setItem|removeItem|clear)/,
217
+ /sessionStorage\.(getItem|setItem|removeItem|clear)/,
218
+ /console\.(log|error|warn|info|debug)/,
219
+ /Math\./,
220
+ /Date\./,
221
+ /JSON\./,
222
+ /Object\./,
223
+ /Array\./,
224
+ /String\./,
225
+ /Number\./,
226
+ /Boolean\./,
227
+ /RegExp\./,
228
+ /Error\./,
229
+ /Promise\./,
230
+ /Symbol\./,
231
+ /Reflect\./,
232
+ /Proxy\./
233
+ ];
234
+
235
+ // Check for imported module-level constants (classes, functions, constants)
236
+ // Pattern: ImportedClass.method() or importedFunction() or importedConstant
237
+ const importedModulePattern = /[A-Z]\w+\.\w+\(|^[A-Z]\w+\(|\b[A-Z][A-Z_]+/;
238
+
239
+ // Check if callback body only contains stable globals or simple operations
240
+ const hasOnlyStableGlobals = stableGlobals.some(pattern => pattern.test(callbackBody));
241
+ const hasImportedModule = importedModulePattern.test(callbackBody);
242
+
243
+ // Check if callback uses reactive values (props, state, context) that would need deps
244
+ const hasReactiveValues = /\b(props|state|context|use[A-Z]\w+\(\))/.test(callbackBody);
245
+
246
+ // Check for variable references that might be reactive
247
+ // Simple heuristic: if callback references variables that aren't stable globals
248
+ const variablePattern = /\b[a-z][a-zA-Z0-9_]*\b/g;
249
+ const variables = callbackBody.match(variablePattern) || [];
250
+ const hasNonGlobalVariables = variables.some(v => {
251
+ // Skip common stable globals and built-ins
252
+ const stableVars = ['window', 'document', 'localStorage', 'sessionStorage', 'console',
253
+ 'Math', 'Date', 'JSON', 'Object', 'Array', 'String', 'Number',
254
+ 'Boolean', 'RegExp', 'Error', 'Promise', 'Symbol', 'Reflect', 'Proxy',
255
+ 'true', 'false', 'null', 'undefined', 'this', 'return', 'if', 'else',
256
+ 'const', 'let', 'var', 'function', 'async', 'await', 'for', 'while',
257
+ 'switch', 'case', 'default', 'break', 'continue', 'try', 'catch', 'finally'];
258
+ return !stableVars.includes(v) && !v.match(/^[A-Z]/); // Lowercase vars that aren't stable
259
+ });
260
+
261
+ // Only warn if callback uses reactive values or non-global variables
262
+ if (hasReactiveValues || (hasNonGlobalVariables && !hasOnlyStableGlobals && !hasImportedModule)) {
263
+ warnings.push({
264
+ type: 'missing-dependencies',
265
+ file: relativePath,
266
+ line: getLineNumber(content, depMatch.index),
267
+ message: `${hookName} has empty dependency array but may need dependencies`,
268
+ recommendation: 'Review the hook body and add all dependencies to the dependency array. If the callback only uses stable global APIs (window, document, localStorage, etc.) or imported module-level constants, an empty dependency array is correct.'
269
+ });
270
+ }
271
+ }
272
+ }
273
+ });
274
+
275
+ // Check hook order (hooks should be called in consistent order)
276
+ // This is a simplified check - full implementation would track hook order across renders
277
+
278
+ // SKIP hook grouping suggestions for pace-core library files
279
+ // Library components often have complex hook usage organized by feature/concern,
280
+ // and strict grouping is less critical than in consuming applications.
281
+ // The grouping suggestions are primarily for consuming apps, not library code.
282
+ if (isPaceCorePackage) {
283
+ continue; // Skip grouping suggestions for pace-core library files
284
+ }
285
+
286
+ // Filter to only actual React hooks (not custom hooks) for grouping suggestions
287
+ // Files with only custom hooks (like useToast()) don't need grouping suggestions
288
+ const actualReactHookCalls = hookCalls.filter(h => actualReactHooks.includes(h.name));
289
+
290
+ // Skip files that only have custom hooks (no actual React hooks)
291
+ // Custom hooks are typically called once and don't need grouping
292
+ // This also filters out files like Toast.tsx that only have forwardRef components
293
+ // (forwardRef components don't use React hooks, so actualReactHookCalls will be empty)
294
+ if (actualReactHookCalls.length === 0) {
295
+ continue; // No actual React hooks, skip grouping suggestion
296
+ }
297
+
298
+ // Check if hooks are already grouped (look for grouping comments)
299
+ // Accept more flexible patterns:
300
+ // - Specific keywords: // ============================================================================ // REFS / STATE HOOKS / etc.
301
+ // - Section dividers: // ============================================================================ (any text)
302
+ // - Comment blocks near hooks: // HOOKS or // State management or similar
303
+ const hasGroupingComments =
304
+ /\/\/\s*=+\s*(REFS|STATE HOOKS|CUSTOM HOOKS|MEMOIZATION HOOKS|EFFECT HOOKS|HOOKS|STATE|EFFECTS|MEMOIZATION|CALLBACKS)/i.test(content) ||
305
+ /\/\/\s*=+\s*[A-Z].*HOOK/i.test(content) ||
306
+ /\/\/\s*=+\s*[A-Z].*STATE/i.test(content) ||
307
+ /\/\/\s*=+\s*[A-Z].*EFFECT/i.test(content);
308
+
309
+ // Only suggest grouping if:
310
+ // 1. File has actual React hooks (not just custom hooks)
311
+ // 2. Hooks are called multiple times (duplicate hook names)
312
+ // 3. Hooks are not already grouped (no grouping comments found)
313
+ const hookOrder = actualReactHookCalls.map(h => h.name);
314
+ const uniqueHooks = [...new Set(hookOrder)];
315
+
316
+ if (uniqueHooks.length !== hookOrder.length && !hasGroupingComments) {
317
+ // Hooks are called multiple times and not grouped
318
+ suggestions.push({
319
+ type: 'hook-order',
320
+ file: relativePath,
321
+ message: 'Consider grouping related hooks together for better readability',
322
+ recommendation: 'Group hooks by purpose (state hooks, effect hooks, custom hooks)'
323
+ });
324
+ }
325
+ } catch (error) {
326
+ // Skip files with errors
327
+ }
328
+ }
329
+
330
+ return { issues, warnings, suggestions };
331
+ }
332
+ };
333
+
334
+ module.exports = hooksCheck;