@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.
- package/CHANGELOG.md +38 -0
- package/README.md +60 -1
- package/core-usage-manifest.json +312 -0
- package/dist/{DataTable-QAB34V6K.js → DataTable-IX2NBUTP.js} +6 -6
- package/dist/{DataTable-Bz8ffqyA.d.ts → DataTable-Z9NLVJh0.d.ts} +1 -1
- package/dist/{index-Bl--n7-T.d.ts → PublicPageProvider-DIzEzwKl.d.ts} +23 -10
- package/dist/{UnifiedAuthProvider-7F6T4B6K.js → UnifiedAuthProvider-A4BCQRJY.js} +4 -2
- package/dist/{UnifiedAuthProvider-F86d7dSi.d.ts → UnifiedAuthProvider-BG0AL5eE.d.ts} +2 -1
- package/dist/{api-ROMBCNKU.js → api-BMFCXVQX.js} +2 -2
- package/dist/{chunk-RA3JUFMW.js → chunk-445GEP27.js} +154 -4
- package/dist/{chunk-RA3JUFMW.js.map → chunk-445GEP27.js.map} +1 -1
- package/dist/{chunk-W22JP75J.js → chunk-DAGICKHT.js} +9 -7
- package/dist/chunk-DAGICKHT.js.map +1 -0
- package/dist/{chunk-FUEYYMX5.js → chunk-FXFJRTKI.js} +24 -3
- package/dist/chunk-FXFJRTKI.js.map +1 -0
- package/dist/{chunk-CSOFYHAG.js → chunk-GRIQLQ52.js} +374 -60
- package/dist/chunk-GRIQLQ52.js.map +1 -0
- package/dist/{chunk-NQPMQGS2.js → chunk-HDCUMOOI.js} +497 -399
- package/dist/chunk-HDCUMOOI.js.map +1 -0
- package/dist/chunk-HESYZWZW.js +388 -0
- package/dist/chunk-HESYZWZW.js.map +1 -0
- package/dist/{chunk-QUVSNGIP.js → chunk-HGPQUCBC.js} +34 -9
- package/dist/{chunk-QUVSNGIP.js.map → chunk-HGPQUCBC.js.map} +1 -1
- package/dist/{chunk-PWAHJW4G.js → chunk-OALXJH4Y.js} +86 -33
- package/dist/chunk-OALXJH4Y.js.map +1 -0
- package/dist/{chunk-MI7HBHN3.js → chunk-TC7D3CR3.js} +89 -9
- package/dist/chunk-TC7D3CR3.js.map +1 -0
- package/dist/chunk-THRPYOFK.js +215 -0
- package/dist/chunk-THRPYOFK.js.map +1 -0
- package/dist/{chunk-M7W4CP3M.js → chunk-U6WNSFX5.js} +2 -1
- package/dist/chunk-U6WNSFX5.js.map +1 -0
- package/dist/{chunk-UHNYIBXL.js → chunk-UQWSHFVX.js} +1 -1
- package/dist/chunk-UQWSHFVX.js.map +1 -0
- package/dist/{chunk-QCDXODCA.js → chunk-XAUHJD3L.js} +2 -2
- package/dist/components.d.ts +182 -6
- package/dist/components.js +157 -11
- package/dist/components.js.map +1 -1
- package/dist/{database.generated-CBmg2950.d.ts → database.generated-DI89OQeI.d.ts} +63 -9
- package/dist/eslint-rules/pace-core-compliance.cjs +406 -0
- package/dist/{file-reference-D06mEEWW.d.ts → file-reference-PRTSLxKx.d.ts} +10 -1
- package/dist/hooks.d.ts +52 -15
- package/dist/hooks.js +12 -22
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +82 -18
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +3 -1
- package/dist/rbac/index.d.ts +206 -15
- package/dist/rbac/index.js +28 -6
- package/dist/timezone-_pgH8qrY.d.ts +530 -0
- package/dist/{types-_x1f4QBF.d.ts → types-DUyCRSTj.d.ts} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-JJczomYq.d.ts → usePublicRouteParams-D71QLlg4.d.ts} +114 -3
- package/dist/utils.d.ts +110 -152
- package/dist/utils.js +128 -138
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +60 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +178 -0
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +5 -5
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +54 -0
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +18 -2
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +30 -0
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +85 -0
- package/docs/api/interfaces/DatabaseIssue.md +41 -0
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +6 -6
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +48 -8
- package/docs/api/interfaces/FileUploadProps.md +46 -13
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +9 -9
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +62 -0
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +36 -23
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +52 -0
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +4 -4
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +7 -7
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +5 -5
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +55 -0
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +41 -0
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseFormDialogOptions.md +62 -0
- package/docs/api/interfaces/UseFormDialogReturn.md +117 -0
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +746 -50
- package/docs/api-reference/components.md +26 -12
- package/docs/api-reference/hooks.md +111 -0
- package/docs/api-reference/rpc-functions.md +1 -1
- package/docs/api-reference/utilities.md +184 -0
- package/docs/getting-started/installation-guide.md +75 -16
- package/docs/getting-started/quick-start.md +61 -11
- package/docs/implementation-guides/authentication.md +88 -12
- package/docs/implementation-guides/file-reference-system.md +26 -3
- package/docs/implementation-guides/file-upload-storage.md +30 -1
- package/docs/rbac/README.md +1 -0
- package/docs/rbac/compliance/compliance-guide.md +544 -0
- package/docs/rbac/getting-started.md +158 -33
- package/docs/standards/pace-core-compliance.md +432 -0
- package/eslint-config-pace-core.cjs +93 -0
- package/package.json +15 -3
- package/scripts/analyze-bundle.js +232 -0
- package/scripts/build-css.js +56 -0
- package/scripts/build-docs-incremental.js +1015 -0
- package/scripts/check-pace-core-compliance.cjs +2353 -0
- package/scripts/check-pace-core-compliance.js +512 -0
- package/scripts/generate-docs.js +157 -0
- package/scripts/setup-build-cache.js +73 -0
- package/scripts/utils/command-runner.js +131 -0
- package/scripts/utils/env.js +33 -0
- package/scripts/utils/index.js +10 -0
- package/scripts/utils/logger.js +88 -0
- package/scripts/utils/path-helpers.js +37 -0
- package/scripts/validate-formats.js +133 -0
- package/scripts/validate-master.js +155 -0
- package/scripts/validate-pre-publish.js +140 -0
- package/scripts/validate-theme.js +142 -0
- package/src/components/Calendar/Calendar.tsx +8 -1
- package/src/components/Card/Card.tsx +47 -8
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +314 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +126 -0
- package/src/components/DatePickerWithTimezone/README.md +135 -0
- package/src/components/DatePickerWithTimezone/index.ts +10 -0
- package/src/components/DateTimeField/DateTimeField.test.tsx +358 -0
- package/src/components/DateTimeField/DateTimeField.tsx +232 -0
- package/src/components/DateTimeField/README.md +148 -0
- package/src/components/DateTimeField/index.ts +10 -0
- package/src/components/FileUpload/FileUpload.test.tsx +2 -0
- package/src/components/FileUpload/FileUpload.tsx +10 -1
- package/src/components/Header/Header.test.tsx +47 -18
- package/src/components/Header/Header.tsx +22 -7
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +29 -20
- package/src/components/PaceAppLayout/README.md +9 -0
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +146 -5
- package/src/components/index.ts +8 -0
- package/src/eslint-rules/pace-core-compliance.cjs +406 -0
- package/src/eslint-rules/pace-core-compliance.js +640 -0
- package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useFileReference.test.ts +2 -0
- package/src/hooks/useFormDialog.ts +147 -0
- package/src/hooks/usePreventTabReload.ts +106 -0
- package/src/hooks/useSecureDataAccess.ts +2 -2
- package/src/index.ts +27 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
- package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
- package/src/rbac/__tests__/rbac-role-isolation.test.ts +456 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
- package/src/rbac/compliance/database-validator.ts +165 -0
- package/src/rbac/compliance/index.ts +38 -0
- package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
- package/src/rbac/compliance/runtime-compliance.ts +77 -0
- package/src/rbac/compliance/setup-validator.ts +131 -0
- package/src/rbac/components/PagePermissionGuard.tsx +8 -64
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
- package/src/rbac/docs/event-based-apps.md +285 -0
- package/src/rbac/errors.ts +11 -0
- package/src/rbac/hooks/useRoleManagement.ts +292 -12
- package/src/rbac/index.ts +30 -0
- package/src/services/OrganisationService.ts +4 -0
- package/src/styles/core.css +5 -5
- package/src/types/database.generated.ts +63 -9
- package/src/types/file-reference.ts +9 -0
- package/src/utils/__tests__/timezone.test.ts +345 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +60 -4
- package/src/utils/file-reference/index.ts +13 -2
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
- package/src/utils/formatting/formatting.ts +179 -0
- package/src/utils/index.ts +27 -1
- package/src/utils/location/index.ts +16 -0
- package/src/utils/location/location.test.ts +286 -0
- package/src/utils/location/location.ts +175 -0
- package/src/utils/security/secureDataAccess.ts +1 -1
- package/src/utils/storage/helpers.ts +68 -0
- package/src/utils/timezone/index.ts +17 -0
- package/src/utils/timezone/timezone.test.ts +349 -0
- package/src/utils/timezone/timezone.ts +281 -0
- package/dist/chunk-CSOFYHAG.js.map +0 -1
- package/dist/chunk-FUEYYMX5.js.map +0 -1
- package/dist/chunk-HKIT6O7W.js +0 -198
- package/dist/chunk-HKIT6O7W.js.map +0 -1
- package/dist/chunk-KUEN3HFB.js +0 -94
- package/dist/chunk-KUEN3HFB.js.map +0 -1
- package/dist/chunk-M7W4CP3M.js.map +0 -1
- package/dist/chunk-MI7HBHN3.js.map +0 -1
- package/dist/chunk-NQPMQGS2.js.map +0 -1
- package/dist/chunk-PWAHJW4G.js.map +0 -1
- package/dist/chunk-UHNYIBXL.js.map +0 -1
- package/dist/chunk-W22JP75J.js.map +0 -1
- package/dist/formatting-5wETwiGF.d.ts +0 -162
- /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
- /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
- /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();
|