@jmruthers/pace-core 0.6.4 → 0.6.6

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 (387) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/core-usage-manifest.json +93 -0
  4. package/cursor-rules/00-pace-core-compliance.mdc +128 -26
  5. package/cursor-rules/01-standards-compliance.mdc +49 -8
  6. package/cursor-rules/02-project-structure.mdc +6 -0
  7. package/cursor-rules/03-solid-principles.mdc +2 -0
  8. package/cursor-rules/04-testing-standards.mdc +2 -0
  9. package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
  10. package/cursor-rules/06-code-quality.mdc +2 -0
  11. package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
  12. package/cursor-rules/08-markup-quality.mdc +52 -27
  13. package/cursor-rules/09-rbac-compliance.mdc +462 -0
  14. package/cursor-rules/10-error-handling-patterns.mdc +179 -0
  15. package/cursor-rules/11-performance-optimization.mdc +169 -0
  16. package/cursor-rules/12-ci-cd-integration.mdc +150 -0
  17. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  18. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
  19. package/dist/DataTable-LRJL4IRV.js +15 -0
  20. package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
  21. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  22. package/dist/api-Y4MQWOFW.js +4 -0
  23. package/dist/audit-MYQXYZFU.js +3 -0
  24. package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
  25. package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
  26. package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
  27. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  28. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  29. package/dist/chunk-4T7OBVTU.js +62 -0
  30. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  31. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  32. package/dist/{chunk-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
  33. package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
  34. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  35. package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
  36. package/dist/chunk-C7NSAPTL.js +1 -0
  37. package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
  38. package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
  39. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  40. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  41. package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
  42. package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
  43. package/dist/chunk-MBADTM7L.js +64 -0
  44. package/dist/chunk-OHIK3MIO.js +994 -0
  45. package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
  46. package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
  47. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  48. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  49. package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
  50. package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
  51. package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
  52. package/dist/components.d.ts +6 -6
  53. package/dist/components.js +57 -267
  54. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  55. package/dist/eslint-rules/index.cjs +22 -0
  56. package/dist/eslint-rules/rules/compliance.cjs +348 -0
  57. package/dist/eslint-rules/rules/components.cjs +113 -0
  58. package/dist/eslint-rules/rules/imports.cjs +102 -0
  59. package/dist/eslint-rules/rules/rbac.cjs +790 -0
  60. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  61. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  62. package/dist/hooks.d.ts +5 -5
  63. package/dist/hooks.js +62 -270
  64. package/dist/icons/index.d.ts +1 -0
  65. package/dist/icons/index.js +1 -0
  66. package/dist/index.d.ts +36 -26
  67. package/dist/index.js +87 -690
  68. package/dist/providers.d.ts +2 -2
  69. package/dist/providers.js +8 -35
  70. package/dist/rbac/eslint-rules.d.ts +46 -44
  71. package/dist/rbac/eslint-rules.js +7 -4
  72. package/dist/rbac/index.d.ts +124 -594
  73. package/dist/rbac/index.js +14 -207
  74. package/dist/styles/index.js +2 -12
  75. package/dist/theming/runtime.js +3 -19
  76. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  77. package/dist/{types-CkbwOr4Y.d.ts → types-B-K_5VnO.d.ts} +4 -0
  78. package/dist/types-t9H8qKRw.d.ts +55 -0
  79. package/dist/types.d.ts +1 -1
  80. package/dist/types.js +7 -94
  81. package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
  82. package/dist/utils.d.ts +24 -117
  83. package/dist/utils.js +54 -392
  84. package/docs/README.md +16 -6
  85. package/docs/api/README.md +4 -402
  86. package/docs/api/modules.md +454 -930
  87. package/docs/api-reference/components.md +3 -1
  88. package/docs/api-reference/deprecated.md +31 -6
  89. package/docs/api-reference/rpc-functions.md +78 -3
  90. package/docs/best-practices/accessibility.md +6 -3
  91. package/docs/getting-started/cursor-rules.md +3 -23
  92. package/docs/getting-started/dependencies.md +650 -0
  93. package/docs/getting-started/installation-guide.md +20 -7
  94. package/docs/getting-started/quick-start.md +23 -12
  95. package/docs/implementation-guides/permission-enforcement.md +4 -0
  96. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  97. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  98. package/docs/rbac/README.md +12 -3
  99. package/docs/rbac/edge-functions-guide.md +376 -0
  100. package/docs/rbac/secure-client-protection.md +0 -34
  101. package/docs/standards/00-pace-core-compliance.md +967 -0
  102. package/docs/standards/01-standards-compliance.md +188 -0
  103. package/docs/standards/02-project-structure.md +985 -0
  104. package/docs/standards/03-solid-principles.md +39 -0
  105. package/docs/standards/04-testing-standards.md +36 -0
  106. package/docs/standards/05-bug-reports-and-features.md +27 -0
  107. package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
  108. package/docs/standards/07-tech-stack-compliance.md +30 -0
  109. package/docs/standards/08-markup-quality.md +345 -0
  110. package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
  111. package/docs/standards/10-error-handling-patterns.md +401 -0
  112. package/docs/standards/11-performance-optimization.md +348 -0
  113. package/docs/standards/12-ci-cd-integration.md +370 -0
  114. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
  115. package/docs/standards/README.md +62 -33
  116. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  117. package/eslint-config-pace-core.cjs +20 -4
  118. package/package.json +31 -21
  119. package/scripts/audit/audit-compliance.cjs +1295 -0
  120. package/scripts/audit/audit-components.cjs +260 -0
  121. package/scripts/audit/audit-dependencies.cjs +395 -0
  122. package/scripts/audit/audit-rbac.cjs +954 -0
  123. package/scripts/audit/audit-standards.cjs +1268 -0
  124. package/scripts/audit/index.cjs +1898 -194
  125. package/scripts/install-cursor-rules.cjs +259 -8
  126. package/scripts/validate-master.js +1 -1
  127. package/src/__tests__/fixtures/supabase.ts +1 -1
  128. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
  129. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  130. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  131. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +3 -3
  132. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  133. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  134. package/src/__tests__/public-recipe-view.test.ts +38 -9
  135. package/src/components/Button/Button.tsx +5 -1
  136. package/src/components/ContextSelector/ContextSelector.tsx +42 -39
  137. package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
  138. package/src/components/DataTable/components/DataTableBody.tsx +55 -31
  139. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  140. package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
  141. package/src/components/DataTable/components/EditFields.tsx +23 -3
  142. package/src/components/DataTable/components/EditableRow.tsx +7 -2
  143. package/src/components/DataTable/components/ImportModal.tsx +4 -6
  144. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  145. package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
  146. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
  147. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
  148. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  149. package/src/components/DataTable/core/DataTableContext.tsx +1 -1
  150. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  151. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  152. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  153. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  154. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  155. package/src/components/DataTable/types.ts +5 -0
  156. package/src/components/DateTimeField/DateTimeField.tsx +20 -20
  157. package/src/components/DateTimeField/README.md +5 -2
  158. package/src/components/Dialog/Dialog.test.tsx +361 -318
  159. package/src/components/Dialog/Dialog.tsx +1154 -323
  160. package/src/components/Dialog/index.ts +3 -3
  161. package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
  162. package/src/components/FileDisplay/FileDisplay.tsx +28 -22
  163. package/src/components/Form/Form.test.tsx +9 -10
  164. package/src/components/Form/Form.tsx +369 -9
  165. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
  166. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
  167. package/src/components/LoginForm/LoginForm.tsx +2 -2
  168. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  169. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  170. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  171. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  172. package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
  173. package/src/components/PaceAppLayout/README.md +10 -9
  174. package/src/components/PaceAppLayout/test-setup.tsx +40 -31
  175. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  176. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  177. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
  178. package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
  179. package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
  180. package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
  181. package/src/components/Select/Select.tsx +23 -21
  182. package/src/components/Select/types.ts +1 -1
  183. package/src/components/UserMenu/UserMenu.test.tsx +38 -6
  184. package/src/components/UserMenu/UserMenu.tsx +39 -34
  185. package/src/components/index.ts +3 -4
  186. package/src/eslint-rules/index.cjs +22 -0
  187. package/src/eslint-rules/rules/compliance.cjs +348 -0
  188. package/src/eslint-rules/rules/components.cjs +113 -0
  189. package/src/eslint-rules/rules/imports.cjs +102 -0
  190. package/src/eslint-rules/rules/rbac.cjs +790 -0
  191. package/src/eslint-rules/utils/helpers.cjs +42 -0
  192. package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
  193. package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
  194. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  195. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  196. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  197. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  198. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  199. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  200. package/src/hooks/public/usePublicEvent.ts +62 -190
  201. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  202. package/src/hooks/public/usePublicEventLogo.ts +19 -9
  203. package/src/hooks/useAppConfig.ts +26 -24
  204. package/src/hooks/useEventTheme.test.ts +211 -233
  205. package/src/hooks/useEventTheme.ts +19 -28
  206. package/src/hooks/useEvents.ts +11 -7
  207. package/src/hooks/useKeyboardShortcuts.ts +1 -1
  208. package/src/hooks/useOrganisationPermissions.ts +9 -11
  209. package/src/hooks/useOrganisations.ts +13 -7
  210. package/src/hooks/useQueryCache.ts +0 -1
  211. package/src/hooks/useSessionDraft.ts +380 -0
  212. package/src/hooks/useSessionRestoration.ts +3 -1
  213. package/src/icons/index.ts +27 -0
  214. package/src/index.ts +16 -1
  215. package/src/providers/OrganisationProvider.tsx +23 -14
  216. package/src/providers/services/EventServiceProvider.tsx +1 -24
  217. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  218. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
  219. package/src/rbac/README.md +20 -20
  220. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  221. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  222. package/src/rbac/adapters.tsx +7 -295
  223. package/src/rbac/api.test.ts +44 -56
  224. package/src/rbac/api.ts +10 -17
  225. package/src/rbac/cache-invalidation.ts +0 -1
  226. package/src/rbac/compliance/index.ts +10 -0
  227. package/src/rbac/compliance/pattern-detector.ts +553 -0
  228. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  229. package/src/rbac/components/AccessDenied.tsx +150 -0
  230. package/src/rbac/components/NavigationGuard.tsx +12 -20
  231. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  232. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  233. package/src/rbac/components/index.ts +3 -41
  234. package/src/rbac/eslint-rules.js +1 -1
  235. package/src/rbac/hooks/index.ts +0 -3
  236. package/src/rbac/hooks/permissions/index.ts +0 -3
  237. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  238. package/src/rbac/hooks/usePermissions.ts +0 -3
  239. package/src/rbac/hooks/useRBAC.test.ts +21 -3
  240. package/src/rbac/hooks/useRBAC.ts +4 -3
  241. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  242. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  243. package/src/rbac/hooks/useResourcePermissions.test.ts +241 -60
  244. package/src/rbac/hooks/useResourcePermissions.ts +182 -63
  245. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  246. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  247. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  248. package/src/rbac/index.ts +7 -9
  249. package/src/rbac/permissions.ts +17 -17
  250. package/src/rbac/utils/contextValidator.ts +45 -7
  251. package/src/services/AuthService.ts +132 -23
  252. package/src/services/EventService.ts +4 -97
  253. package/src/services/InactivityService.ts +155 -58
  254. package/src/services/OrganisationService.ts +7 -44
  255. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  256. package/src/services/base/BaseService.ts +0 -3
  257. package/src/styles/core.css +4 -0
  258. package/src/types/database.generated.ts +4733 -3809
  259. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  260. package/src/utils/context/organisationContext.test.ts +13 -28
  261. package/src/utils/context/organisationContext.ts +21 -52
  262. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  263. package/src/utils/file-reference/index.ts +39 -15
  264. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  265. package/src/utils/formatting/formatTime.test.ts +3 -2
  266. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  267. package/src/utils/index.ts +4 -1
  268. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  269. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  270. package/src/utils/persistence/keyDerivation.ts +304 -0
  271. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  272. package/src/utils/security/secureStorage.ts +5 -5
  273. package/src/utils/storage/helpers.ts +3 -3
  274. package/src/utils/supabase/createBaseClient.ts +147 -0
  275. package/src/utils/timezone/timezone.test.ts +1 -2
  276. package/src/utils/timezone/timezone.ts +1 -1
  277. package/src/utils/validation/csrf.ts +4 -4
  278. package/cursor-rules/CHANGELOG.md +0 -119
  279. package/cursor-rules/README.md +0 -192
  280. package/dist/DataTable-E7YQZD7D.js +0 -175
  281. package/dist/DataTable-E7YQZD7D.js.map +0 -1
  282. package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
  283. package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
  284. package/dist/api-6LVZTHDS.js +0 -52
  285. package/dist/api-6LVZTHDS.js.map +0 -1
  286. package/dist/audit-V53FV5AG.js +0 -17
  287. package/dist/audit-V53FV5AG.js.map +0 -1
  288. package/dist/chunk-36LVWXB2.js +0 -227
  289. package/dist/chunk-36LVWXB2.js.map +0 -1
  290. package/dist/chunk-3LPHPB62.js.map +0 -1
  291. package/dist/chunk-5DRSZLL2.js.map +0 -1
  292. package/dist/chunk-5EC5MEWX.js.map +0 -1
  293. package/dist/chunk-63FOKYGO.js.map +0 -1
  294. package/dist/chunk-6SOIHG6Z.js.map +0 -1
  295. package/dist/chunk-7JPAB3T5.js.map +0 -1
  296. package/dist/chunk-ATKZM7RX.js +0 -2053
  297. package/dist/chunk-ATKZM7RX.js.map +0 -1
  298. package/dist/chunk-AVMLPIM7.js.map +0 -1
  299. package/dist/chunk-DGUM43GV.js.map +0 -1
  300. package/dist/chunk-E66EQZE6.js.map +0 -1
  301. package/dist/chunk-FFQEQTNW.js.map +0 -1
  302. package/dist/chunk-FMUCXFII.js.map +0 -1
  303. package/dist/chunk-G37KK66H.js.map +0 -1
  304. package/dist/chunk-I6DAQMWX.js.map +0 -1
  305. package/dist/chunk-J36DSWQK.js.map +0 -1
  306. package/dist/chunk-KQCRWDSA.js +0 -1
  307. package/dist/chunk-KQCRWDSA.js.map +0 -1
  308. package/dist/chunk-L4OXEN46.js.map +0 -1
  309. package/dist/chunk-LMC26NLJ.js +0 -84
  310. package/dist/chunk-LMC26NLJ.js.map +0 -1
  311. package/dist/chunk-M43Y4SSO.js.map +0 -1
  312. package/dist/chunk-M7MPQISP.js.map +0 -1
  313. package/dist/chunk-NN6WWZ5U.js.map +0 -1
  314. package/dist/chunk-OEWDTMG7.js.map +0 -1
  315. package/dist/chunk-PWLANIRT.js.map +0 -1
  316. package/dist/chunk-QXHPKYJV.js.map +0 -1
  317. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  318. package/dist/chunk-YKRAFF5K.js.map +0 -1
  319. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  320. package/dist/components.js.map +0 -1
  321. package/dist/contextValidator-OOPCLPZW.js +0 -9
  322. package/dist/contextValidator-OOPCLPZW.js.map +0 -1
  323. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  324. package/dist/hooks.js.map +0 -1
  325. package/dist/index.js.map +0 -1
  326. package/dist/providers.js.map +0 -1
  327. package/dist/rbac/eslint-rules.js.map +0 -1
  328. package/dist/rbac/index.js.map +0 -1
  329. package/dist/styles/index.js.map +0 -1
  330. package/dist/theming/runtime.js.map +0 -1
  331. package/dist/types.js.map +0 -1
  332. package/dist/utils.js.map +0 -1
  333. package/docs/standards/01-architecture-standard.md +0 -44
  334. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  335. package/docs/standards/03-component-standard.md +0 -32
  336. package/docs/standards/05-security-standard.md +0 -44
  337. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  338. package/docs/standards/pace-core-compliance.md +0 -432
  339. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  340. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  341. package/scripts/audit/core/checks/bundle.cjs +0 -142
  342. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  343. package/scripts/audit/core/checks/config.cjs +0 -54
  344. package/scripts/audit/core/checks/coverage.cjs +0 -84
  345. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  346. package/scripts/audit/core/checks/documentation.cjs +0 -268
  347. package/scripts/audit/core/checks/environment.cjs +0 -116
  348. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  349. package/scripts/audit/core/checks/forms.cjs +0 -172
  350. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  351. package/scripts/audit/core/checks/hooks.cjs +0 -334
  352. package/scripts/audit/core/checks/imports.cjs +0 -244
  353. package/scripts/audit/core/checks/performance.cjs +0 -325
  354. package/scripts/audit/core/checks/routes.cjs +0 -117
  355. package/scripts/audit/core/checks/state.cjs +0 -130
  356. package/scripts/audit/core/checks/structure.cjs +0 -65
  357. package/scripts/audit/core/checks/style.cjs +0 -584
  358. package/scripts/audit/core/checks/testing.cjs +0 -122
  359. package/scripts/audit/core/checks/typescript.cjs +0 -61
  360. package/scripts/audit/core/scanner.cjs +0 -199
  361. package/scripts/audit/core/utils.cjs +0 -137
  362. package/scripts/audit/reporters/console.cjs +0 -151
  363. package/scripts/audit/reporters/json.cjs +0 -54
  364. package/scripts/audit/reporters/markdown.cjs +0 -124
  365. package/scripts/audit-consuming-app.cjs +0 -86
  366. package/src/eslint-rules/pace-core-compliance.cjs +0 -510
  367. package/src/eslint-rules/pace-core-compliance.js +0 -638
  368. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  369. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  370. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  371. package/src/rbac/components/NavigationProvider.tsx +0 -345
  372. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  373. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  374. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  375. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  376. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  377. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  378. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  379. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  380. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  381. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  382. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  383. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  384. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  385. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  386. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  387. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Component Usage Audit Script
5
+ *
6
+ * MIGRATED TO ESLINT: All component usage checks have been migrated to ESLint rules:
7
+ * - Plain <form> tags → ESLint: prefer-pace-core-form
8
+ * - Direct react-hook-form usage → ESLint: prefer-pace-core-form
9
+ *
10
+ * This file is kept for reference but is no longer actively used.
11
+ * Use ESLint rules for real-time feedback during development.
12
+ *
13
+ * Usage:
14
+ * const { runComponentAudit } = require('./audit-components.cjs');
15
+ * const issues = runComponentAudit(consumingAppPath);
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ /**
22
+ * Recursively find all TypeScript/JavaScript files in a directory
23
+ */
24
+ function findSourceFiles(dir, fileList = []) {
25
+ const files = fs.readdirSync(dir);
26
+
27
+ files.forEach(file => {
28
+ const filePath = path.join(dir, file);
29
+ const stat = fs.statSync(filePath);
30
+
31
+ if (stat.isDirectory()) {
32
+ // Skip node_modules, dist, build, .git, etc.
33
+ if (!['node_modules', 'dist', 'build', '.git', '.next', '.vite', 'coverage'].includes(file)) {
34
+ findSourceFiles(filePath, fileList);
35
+ }
36
+ } else if (/\.(ts|tsx|js|jsx)$/.test(file)) {
37
+ fileList.push(filePath);
38
+ }
39
+ });
40
+
41
+ return fileList;
42
+ }
43
+
44
+ /**
45
+ * Get line number from index in content
46
+ */
47
+ function getLineNumber(content, index) {
48
+ return content.substring(0, index).split('\n').length;
49
+ }
50
+
51
+ /**
52
+ * Check if a file imports Form from pace-core
53
+ */
54
+ function importsPaceCoreForm(content) {
55
+ // Check for imports like:
56
+ // import { Form } from '@jmruthers/pace-core
57
+ // import { Form } from '@jmruthers/pace-core/components'
58
+ // import Form from '@jmruthers/pace-core'
59
+ const paceCoreFormPattern = /from\s+['"]@jmruthers\/pace-core(?:\/components)?['"].*Form|import\s+.*Form.*from\s+['"]@jmruthers\/pace-core/;
60
+ return paceCoreFormPattern.test(content);
61
+ }
62
+
63
+ /**
64
+ * Check for plain <form> tags in JSX
65
+ *
66
+ * MIGRATED TO ESLINT: This check is now handled by 'prefer-pace-core-form' ESLint rule.
67
+ * Kept for reference only.
68
+ */
69
+ function checkPlainFormTags_MIGRATED_TO_ESLINT(content, filePath, consumingAppPath) {
70
+ const issues = [];
71
+
72
+ // Pattern to match <form> tags (but not <Form> component)
73
+ // This regex looks for <form followed by whitespace or attributes, but not <Form
74
+ const formTagPattern = /<form(\s|>)/g;
75
+ let match;
76
+
77
+ while ((match = formTagPattern.exec(content)) !== null) {
78
+ // Check if this is actually a Form component (capital F)
79
+ const beforeMatch = content.substring(Math.max(0, match.index - 10), match.index);
80
+ const afterMatch = content.substring(match.index, match.index + 20);
81
+
82
+ // Skip if it's a comment or string literal
83
+ const beforeContext = content.substring(0, match.index);
84
+ const lastComment = beforeContext.lastIndexOf('//');
85
+ const lastNewline = beforeContext.lastIndexOf('\n');
86
+ const isInComment = lastComment > lastNewline && !beforeContext.substring(lastComment, match.index).includes('\n');
87
+
88
+ if (isInComment) continue;
89
+
90
+ // Check if it's inside a string literal
91
+ const beforeQuote = beforeContext.lastIndexOf('"');
92
+ const afterQuote = content.indexOf('"', match.index);
93
+ const isInString = beforeQuote > lastNewline && afterQuote > match.index && beforeQuote < match.index;
94
+
95
+ if (isInString) continue;
96
+
97
+ // This is a plain <form> tag
98
+ // Get relative path from consuming app root
99
+ const relativePath = path.relative(consumingAppPath || process.cwd(), filePath);
100
+
101
+ issues.push({
102
+ type: 'plainFormTag',
103
+ file: relativePath,
104
+ line: getLineNumber(content, match.index),
105
+ message: 'Plain <form> tag detected. Use pace-core Form component instead.',
106
+ code: content.substring(Math.max(0, match.index - 30), Math.min(content.length, match.index + 50)).trim(),
107
+ });
108
+ }
109
+
110
+ return issues;
111
+ }
112
+
113
+ /**
114
+ * Check for direct react-hook-form imports and usage
115
+ *
116
+ * MIGRATED TO ESLINT: This check is now handled by 'prefer-pace-core-form' ESLint rule.
117
+ * Kept for reference only.
118
+ */
119
+ function checkReactHookFormUsage_MIGRATED_TO_ESLINT(content, filePath, consumingAppPath) {
120
+ const issues = [];
121
+
122
+ // Pattern to match imports from react-hook-form
123
+ const importPattern = /from\s+['"]react-hook-form['"]/g;
124
+ let match;
125
+
126
+ while ((match = importPattern.exec(content)) !== null) {
127
+ // Get the import statement
128
+ const lineStart = content.lastIndexOf('\n', match.index) + 1;
129
+ const lineEnd = content.indexOf('\n', match.index);
130
+ const importLine = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);
131
+
132
+ // Check if this file also imports Form from pace-core
133
+ // If it does, this might be acceptable (e.g., using useFormContext within Form)
134
+ const hasPaceCoreForm = importsPaceCoreForm(content);
135
+
136
+ // Extract what's being imported
137
+ const importMatch = importLine.match(/import\s+(?:{([^}]+)}|(\w+))\s+from/);
138
+ if (importMatch) {
139
+ const importedItems = importMatch[1] || importMatch[2] || '';
140
+ const items = importedItems.split(',').map(item => item.trim().split(' as ')[0].trim());
141
+
142
+ // Check for problematic imports
143
+ const problematicImports = [
144
+ 'useForm',
145
+ 'FormProvider',
146
+ 'Controller',
147
+ 'useFormContext',
148
+ 'useWatch',
149
+ 'useFieldArray',
150
+ 'useController',
151
+ ];
152
+
153
+ const foundProblematic = items.filter(item => problematicImports.includes(item));
154
+
155
+ if (foundProblematic.length > 0) {
156
+ // If they're using useFormContext within a pace-core Form, that's acceptable
157
+ // But if they're using useForm or FormProvider, that's not acceptable
158
+ const isAcceptable = hasPaceCoreForm &&
159
+ foundProblematic.length === 1 &&
160
+ foundProblematic[0] === 'useFormContext';
161
+
162
+ if (!isAcceptable) {
163
+ // Get relative path from consuming app root
164
+ const relativePath = path.relative(consumingAppPath || process.cwd(), filePath);
165
+
166
+ issues.push({
167
+ type: 'reactHookFormImport',
168
+ file: relativePath,
169
+ line: getLineNumber(content, match.index),
170
+ message: `Direct import from 'react-hook-form' detected: ${foundProblematic.join(', ')}. Use pace-core Form component instead.`,
171
+ code: importLine.trim(),
172
+ imports: foundProblematic,
173
+ });
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ return issues;
180
+ }
181
+
182
+ /**
183
+ * Main audit function - scans source files for component usage issues
184
+ */
185
+ function runComponentAudit(consumingAppPath = process.cwd()) {
186
+ const srcPath = path.join(consumingAppPath, 'src');
187
+
188
+ // If src directory doesn't exist, try root directory
189
+ const searchPath = fs.existsSync(srcPath) ? srcPath : consumingAppPath;
190
+
191
+ if (!fs.existsSync(searchPath)) {
192
+ return {
193
+ error: `Source directory not found at ${searchPath}`,
194
+ issues: { formIssues: [] },
195
+ };
196
+ }
197
+
198
+ // Find all source files
199
+ const sourceFiles = findSourceFiles(searchPath);
200
+
201
+ if (sourceFiles.length === 0) {
202
+ return {
203
+ error: `No source files found in ${searchPath}`,
204
+ issues: { formIssues: [] },
205
+ };
206
+ }
207
+
208
+ const issues = {
209
+ formIssues: [],
210
+ };
211
+
212
+ // NOTE: All component checks have been migrated to ESLint rules.
213
+ // This function now returns empty results as checks are handled by ESLint.
214
+ // Kept for API compatibility.
215
+ sourceFiles.forEach(filePath => {
216
+ // No checks performed - all migrated to ESLint
217
+ // (checkPlainFormTags and checkReactHookFormUsage are now ESLint rules)
218
+ });
219
+
220
+ return {
221
+ issues,
222
+ };
223
+ }
224
+
225
+ // Export for use by other scripts
226
+ if (typeof module !== 'undefined' && module.exports) {
227
+ module.exports = { runComponentAudit };
228
+ }
229
+
230
+ // If run directly, output results
231
+ if (require.main === module) {
232
+ const consumingAppPath = process.argv[2] || process.cwd();
233
+ const result = runComponentAudit(consumingAppPath);
234
+
235
+ if (result.error) {
236
+ console.error(`Error: ${result.error}`);
237
+ process.exit(1);
238
+ }
239
+
240
+ const { issues } = result;
241
+
242
+ if (issues.formIssues.length === 0) {
243
+ console.log('✅ No form usage issues found!');
244
+ process.exit(0);
245
+ }
246
+
247
+ console.log(`\n❌ Found ${issues.formIssues.length} form usage issue(s):\n`);
248
+
249
+ issues.formIssues.forEach(issue => {
250
+ console.log(`${issue.file}:${issue.line}`);
251
+ console.log(` ${issue.message}`);
252
+ if (issue.code) {
253
+ console.log(` Code: ${issue.code.substring(0, 80)}...`);
254
+ }
255
+ console.log();
256
+ });
257
+
258
+ process.exit(1);
259
+ }
260
+
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Dependency Audit Script
5
+ *
6
+ * Audits consuming apps against pace-core dependency standards.
7
+ * Checks for:
8
+ * - Included dependencies that should NOT be installed
9
+ * - Missing required peer dependencies
10
+ * - Incorrect version ranges
11
+ * - Dependencies in wrong location (devDependencies vs dependencies)
12
+ *
13
+ * Usage:
14
+ * node scripts/audit/audit-dependencies.cjs [path-to-consuming-app]
15
+ *
16
+ * If no path provided, assumes current directory is consuming app.
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ // Colors for terminal output
23
+ const colors = {
24
+ reset: '\x1b[0m',
25
+ red: '\x1b[31m',
26
+ green: '\x1b[32m',
27
+ yellow: '\x1b[33m',
28
+ blue: '\x1b[34m',
29
+ cyan: '\x1b[36m',
30
+ bold: '\x1b[1m',
31
+ };
32
+
33
+ // Get pace-core package.json
34
+ // When run from consuming app: __dirname is node_modules/@jmruthers/pace-core/scripts/audit
35
+ // When run from pace-core repo: __dirname is packages/core/scripts/audit
36
+ function findPaceCorePackageJson(consumingAppPath) {
37
+ // Try relative to script location first (when in pace-core repo or installed package)
38
+ // Now we're in audit/ subdirectory, so need to go up two levels
39
+ let paceCorePath = path.resolve(__dirname, '../../package.json');
40
+ if (fs.existsSync(paceCorePath)) {
41
+ return paceCorePath;
42
+ }
43
+
44
+ // Try alternative location (if script is in different structure)
45
+ paceCorePath = path.resolve(__dirname, '../../../package.json');
46
+ if (fs.existsSync(paceCorePath)) {
47
+ return paceCorePath;
48
+ }
49
+
50
+ // Try finding from consuming app's node_modules (when run from consuming app)
51
+ if (consumingAppPath) {
52
+ const nodeModulesPath = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
53
+ if (fs.existsSync(nodeModulesPath)) {
54
+ return nodeModulesPath;
55
+ }
56
+ }
57
+
58
+ // Fallback: try from process.argv (for standalone CLI usage)
59
+ const fallbackPath = process.argv[2] || process.cwd();
60
+ const fallbackNodeModules = path.join(fallbackPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
61
+ if (fs.existsSync(fallbackNodeModules)) {
62
+ return fallbackNodeModules;
63
+ }
64
+
65
+ return null;
66
+ }
67
+
68
+ // Version range matcher
69
+ function matchesVersionRange(version, range) {
70
+ if (!range) return true;
71
+
72
+ // Simple check for caret ranges (^)
73
+ if (range.startsWith('^')) {
74
+ const major = parseInt(range.slice(1).split('.')[0]);
75
+ const versionMajor = parseInt(version.split('.')[0]);
76
+ return versionMajor >= major;
77
+ }
78
+
79
+ // For exact matches or other ranges, use semver if available
80
+ // For now, just check if version starts with the range
81
+ return version.startsWith(range.replace(/[\^~]/, ''));
82
+ }
83
+
84
+ // Check version compatibility
85
+ function checkVersion(installed, required) {
86
+ if (!required) return { valid: true };
87
+
88
+ // Extract version number from installed (remove ^, ~, etc.)
89
+ const installedVersion = installed.replace(/[\^~]/, '');
90
+ const requiredVersion = required.replace(/[\^~]/, '');
91
+
92
+ // Check major version
93
+ const installedMajor = parseInt(installedVersion.split('.')[0]);
94
+ const requiredMajor = parseInt(requiredVersion.split('.')[0]);
95
+
96
+ if (required.startsWith('^')) {
97
+ return {
98
+ valid: installedMajor >= requiredMajor,
99
+ installed,
100
+ required,
101
+ };
102
+ }
103
+
104
+ return {
105
+ valid: installedMajor === requiredMajor,
106
+ installed,
107
+ required,
108
+ };
109
+ }
110
+
111
+ // Main audit function - returns data structure for programmatic use
112
+ function runDependencyAudit(consumingAppPath = process.cwd()) {
113
+ const packageJsonPath = path.join(consumingAppPath, 'package.json');
114
+
115
+ if (!fs.existsSync(packageJsonPath)) {
116
+ return {
117
+ error: `package.json not found at ${packageJsonPath}`,
118
+ issues: { includedDeps: [], missingRequired: [], missingOptional: [], versionIssues: [], wrongLocation: [] }
119
+ };
120
+ }
121
+
122
+ // Load pace-core package.json
123
+ const paceCorePath = findPaceCorePackageJson(consumingAppPath);
124
+ if (!paceCorePath) {
125
+ return {
126
+ error: 'Could not find pace-core package.json. Make sure @jmruthers/pace-core is installed in your project.',
127
+ issues: { includedDeps: [], missingRequired: [], missingOptional: [], versionIssues: [], wrongLocation: [] }
128
+ };
129
+ }
130
+
131
+ const paceCorePkg = JSON.parse(fs.readFileSync(paceCorePath, 'utf8'));
132
+ const INCLUDED_DEPS = Object.keys(paceCorePkg.dependencies || {});
133
+ const PEER_DEPS = paceCorePkg.peerDependencies || {};
134
+ const REQUIRED_PEERS = ['react', 'react-dom', 'react-router-dom', 'tailwindcss'];
135
+ const OPTIONAL_PEERS = Object.keys(PEER_DEPS).filter(dep => !REQUIRED_PEERS.includes(dep));
136
+
137
+ // Verify pace-core is installed
138
+ const paceCoreInNodeModules = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
139
+ if (!fs.existsSync(paceCoreInNodeModules)) {
140
+ return {
141
+ error: '@jmruthers/pace-core not found in node_modules. Please run: npm install @jmruthers/pace-core',
142
+ issues: { includedDeps: [], missingRequired: [], missingOptional: [], versionIssues: [], wrongLocation: [] }
143
+ };
144
+ }
145
+
146
+ const consumingPkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
147
+ const allDeps = {
148
+ ...(consumingPkg.dependencies || {}),
149
+ ...(consumingPkg.devDependencies || {}),
150
+ };
151
+
152
+ const issues = {
153
+ includedDeps: [],
154
+ missingRequired: [],
155
+ missingOptional: [],
156
+ versionIssues: [],
157
+ wrongLocation: [],
158
+ };
159
+
160
+ // Check for included dependencies
161
+ INCLUDED_DEPS.forEach(dep => {
162
+ if (allDeps[dep]) {
163
+ issues.includedDeps.push({
164
+ package: dep,
165
+ installed: allDeps[dep],
166
+ location: consumingPkg.dependencies?.[dep] ? 'dependencies' : 'devDependencies',
167
+ });
168
+ }
169
+ });
170
+
171
+ // Check for required peer dependencies
172
+ REQUIRED_PEERS.forEach(dep => {
173
+ if (!allDeps[dep]) {
174
+ issues.missingRequired.push({
175
+ package: dep,
176
+ required: PEER_DEPS[dep],
177
+ });
178
+ } else {
179
+ // Check version
180
+ const versionCheck = checkVersion(allDeps[dep], PEER_DEPS[dep]);
181
+ if (!versionCheck.valid) {
182
+ issues.versionIssues.push({
183
+ package: dep,
184
+ installed: versionCheck.installed,
185
+ required: versionCheck.required,
186
+ });
187
+ }
188
+ }
189
+ });
190
+
191
+ // Check for optional peer dependencies (warn if missing, but don't fail)
192
+ OPTIONAL_PEERS.forEach(dep => {
193
+ if (!allDeps[dep]) {
194
+ issues.missingOptional.push({
195
+ package: dep,
196
+ required: PEER_DEPS[dep],
197
+ });
198
+ } else {
199
+ // Check version
200
+ const versionCheck = checkVersion(allDeps[dep], PEER_DEPS[dep]);
201
+ if (!versionCheck.valid) {
202
+ issues.versionIssues.push({
203
+ package: dep,
204
+ installed: versionCheck.installed,
205
+ required: versionCheck.required,
206
+ });
207
+ }
208
+ }
209
+ });
210
+
211
+ // Check for @tailwindcss/vite in wrong location
212
+ if (consumingPkg.dependencies?.['@tailwindcss/vite']) {
213
+ issues.wrongLocation.push({
214
+ package: '@tailwindcss/vite',
215
+ current: 'dependencies',
216
+ shouldBe: 'devDependencies',
217
+ });
218
+ }
219
+
220
+ // Find pace-core version - check dependencies, devDependencies, or installed package
221
+ let paceCoreVersion = consumingPkg.dependencies?.['@jmruthers/pace-core'] ||
222
+ consumingPkg.devDependencies?.['@jmruthers/pace-core'];
223
+
224
+ // If not in package.json, try to read from installed package
225
+ if (!paceCoreVersion) {
226
+ const installedPkgPath = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
227
+ if (fs.existsSync(installedPkgPath)) {
228
+ try {
229
+ const installedPkg = JSON.parse(fs.readFileSync(installedPkgPath, 'utf8'));
230
+ paceCoreVersion = `installed: ${installedPkg.version}`;
231
+ } catch (e) {
232
+ paceCoreVersion = 'installed (version unknown)';
233
+ }
234
+ } else {
235
+ paceCoreVersion = 'NOT FOUND';
236
+ }
237
+ }
238
+
239
+ return {
240
+ projectName: consumingPkg.name || 'unknown',
241
+ paceCoreVersion,
242
+ issues,
243
+ };
244
+ }
245
+
246
+ // CLI function - displays results and returns exit code
247
+ function auditDependencies(consumingAppPath = process.cwd()) {
248
+ const result = runDependencyAudit(consumingAppPath);
249
+
250
+ if (result.error) {
251
+ console.error(`${colors.red}Error: ${result.error}${colors.reset}`);
252
+ process.exit(1);
253
+ }
254
+
255
+ const { projectName, paceCoreVersion, issues } = result;
256
+
257
+ // Report results
258
+ console.log(`\n${colors.bold}${colors.cyan}Dependency Audit Report${colors.reset}`);
259
+ console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
260
+ console.log(`Project: ${colors.bold}${projectName}${colors.reset}`);
261
+ console.log(`pace-core: ${colors.bold}${paceCoreVersion}${colors.reset}\n`);
262
+
263
+ let hasErrors = false;
264
+
265
+ // Included dependencies (errors)
266
+ if (issues.includedDeps.length > 0) {
267
+ hasErrors = true;
268
+ console.log(`${colors.red}❌ INCLUDED DEPENDENCIES (MUST REMOVE):${colors.reset}`);
269
+ issues.includedDeps.forEach(issue => {
270
+ console.log(` - ${colors.red}${issue.package}${colors.reset}@${issue.installed} (in ${issue.location})`);
271
+ console.log(` → Should NOT be installed (already included in pace-core)`);
272
+ });
273
+ console.log();
274
+ }
275
+
276
+ // Missing required dependencies (errors)
277
+ if (issues.missingRequired.length > 0) {
278
+ hasErrors = true;
279
+ console.log(`${colors.red}❌ MISSING REQUIRED DEPENDENCIES:${colors.reset}`);
280
+ issues.missingRequired.forEach(issue => {
281
+ console.log(` - ${colors.red}${issue.package}${colors.reset} (required: ${issue.required})`);
282
+ });
283
+ console.log();
284
+ }
285
+
286
+ // Version issues (errors)
287
+ if (issues.versionIssues.length > 0) {
288
+ hasErrors = true;
289
+ console.log(`${colors.yellow}⚠️ VERSION ISSUES:${colors.reset}`);
290
+ issues.versionIssues.forEach(issue => {
291
+ console.log(` - ${colors.yellow}${issue.package}${colors.reset}`);
292
+ console.log(` Installed: ${issue.installed}`);
293
+ console.log(` Required: ${issue.required}`);
294
+ });
295
+ console.log();
296
+ }
297
+
298
+ // Wrong location (warnings)
299
+ if (issues.wrongLocation.length > 0) {
300
+ console.log(`${colors.yellow}⚠️ WRONG LOCATION:${colors.reset}`);
301
+ issues.wrongLocation.forEach(issue => {
302
+ console.log(` - ${colors.yellow}${issue.package}${colors.reset}`);
303
+ console.log(` Currently in: ${issue.current}`);
304
+ console.log(` Should be in: ${issue.shouldBe}`);
305
+ });
306
+ console.log();
307
+ }
308
+
309
+ // Missing optional dependencies (warnings)
310
+ if (issues.missingOptional.length > 0) {
311
+ console.log(`${colors.blue}ℹ️ MISSING OPTIONAL DEPENDENCIES:${colors.reset}`);
312
+ console.log(` (Only install if you use these features)`);
313
+ issues.missingOptional.forEach(issue => {
314
+ console.log(` - ${colors.blue}${issue.package}${colors.reset} (required: ${issue.required})`);
315
+ });
316
+ console.log();
317
+ }
318
+
319
+ // Success message
320
+ if (!hasErrors && issues.missingOptional.length === 0 && issues.wrongLocation.length === 0) {
321
+ console.log(`${colors.green}✅ All dependencies are correctly configured!${colors.reset}\n`);
322
+ return 0;
323
+ }
324
+
325
+ // Generate fix commands
326
+ if (hasErrors || issues.wrongLocation.length > 0) {
327
+ console.log(`${colors.bold}Fix Commands:${colors.reset}\n`);
328
+
329
+ if (issues.includedDeps.length > 0) {
330
+ const depsToRemove = issues.includedDeps.map(i => i.package).join(' ');
331
+ console.log(`${colors.cyan}# Remove included dependencies${colors.reset}`);
332
+ console.log(`npm uninstall ${depsToRemove}\n`);
333
+ }
334
+
335
+ if (issues.missingRequired.length > 0) {
336
+ const depsToInstall = issues.missingRequired
337
+ .map(i => `${i.package}@${i.required}`)
338
+ .join(' ');
339
+ console.log(`${colors.cyan}# Install missing required dependencies${colors.reset}`);
340
+ console.log(`npm install ${depsToInstall}\n`);
341
+ }
342
+
343
+ if (issues.versionIssues.length > 0) {
344
+ const depsToFix = issues.versionIssues
345
+ .map(i => `${i.package}@${i.required}`)
346
+ .join(' ');
347
+ console.log(`${colors.cyan}# Fix version ranges${colors.reset}`);
348
+ console.log(`npm install ${depsToFix}\n`);
349
+ }
350
+
351
+ if (issues.wrongLocation.length > 0) {
352
+ issues.wrongLocation.forEach(issue => {
353
+ console.log(`${colors.cyan}# Move ${issue.package} to devDependencies${colors.reset}`);
354
+ console.log(`npm uninstall ${issue.package}`);
355
+ console.log(`npm install -D ${issue.package}\n`);
356
+ });
357
+ }
358
+ }
359
+
360
+ return hasErrors ? 1 : 0;
361
+ }
362
+
363
+ // Export for use by other scripts
364
+ if (typeof module !== 'undefined' && module.exports) {
365
+ module.exports = { runDependencyAudit, checkVersion, findPaceCorePackageJson };
366
+ }
367
+
368
+ // Run audit only if this file is executed directly (not required)
369
+ if (require.main === module) {
370
+ // If run from pace-core repo without path, try to detect consuming app in parent directory
371
+ let consumingAppPath = process.argv[2];
372
+ if (!consumingAppPath) {
373
+ const cwd = process.cwd();
374
+ // If we're in pace-core repo, try to find consuming app in parent directory
375
+ if (cwd.includes('pace-core')) {
376
+ // Look for sibling directories that might be consuming apps
377
+ const parentDir = path.resolve(cwd, '../..');
378
+ const possibleApps = ['pace-admin', 'pace-base', 'pace-cake', 'pace-gear', 'pace-mint', 'pace-portal', 'pace-pump', 'pace-trac'];
379
+ for (const app of possibleApps) {
380
+ const appPath = path.join(parentDir, app);
381
+ if (fs.existsSync(path.join(appPath, 'package.json'))) {
382
+ consumingAppPath = appPath;
383
+ break;
384
+ }
385
+ }
386
+ }
387
+ // Fall back to current directory
388
+ if (!consumingAppPath) {
389
+ consumingAppPath = process.cwd();
390
+ }
391
+ }
392
+ const exitCode = auditDependencies(consumingAppPath);
393
+ process.exit(exitCode);
394
+ }
395
+