@jmruthers/pace-core 0.6.1 → 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 (502) hide show
  1. package/CHANGELOG.md +43 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +18 -91
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-TPTKCX4D.js} +10 -9
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +356 -111
  17. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  18. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  19. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  20. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  21. package/dist/chunk-24UVZUZG.js.map +1 -0
  22. package/dist/{chunk-4N5C5XZU.js → chunk-2UOI2FG5.js} +4 -4
  23. package/dist/chunk-2UOI2FG5.js.map +1 -0
  24. package/dist/{chunk-T33XF5ZC.js → chunk-3XC4CPTD.js} +4317 -3963
  25. package/dist/chunk-3XC4CPTD.js.map +1 -0
  26. package/dist/{chunk-4ZC4GX36.js → chunk-6J4GEEJR.js} +172 -45
  27. package/dist/chunk-6J4GEEJR.js.map +1 -0
  28. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  29. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-EHMR7VYL.js} +4 -4
  31. package/dist/chunk-EHMR7VYL.js.map +1 -0
  32. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  33. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  34. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  35. package/dist/chunk-FFQEQTNW.js.map +1 -0
  36. package/dist/chunk-FMUCXFII.js +76 -0
  37. package/dist/chunk-FMUCXFII.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-3XTALGJF.js → chunk-MMZ7JXPU.js} +60 -223
  43. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  44. package/dist/{chunk-GLK6VM3F.js → chunk-NECFR5MM.js} +254 -170
  45. package/dist/chunk-NECFR5MM.js.map +1 -0
  46. package/dist/{chunk-JBKQ3SAO.js → chunk-SFZUDBL5.js} +40 -4
  47. package/dist/chunk-SFZUDBL5.js.map +1 -0
  48. package/dist/{chunk-XM25TVIE.js → chunk-XWQCNGTQ.js} +724 -363
  49. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  50. package/dist/components.d.ts +5 -5
  51. package/dist/components.js +14 -11
  52. package/dist/components.js.map +1 -1
  53. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  54. package/dist/hooks.d.ts +55 -122
  55. package/dist/hooks.js +8 -12
  56. package/dist/hooks.js.map +1 -1
  57. package/dist/index.d.ts +60 -13
  58. package/dist/index.js +19 -19
  59. package/dist/index.js.map +1 -1
  60. package/dist/providers.d.ts +21 -3
  61. package/dist/providers.js +2 -2
  62. package/dist/rbac/index.d.ts +145 -114
  63. package/dist/rbac/index.js +8 -11
  64. package/dist/theming/runtime.d.ts +1 -13
  65. package/dist/theming/runtime.js +1 -1
  66. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  67. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  68. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  69. package/dist/types.d.ts +2 -2
  70. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +31 -1
  71. package/dist/utils.d.ts +4 -5
  72. package/dist/utils.js +14 -14
  73. package/dist/utils.js.map +1 -1
  74. package/docs/api/README.md +7 -1
  75. package/docs/api/classes/ColumnFactory.md +8 -8
  76. package/docs/api/classes/InvalidScopeError.md +4 -4
  77. package/docs/api/classes/Logger.md +1 -1
  78. package/docs/api/classes/MissingUserContextError.md +4 -4
  79. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  80. package/docs/api/classes/PermissionDeniedError.md +4 -4
  81. package/docs/api/classes/RBACAuditManager.md +1 -1
  82. package/docs/api/classes/RBACCache.md +1 -1
  83. package/docs/api/classes/RBACEngine.md +1 -1
  84. package/docs/api/classes/RBACError.md +4 -4
  85. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  86. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  87. package/docs/api/classes/StorageUtils.md +1 -1
  88. package/docs/api/enums/FileCategory.md +1 -1
  89. package/docs/api/enums/LogLevel.md +1 -1
  90. package/docs/api/enums/RBACErrorCode.md +1 -1
  91. package/docs/api/enums/RPCFunction.md +1 -1
  92. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  93. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  94. package/docs/api/interfaces/AggregateConfig.md +4 -4
  95. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  96. package/docs/api/interfaces/AvatarProps.md +1 -1
  97. package/docs/api/interfaces/BadgeProps.md +9 -2
  98. package/docs/api/interfaces/ButtonProps.md +7 -4
  99. package/docs/api/interfaces/CalendarProps.md +8 -5
  100. package/docs/api/interfaces/CardProps.md +8 -5
  101. package/docs/api/interfaces/ColorPalette.md +1 -1
  102. package/docs/api/interfaces/ColorShade.md +1 -1
  103. package/docs/api/interfaces/ComplianceResult.md +1 -1
  104. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  105. package/docs/api/interfaces/DataRecord.md +1 -1
  106. package/docs/api/interfaces/DataTableAction.md +24 -21
  107. package/docs/api/interfaces/DataTableColumn.md +31 -31
  108. package/docs/api/interfaces/DataTableProps.md +1 -1
  109. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  110. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  111. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  112. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  113. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  115. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  116. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  117. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  118. package/docs/api/interfaces/ExportColumn.md +1 -1
  119. package/docs/api/interfaces/ExportOptions.md +8 -8
  120. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  121. package/docs/api/interfaces/FileMetadata.md +1 -1
  122. package/docs/api/interfaces/FileReference.md +1 -1
  123. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  124. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  125. package/docs/api/interfaces/FileUploadProps.md +26 -23
  126. package/docs/api/interfaces/FooterProps.md +10 -8
  127. package/docs/api/interfaces/FormFieldProps.md +10 -10
  128. package/docs/api/interfaces/FormProps.md +1 -1
  129. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  130. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  131. package/docs/api/interfaces/InputProps.md +7 -4
  132. package/docs/api/interfaces/LabelProps.md +1 -1
  133. package/docs/api/interfaces/LoggerConfig.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +14 -11
  135. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  136. package/docs/api/interfaces/NavigationContextType.md +1 -1
  137. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  138. package/docs/api/interfaces/NavigationItem.md +11 -11
  139. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  140. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  149. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  152. package/docs/api/interfaces/PaletteData.md +1 -1
  153. package/docs/api/interfaces/ParsedAddress.md +1 -1
  154. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  155. package/docs/api/interfaces/ProgressProps.md +1 -1
  156. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  157. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  158. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  159. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  160. package/docs/api/interfaces/QuickFix.md +1 -1
  161. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  162. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  163. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  164. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  165. package/docs/api/interfaces/RBACConfig.md +1 -1
  166. package/docs/api/interfaces/RBACContext.md +1 -1
  167. package/docs/api/interfaces/RBACLogger.md +1 -1
  168. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  169. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  170. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  171. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  172. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  173. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  174. package/docs/api/interfaces/RBACResult.md +1 -1
  175. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  176. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  177. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  178. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  181. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  182. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  183. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  184. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  185. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  186. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  187. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  188. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  189. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  190. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  191. package/docs/api/interfaces/RouteConfig.md +1 -1
  192. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  193. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  194. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  195. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  196. package/docs/api/interfaces/SetupIssue.md +1 -1
  197. package/docs/api/interfaces/StorageConfig.md +1 -1
  198. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  199. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  200. package/docs/api/interfaces/StorageListOptions.md +1 -1
  201. package/docs/api/interfaces/StorageListResult.md +1 -1
  202. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  203. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  204. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  205. package/docs/api/interfaces/StyleImport.md +1 -1
  206. package/docs/api/interfaces/SwitchProps.md +1 -1
  207. package/docs/api/interfaces/TabsContentProps.md +1 -1
  208. package/docs/api/interfaces/TabsListProps.md +1 -1
  209. package/docs/api/interfaces/TabsProps.md +1 -1
  210. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  211. package/docs/api/interfaces/TextareaProps.md +1 -1
  212. package/docs/api/interfaces/ToastActionElement.md +4 -1
  213. package/docs/api/interfaces/ToastProps.md +1 -1
  214. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  215. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  216. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  217. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  218. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  219. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  220. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  221. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  222. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  223. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  224. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  225. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  226. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  227. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  228. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  229. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  230. package/docs/api/interfaces/UserEventAccess.md +14 -11
  231. package/docs/api/interfaces/UserMenuProps.md +8 -6
  232. package/docs/api/interfaces/UserProfile.md +1 -1
  233. package/docs/api/modules.md +575 -634
  234. package/docs/architecture/database-schema-requirements.md +161 -0
  235. package/docs/core-concepts/rbac-system.md +3 -3
  236. package/docs/documentation-index.md +2 -4
  237. package/docs/getting-started/cursor-rules.md +2 -1
  238. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  239. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  240. package/docs/migration/README.md +52 -6
  241. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  242. package/docs/migration/database-changes-december-2025.md +3 -3
  243. package/docs/rbac/event-based-apps.md +1 -1
  244. package/docs/rbac/getting-started.md +1 -1
  245. package/docs/rbac/quick-start.md +1 -1
  246. package/docs/standards/README.md +1 -0
  247. package/package.json +2 -1
  248. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  249. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  250. package/scripts/audit/core/checks/bundle.cjs +142 -0
  251. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +714 -687
  252. package/scripts/audit/core/checks/config.cjs +54 -0
  253. package/scripts/audit/core/checks/coverage.cjs +84 -0
  254. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  255. package/scripts/audit/core/checks/documentation.cjs +203 -0
  256. package/scripts/audit/core/checks/environment.cjs +128 -0
  257. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  258. package/scripts/audit/core/checks/forms.cjs +172 -0
  259. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  260. package/scripts/audit/core/checks/hooks.cjs +334 -0
  261. package/scripts/audit/core/checks/imports.cjs +244 -0
  262. package/scripts/audit/core/checks/performance.cjs +325 -0
  263. package/scripts/audit/core/checks/routes.cjs +117 -0
  264. package/scripts/audit/core/checks/state.cjs +130 -0
  265. package/scripts/audit/core/checks/structure.cjs +65 -0
  266. package/scripts/audit/core/checks/style.cjs +584 -0
  267. package/scripts/audit/core/checks/testing.cjs +122 -0
  268. package/scripts/audit/core/checks/typescript.cjs +61 -0
  269. package/scripts/audit/core/scanner.cjs +199 -0
  270. package/scripts/audit/core/utils.cjs +137 -0
  271. package/scripts/audit/index.cjs +223 -0
  272. package/scripts/audit/reporters/console.cjs +151 -0
  273. package/scripts/audit/reporters/json.cjs +54 -0
  274. package/scripts/audit/reporters/markdown.cjs +124 -0
  275. package/scripts/audit-consuming-app.cjs +61 -936
  276. package/scripts/build-docs/build-decision.js +240 -0
  277. package/scripts/build-docs/cache-utils.js +105 -0
  278. package/scripts/build-docs/content-normalization.js +150 -0
  279. package/scripts/build-docs/file-utils.js +105 -0
  280. package/scripts/build-docs/git-utils.js +86 -0
  281. package/scripts/build-docs/hash-utils.js +116 -0
  282. package/scripts/build-docs/typedoc-runner.js +220 -0
  283. package/scripts/build-docs-incremental.js +77 -913
  284. package/scripts/utils/command-runner.js +16 -11
  285. package/scripts/validate-formats.js +61 -56
  286. package/scripts/validate-master.js +74 -69
  287. package/scripts/validate-pre-publish.js +70 -65
  288. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  289. package/src/components/Alert/Alert.test.tsx +12 -18
  290. package/src/components/Alert/Alert.tsx +5 -7
  291. package/src/components/Avatar/Avatar.test.tsx +4 -4
  292. package/src/components/Badge/Badge.tsx +14 -0
  293. package/src/components/Button/Button.tsx +22 -0
  294. package/src/components/Calendar/Calendar.tsx +8 -2
  295. package/src/components/Card/Card.tsx +4 -0
  296. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  297. package/src/components/Checkbox/Checkbox.tsx +2 -2
  298. package/src/components/DataTable/DataTable.tsx +38 -4
  299. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  300. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  301. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  302. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  303. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  304. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  305. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  306. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  307. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  308. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  309. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  310. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  311. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  312. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  313. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  314. package/src/components/DataTable/components/EditFields.tsx +307 -0
  315. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  316. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  317. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  318. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  319. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  320. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  321. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  322. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  323. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  324. package/src/components/DataTable/components/UnifiedTableBody.tsx +61 -849
  325. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  326. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  327. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  328. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  329. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  330. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  331. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  332. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  333. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  334. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  335. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  336. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  337. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  338. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  339. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  340. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  341. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  342. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  343. package/src/components/DataTable/styles.ts +6 -6
  344. package/src/components/DataTable/types.ts +6 -10
  345. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  346. package/src/components/DataTable/utils/debugTools.ts +18 -113
  347. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  348. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  349. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  350. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  351. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  352. package/src/components/Dialog/Dialog.tsx +2 -2
  353. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  354. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  355. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  356. package/src/components/ErrorBoundary/index.ts +27 -2
  357. package/src/components/EventSelector/EventSelector.tsx +3 -0
  358. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  359. package/src/components/FileUpload/FileUpload.tsx +22 -2
  360. package/src/components/Footer/Footer.test.tsx +16 -16
  361. package/src/components/Footer/Footer.tsx +14 -11
  362. package/src/components/Form/Form.tsx +1 -0
  363. package/src/components/Header/Header.tsx +21 -10
  364. package/src/components/Input/Input.test.tsx +2 -2
  365. package/src/components/Input/Input.tsx +8 -4
  366. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  367. package/src/components/LoginForm/LoginForm.tsx +4 -0
  368. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  369. package/src/components/NavigationMenu/types.ts +56 -0
  370. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  371. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  372. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -2
  373. package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -11
  374. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  375. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  376. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  377. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  378. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  379. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  380. package/src/components/Select/Select.tsx +80 -434
  381. package/src/components/Select/context.ts +23 -0
  382. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  383. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  384. package/src/components/Select/hooks/useSelectState.ts +104 -0
  385. package/src/components/Select/index.ts +9 -1
  386. package/src/components/Select/types.ts +123 -0
  387. package/src/components/Select/utils/text.ts +26 -0
  388. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  389. package/src/components/Switch/Switch.tsx +4 -4
  390. package/src/components/Tabs/Tabs.tsx +1 -1
  391. package/src/components/Toast/Toast.tsx +4 -0
  392. package/src/components/Tooltip/Tooltip.tsx +2 -2
  393. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  394. package/src/components/UserMenu/UserMenu.tsx +21 -18
  395. package/src/components/index.ts +2 -2
  396. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  397. package/src/hooks/index.ts +1 -2
  398. package/src/hooks/public/usePublicEvent.ts +4 -0
  399. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  400. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  401. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  402. package/src/hooks/services/useAuth.ts +32 -0
  403. package/src/hooks/services/useCurrentEvent.ts +6 -0
  404. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  405. package/src/hooks/useDebounce.ts +9 -0
  406. package/src/hooks/useEventTheme.ts +6 -0
  407. package/src/hooks/useFileDisplay.ts +4 -0
  408. package/src/hooks/useFileReference.ts +25 -7
  409. package/src/hooks/useFileUrl.ts +11 -1
  410. package/src/hooks/useFocusManagement.ts +14 -0
  411. package/src/hooks/useFocusTrap.ts +3 -0
  412. package/src/hooks/useInactivityTracker.ts +3 -0
  413. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  414. package/src/hooks/useOrganisationPermissions.ts +4 -0
  415. package/src/hooks/useOrganisationSecurity.ts +4 -0
  416. package/src/hooks/usePerformanceMonitor.ts +4 -0
  417. package/src/hooks/usePermissionCache.ts +7 -0
  418. package/src/hooks/useQueryCache.ts +12 -1
  419. package/src/hooks/useSessionRestoration.ts +4 -0
  420. package/src/hooks/useStorage.ts +4 -0
  421. package/src/hooks/useToast.ts +1 -1
  422. package/src/index.ts +2 -1
  423. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  424. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  425. package/src/providers/services/EventServiceProvider.tsx +18 -0
  426. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  427. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  428. package/src/providers/services/UnifiedAuthProvider.tsx +36 -0
  429. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  430. package/src/rbac/README.md +1 -1
  431. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +2 -2
  432. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  433. package/src/rbac/adapters.tsx +14 -5
  434. package/src/rbac/api.ts +100 -67
  435. package/src/rbac/components/NavigationProvider.tsx +4 -1
  436. package/src/rbac/components/PagePermissionGuard.tsx +157 -17
  437. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  438. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  439. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  440. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  441. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  442. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  443. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  444. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  445. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  446. package/src/rbac/engine.ts +38 -14
  447. package/src/rbac/hooks/permissions/index.ts +7 -0
  448. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  449. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  450. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  451. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  452. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  453. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  454. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  455. package/src/rbac/hooks/useCan.test.ts +71 -64
  456. package/src/rbac/hooks/usePermissions.ts +14 -995
  457. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  458. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  459. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  460. package/src/rbac/permissions.ts +0 -30
  461. package/src/rbac/secureClient.ts +200 -61
  462. package/src/rbac/types.ts +8 -0
  463. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  464. package/src/theming/parseEventColours.ts +5 -19
  465. package/src/types/vitest-globals.d.ts +51 -26
  466. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  467. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  468. package/src/utils/__tests__/index.unit.test.ts +2 -2
  469. package/src/utils/audit/audit.ts +0 -3
  470. package/src/utils/core/cn.ts +1 -1
  471. package/src/utils/file-reference/index.ts +53 -1
  472. package/src/utils/formatting/formatting.ts +8 -18
  473. package/src/utils/index.ts +0 -1
  474. package/dist/chunk-3QRJFVBR.js.map +0 -1
  475. package/dist/chunk-3XTALGJF.js.map +0 -1
  476. package/dist/chunk-4N5C5XZU.js.map +0 -1
  477. package/dist/chunk-4ZC4GX36.js.map +0 -1
  478. package/dist/chunk-BYFSK72L.js.map +0 -1
  479. package/dist/chunk-EXUD6RNJ.js +0 -451
  480. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  481. package/dist/chunk-GLK6VM3F.js.map +0 -1
  482. package/dist/chunk-I7PSE6JW.js.map +0 -1
  483. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  484. package/dist/chunk-KNC55RTG.js.map +0 -1
  485. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  486. package/dist/chunk-R77UEZ4E.js.map +0 -1
  487. package/dist/chunk-SQGMNID3.js.map +0 -1
  488. package/dist/chunk-T33XF5ZC.js.map +0 -1
  489. package/dist/chunk-XM25TVIE.js.map +0 -1
  490. package/docs/api/classes/ErrorBoundary.md +0 -144
  491. package/docs/migration/quick-migration-guide.md +0 -356
  492. package/docs/migration/service-architecture.md +0 -281
  493. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  494. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  495. package/src/hooks/useSecureDataAccess.ts +0 -681
  496. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  497. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  498. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  499. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  500. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  501. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  502. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -427,7 +427,7 @@ const { data: files } = await supabase
427
427
 
428
428
  **Migration:** `20251205211120_migrate_profiles_to_person_scoped.sql` + related migrations
429
429
  **Date:** 2025-12-05
430
- **Documentation:** See [Person-Scoped Profiles Migration Guide](./person-scoped-profiles-migration-guide.md)
430
+ **Documentation:** See [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md)
431
431
 
432
432
  ### Quick Summary
433
433
 
@@ -501,7 +501,7 @@ const { data } = await supabase
501
501
  - [ ] Update `pace_contact` queries to use `person_id` instead of `member_id`
502
502
  - [ ] Update profile creation logic (no `organisation_id` needed)
503
503
  - [ ] Review profile access patterns (person-scoped, not org-scoped)
504
- - [ ] See [Person-Scoped Profiles Migration Guide](./person-scoped-profiles-migration-guide.md) for detailed checklist
504
+ - [ ] See [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md) for detailed checklist
505
505
 
506
506
  ### Phase 3: Testing
507
507
 
@@ -708,7 +708,7 @@ All migration files are located in:
708
708
 
709
709
  ### Documentation
710
710
 
711
- - [Person-Scoped Profiles Migration Guide](./person-scoped-profiles-migration-guide.md)
711
+ - [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md)
712
712
  - [API & RPC Standard](../standards/02-api-and-rpc-standard.md)
713
713
  - [Database Schema Requirements](../architecture/database-schema-requirements.md)
714
714
 
@@ -973,7 +973,7 @@ VALUES (
973
973
  - **[API Reference](./api-reference.md)** - Complete API documentation
974
974
  - **[Examples](./examples.md)** - More complex usage patterns
975
975
  - **[Troubleshooting](./troubleshooting.md)** - Advanced troubleshooting
976
- - **[Migration Guide](../migration/rbac-migration.md)** - If migrating from legacy RBAC
976
+ - **[Migration Guide](../migration/V0.4.0_rbac-migration.md)** - If migrating from legacy RBAC
977
977
 
978
978
  ## 🆘 Still Having Issues?
979
979
 
@@ -843,7 +843,7 @@ function AdaptiveComponent({ appId }) {
843
843
  - [Examples](./examples.md) - More complex examples
844
844
  - [Event-Based Apps](./event-based-apps.md) - Guide for event-based applications
845
845
  - [Troubleshooting](./troubleshooting.md) - Common issues and solutions
846
- - [Migration Guide](../migration/rbac-migration.md) - Migrating from legacy RBAC
846
+ - [Migration Guide](../migration/V0.4.0_rbac-migration.md) - Migrating from legacy RBAC
847
847
 
848
848
  ## ♿ Accessibility
849
849
 
@@ -740,7 +740,7 @@ The system will automatically resolve the organisation ID from the event context
740
740
  - **[API Reference](./api-reference.md)** - Complete API documentation
741
741
  - **[Examples](./examples.md)** - More complex usage patterns
742
742
  - **[Troubleshooting](./troubleshooting.md)** - Advanced troubleshooting
743
- - **[Migration Guide](../migration/rbac-migration.md)** - If migrating from legacy RBAC
743
+ - **[Migration Guide](../migration/V0.4.0_rbac-migration.md)** - If migrating from legacy RBAC
744
744
 
745
745
  ## 🆘 Still Having Issues?
746
746
 
@@ -57,6 +57,7 @@ The cursor rules cover:
57
57
  - **05-bug-reports-and-features** - Templates for issue reporting
58
58
  - **06-code-quality** - Enforce code quality standards
59
59
  - **07-tech-stack-compliance** - Enforce tech stack versions
60
+ - **08-markup-quality** - Enforce clean markup standards, semantic HTML usage, and pace-core component patterns
60
61
 
61
62
  ### Audit Tool
62
63
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -73,6 +73,7 @@
73
73
  "./core-usage-manifest.json": "./core-usage-manifest.json",
74
74
  "./cursor-rules": "./cursor-rules",
75
75
  "./scripts/install-cursor-rules": "./scripts/install-cursor-rules.cjs",
76
+ "./scripts/audit": "./scripts/audit/index.cjs",
76
77
  "./scripts/audit-consuming-app": "./scripts/audit-consuming-app.cjs",
77
78
  "./source": {
78
79
  "import": "./src/index.ts",
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Accessibility Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Accessibility
7
+ *
8
+ * Checks for:
9
+ * - Missing aria-* attributes
10
+ * - Missing keyboard navigation
11
+ * - Missing focus management
12
+ * - Missing alt text on images
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const { getRelativePath, getLineNumber } = require('../utils.cjs');
17
+
18
+ const accessibilityCheck = {
19
+ name: 'accessibility',
20
+ description: 'Accessibility checks (aria attributes, keyboard navigation, alt text)',
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 root-level src directory - in pace-core repository, this is a demo/showcase app
45
+ // Note: We DO check packages/core/ files because accessibility issues (missing alt attributes, missing form labels) are real issues that should be fixed
46
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
47
+ if (isRootSrc) {
48
+ continue; // Skip demo app files
49
+ }
50
+
51
+ // Skip scripts directory - utility scripts don't need accessibility validation
52
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
53
+ if (isScript) {
54
+ continue; // Skip script files
55
+ }
56
+
57
+ // Skip pace-core library components and examples - these are designed to accept accessibility props
58
+ // Library components accept id, aria-label, etc. via props spread
59
+ // Examples are demonstration code, not production code
60
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
61
+ const isExample = normalizedPath.includes('/examples/');
62
+ const isLibraryComponent = isPaceCorePackage && !isExample;
63
+
64
+ // Check for images without alt text
65
+ // Skip library components - they accept alt as a prop
66
+ if (!isLibraryComponent) {
67
+ const imgPattern = /<img[^>]*>/g;
68
+ let imgMatch;
69
+ while ((imgMatch = imgPattern.exec(content)) !== null) {
70
+ const imgTag = imgMatch[0];
71
+ // Check for alt attribute, including React props (alt=, alt =, alt={)
72
+ const hasAlt = imgTag.includes('alt=') || imgTag.includes('alt =') || imgTag.includes('alt={');
73
+ if (!hasAlt) {
74
+ warnings.push({
75
+ type: 'missing-alt-text',
76
+ file: relativePath,
77
+ line: getLineNumber(content, imgMatch.index),
78
+ message: 'Image element missing alt attribute',
79
+ recommendation: 'Add alt text to all images for accessibility: <img alt="description" ... />'
80
+ });
81
+ }
82
+ }
83
+ }
84
+
85
+ // Check for buttons/clickable elements without aria-label or accessible text
86
+ const buttonPattern = /<(button|a|div)\s+[^>]*(onClick|role=["']button["'])[^>]*>/g;
87
+ let buttonMatch;
88
+ while ((buttonMatch = buttonPattern.exec(content)) !== null) {
89
+ const buttonTag = buttonMatch[0];
90
+ const hasAriaLabel = buttonTag.includes('aria-label=') || buttonTag.includes('ariaLabel=');
91
+ const hasAccessibleText = buttonTag.includes('>') && content.substring(buttonMatch.index).match(/<[^>]*>([^<]+)</);
92
+
93
+ if (!hasAriaLabel && !hasAccessibleText) {
94
+ warnings.push({
95
+ type: 'missing-aria-label',
96
+ file: relativePath,
97
+ line: getLineNumber(content, buttonMatch.index),
98
+ message: 'Interactive element missing accessible label',
99
+ recommendation: 'Add aria-label or ensure element contains accessible text content'
100
+ });
101
+ }
102
+ }
103
+
104
+ // Check for form inputs without labels
105
+ // Skip library components - they accept id, aria-label, etc. via props spread
106
+ // Skip examples - they're demonstration code
107
+ if (!isLibraryComponent && !isExample) {
108
+ const inputPattern = /<input[^>]*>/g;
109
+ let inputMatch;
110
+ while ((inputMatch = inputPattern.exec(content)) !== null) {
111
+ // Get the full input tag including multi-line attributes
112
+ const inputStart = inputMatch.index;
113
+ const afterInput = content.substring(inputStart, Math.min(content.length, inputStart + 500));
114
+ const inputTagEnd = afterInput.indexOf('>');
115
+ const fullInputTag = inputTagEnd !== -1 ? afterInput.substring(0, inputTagEnd + 1) : inputMatch[0];
116
+
117
+ // Check for id (including React props)
118
+ const hasId = /id=["']([^"']+)["']|id=\{/.test(fullInputTag);
119
+ // Check for aria-label (including React props)
120
+ const hasAriaLabel = /aria-label=["']|ariaLabel=|aria-label=\{/.test(fullInputTag);
121
+
122
+ if (hasId) {
123
+ const idMatch = fullInputTag.match(/id=["']([^"']+)["']/);
124
+ if (idMatch) {
125
+ const id = idMatch[1];
126
+ // Check if there's a corresponding label
127
+ const beforeInput = content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index);
128
+ const hasLabel = new RegExp(`<label[^>]*for=["']${id}["']`, 'i').test(beforeInput);
129
+
130
+ if (!hasLabel && !hasAriaLabel) {
131
+ warnings.push({
132
+ type: 'missing-input-label',
133
+ file: relativePath,
134
+ line: getLineNumber(content, inputMatch.index),
135
+ message: 'Form input missing associated label',
136
+ recommendation: 'Add a <label> element with for attribute matching input id, or use aria-label'
137
+ });
138
+ }
139
+ }
140
+ } else if (!hasAriaLabel) {
141
+ warnings.push({
142
+ type: 'missing-input-label',
143
+ file: relativePath,
144
+ line: getLineNumber(content, inputMatch.index),
145
+ message: 'Form input missing id and label',
146
+ recommendation: 'Add id to input and corresponding label, or use aria-label'
147
+ });
148
+ }
149
+ }
150
+ }
151
+
152
+ // Check for missing focus management in modals/dialogs
153
+ const dialogPattern = /<Dialog|<dialog/gi;
154
+ if (dialogPattern.test(content)) {
155
+ // Skip Dialog.tsx itself - it IS the Dialog component
156
+ if (normalizedPath.includes('/Dialog/Dialog.tsx') || normalizedPath.includes('/Dialog/Dialog.jsx')) {
157
+ continue; // Skip the Dialog component file itself
158
+ }
159
+
160
+ // Check if Dialog from pace-core is used (which handles focus automatically)
161
+ // Check for both published package import and relative imports (within pace-core repo)
162
+ const usesPaceCoreDialog = content.includes('from \'@jmruthers/pace-core\'') ||
163
+ content.includes('from "@jmruthers/pace-core"') ||
164
+ content.includes('from \'../Dialog') ||
165
+ content.includes('from "../Dialog') ||
166
+ content.includes('from \'../../Dialog') ||
167
+ content.includes('from "../../Dialog') ||
168
+ content.includes('from \'../../../Dialog') ||
169
+ content.includes('from "../../../Dialog') ||
170
+ content.includes('from \'./Dialog') ||
171
+ content.includes('from "./Dialog');
172
+
173
+ if (!usesPaceCoreDialog) {
174
+ suggestions.push({
175
+ type: 'dialog-focus-management',
176
+ file: relativePath,
177
+ message: 'Custom dialog implementation detected',
178
+ recommendation: 'Use Dialog component from @jmruthers/pace-core which handles focus management automatically'
179
+ });
180
+ }
181
+ }
182
+
183
+ // Check for color contrast issues (heuristic - check for color props without sufficient contrast)
184
+ const colorPattern = /(?:color|bg-|text-)=["']([^"']+)["']/g;
185
+ // This is a simplified check - full implementation would need color contrast calculation
186
+ // For now, just suggest using pace-core components which handle contrast
187
+
188
+ } catch (error) {
189
+ // Skip files with errors
190
+ }
191
+ }
192
+
193
+ return { issues, warnings, suggestions };
194
+ }
195
+ };
196
+
197
+ module.exports = accessibilityCheck;
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * API Usage Patterns Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/ApiUsage
7
+ *
8
+ * Checks for:
9
+ * - Incorrect usage of pace-core hooks
10
+ * - Missing loading states
11
+ * - Missing error states
12
+ * - Race conditions in async operations
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const { getRelativePath, getLineNumber } = require('../utils.cjs');
17
+
18
+ const apiUsageCheck = {
19
+ name: 'api-usage',
20
+ description: 'API usage patterns (hook usage, loading/error states)',
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
+ // pace-core hooks that return loading/error states
34
+ const asyncHooks = {
35
+ 'useUnifiedAuth': ['loading', 'error'],
36
+ 'useOrganisations': ['isLoading', 'error'],
37
+ 'useEvents': ['isLoading', 'error'],
38
+ 'useSecureSupabase': [],
39
+ 'useFileReference': ['isLoading', 'error'],
40
+ 'useRBAC': ['isLoading', 'error']
41
+ };
42
+
43
+ for (const filePath of files) {
44
+ try {
45
+ // Only check React component files
46
+ if (!filePath.match(/\.(tsx|jsx)$/)) {
47
+ continue;
48
+ }
49
+
50
+ const content = fs.readFileSync(filePath, 'utf8');
51
+ const relativePath = getRelativePath(filePath, projectRoot);
52
+ const normalizedPath = relativePath.replace(/\\/g, '/');
53
+
54
+ // Skip pace-core package files - api-usage check is for consuming applications, not the library itself
55
+ // Note: Library components may intentionally not check loading/error states in all cases
56
+ // (e.g., internal components that assume context is already set up)
57
+ const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
58
+ if (isPaceCorePackage) {
59
+ continue; // Skip library files (including examples)
60
+ }
61
+
62
+ // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
63
+ const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
64
+ if (isRootSrc) {
65
+ continue; // Skip demo app files
66
+ }
67
+
68
+ // Skip scripts directory - utility scripts don't need API usage validation
69
+ const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
70
+ if (isScript) {
71
+ continue; // Skip script files
72
+ }
73
+
74
+ // Check each async hook
75
+ Object.entries(asyncHooks).forEach(([hookName, expectedStates]) => {
76
+ if (!content.includes(hookName)) {
77
+ return;
78
+ }
79
+
80
+ const hookPattern = new RegExp(`const\\s+[^=]+=\\s+${hookName}\\s*\\(`, 'g');
81
+ let hookMatch;
82
+ while ((hookMatch = hookPattern.exec(content)) !== null) {
83
+ // Check the entire component, not just 300 chars after hook
84
+ // React's Rules of Hooks require all hooks to be called before conditional returns,
85
+ // so state checks may occur later in the component
86
+ const hookLine = getLineNumber(content, hookMatch.index);
87
+ const lines = content.split('\n');
88
+ const hookLineIndex = hookLine - 1;
89
+
90
+ // Get the rest of the component after this hook
91
+ const afterHookContent = lines.slice(hookLineIndex).join('\n');
92
+
93
+ // Check if loading state is used
94
+ if (expectedStates.includes('loading') || expectedStates.includes('isLoading')) {
95
+ const loadingState = expectedStates.includes('loading') ? 'loading' : 'isLoading';
96
+ // Check for loading state usage in the entire component
97
+ // Also check for common patterns: isLoading, authLoading, etc.
98
+ const loadingPattern = new RegExp(`\\b(${loadingState}|authLoading|rbacLoading|orgLoading)\\b`);
99
+ if (!loadingPattern.test(afterHookContent)) {
100
+ warnings.push({
101
+ type: 'missing-loading-state',
102
+ file: relativePath,
103
+ line: hookLine,
104
+ message: `${hookName} returns loading state but it's not being checked`,
105
+ recommendation: `Check ${loadingState} state and show loading UI while data is fetching. You can check loading state after all hooks are called (required by React's Rules of Hooks).`
106
+ });
107
+ }
108
+ }
109
+
110
+ // Check if error state is used
111
+ if (expectedStates.includes('error')) {
112
+ // Check for error state usage in the entire component
113
+ // Also check for common patterns: error, authError, orgError, rbacError
114
+ // Also check for null checks for useSecureSupabase
115
+ const errorPattern = /\b(error|authError|orgError|rbacError)\b/;
116
+ const hasNullCheck = hookName === 'useSecureSupabase' && /secureSupabase\s*===?\s*null|!secureSupabase/.test(afterHookContent);
117
+
118
+ if (!errorPattern.test(afterHookContent) && !hasNullCheck) {
119
+ warnings.push({
120
+ type: 'missing-error-state',
121
+ file: relativePath,
122
+ line: hookLine,
123
+ message: `${hookName} returns error state but it's not being checked`,
124
+ recommendation: 'Check error state and handle errors appropriately. You can check error state after all hooks are called (required by React\'s Rules of Hooks), or handle errors via useEffect hooks. For useSecureSupabase, null checks are also valid error handling.'
125
+ });
126
+ }
127
+ }
128
+ }
129
+ });
130
+
131
+ // Check for race conditions in async operations
132
+ const asyncOperationPattern = /(?:const|let)\s+(\w+)\s*=\s*(?:await\s+)?(\w+)\.(from|rpc|select)/g;
133
+ let asyncMatch;
134
+ while ((asyncMatch = asyncOperationPattern.exec(content)) !== null) {
135
+ const varName = asyncMatch[1];
136
+ const operation = asyncMatch[0];
137
+
138
+ // Check if this is in a useEffect without cleanup
139
+ const beforeMatch = content.substring(Math.max(0, asyncMatch.index - 500), asyncMatch.index);
140
+ const isInUseEffect = /useEffect\s*\(/.test(beforeMatch);
141
+
142
+ if (isInUseEffect) {
143
+ // Check if there's cleanup/abort
144
+ const afterMatch = content.substring(asyncMatch.index, Math.min(content.length, asyncMatch.index + 500));
145
+ const hasCleanup = /return\s+\(\)\s*=>|AbortController|signal/.test(afterMatch);
146
+
147
+ if (!hasCleanup) {
148
+ suggestions.push({
149
+ type: 'potential-race-condition',
150
+ file: relativePath,
151
+ line: getLineNumber(content, asyncMatch.index),
152
+ message: 'Async operation in useEffect without cleanup - potential race condition',
153
+ recommendation: 'Add cleanup function to cancel requests when component unmounts or dependencies change'
154
+ });
155
+ }
156
+ }
157
+ }
158
+
159
+ // Check for missing request cancellation
160
+ const fetchPattern = /fetch\s*\(|axios\.(get|post|put|delete)\s*\(/g;
161
+ let fetchMatch;
162
+ while ((fetchMatch = fetchPattern.exec(content)) !== null) {
163
+ const beforeMatch = content.substring(Math.max(0, fetchMatch.index - 200), fetchMatch.index);
164
+ const isInUseEffect = /useEffect\s*\(/.test(beforeMatch);
165
+
166
+ if (isInUseEffect) {
167
+ const afterMatch = content.substring(fetchMatch.index, Math.min(content.length, fetchMatch.index + 500));
168
+ const hasAbort = /AbortController|signal|abort/.test(afterMatch);
169
+
170
+ if (!hasAbort) {
171
+ suggestions.push({
172
+ type: 'missing-abort',
173
+ file: relativePath,
174
+ line: getLineNumber(content, fetchMatch.index),
175
+ message: 'Fetch/axios request without abort signal',
176
+ recommendation: 'Use AbortController to cancel requests when component unmounts'
177
+ });
178
+ }
179
+ }
180
+ }
181
+
182
+ } catch (error) {
183
+ // Skip files with errors
184
+ }
185
+ }
186
+
187
+ return { issues, warnings, suggestions };
188
+ }
189
+ };
190
+
191
+ module.exports = apiUsageCheck;
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Bundle Analysis Check Module
5
+ * @package @jmruthers/pace-core
6
+ * @module Audit/Checks/Bundle
7
+ *
8
+ * Checks for:
9
+ * - Large bundle sizes
10
+ * - Duplicate dependencies
11
+ * - Unused code imports
12
+ * - Missing code splitting opportunities
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { getRelativePath } = require('../utils.cjs');
18
+
19
+ const bundleCheck = {
20
+ name: 'bundle',
21
+ description: 'Bundle analysis (size, duplicates, code splitting)',
22
+ severity: 'suggestion',
23
+
24
+ async run(context) {
25
+ const { projectRoot } = context;
26
+ const issues = [];
27
+ const warnings = [];
28
+ const suggestions = [];
29
+
30
+ // Skip if this is the pace-core repository itself
31
+ // Detect pace-core repository by checking if packages/core exists
32
+ // Bundle analysis is more relevant for consuming applications, not the library itself
33
+ const packagesCorePath = path.join(projectRoot, 'packages', 'core');
34
+ const isPaceCoreRepository = fs.existsSync(packagesCorePath);
35
+
36
+ // Check for bundle analysis reports
37
+ const bundleReportPaths = [
38
+ path.join(projectRoot, 'dist', 'stats.html'),
39
+ path.join(projectRoot, 'dist', 'bundle-analysis.json'),
40
+ path.join(projectRoot, '.vite', 'dist', 'stats.html')
41
+ ];
42
+
43
+ let hasBundleReport = false;
44
+ for (const reportPath of bundleReportPaths) {
45
+ if (fs.existsSync(reportPath)) {
46
+ hasBundleReport = true;
47
+ break;
48
+ }
49
+ }
50
+
51
+ // Skip bundle analysis suggestion if this is the pace-core repository
52
+ // The library itself doesn't need bundle analysis in the same way as consuming apps
53
+ if (!hasBundleReport && !isPaceCoreRepository) {
54
+ suggestions.push({
55
+ type: 'missing-bundle-analysis',
56
+ file: 'package.json',
57
+ message: 'No bundle analysis report found',
58
+ recommendation: 'Run bundle analysis to identify optimization opportunities: npm run build -- --analyze or use vite-bundle-visualizer'
59
+ });
60
+ return { issues, warnings, suggestions };
61
+ }
62
+
63
+ // Check dist folder size
64
+ const distPath = path.join(projectRoot, 'dist');
65
+ if (fs.existsSync(distPath)) {
66
+ try {
67
+ const distSize = getDirectorySize(distPath);
68
+ const sizeMB = distSize / (1024 * 1024);
69
+
70
+ if (sizeMB > 5) {
71
+ warnings.push({
72
+ type: 'large-bundle',
73
+ file: 'dist/',
74
+ message: `Bundle size is ${sizeMB.toFixed(2)}MB, which is quite large`,
75
+ recommendation: 'Consider code splitting, lazy loading, and removing unused dependencies'
76
+ });
77
+ }
78
+ } catch (error) {
79
+ // Skip if can't calculate size
80
+ }
81
+ }
82
+
83
+ // Check for duplicate imports (same package imported multiple ways)
84
+ // This is a simplified check - full implementation would analyze actual bundle
85
+ const packageJsonPath = path.join(projectRoot, 'package.json');
86
+ if (fs.existsSync(packageJsonPath)) {
87
+ try {
88
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
89
+ const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
90
+
91
+ // Check for potential duplicates (e.g., lodash and lodash-es)
92
+ // Note: '@supabase/supabase-js' (client library) and 'supabase' (CLI tool) are NOT duplicates
93
+ // - '@supabase/supabase-js' is the JavaScript client library (runtime dependency)
94
+ // - 'supabase' is the Supabase CLI tool (dev dependency for migrations, local dev)
95
+ // The CLI tool is not bundled in the application, so having both is correct
96
+ const duplicatePatterns = [
97
+ ['lodash', 'lodash-es'],
98
+ ['date-fns', 'date-fns-tz']
99
+ ];
100
+
101
+ duplicatePatterns.forEach(([dep1, dep2]) => {
102
+ if (allDeps[dep1] && allDeps[dep2]) {
103
+ warnings.push({
104
+ type: 'duplicate-dependency',
105
+ file: 'package.json',
106
+ message: `Both '${dep1}' and '${dep2}' are installed - may cause duplicate code in bundle`,
107
+ recommendation: `Remove one and use pace-core alternatives if available, or ensure only one is bundled`
108
+ });
109
+ }
110
+ });
111
+ } catch (error) {
112
+ // Skip if can't parse package.json
113
+ }
114
+ }
115
+
116
+ return { issues, warnings, suggestions };
117
+ }
118
+ };
119
+
120
+ /**
121
+ * Calculate directory size recursively
122
+ */
123
+ function getDirectorySize(dirPath) {
124
+ let size = 0;
125
+ try {
126
+ const items = fs.readdirSync(dirPath);
127
+ items.forEach(item => {
128
+ const itemPath = path.join(dirPath, item);
129
+ const stat = fs.statSync(itemPath);
130
+ if (stat.isDirectory()) {
131
+ size += getDirectorySize(itemPath);
132
+ } else {
133
+ size += stat.size;
134
+ }
135
+ });
136
+ } catch (error) {
137
+ // Skip if can't read
138
+ }
139
+ return size;
140
+ }
141
+
142
+ module.exports = bundleCheck;