@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,244 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Import Patterns Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Imports
7
+ *
8
+ * Checks for:
9
+ * - Unused imports from pace-core
10
+ * - Missing default imports
11
+ * - Circular dependencies
12
+ * - Wrong import paths
13
+ * - Barrel import anti-patterns
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const { findSourceFiles, getRelativePath } = require('../utils.cjs');
19
+
20
+ const importsCheck = {
21
+ name: 'imports',
22
+ description: 'Import pattern analysis (unused imports, circular dependencies, wrong paths)',
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
+ // Track imports and usage
36
+ const importMap = new Map(); // file -> { imports: [], exports: [] }
37
+ const usageMap = new Map(); // file -> Set of used identifiers
38
+
39
+ // First pass: collect imports and exports
40
+ for (const filePath of files) {
41
+ try {
42
+ const content = fs.readFileSync(filePath, 'utf8');
43
+ const relativePath = getRelativePath(filePath, projectRoot);
44
+
45
+ const imports = [];
46
+ const exports = [];
47
+ const used = new Set();
48
+
49
+ // Find all imports
50
+ const importPattern = /import\s+(?:(?:\*\s+as\s+(\w+))|(?:{([^}]+)})|(\w+))\s+from\s+['"]([^'"]+)['"]/g;
51
+ let match;
52
+ while ((match = importPattern.exec(content)) !== null) {
53
+ const namespace = match[1];
54
+ const namedImports = match[2];
55
+ const defaultImport = match[3];
56
+ const modulePath = match[4];
57
+
58
+ imports.push({
59
+ namespace,
60
+ namedImports: namedImports ? namedImports.split(',').map(s => s.trim().replace(/\s+as\s+\w+/, '')) : [],
61
+ defaultImport,
62
+ modulePath,
63
+ line: content.substring(0, match.index).split('\n').length
64
+ });
65
+ }
66
+
67
+ // Find all exports
68
+ const exportPattern = /export\s+(?:(?:default\s+)?(?:function|const|class|interface|type)\s+(\w+)|(?:{([^}]+)})|(\w+))/g;
69
+ while ((match = exportPattern.exec(content)) !== null) {
70
+ const namedExport = match[1] || match[3];
71
+ const namedExports = match[2];
72
+
73
+ if (namedExport) {
74
+ exports.push(namedExport);
75
+ }
76
+ if (namedExports) {
77
+ exports.push(...namedExports.split(',').map(s => s.trim()));
78
+ }
79
+ }
80
+
81
+ // Find usage of imported identifiers (simplified heuristic)
82
+ imports.forEach(imp => {
83
+ if (imp.defaultImport) {
84
+ // Check if default import is used
85
+ const usagePattern = new RegExp(`\\b${imp.defaultImport}\\b`, 'g');
86
+ if (usagePattern.test(content)) {
87
+ used.add(imp.defaultImport);
88
+ }
89
+ }
90
+ if (imp.namespace) {
91
+ const usagePattern = new RegExp(`\\b${imp.namespace}\\.`, 'g');
92
+ if (usagePattern.test(content)) {
93
+ used.add(imp.namespace);
94
+ }
95
+ }
96
+ imp.namedImports.forEach(name => {
97
+ const cleanName = name.trim();
98
+ const usagePattern = new RegExp(`\\b${cleanName}\\b`, 'g');
99
+ // Count occurrences - if more than 1, it's likely used (1 is the import itself)
100
+ const matches = content.match(usagePattern);
101
+ if (matches && matches.length > 1) {
102
+ used.add(cleanName);
103
+ }
104
+ });
105
+ });
106
+
107
+ importMap.set(relativePath, { imports, exports, content });
108
+ usageMap.set(relativePath, used);
109
+ } catch (error) {
110
+ // Skip files with errors
111
+ }
112
+ }
113
+
114
+ // Second pass: analyze imports
115
+ for (const [filePath, { imports, content }] of importMap.entries()) {
116
+ const relativePath = filePath;
117
+ const normalizedPath = relativePath.replace(/\\/g, '/');
118
+
119
+ // Skip pace-core package files - imports check is for consuming applications, not the library itself
120
+ // The library must use internal import paths and may have unused imports in examples/config files
121
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
122
+ if (isPaceCorePackage) {
123
+ continue; // Skip library files
124
+ }
125
+
126
+ // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
127
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
128
+ if (isRootSrc) {
129
+ continue; // Skip demo app files
130
+ }
131
+
132
+ // Skip scripts directory - utility scripts don't need import validation
133
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
134
+ if (isScript) {
135
+ continue; // Skip script files
136
+ }
137
+
138
+ const used = usageMap.get(filePath) || new Set();
139
+
140
+ imports.forEach(imp => {
141
+ // Check for unused imports from pace-core
142
+ if (imp.modulePath === '@jmruthers/pace-core' || imp.modulePath.startsWith('@jmruthers/pace-core/')) {
143
+ const unused = [];
144
+
145
+ if (imp.defaultImport && !used.has(imp.defaultImport)) {
146
+ unused.push(imp.defaultImport);
147
+ }
148
+
149
+ imp.namedImports.forEach(name => {
150
+ if (!used.has(name.trim())) {
151
+ unused.push(name.trim());
152
+ }
153
+ });
154
+
155
+ if (imp.namespace && !used.has(imp.namespace)) {
156
+ unused.push(`${imp.namespace} (namespace)`);
157
+ }
158
+
159
+ if (unused.length > 0) {
160
+ warnings.push({
161
+ type: 'unused-import',
162
+ file: relativePath,
163
+ line: imp.line,
164
+ message: `Unused imports from pace-core: ${unused.join(', ')}`,
165
+ recommendation: `Remove unused imports to reduce bundle size`
166
+ });
167
+ }
168
+ }
169
+
170
+ // Check for wrong import paths
171
+ if (imp.modulePath.startsWith('@jmruthers/pace-core/')) {
172
+ const validPaths = [
173
+ '@jmruthers/pace-core',
174
+ '@jmruthers/pace-core/components',
175
+ '@jmruthers/pace-core/hooks',
176
+ '@jmruthers/pace-core/utils',
177
+ '@jmruthers/pace-core/providers',
178
+ '@jmruthers/pace-core/rbac',
179
+ '@jmruthers/pace-core/types'
180
+ ];
181
+
182
+ if (!validPaths.includes(imp.modulePath)) {
183
+ warnings.push({
184
+ type: 'wrong-import-path',
185
+ file: relativePath,
186
+ line: imp.line,
187
+ message: `Invalid import path: ${imp.modulePath}`,
188
+ recommendation: `Use one of the valid paths: ${validPaths.join(', ')}`
189
+ });
190
+ }
191
+ }
192
+
193
+ // Check for barrel import anti-patterns (importing entire modules)
194
+ if (imp.namespace && (imp.modulePath === '@jmruthers/pace-core' || imp.modulePath.startsWith('@jmruthers/pace-core/'))) {
195
+ warnings.push({
196
+ type: 'barrel-import',
197
+ file: relativePath,
198
+ line: imp.line,
199
+ message: `Namespace import from pace-core: import * as ${imp.namespace}`,
200
+ recommendation: `Import specific exports instead to enable tree-shaking: import { Component1, Component2 } from '@jmruthers/pace-core'`
201
+ });
202
+ }
203
+ });
204
+ }
205
+
206
+ // Check for circular dependencies (simplified - check if file A imports B and B imports A)
207
+ const filePaths = Array.from(importMap.keys());
208
+ for (let i = 0; i < filePaths.length; i++) {
209
+ for (let j = i + 1; j < filePaths.length; j++) {
210
+ const fileA = filePaths[i];
211
+ const fileB = filePaths[j];
212
+ const importsA = importMap.get(fileA)?.imports || [];
213
+ const importsB = importMap.get(fileB)?.imports || [];
214
+
215
+ // Check if A imports B
216
+ const aImportsB = importsA.some(imp => {
217
+ const importPath = imp.modulePath;
218
+ return importPath.startsWith('./') || importPath.startsWith('../') ?
219
+ path.resolve(path.dirname(fileA), importPath) === fileB : false;
220
+ });
221
+
222
+ // Check if B imports A
223
+ const bImportsA = importsB.some(imp => {
224
+ const importPath = imp.modulePath;
225
+ return importPath.startsWith('./') || importPath.startsWith('../') ?
226
+ path.resolve(path.dirname(fileB), importPath) === fileA : false;
227
+ });
228
+
229
+ if (aImportsB && bImportsA) {
230
+ issues.push({
231
+ type: 'circular-dependency',
232
+ file: fileA,
233
+ message: `Circular dependency detected between ${fileA} and ${fileB}`,
234
+ recommendation: `Refactor to break the circular dependency by extracting shared code to a third module`
235
+ });
236
+ }
237
+ }
238
+ }
239
+
240
+ return { issues, warnings, suggestions };
241
+ }
242
+ };
243
+
244
+ module.exports = importsCheck;
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Performance Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Performance
7
+ *
8
+ * Checks for:
9
+ * - Missing React.memo on expensive components
10
+ * - Missing useMemo/useCallback for expensive computations
11
+ * - Unnecessary re-renders
12
+ * - Large inline objects/functions in JSX props
13
+ * - Missing key props in lists
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const { getRelativePath, getLineNumber } = require('../utils.cjs');
18
+
19
+ const performanceCheck = {
20
+ name: 'performance',
21
+ description: 'Performance anti-patterns (missing memoization, unnecessary re-renders)',
22
+ severity: 'warning',
23
+
24
+ async run(context) {
25
+ const { projectRoot, files } = context;
26
+ const issues = [];
27
+ const warnings = [];
28
+ const suggestions = [];
29
+
30
+ if (!files || files.length === 0) {
31
+ return { issues, warnings, suggestions };
32
+ }
33
+
34
+ for (const filePath of files) {
35
+ try {
36
+ // Only check React component files
37
+ if (!filePath.match(/\.(tsx|jsx)$/)) {
38
+ continue;
39
+ }
40
+
41
+ const content = fs.readFileSync(filePath, 'utf8');
42
+ const relativePath = getRelativePath(filePath, projectRoot);
43
+ const normalizedPath = relativePath.replace(/\\/g, '/');
44
+
45
+ // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
46
+ // Note: We DO check packages/core/ files because performance issues (like missing key props) are real issues that should be fixed
47
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
48
+ if (isRootSrc) {
49
+ continue; // Skip demo app files
50
+ }
51
+
52
+ // Skip scripts directory - utility scripts don't need performance validation
53
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
54
+ if (isScript) {
55
+ continue; // Skip script files
56
+ }
57
+
58
+ // Skip examples directory - these are demo/example files, not production code
59
+ // Examples are meant to show usage patterns, not be optimized for performance
60
+ const isExample = normalizedPath.includes('/examples/') || normalizedPath.startsWith('examples/');
61
+ if (isExample) {
62
+ continue; // Skip example files
63
+ }
64
+
65
+ // Skip test files - test code doesn't need performance optimization
66
+ const isTestFile = normalizedPath.includes('.test.') ||
67
+ normalizedPath.includes('.spec.') ||
68
+ normalizedPath.includes('test-setup') ||
69
+ normalizedPath.includes('__tests__');
70
+ if (isTestFile) {
71
+ continue; // Skip test files
72
+ }
73
+
74
+ // For library code (packages/core/src), be more lenient with performance suggestions
75
+ // Library components often use inline functions which are acceptable patterns
76
+ const isLibraryCode = normalizedPath.includes('packages/core/src/');
77
+
78
+ // Check for components that could benefit from React.memo
79
+ // Note: For library components, React.memo should be used judiciously
80
+ // We only suggest it for components that are likely to render frequently
81
+ const componentPattern = /export\s+(default\s+)?(function|const)\s+(\w+)\s*[=\(]/g;
82
+ let match;
83
+ while ((match = componentPattern.exec(content)) !== null) {
84
+ const componentName = match[3];
85
+ const isMemoized = content.includes(`React.memo(${componentName})`) ||
86
+ content.includes(`memo(${componentName})`) ||
87
+ content.includes(`export default memo(${componentName})`);
88
+
89
+ // Check if component receives props and is not memoized
90
+ if (match[0].includes('props') || match[0].includes('{') || content.includes(`${componentName}({`)) {
91
+ // Check if component has significant JSX (heuristic: more than 5 lines of JSX)
92
+ const componentStart = match.index;
93
+ const afterMatch = content.substring(componentStart);
94
+ const jsxLines = (afterMatch.match(/<[A-Z]/g) || []).length;
95
+
96
+ // Skip if component uses forwardRef (memoization handled differently)
97
+ // Skip if component is a hook (starts with 'use')
98
+ // Skip if component is very simple (less than 5 JSX elements)
99
+ // Skip if component is already memoized
100
+ if (jsxLines > 5 && !isMemoized && !content.includes('forwardRef') && !componentName.startsWith('use')) {
101
+ // For library components, be more conservative - only suggest for components
102
+ // that are likely to be in lists or render frequently
103
+ // Skip if it's a page-level component (like *Page, *Layout, *Route)
104
+ const isPageComponent = /Page|Layout|Route|Modal|Dialog/.test(componentName);
105
+
106
+ // Only suggest memo for components that are likely to render frequently
107
+ // (not page-level components, which typically render once per route)
108
+ // For library code, skip React.memo suggestions entirely - memoization decisions
109
+ // should be made by the consuming application based on their specific use case
110
+ if (!isPageComponent && !isLibraryCode) {
111
+ suggestions.push({
112
+ type: 'missing-memo',
113
+ file: relativePath,
114
+ line: getLineNumber(content, match.index),
115
+ message: `Component '${componentName}' receives props but is not memoized`,
116
+ recommendation: `Consider wrapping with React.memo if the component renders frequently: export default React.memo(${componentName})`
117
+ });
118
+ }
119
+ // For library code, skip React.memo suggestions - library components are often
120
+ // controlled by parent components, and memoization should be decided by consumers
121
+ }
122
+ }
123
+ }
124
+
125
+ // Check for large inline objects/functions in JSX props
126
+ // But exclude JSX children (which is standard React pattern)
127
+ const jsxPropPattern = /<[A-Z]\w+\s+[^>]*\{[^}]{100,}[^>]*>/g;
128
+ let jsxMatch;
129
+ while ((jsxMatch = jsxPropPattern.exec(content)) !== null) {
130
+ const matchText = jsxMatch[0];
131
+
132
+ // Check if this is JSX children (element prop) or guard component props
133
+ // Patterns to exclude (these are valid React patterns):
134
+ // 1. <Route element={<Component />} /> - React Router pattern
135
+ // 2. <GuardComponent fallback={<Component />} /> - Guard component pattern
136
+ // 3. <Component>{children}</Component> - JSX children (but this won't match our pattern)
137
+ // 4. Any prop that contains JSX elements (starts with <)
138
+
139
+ const isRouteElement = /element\s*=\s*\{/.test(matchText);
140
+ const isGuardProp = /(fallback|loading|error|children)\s*=\s*\{/.test(matchText);
141
+ const hasJSXElement = /<[A-Z]\w+/.test(matchText);
142
+
143
+ // Also check if it's a className utility call (like cn()) - not a performance issue
144
+ const isUtilityCall = /cn\s*\(|clsx\s*\(|classnames\s*\(/.test(matchText);
145
+
146
+ // Only flag if it's an actual inline object/function, not JSX children
147
+ if (!isRouteElement && !isGuardProp && !hasJSXElement && !isUtilityCall) {
148
+ warnings.push({
149
+ type: 'inline-object-in-jsx',
150
+ file: relativePath,
151
+ line: getLineNumber(content, jsxMatch.index),
152
+ message: 'Large inline object or function in JSX props detected',
153
+ recommendation: 'Extract inline objects/functions to variables or useMemo/useCallback to prevent unnecessary re-renders. JSX children in props (like React Router element prop or guard component fallback prop) are standard React patterns and not performance issues.'
154
+ });
155
+ }
156
+ }
157
+
158
+ // Check for missing key props in lists
159
+ // Only flag if JSX is returned, not data arrays
160
+ const mapPattern = /\.map\s*\(\s*\([^)]+\)\s*=>/g;
161
+ while ((match = mapPattern.exec(content)) !== null) {
162
+ const afterMap = content.substring(match.index, match.index + 500);
163
+ // Check if JSX is returned without key
164
+ // Also check if it's actually returning JSX (not just data objects)
165
+ const returnsJSX = afterMap.includes('return') && afterMap.includes('<');
166
+
167
+ // Check for key in various formats: key=, key:, or inside React.cloneElement
168
+ const hasKey = afterMap.includes('key=') ||
169
+ /key\s*:/.test(afterMap) ||
170
+ /React\.cloneElement\s*\([^,]+,\s*\{[^}]*key\s*:/.test(afterMap);
171
+
172
+ // Check if it's a data transformation (returns object/array/primitive, not JSX)
173
+ // Pattern 1: Returns a simple value like .map(([key, _]) => key)
174
+ const returnsSimpleValue = /=>\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*;/.test(afterMap) && !afterMap.includes('<');
175
+ // Pattern 2: Returns a data object without JSX
176
+ const returnsDataObject = /return\s*\{[^<]*\}/.test(afterMap) && !afterMap.match(/return\s*\{[^<]*<[A-Z]/);
177
+ // Pattern 3: Returns array element access like .map((subRow) => subRow.original)
178
+ const returnsDataProperty = /=>\s*[a-zA-Z_$][a-zA-Z0-9_$]*\.[a-zA-Z_$][a-zA-Z0-9_$]*\s*[;}]/.test(afterMap) && !afterMap.includes('<');
179
+
180
+ // Skip pace-core files for this check - library code has complex patterns that are hard to detect accurately
181
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
182
+
183
+ if (returnsJSX && !hasKey && !returnsDataObject && !returnsSimpleValue && !returnsDataProperty && !isPaceCorePackage) {
184
+ warnings.push({
185
+ type: 'missing-key',
186
+ file: relativePath,
187
+ line: getLineNumber(content, match.index),
188
+ message: 'Array map returns JSX without key prop',
189
+ recommendation: 'Add a unique key prop to list items: {items.map(item => <Item key={item.id} ... />)}. Data array transformations (not JSX) do not need keys.'
190
+ });
191
+ }
192
+ }
193
+
194
+ // Check for expensive computations that could use useMemo
195
+ const expensivePatterns = [
196
+ /\.filter\([^)]*\)\.map\(/g, // filter().map() chains
197
+ /\.sort\([^)]*\)\.map\(/g, // sort().map() chains
198
+ /\.reduce\([^)]*\)/g // reduce operations
199
+ ];
200
+
201
+ expensivePatterns.forEach(pattern => {
202
+ let expMatch;
203
+ while ((expMatch = pattern.exec(content)) !== null) {
204
+ // Check a larger context to see if it's already in useMemo or useCallback
205
+ const beforeMatch = content.substring(Math.max(0, expMatch.index - 500), expMatch.index);
206
+ const afterMatch = content.substring(expMatch.index, Math.min(content.length, expMatch.index + 200));
207
+
208
+ // Check if it's already in useMemo or useCallback
209
+ if (beforeMatch.includes('useMemo') || beforeMatch.includes('useCallback')) {
210
+ continue;
211
+ }
212
+
213
+ // Check if it's inside a function definition that's static (like aggregateFn, cell renderer, etc.)
214
+ // These are function definitions that are called by the library, not recalculated on every render
215
+ const isStaticFunctionDefinition = /(aggregateFn|cell|header|footer|render|format|transform)\s*[:=]\s*\(/.test(beforeMatch);
216
+ if (isStaticFunctionDefinition) {
217
+ continue; // Skip static function definitions
218
+ }
219
+
220
+ // Check if it's inside a const/let declaration that's likely already memoized
221
+ // Pattern: const x = useMemo(() => ... or const x = useCallback(() => ...
222
+ const isInMemoizedConst = /const\s+\w+\s*=\s*(useMemo|useCallback)\s*\(/.test(beforeMatch);
223
+ if (isInMemoizedConst) {
224
+ continue;
225
+ }
226
+
227
+ // Check if it's part of a column definition (static, not recalculated)
228
+ const isColumnDefinition = /(accessorKey|header|cell|aggregateFn|aggregateCell)\s*:/.test(beforeMatch);
229
+ if (isColumnDefinition) {
230
+ continue; // Skip column definitions - these are static
231
+ }
232
+
233
+ suggestions.push({
234
+ type: 'expensive-computation',
235
+ file: relativePath,
236
+ line: getLineNumber(content, expMatch.index),
237
+ message: 'Expensive computation detected that could benefit from useMemo',
238
+ recommendation: 'Wrap expensive computations in useMemo to prevent recalculation on every render'
239
+ });
240
+ }
241
+ });
242
+
243
+ // Check for functions passed as props that could use useCallback
244
+ // Only flag complex inline functions, not simple event handlers
245
+ // For library code, be very lenient - inline functions are often acceptable patterns
246
+ if (!isLibraryCode) {
247
+ // Only check for consuming applications, not library code
248
+ const functionPropPattern = /(onClick|onChange|onSubmit|onFocus|onBlur|onMouseEnter|onMouseLeave|onValueChange)\s*=\s*\{[^}]*=>/g;
249
+ let funcMatch;
250
+ while ((funcMatch = functionPropPattern.exec(content)) !== null) {
251
+ const beforeMatch = content.substring(Math.max(0, funcMatch.index - 150), funcMatch.index);
252
+ const afterMatch = content.substring(funcMatch.index, Math.min(content.length, funcMatch.index + 300));
253
+
254
+ // Skip if already using useCallback or defined as const
255
+ if (beforeMatch.includes('useCallback') || beforeMatch.includes('const ')) {
256
+ continue;
257
+ }
258
+
259
+ // Extract the function body to check complexity
260
+ // Handle both arrow functions with and without braces
261
+ let funcBody = '';
262
+ const arrowMatch = afterMatch.match(/=>\s*(\{?)([^}]*?)(\}?)/);
263
+ if (arrowMatch) {
264
+ if (arrowMatch[1] === '{') {
265
+ // Multi-line function with braces - extract content between braces
266
+ const braceMatch = afterMatch.match(/=>\s*\{([^}]+)\}/);
267
+ if (braceMatch) {
268
+ funcBody = braceMatch[1];
269
+ }
270
+ } else {
271
+ // Single expression arrow function
272
+ funcBody = arrowMatch[2];
273
+ }
274
+ }
275
+
276
+ if (funcBody) {
277
+ const trimmedBody = funcBody.trim();
278
+
279
+ // Very short handlers are usually simple
280
+ if (trimmedBody.length < 50) {
281
+ continue;
282
+ }
283
+
284
+ // Check for simple setState patterns (even with object spread)
285
+ const isSimpleSetState = /^\s*set\w+\s*\([^)]*\)\s*;?\s*$/.test(trimmedBody) ||
286
+ /^\s*set\w+\s*\([^)]*prev\s*=>\s*\(\{[^}]*\}\)[^}]*\)\s*;?\s*$/.test(trimmedBody);
287
+
288
+ // Check for simple function calls
289
+ const isSimpleFunctionCall = /^\s*(handle\w+|on\w+|navigate|window\.|document\.)\s*\([^)]*\)\s*;?\s*$/.test(trimmedBody);
290
+
291
+ // Check for simple conditional (single if/ternary)
292
+ const isSimpleConditional = /^\s*(if\s*\([^)]+\)\s*\{[^}]{0,50}\}|[^?]+\?[^:]+:[^;]+;?)\s*$/.test(trimmedBody);
293
+
294
+ // Check for simple value transformations (common in library components)
295
+ // Pattern: (e) => handleFunc(e.target.value || undefined)
296
+ // Pattern: (e) => handleFunc(e.target.value ? transform(e.target.value) : undefined)
297
+ const isSimpleValueTransform = /^\s*\w+\s*\([^)]*\.(target|value|checked|selected)[^)]*\)\s*;?\s*$/.test(trimmedBody) ||
298
+ /^\s*\w+\s*\([^)]*\?[^:]+:[^;]+\)\s*;?\s*$/.test(trimmedBody);
299
+
300
+ // Skip simple handlers - they're fine as inline functions
301
+ if (isSimpleSetState || isSimpleFunctionCall || isSimpleConditional || isSimpleValueTransform) {
302
+ continue;
303
+ }
304
+ }
305
+
306
+ suggestions.push({
307
+ type: 'inline-function-prop',
308
+ file: relativePath,
309
+ line: getLineNumber(content, funcMatch.index),
310
+ message: 'Complex inline function passed as prop',
311
+ recommendation: 'Use useCallback to memoize complex functions passed as props to prevent child re-renders. Simple event handlers (single setState call or function invocation) are fine as inline functions.'
312
+ });
313
+ }
314
+ }
315
+ // For library code, skip this check entirely - inline functions are acceptable patterns
316
+ } catch (error) {
317
+ // Skip files with errors
318
+ }
319
+ }
320
+
321
+ return { issues, warnings, suggestions };
322
+ }
323
+ };
324
+
325
+ module.exports = performanceCheck;