@jmruthers/pace-core 0.5.184 → 0.5.186

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 (319) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +60 -1
  3. package/core-usage-manifest.json +312 -0
  4. package/dist/{DataTable-QAB34V6K.js → DataTable-IX2NBUTP.js} +6 -6
  5. package/dist/{DataTable-Bz8ffqyA.d.ts → DataTable-Z9NLVJh0.d.ts} +1 -1
  6. package/dist/{index-Bl--n7-T.d.ts → PublicPageProvider-DIzEzwKl.d.ts} +23 -10
  7. package/dist/{UnifiedAuthProvider-7F6T4B6K.js → UnifiedAuthProvider-A4BCQRJY.js} +4 -2
  8. package/dist/{UnifiedAuthProvider-F86d7dSi.d.ts → UnifiedAuthProvider-BG0AL5eE.d.ts} +2 -1
  9. package/dist/{api-ROMBCNKU.js → api-BMFCXVQX.js} +2 -2
  10. package/dist/{chunk-RA3JUFMW.js → chunk-445GEP27.js} +154 -4
  11. package/dist/{chunk-RA3JUFMW.js.map → chunk-445GEP27.js.map} +1 -1
  12. package/dist/{chunk-W22JP75J.js → chunk-DAGICKHT.js} +9 -7
  13. package/dist/chunk-DAGICKHT.js.map +1 -0
  14. package/dist/{chunk-FUEYYMX5.js → chunk-FXFJRTKI.js} +24 -3
  15. package/dist/chunk-FXFJRTKI.js.map +1 -0
  16. package/dist/{chunk-CSOFYHAG.js → chunk-GRIQLQ52.js} +374 -60
  17. package/dist/chunk-GRIQLQ52.js.map +1 -0
  18. package/dist/{chunk-NQPMQGS2.js → chunk-HDCUMOOI.js} +497 -399
  19. package/dist/chunk-HDCUMOOI.js.map +1 -0
  20. package/dist/chunk-HESYZWZW.js +388 -0
  21. package/dist/chunk-HESYZWZW.js.map +1 -0
  22. package/dist/{chunk-QUVSNGIP.js → chunk-HGPQUCBC.js} +34 -9
  23. package/dist/{chunk-QUVSNGIP.js.map → chunk-HGPQUCBC.js.map} +1 -1
  24. package/dist/{chunk-PWAHJW4G.js → chunk-OALXJH4Y.js} +86 -33
  25. package/dist/chunk-OALXJH4Y.js.map +1 -0
  26. package/dist/{chunk-MI7HBHN3.js → chunk-TC7D3CR3.js} +89 -9
  27. package/dist/chunk-TC7D3CR3.js.map +1 -0
  28. package/dist/chunk-THRPYOFK.js +215 -0
  29. package/dist/chunk-THRPYOFK.js.map +1 -0
  30. package/dist/{chunk-M7W4CP3M.js → chunk-U6WNSFX5.js} +2 -1
  31. package/dist/chunk-U6WNSFX5.js.map +1 -0
  32. package/dist/{chunk-UHNYIBXL.js → chunk-UQWSHFVX.js} +1 -1
  33. package/dist/chunk-UQWSHFVX.js.map +1 -0
  34. package/dist/{chunk-QCDXODCA.js → chunk-XAUHJD3L.js} +2 -2
  35. package/dist/components.d.ts +182 -6
  36. package/dist/components.js +157 -11
  37. package/dist/components.js.map +1 -1
  38. package/dist/{database.generated-CBmg2950.d.ts → database.generated-DI89OQeI.d.ts} +63 -9
  39. package/dist/eslint-rules/pace-core-compliance.cjs +406 -0
  40. package/dist/{file-reference-D06mEEWW.d.ts → file-reference-PRTSLxKx.d.ts} +10 -1
  41. package/dist/hooks.d.ts +52 -15
  42. package/dist/hooks.js +12 -22
  43. package/dist/hooks.js.map +1 -1
  44. package/dist/index.d.ts +12 -12
  45. package/dist/index.js +82 -18
  46. package/dist/index.js.map +1 -1
  47. package/dist/providers.d.ts +1 -1
  48. package/dist/providers.js +3 -1
  49. package/dist/rbac/index.d.ts +206 -15
  50. package/dist/rbac/index.js +28 -6
  51. package/dist/timezone-_pgH8qrY.d.ts +530 -0
  52. package/dist/{types-_x1f4QBF.d.ts → types-DUyCRSTj.d.ts} +1 -1
  53. package/dist/types.d.ts +2 -2
  54. package/dist/types.js +1 -1
  55. package/dist/{usePublicRouteParams-JJczomYq.d.ts → usePublicRouteParams-D71QLlg4.d.ts} +114 -3
  56. package/dist/utils.d.ts +110 -152
  57. package/dist/utils.js +128 -138
  58. package/dist/utils.js.map +1 -1
  59. package/docs/api/README.md +60 -1
  60. package/docs/api/classes/ColumnFactory.md +1 -1
  61. package/docs/api/classes/ErrorBoundary.md +1 -1
  62. package/docs/api/classes/InvalidScopeError.md +1 -1
  63. package/docs/api/classes/Logger.md +178 -0
  64. package/docs/api/classes/MissingUserContextError.md +1 -1
  65. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  66. package/docs/api/classes/PermissionDeniedError.md +1 -1
  67. package/docs/api/classes/RBACAuditManager.md +2 -2
  68. package/docs/api/classes/RBACCache.md +1 -1
  69. package/docs/api/classes/RBACEngine.md +2 -2
  70. package/docs/api/classes/RBACError.md +1 -1
  71. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  72. package/docs/api/classes/SecureSupabaseClient.md +5 -5
  73. package/docs/api/classes/StorageUtils.md +1 -1
  74. package/docs/api/enums/FileCategory.md +1 -1
  75. package/docs/api/enums/LogLevel.md +54 -0
  76. package/docs/api/enums/RBACErrorCode.md +1 -1
  77. package/docs/api/enums/RPCFunction.md +1 -1
  78. package/docs/api/interfaces/AggregateConfig.md +1 -1
  79. package/docs/api/interfaces/BadgeProps.md +1 -1
  80. package/docs/api/interfaces/ButtonProps.md +1 -1
  81. package/docs/api/interfaces/CalendarProps.md +18 -2
  82. package/docs/api/interfaces/CardProps.md +1 -1
  83. package/docs/api/interfaces/ColorPalette.md +1 -1
  84. package/docs/api/interfaces/ColorShade.md +1 -1
  85. package/docs/api/interfaces/ComplianceResult.md +30 -0
  86. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  87. package/docs/api/interfaces/DataRecord.md +1 -1
  88. package/docs/api/interfaces/DataTableAction.md +1 -1
  89. package/docs/api/interfaces/DataTableColumn.md +1 -1
  90. package/docs/api/interfaces/DataTableProps.md +1 -1
  91. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  92. package/docs/api/interfaces/DatabaseComplianceResult.md +85 -0
  93. package/docs/api/interfaces/DatabaseIssue.md +41 -0
  94. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  95. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/EventAppRoleData.md +6 -6
  97. package/docs/api/interfaces/ExportColumn.md +1 -1
  98. package/docs/api/interfaces/ExportOptions.md +1 -1
  99. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  100. package/docs/api/interfaces/FileMetadata.md +1 -1
  101. package/docs/api/interfaces/FileReference.md +1 -1
  102. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  103. package/docs/api/interfaces/FileUploadOptions.md +48 -8
  104. package/docs/api/interfaces/FileUploadProps.md +46 -13
  105. package/docs/api/interfaces/FooterProps.md +1 -1
  106. package/docs/api/interfaces/FormFieldProps.md +1 -1
  107. package/docs/api/interfaces/FormProps.md +1 -1
  108. package/docs/api/interfaces/GrantEventAppRoleParams.md +9 -9
  109. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  110. package/docs/api/interfaces/InputProps.md +1 -1
  111. package/docs/api/interfaces/LabelProps.md +1 -1
  112. package/docs/api/interfaces/LoggerConfig.md +62 -0
  113. package/docs/api/interfaces/LoginFormProps.md +1 -1
  114. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  115. package/docs/api/interfaces/NavigationContextType.md +1 -1
  116. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  117. package/docs/api/interfaces/NavigationItem.md +1 -1
  118. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  119. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  120. package/docs/api/interfaces/Organisation.md +1 -1
  121. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  122. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  123. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  124. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  125. package/docs/api/interfaces/PaceAppLayoutProps.md +36 -23
  126. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  127. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  128. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  129. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  130. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  131. package/docs/api/interfaces/PaletteData.md +1 -1
  132. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  133. package/docs/api/interfaces/ProgressProps.md +1 -1
  134. package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
  135. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  136. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  137. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  138. package/docs/api/interfaces/QuickFix.md +52 -0
  139. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  140. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  141. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  142. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  143. package/docs/api/interfaces/RBACConfig.md +4 -4
  144. package/docs/api/interfaces/RBACContext.md +1 -1
  145. package/docs/api/interfaces/RBACLogger.md +1 -1
  146. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  147. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  148. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  149. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  150. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  151. package/docs/api/interfaces/RBACResult.md +1 -1
  152. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  153. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  154. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  155. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  156. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  157. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  158. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  159. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  160. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  161. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  162. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  163. package/docs/api/interfaces/RevokeEventAppRoleParams.md +7 -7
  164. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  165. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  166. package/docs/api/interfaces/RoleManagementResult.md +5 -5
  167. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  168. package/docs/api/interfaces/RouteConfig.md +1 -1
  169. package/docs/api/interfaces/RuntimeComplianceResult.md +55 -0
  170. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  171. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  172. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  173. package/docs/api/interfaces/SetupIssue.md +41 -0
  174. package/docs/api/interfaces/StorageConfig.md +1 -1
  175. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  176. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  177. package/docs/api/interfaces/StorageListOptions.md +1 -1
  178. package/docs/api/interfaces/StorageListResult.md +1 -1
  179. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  180. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  181. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  182. package/docs/api/interfaces/StyleImport.md +1 -1
  183. package/docs/api/interfaces/SwitchProps.md +1 -1
  184. package/docs/api/interfaces/TabsContentProps.md +1 -1
  185. package/docs/api/interfaces/TabsListProps.md +1 -1
  186. package/docs/api/interfaces/TabsProps.md +1 -1
  187. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  188. package/docs/api/interfaces/TextareaProps.md +1 -1
  189. package/docs/api/interfaces/ToastActionElement.md +1 -1
  190. package/docs/api/interfaces/ToastProps.md +1 -1
  191. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  192. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  193. package/docs/api/interfaces/UseFormDialogOptions.md +62 -0
  194. package/docs/api/interfaces/UseFormDialogReturn.md +117 -0
  195. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  196. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  197. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  198. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  199. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  200. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  201. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  202. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  203. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  204. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  205. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  206. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  207. package/docs/api/interfaces/UserEventAccess.md +1 -1
  208. package/docs/api/interfaces/UserMenuProps.md +1 -1
  209. package/docs/api/interfaces/UserProfile.md +1 -1
  210. package/docs/api/modules.md +746 -50
  211. package/docs/api-reference/components.md +26 -12
  212. package/docs/api-reference/hooks.md +111 -0
  213. package/docs/api-reference/rpc-functions.md +1 -1
  214. package/docs/api-reference/utilities.md +184 -0
  215. package/docs/getting-started/installation-guide.md +75 -16
  216. package/docs/getting-started/quick-start.md +61 -11
  217. package/docs/implementation-guides/authentication.md +88 -12
  218. package/docs/implementation-guides/file-reference-system.md +26 -3
  219. package/docs/implementation-guides/file-upload-storage.md +30 -1
  220. package/docs/rbac/README.md +1 -0
  221. package/docs/rbac/compliance/compliance-guide.md +544 -0
  222. package/docs/rbac/getting-started.md +158 -33
  223. package/docs/standards/pace-core-compliance.md +432 -0
  224. package/eslint-config-pace-core.cjs +93 -0
  225. package/package.json +15 -3
  226. package/scripts/analyze-bundle.js +232 -0
  227. package/scripts/build-css.js +56 -0
  228. package/scripts/build-docs-incremental.js +1015 -0
  229. package/scripts/check-pace-core-compliance.cjs +2353 -0
  230. package/scripts/check-pace-core-compliance.js +512 -0
  231. package/scripts/generate-docs.js +157 -0
  232. package/scripts/setup-build-cache.js +73 -0
  233. package/scripts/utils/command-runner.js +131 -0
  234. package/scripts/utils/env.js +33 -0
  235. package/scripts/utils/index.js +10 -0
  236. package/scripts/utils/logger.js +88 -0
  237. package/scripts/utils/path-helpers.js +37 -0
  238. package/scripts/validate-formats.js +133 -0
  239. package/scripts/validate-master.js +155 -0
  240. package/scripts/validate-pre-publish.js +140 -0
  241. package/scripts/validate-theme.js +142 -0
  242. package/src/components/Calendar/Calendar.tsx +8 -1
  243. package/src/components/Card/Card.tsx +47 -8
  244. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +314 -0
  245. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +126 -0
  246. package/src/components/DatePickerWithTimezone/README.md +135 -0
  247. package/src/components/DatePickerWithTimezone/index.ts +10 -0
  248. package/src/components/DateTimeField/DateTimeField.test.tsx +358 -0
  249. package/src/components/DateTimeField/DateTimeField.tsx +232 -0
  250. package/src/components/DateTimeField/README.md +148 -0
  251. package/src/components/DateTimeField/index.ts +10 -0
  252. package/src/components/FileUpload/FileUpload.test.tsx +2 -0
  253. package/src/components/FileUpload/FileUpload.tsx +10 -1
  254. package/src/components/Header/Header.test.tsx +47 -18
  255. package/src/components/Header/Header.tsx +22 -7
  256. package/src/components/PaceAppLayout/PaceAppLayout.tsx +29 -20
  257. package/src/components/PaceAppLayout/README.md +9 -0
  258. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
  259. package/src/components/ProtectedRoute/ProtectedRoute.tsx +146 -5
  260. package/src/components/index.ts +8 -0
  261. package/src/eslint-rules/pace-core-compliance.cjs +406 -0
  262. package/src/eslint-rules/pace-core-compliance.js +640 -0
  263. package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
  264. package/src/hooks/index.ts +5 -0
  265. package/src/hooks/useFileReference.test.ts +2 -0
  266. package/src/hooks/useFormDialog.ts +147 -0
  267. package/src/hooks/usePreventTabReload.ts +106 -0
  268. package/src/hooks/useSecureDataAccess.ts +2 -2
  269. package/src/index.ts +27 -0
  270. package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
  271. package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
  272. package/src/rbac/__tests__/rbac-role-isolation.test.ts +456 -0
  273. package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
  274. package/src/rbac/compliance/database-validator.ts +165 -0
  275. package/src/rbac/compliance/index.ts +38 -0
  276. package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
  277. package/src/rbac/compliance/runtime-compliance.ts +77 -0
  278. package/src/rbac/compliance/setup-validator.ts +131 -0
  279. package/src/rbac/components/PagePermissionGuard.tsx +8 -64
  280. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
  281. package/src/rbac/docs/event-based-apps.md +285 -0
  282. package/src/rbac/errors.ts +11 -0
  283. package/src/rbac/hooks/useRoleManagement.ts +292 -12
  284. package/src/rbac/index.ts +30 -0
  285. package/src/services/OrganisationService.ts +4 -0
  286. package/src/styles/core.css +5 -5
  287. package/src/types/database.generated.ts +63 -9
  288. package/src/types/file-reference.ts +9 -0
  289. package/src/utils/__tests__/timezone.test.ts +345 -0
  290. package/src/utils/file-reference/__tests__/file-reference.test.ts +60 -4
  291. package/src/utils/file-reference/index.ts +13 -2
  292. package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
  293. package/src/utils/formatting/formatting.ts +179 -0
  294. package/src/utils/index.ts +27 -1
  295. package/src/utils/location/index.ts +16 -0
  296. package/src/utils/location/location.test.ts +286 -0
  297. package/src/utils/location/location.ts +175 -0
  298. package/src/utils/security/secureDataAccess.ts +1 -1
  299. package/src/utils/storage/helpers.ts +68 -0
  300. package/src/utils/timezone/index.ts +17 -0
  301. package/src/utils/timezone/timezone.test.ts +349 -0
  302. package/src/utils/timezone/timezone.ts +281 -0
  303. package/dist/chunk-CSOFYHAG.js.map +0 -1
  304. package/dist/chunk-FUEYYMX5.js.map +0 -1
  305. package/dist/chunk-HKIT6O7W.js +0 -198
  306. package/dist/chunk-HKIT6O7W.js.map +0 -1
  307. package/dist/chunk-KUEN3HFB.js +0 -94
  308. package/dist/chunk-KUEN3HFB.js.map +0 -1
  309. package/dist/chunk-M7W4CP3M.js.map +0 -1
  310. package/dist/chunk-MI7HBHN3.js.map +0 -1
  311. package/dist/chunk-NQPMQGS2.js.map +0 -1
  312. package/dist/chunk-PWAHJW4G.js.map +0 -1
  313. package/dist/chunk-UHNYIBXL.js.map +0 -1
  314. package/dist/chunk-W22JP75J.js.map +0 -1
  315. package/dist/formatting-5wETwiGF.d.ts +0 -162
  316. /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
  317. /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
  318. /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
  319. /package/dist/{chunk-QCDXODCA.js.map → chunk-XAUHJD3L.js.map} +0 -0
@@ -0,0 +1,512 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Static Analysis Script for pace-core Compliance
5
+ * @package @jmruthers/pace-core
6
+ * @module Scripts/check-pace-core-compliance
7
+ *
8
+ * Scans a consuming app's codebase to check compliance with pace-core usage.
9
+ * Generates a report of violations and suggestions.
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ // ANSI color codes for terminal output
16
+ const colors = {
17
+ reset: '\x1b[0m',
18
+ red: '\x1b[31m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ blue: '\x1b[34m',
22
+ cyan: '\x1b[36m',
23
+ bold: '\x1b[1m'
24
+ };
25
+
26
+ // Load manifest
27
+ function loadManifest() {
28
+ const manifestPath = path.join(__dirname, '../core-usage-manifest.json');
29
+ if (!fs.existsSync(manifestPath)) {
30
+ console.error(`${colors.red}Error: core-usage-manifest.json not found at ${manifestPath}${colors.reset}`);
31
+ process.exit(1);
32
+ }
33
+ return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
34
+ }
35
+
36
+ // Find project root (look for package.json, going up from current dir or script location)
37
+ function findProjectRoot(startDir = process.cwd()) {
38
+ let current = path.resolve(startDir);
39
+ while (current !== path.dirname(current)) {
40
+ if (fs.existsSync(path.join(current, 'package.json'))) {
41
+ return current;
42
+ }
43
+ current = path.dirname(current);
44
+ }
45
+ return startDir;
46
+ }
47
+
48
+ // Scan file for violations
49
+ function scanFile(filePath, manifest) {
50
+ const violations = {
51
+ restrictedImports: [],
52
+ duplicateComponents: [],
53
+ duplicateHooks: [],
54
+ duplicateUtils: [],
55
+ suggestions: [],
56
+ customAuthCode: [],
57
+ duplicateConfig: [],
58
+ unprotectedPages: [],
59
+ directSupabaseAuth: []
60
+ };
61
+
62
+ const content = fs.readFileSync(filePath, 'utf8');
63
+ const relativePath = path.relative(process.cwd(), filePath);
64
+
65
+ // Check for restricted imports
66
+ manifest.restrictedImports.forEach(({ module, reason }) => {
67
+ const importPattern = new RegExp(`from\\s+['"]${module.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]`, 'g');
68
+ if (importPattern.test(content)) {
69
+ violations.restrictedImports.push({
70
+ module,
71
+ reason,
72
+ file: relativePath,
73
+ line: getLineNumber(content, content.match(importPattern)[0])
74
+ });
75
+ }
76
+
77
+ // Also check for @radix-ui/* pattern
78
+ if (module.startsWith('@radix-ui/')) {
79
+ const radixPattern = /from\s+['"]@radix-ui\/[^'"]+['"]/g;
80
+ const matches = content.match(radixPattern);
81
+ if (matches) {
82
+ matches.forEach(match => {
83
+ const matchedModule = match.match(/['"]([^'"]+)['"]/)[1];
84
+ if (!manifest.restrictedImports.find(ri => ri.module === matchedModule)) {
85
+ violations.restrictedImports.push({
86
+ module: matchedModule,
87
+ reason: 'Use pace-core component instead of direct Radix UI import',
88
+ file: relativePath,
89
+ line: getLineNumber(content, match)
90
+ });
91
+ }
92
+ });
93
+ }
94
+ }
95
+ });
96
+
97
+ // Check for duplicate component names
98
+ const filename = path.basename(filePath);
99
+ const componentName = filename.replace(/\.(tsx?|jsx?)$/, '').replace(/\.(test|spec)$/, '');
100
+
101
+ if (manifest.components.includes(componentName)) {
102
+ // Check if this file exports a component
103
+ if (content.match(/export\s+(default\s+)?(function|const|class)\s+(\w+)?/)) {
104
+ violations.duplicateComponents.push({
105
+ component: componentName,
106
+ file: relativePath
107
+ });
108
+ }
109
+ }
110
+
111
+ // Check for duplicate hook names
112
+ if (filename.startsWith('use') && filename.endsWith('.ts') || filename.endsWith('.tsx')) {
113
+ const hookName = filename.replace(/\.(tsx?|jsx?)$/, '').replace(/\.(test|spec)$/, '');
114
+ if (manifest.hooks.includes(hookName)) {
115
+ if (content.match(/export\s+(default\s+)?(function|const)\s+(\w+)?/)) {
116
+ violations.duplicateHooks.push({
117
+ hook: hookName,
118
+ file: relativePath
119
+ });
120
+ }
121
+ }
122
+ }
123
+
124
+ // Check for duplicate util names
125
+ const utilName = filename.replace(/\.(ts|js)$/, '').replace(/\.(test|spec)$/, '');
126
+ if (manifest.utils.includes(utilName)) {
127
+ if (content.match(/export\s+(default\s+)?(function|const)\s+(\w+)?/)) {
128
+ violations.duplicateUtils.push({
129
+ util: utilName,
130
+ file: relativePath
131
+ });
132
+ }
133
+ }
134
+
135
+ // Check for native HTML elements that should use pace-core components
136
+ const nativeElementPatterns = {
137
+ '<button': { suggestion: 'Use Button from @jmruthers/pace-core' },
138
+ '<input': { suggestion: 'Use Input from @jmruthers/pace-core' },
139
+ '<textarea': { suggestion: 'Use Textarea from @jmruthers/pace-core' },
140
+ '<label': { suggestion: 'Use Label from @jmruthers/pace-core' }
141
+ };
142
+
143
+ Object.entries(nativeElementPatterns).forEach(([pattern, { suggestion }]) => {
144
+ if (content.includes(pattern) && !content.includes('from \'@jmruthers/pace-core\'')) {
145
+ violations.suggestions.push({
146
+ type: 'native-element',
147
+ suggestion,
148
+ file: relativePath,
149
+ pattern
150
+ });
151
+ }
152
+ });
153
+
154
+ // ============================================
155
+ // RBAC/Auth Compliance Checks
156
+ // ============================================
157
+
158
+ // Check for custom auth/rbac/permission code that doesn't import from pace-core
159
+ const authRbacPatterns = [
160
+ // Custom auth hooks
161
+ { pattern: /export\s+(default\s+)?(function|const)\s+useAuth\s*[=\(]/g, name: 'useAuth', type: 'hook' },
162
+ { pattern: /export\s+(default\s+)?(function|const)\s+useLogin\s*[=\(]/g, name: 'useLogin', type: 'hook' },
163
+ { pattern: /export\s+(default\s+)?(function|const)\s+useLogout\s*[=\(]/g, name: 'useLogout', type: 'hook' },
164
+ { pattern: /export\s+(default\s+)?(function|const)\s+useSession\s*[=\(]/g, name: 'useSession', type: 'hook' },
165
+ { pattern: /export\s+(default\s+)?(function|const)\s+useUser\s*[=\(]/g, name: 'useUser', type: 'hook' },
166
+ { pattern: /export\s+(default\s+)?(function|const)\s+useAuthentication\s*[=\(]/g, name: 'useAuthentication', type: 'hook' },
167
+ // Custom RBAC hooks
168
+ { pattern: /export\s+(default\s+)?(function|const)\s+usePermissions\s*[=\(]/g, name: 'usePermissions', type: 'hook' },
169
+ { pattern: /export\s+(default\s+)?(function|const)\s+useCan\s*[=\(]/g, name: 'useCan', type: 'hook' },
170
+ { pattern: /export\s+(default\s+)?(function|const)\s+useAccessLevel\s*[=\(]/g, name: 'useAccessLevel', type: 'hook' },
171
+ { pattern: /export\s+(default\s+)?(function|const)\s+useRole\s*[=\(]/g, name: 'useRole', type: 'hook' },
172
+ // Custom RBAC components
173
+ { pattern: /export\s+(default\s+)?(function|const)\s+PermissionGuard\s*[=\(]/g, name: 'PermissionGuard', type: 'component' },
174
+ { pattern: /export\s+(default\s+)?(function|const)\s+AuthGuard\s*[=\(]/g, name: 'AuthGuard', type: 'component' },
175
+ { pattern: /export\s+(default\s+)?(function|const)\s+RoleGuard\s*[=\(]/g, name: 'RoleGuard', type: 'component' },
176
+ { pattern: /export\s+(default\s+)?(function|const)\s+AccessGuard\s*[=\(]/g, name: 'AccessGuard', type: 'component' },
177
+ // Custom permission utilities
178
+ { pattern: /export\s+(default\s+)?(function|const)\s+checkPermission\s*[=\(]/g, name: 'checkPermission', type: 'util' },
179
+ { pattern: /export\s+(default\s+)?(function|const)\s+hasPermission\s*[=\(]/g, name: 'hasPermission', type: 'util' },
180
+ { pattern: /export\s+(default\s+)?(function|const)\s+hasAccess\s*[=\(]/g, name: 'hasAccess', type: 'util' },
181
+ { pattern: /export\s+(default\s+)?(function|const)\s+canAccess\s*[=\(]/g, name: 'canAccess', type: 'util' },
182
+ { pattern: /export\s+(default\s+)?(function|const)\s+isPermitted\s*[=\(]/g, name: 'isPermitted', type: 'util' }
183
+ ];
184
+
185
+ // Check if file imports from pace-core for auth/rbac
186
+ const hasPaceCoreImport = /from\s+['"]@jmruthers\/pace-core/.test(content) ||
187
+ /from\s+['"]@jmruthers\/pace-core\/rbac/.test(content) ||
188
+ /from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
189
+
190
+ authRbacPatterns.forEach(({ pattern, name, type }) => {
191
+ if (pattern.test(content) && !hasPaceCoreImport) {
192
+ violations.customAuthCode.push({
193
+ name,
194
+ type,
195
+ file: relativePath,
196
+ line: getLineNumber(content, content.match(pattern)[0]),
197
+ reason: `Custom ${type} '${name}' detected. Use pace-core's ${name} instead.`
198
+ });
199
+ }
200
+ });
201
+
202
+ // Check for duplicate Supabase client configurations
203
+ const supabaseCreateClientPattern = /createClient\s*\(/g;
204
+ const supabaseCreateClientMatches = content.match(supabaseCreateClientPattern);
205
+ if (supabaseCreateClientMatches && supabaseCreateClientMatches.length > 1) {
206
+ violations.duplicateConfig.push({
207
+ type: 'supabase-client',
208
+ file: relativePath,
209
+ count: supabaseCreateClientMatches.length,
210
+ reason: `Multiple Supabase client instantiations found (${supabaseCreateClientMatches.length}). Consolidate to a single client configuration.`
211
+ });
212
+ }
213
+
214
+ // Check for Supabase URL/key configuration in multiple places
215
+ const supabaseUrlPattern = /(SUPABASE_URL|VITE_SUPABASE_URL|NEXT_PUBLIC_SUPABASE_URL|REACT_APP_SUPABASE_URL)/g;
216
+ const supabaseKeyPattern = /(SUPABASE_ANON_KEY|VITE_SUPABASE_ANON_KEY|NEXT_PUBLIC_SUPABASE_ANON_KEY|REACT_APP_SUPABASE_ANON_KEY)/g;
217
+ const urlMatches = content.match(supabaseUrlPattern);
218
+ const keyMatches = content.match(supabaseKeyPattern);
219
+
220
+ if ((urlMatches && urlMatches.length > 2) || (keyMatches && keyMatches.length > 2)) {
221
+ violations.duplicateConfig.push({
222
+ type: 'supabase-env',
223
+ file: relativePath,
224
+ reason: 'Supabase environment variables referenced multiple times. Consider centralizing configuration.'
225
+ });
226
+ }
227
+
228
+ // Check for unprotected pages/routes
229
+ // Look for route definitions without PagePermissionGuard
230
+ const routePatterns = [
231
+ /<Route\s+path=["'][^"']+["']/g,
232
+ /<Route\s+element\s*=/g,
233
+ /createBrowserRouter\s*\(/g,
234
+ /createRoutesFromElements/g
235
+ ];
236
+
237
+ const isRouteFile = routePatterns.some(pattern => pattern.test(content));
238
+ const hasPagePermissionGuard = /PagePermissionGuard/.test(content) ||
239
+ /from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(content);
240
+
241
+ if (isRouteFile && !hasPagePermissionGuard && !relativePath.includes('test') && !relativePath.includes('spec')) {
242
+ violations.unprotectedPages.push({
243
+ file: relativePath,
244
+ reason: 'Route file found without PagePermissionGuard. All routes should be protected with PagePermissionGuard from pace-core.'
245
+ });
246
+ }
247
+
248
+ // Check for direct Supabase auth usage (should use UnifiedAuthProvider)
249
+ const directSupabaseAuthPatterns = [
250
+ /\.auth\.signIn/g,
251
+ /\.auth\.signUp/g,
252
+ /\.auth\.signOut/g,
253
+ /\.auth\.getSession/g,
254
+ /\.auth\.getUser/g,
255
+ /supabase\.auth\./g
256
+ ];
257
+
258
+ const hasUnifiedAuthImport = /UnifiedAuthProvider/.test(content) ||
259
+ /useUnifiedAuth/.test(content) ||
260
+ /from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
261
+
262
+ directSupabaseAuthPatterns.forEach(pattern => {
263
+ if (pattern.test(content) && !hasUnifiedAuthImport && !hasPaceCoreImport) {
264
+ violations.directSupabaseAuth.push({
265
+ file: relativePath,
266
+ line: getLineNumber(content, content.match(pattern)[0]),
267
+ reason: 'Direct Supabase auth usage detected. Use UnifiedAuthProvider and useUnifiedAuth from pace-core instead.'
268
+ });
269
+ }
270
+ });
271
+
272
+ return violations;
273
+ }
274
+
275
+ // Get line number for a match
276
+ function getLineNumber(content, match) {
277
+ const lines = content.substring(0, content.indexOf(match)).split('\n');
278
+ return lines.length;
279
+ }
280
+
281
+ // Generate report
282
+ function generateReport(allViolations, manifest) {
283
+ const totalRestricted = allViolations.restrictedImports.length;
284
+ const totalDuplicates =
285
+ allViolations.duplicateComponents.length +
286
+ allViolations.duplicateHooks.length +
287
+ allViolations.duplicateUtils.length;
288
+ const totalSuggestions = allViolations.suggestions.length;
289
+ const totalRbacAuth =
290
+ allViolations.customAuthCode.length +
291
+ allViolations.duplicateConfig.length +
292
+ allViolations.unprotectedPages.length +
293
+ allViolations.directSupabaseAuth.length;
294
+ const totalIssues = totalRestricted + totalDuplicates + totalSuggestions + totalRbacAuth;
295
+
296
+ console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
297
+ console.log(`${colors.bold}${colors.cyan} pace-core Compliance Report${colors.reset}`);
298
+ console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
299
+
300
+ // Restricted Imports
301
+ if (totalRestricted > 0) {
302
+ console.log(`${colors.red}${colors.bold}❌ Restricted Imports Found: ${totalRestricted}${colors.reset}\n`);
303
+ allViolations.restrictedImports.forEach(({ module, reason, file, line }) => {
304
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
305
+ console.log(` Import: ${colors.cyan}${module}${colors.reset}`);
306
+ console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
307
+ });
308
+ } else {
309
+ console.log(`${colors.green}✅ No restricted imports found${colors.reset}\n`);
310
+ }
311
+
312
+ // Duplicate Components
313
+ if (allViolations.duplicateComponents.length > 0) {
314
+ console.log(`${colors.red}${colors.bold}❌ Duplicate Components Found: ${allViolations.duplicateComponents.length}${colors.reset}\n`);
315
+ allViolations.duplicateComponents.forEach(({ component, file }) => {
316
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
317
+ console.log(` Component '${colors.cyan}${component}${colors.reset}' conflicts with pace-core component`);
318
+ console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${component}' from '@jmruthers/pace-core' instead\n`);
319
+ });
320
+ }
321
+
322
+ // Duplicate Hooks
323
+ if (allViolations.duplicateHooks.length > 0) {
324
+ console.log(`${colors.red}${colors.bold}❌ Duplicate Hooks Found: ${allViolations.duplicateHooks.length}${colors.reset}\n`);
325
+ allViolations.duplicateHooks.forEach(({ hook, file }) => {
326
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
327
+ console.log(` Hook '${colors.cyan}${hook}${colors.reset}' conflicts with pace-core hook`);
328
+ console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${hook}' from '@jmruthers/pace-core' instead\n`);
329
+ });
330
+ }
331
+
332
+ // Duplicate Utils
333
+ if (allViolations.duplicateUtils.length > 0) {
334
+ console.log(`${colors.red}${colors.bold}❌ Duplicate Utils Found: ${allViolations.duplicateUtils.length}${colors.reset}\n`);
335
+ allViolations.duplicateUtils.forEach(({ util, file }) => {
336
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
337
+ console.log(` Util '${colors.cyan}${util}${colors.reset}' conflicts with pace-core util`);
338
+ console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${util}' from '@jmruthers/pace-core' instead\n`);
339
+ });
340
+ }
341
+
342
+ // Suggestions
343
+ if (totalSuggestions > 0) {
344
+ console.log(`${colors.yellow}${colors.bold}💡 Suggestions: ${totalSuggestions}${colors.reset}\n`);
345
+ const grouped = {};
346
+ allViolations.suggestions.forEach(s => {
347
+ if (!grouped[s.file]) grouped[s.file] = [];
348
+ grouped[s.file].push(s);
349
+ });
350
+ Object.entries(grouped).forEach(([file, suggestions]) => {
351
+ console.log(` ${colors.yellow}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
352
+ suggestions.forEach(s => {
353
+ console.log(` ${s.suggestion}\n`);
354
+ });
355
+ });
356
+ }
357
+
358
+ // RBAC/Auth Compliance Section
359
+ if (totalRbacAuth > 0) {
360
+ console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
361
+ console.log(`${colors.bold}${colors.cyan} RBAC/Auth Compliance${colors.reset}`);
362
+ console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
363
+
364
+ // Custom Auth/RBAC Code
365
+ if (allViolations.customAuthCode.length > 0) {
366
+ console.log(`${colors.red}${colors.bold}❌ Custom Auth/RBAC Code Found: ${allViolations.customAuthCode.length}${colors.reset}\n`);
367
+ allViolations.customAuthCode.forEach(({ name, type, file, line, reason }) => {
368
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
369
+ console.log(` ${type}: ${colors.cyan}${name}${colors.reset}`);
370
+ console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
371
+ });
372
+ }
373
+
374
+ // Duplicate Configurations
375
+ if (allViolations.duplicateConfig.length > 0) {
376
+ console.log(`${colors.red}${colors.bold}❌ Duplicate Configurations Found: ${allViolations.duplicateConfig.length}${colors.reset}\n`);
377
+ allViolations.duplicateConfig.forEach(({ type, file, count, reason }) => {
378
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
379
+ console.log(` Type: ${colors.cyan}${type}${colors.reset}${count ? ` (${count} instances)` : ''}`);
380
+ console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
381
+ });
382
+ }
383
+
384
+ // Unprotected Pages
385
+ if (allViolations.unprotectedPages.length > 0) {
386
+ console.log(`${colors.red}${colors.bold}❌ Unprotected Pages Found: ${allViolations.unprotectedPages.length}${colors.reset}\n`);
387
+ allViolations.unprotectedPages.forEach(({ file, reason }) => {
388
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
389
+ console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
390
+ });
391
+ }
392
+
393
+ // Direct Supabase Auth Usage
394
+ if (allViolations.directSupabaseAuth.length > 0) {
395
+ console.log(`${colors.red}${colors.bold}❌ Direct Supabase Auth Usage Found: ${allViolations.directSupabaseAuth.length}${colors.reset}\n`);
396
+ allViolations.directSupabaseAuth.forEach(({ file, line, reason }) => {
397
+ console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
398
+ console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
399
+ });
400
+ }
401
+ } else {
402
+ console.log(`\n${colors.green}✅ RBAC/Auth compliance: All checks passed${colors.reset}\n`);
403
+ }
404
+
405
+ // Summary
406
+ console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
407
+ console.log(`${colors.bold}Summary:${colors.reset}`);
408
+ console.log(` Total Issues: ${totalIssues > 0 ? colors.red : colors.green}${totalIssues}${colors.reset}`);
409
+ console.log(` - Restricted Imports: ${totalRestricted > 0 ? colors.red : colors.green}${totalRestricted}${colors.reset}`);
410
+ console.log(` - Duplicate Components/Hooks/Utils: ${totalDuplicates > 0 ? colors.red : colors.green}${totalDuplicates}${colors.reset}`);
411
+ console.log(` - Suggestions: ${colors.yellow}${totalSuggestions}${colors.reset}`);
412
+ console.log(` - RBAC/Auth Issues: ${totalRbacAuth > 0 ? colors.red : colors.green}${totalRbacAuth}${colors.reset}`);
413
+
414
+ if (totalIssues === 0) {
415
+ console.log(`\n${colors.green}${colors.bold}✅ Excellent! Your codebase is fully compliant with pace-core standards.${colors.reset}\n`);
416
+ return 0;
417
+ } else {
418
+ console.log(`\n${colors.yellow}${colors.bold}⚠️ Please review the issues above and migrate to pace-core components/hooks/utils.${colors.reset}\n`);
419
+ return 1;
420
+ }
421
+ }
422
+
423
+ // Recursively find source files
424
+ function findSourceFiles(dir, fileList = []) {
425
+ const ignoreDirs = ['node_modules', 'dist', 'build', '.next', 'coverage', '__tests__', '__mocks__'];
426
+ const ignoreFiles = /\.(test|spec)\.(ts|tsx|js|jsx)$/;
427
+ const sourceExtensions = /\.(ts|tsx|js|jsx)$/;
428
+
429
+ try {
430
+ const items = fs.readdirSync(dir);
431
+
432
+ items.forEach(item => {
433
+ const fullPath = path.join(dir, item);
434
+ const stat = fs.statSync(fullPath);
435
+
436
+ if (stat.isDirectory()) {
437
+ if (!ignoreDirs.includes(item) && !item.startsWith('.')) {
438
+ findSourceFiles(fullPath, fileList);
439
+ }
440
+ } else if (stat.isFile()) {
441
+ if (sourceExtensions.test(item) && !ignoreFiles.test(item)) {
442
+ fileList.push(fullPath);
443
+ }
444
+ }
445
+ });
446
+ } catch (error) {
447
+ // Skip directories we can't read
448
+ }
449
+
450
+ return fileList;
451
+ }
452
+
453
+ // Main function
454
+ function main() {
455
+ const manifest = loadManifest();
456
+ const projectRoot = findProjectRoot();
457
+
458
+ console.log(`${colors.cyan}Scanning project at: ${projectRoot}${colors.reset}`);
459
+
460
+ // Find all TypeScript/JavaScript files (excluding node_modules, dist, etc.)
461
+ const files = findSourceFiles(projectRoot);
462
+
463
+ console.log(`Found ${files.length} files to scan...\n`);
464
+
465
+ // Scan all files
466
+ const allViolations = {
467
+ restrictedImports: [],
468
+ duplicateComponents: [],
469
+ duplicateHooks: [],
470
+ duplicateUtils: [],
471
+ suggestions: [],
472
+ customAuthCode: [],
473
+ duplicateConfig: [],
474
+ unprotectedPages: [],
475
+ directSupabaseAuth: []
476
+ };
477
+
478
+ files.forEach(file => {
479
+ try {
480
+ const violations = scanFile(file, manifest);
481
+ allViolations.restrictedImports.push(...violations.restrictedImports);
482
+ allViolations.duplicateComponents.push(...violations.duplicateComponents);
483
+ allViolations.duplicateHooks.push(...violations.duplicateHooks);
484
+ allViolations.duplicateUtils.push(...violations.duplicateUtils);
485
+ allViolations.suggestions.push(...violations.suggestions);
486
+ allViolations.customAuthCode.push(...violations.customAuthCode);
487
+ allViolations.duplicateConfig.push(...violations.duplicateConfig);
488
+ allViolations.unprotectedPages.push(...violations.unprotectedPages);
489
+ allViolations.directSupabaseAuth.push(...violations.directSupabaseAuth);
490
+ } catch (error) {
491
+ console.error(`${colors.red}Error scanning ${file}: ${error.message}${colors.reset}`);
492
+ }
493
+ });
494
+
495
+ // Generate and display report
496
+ const exitCode = generateReport(allViolations, manifest);
497
+ process.exit(exitCode);
498
+ }
499
+
500
+ // Run if called directly
501
+ if (require.main === module) {
502
+ try {
503
+ main();
504
+ } catch (error) {
505
+ console.error(`${colors.red}Error: ${error.message}${colors.reset}`);
506
+ console.error(error.stack);
507
+ process.exit(1);
508
+ }
509
+ }
510
+
511
+ module.exports = { main, scanFile, generateReport };
512
+
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Documentation Generator for @jmruthers/pace-core
5
+ *
6
+ * This script helps generate and maintain documentation by:
7
+ * 1. Extracting JSDoc comments from components
8
+ * 2. Generating API documentation
9
+ * 3. Creating component examples
10
+ * 4. Updating documentation structure
11
+ */
12
+
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+
20
+ const SRC_DIR = path.join(__dirname, '../src');
21
+ const DOCS_DIR = path.join(__dirname, '../docs');
22
+
23
+ /**
24
+ * Extract JSDoc comments from a TypeScript/JavaScript file
25
+ */
26
+ function extractJSDocComments(filePath) {
27
+ const content = fs.readFileSync(filePath, 'utf-8');
28
+ const jsdocRegex = /\/\*\*[\s\S]*?\*\//g;
29
+ const matches = content.match(jsdocRegex) || [];
30
+
31
+ return matches.map(comment => {
32
+ // Clean up the comment
33
+ return comment
34
+ .replace(/\/\*\*/, '')
35
+ .replace(/\*\//, '')
36
+ .replace(/^\s*\*\s*/gm, '')
37
+ .trim();
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Generate component documentation from JSDoc comments
43
+ */
44
+ function generateComponentDocs(componentName) {
45
+ const componentPath = path.join(SRC_DIR, 'components', componentName, `${componentName}.tsx`);
46
+
47
+ if (!fs.existsSync(componentPath)) {
48
+ console.warn(`Component ${componentName} not found at ${componentPath}`);
49
+ return;
50
+ }
51
+
52
+ const comments = extractJSDocComments(componentPath);
53
+ const docPath = path.join(DOCS_DIR, 'components', `${componentName}.md`);
54
+
55
+ // Create basic documentation structure
56
+ let docContent = `# ${componentName}\n\n`;
57
+
58
+ if (comments.length > 0) {
59
+ docContent += `## Overview\n\n${comments[0]}\n\n`;
60
+ }
61
+
62
+ docContent += `## Usage\n\n\`\`\`tsx\nimport { ${componentName} } from '@jmruthers/pace-core';\n\`\`\`\n\n`;
63
+ docContent += `## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n`;
64
+ docContent += `| ... | ... | ... | ... |\n\n`;
65
+ docContent += `## Examples\n\n### Basic Usage\n\n\`\`\`tsx\n<${componentName} />\n\`\`\`\n\n`;
66
+ docContent += `## Accessibility\n\nThis component follows accessibility best practices.\n\n`;
67
+ docContent += `## Testing\n\nThis component includes comprehensive tests.\n\n`;
68
+
69
+ fs.writeFileSync(docPath, docContent);
70
+ console.log(`Generated documentation for ${componentName}`);
71
+ }
72
+
73
+ /**
74
+ * Update the components index
75
+ */
76
+ function updateComponentsIndex() {
77
+ const componentsDir = path.join(SRC_DIR, 'components');
78
+ const components = fs.readdirSync(componentsDir, { withFileTypes: true })
79
+ .filter(dirent => dirent.isDirectory())
80
+ .map(dirent => dirent.name);
81
+
82
+ let indexContent = `# Components\n\nThis section contains documentation for all UI components in \`@jmruthers/pace-core\`.\n\n`;
83
+ indexContent += `## Component List\n\n`;
84
+
85
+ components.forEach(component => {
86
+ indexContent += `- [${component}](./${component}.md) - ${component} component\n`;
87
+ });
88
+
89
+ indexContent += `\n## Design System\n\nAll components follow our design system principles.\n\n`;
90
+ indexContent += `### Colors\n- **Primary**: Blue-based color scheme\n`;
91
+ indexContent += `- **Secondary**: Gray-based color scheme\n`;
92
+ indexContent += `- **Success**: Green for positive actions\n`;
93
+ indexContent += `- **Warning**: Yellow for caution\n`;
94
+ indexContent += `- **Error**: Red for errors and destructive actions\n\n`;
95
+
96
+ const indexPath = path.join(DOCS_DIR, 'components', 'README.md');
97
+ fs.writeFileSync(indexPath, indexContent);
98
+ console.log('Updated components index');
99
+ }
100
+
101
+ /**
102
+ * Generate API documentation from TypeScript types
103
+ */
104
+ function generateAPIDocs() {
105
+ const apiDir = path.join(DOCS_DIR, 'api');
106
+ if (!fs.existsSync(apiDir)) {
107
+ fs.mkdirSync(apiDir, { recursive: true });
108
+ }
109
+
110
+ // This would typically use TypeDoc or similar tool
111
+ console.log('API documentation generation would go here');
112
+ }
113
+
114
+ /**
115
+ * Main function
116
+ */
117
+ function main() {
118
+ const command = process.argv[2];
119
+
120
+ switch (command) {
121
+ case 'generate':
122
+ const componentName = process.argv[3];
123
+ if (componentName) {
124
+ generateComponentDocs(componentName);
125
+ } else {
126
+ console.log('Usage: npm run docs:generate <component-name>');
127
+ }
128
+ break;
129
+
130
+ case 'update-index':
131
+ updateComponentsIndex();
132
+ break;
133
+
134
+ case 'generate-api':
135
+ generateAPIDocs();
136
+ break;
137
+
138
+ case 'all':
139
+ updateComponentsIndex();
140
+ generateAPIDocs();
141
+ console.log('Documentation generation complete');
142
+ break;
143
+
144
+ default:
145
+ console.log(`
146
+ Documentation Generator
147
+
148
+ Usage:
149
+ npm run docs:generate <component-name> - Generate docs for a specific component
150
+ npm run docs:update-index - Update the components index
151
+ npm run docs:generate-api - Generate API documentation
152
+ npm run docs:all - Run all documentation tasks
153
+ `);
154
+ }
155
+ }
156
+
157
+ main();