@jmruthers/pace-core 0.6.6 → 0.6.8
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/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +227 -22
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +240 -0
- package/audit-tool/audits/03-architecture.cjs +224 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +554 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +355 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +295 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +380 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
- package/cursor-rules/02-project-structure.mdc +37 -5
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
- package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/DataTable-6RMSCQJ6.js +15 -0
- package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-CIGSujI2.d.ts} +40 -24
- package/dist/{UnifiedAuthProvider-ZT6TIGM7.js → UnifiedAuthProvider-7SNDOWYD.js} +2 -2
- package/dist/{api-Y4MQWOFW.js → api-7P7DI652.js} +1 -1
- package/dist/{chunk-MAGBIDNS.js → chunk-4DDCYDQ3.js} +8 -7
- package/dist/{chunk-BVP2BCJF.js → chunk-5W2A3DRC.js} +10 -9
- package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
- package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
- package/dist/{chunk-3O3WHILE.js → chunk-EF2UGZWY.js} +239 -63
- package/dist/{chunk-LAZMKTTF.js → chunk-EURB7QFZ.js} +341 -337
- package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
- package/dist/{chunk-7TYHROIV.js → chunk-GS5672WG.js} +55 -13
- package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
- package/dist/{chunk-ZFYPMX46.js → chunk-LX6U42O3.js} +1 -1
- package/dist/{chunk-FENMYN2U.js → chunk-MPBLMWVR.js} +3 -3
- package/dist/{chunk-ZS5VO5JB.js → chunk-NKHKXPI4.js} +408 -453
- package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
- package/dist/{chunk-4T7OBVTU.js → chunk-S6ZQKDY6.js} +1 -1
- package/dist/{chunk-FTCRZOG2.js → chunk-T5CVK4R3.js} +5 -5
- package/dist/{chunk-OHIK3MIO.js → chunk-Z2FNRKF3.js} +13 -13
- package/dist/components.d.ts +5 -4
- package/dist/components.js +29 -34
- package/dist/eslint-rules/index.cjs +22 -9
- package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
- package/dist/eslint-rules/rules/04-code-quality.cjs +346 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +34 -13
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +385 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/{functions-DHebl8-F.d.ts → functions-lBy5L2ry.d.ts} +1 -1
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +8 -8
- package/dist/index.d.ts +7 -7
- package/dist/index.js +21 -20
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +8 -8
- package/dist/theming/runtime.d.ts +61 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +8 -8
- package/docs/README.md +1 -1
- package/docs/api/modules.md +106 -41
- package/docs/api-reference/components.md +18 -20
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/dependencies.md +23 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/implementation-guides/app-layout.md +1 -1
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +127 -34
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +1 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/README.md +5 -5
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -1
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +241 -185
- package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
- package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +203 -104
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/eslint-config-pace-core.cjs +50 -20
- package/package.json +50 -19
- package/scripts/eslint-audit.cjs +123 -0
- package/scripts/install-cursor-rules.cjs +11 -243
- package/scripts/install-eslint-config.cjs +349 -0
- package/scripts/validate-dependencies.cjs +248 -0
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +30 -18
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +10 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/AddressField/AddressField.tsx +26 -1
- package/src/components/Alert/Alert.test.tsx +86 -22
- package/src/components/Alert/Alert.tsx +19 -11
- package/src/components/Badge/Badge.tsx +1 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/Checkbox/Checkbox.test.tsx +2 -1
- package/src/components/ContextSelector/ContextSelector.tsx +108 -126
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/DataTable.tsx +1 -19
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +6 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +21 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +3 -2
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
- package/src/components/DataTable/components/EditableRow.tsx +5 -7
- package/src/components/DataTable/components/EmptyState.tsx +11 -10
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +45 -27
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/types.ts +0 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +7 -8
- package/src/components/Dialog/Dialog.test.tsx +1 -0
- package/src/components/Dialog/Dialog.tsx +25 -8
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileUpload/FileUpload.test.tsx +45 -16
- package/src/components/FileUpload/FileUpload.tsx +141 -130
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +48 -12
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +9 -9
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +30 -30
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -4
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +7 -1
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +86 -77
- package/src/components/Select/types.ts +3 -0
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +8 -5
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +4 -0
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +99 -99
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +45 -8
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +22 -2
- package/src/hooks/public/usePublicEvent.ts +5 -5
- package/src/hooks/public/usePublicEventLogo.ts +5 -5
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +13 -9
- package/src/hooks/useAddressAutocomplete.test.ts +18 -18
- package/src/hooks/useAppConfig.ts +2 -2
- package/src/hooks/useEventTheme.test.ts +7 -7
- package/src/hooks/useEventTheme.ts +2 -1
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useFileUrl.ts +52 -8
- package/src/hooks/useOrganisationSecurity.test.ts +2 -1
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +38 -38
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +15 -6
- package/src/rbac/__tests__/rbac-functions.test.ts +3 -3
- package/src/rbac/api.test.ts +104 -0
- package/src/rbac/engine.ts +1 -1
- package/src/rbac/hooks/useCan.test.ts +2 -2
- package/src/rbac/secureClient.ts +1 -1
- package/src/rbac/types/functions.ts +1 -1
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +118 -3
- package/src/theming/parseEventColours.ts +77 -11
- package/src/types/supabase.ts +2 -3
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +9 -9
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/file-reference/__tests__/file-reference.test.ts +4 -0
- package/src/utils/formatting/formatDate.test.ts +3 -2
- package/src/utils/formatting/formatDateTime.test.ts +2 -2
- package/src/utils/google-places/googlePlacesUtils.test.ts +36 -24
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/__tests__/helpers.unit.test.ts +19 -12
- package/src/utils/storage/helpers.test.ts +69 -3
- package/cursor-rules/01-standards-compliance.mdc +0 -285
- package/cursor-rules/04-testing-standards.mdc +0 -270
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
- package/cursor-rules/06-code-quality.mdc +0 -311
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
- package/cursor-rules/10-error-handling-patterns.mdc +0 -179
- package/cursor-rules/11-performance-optimization.mdc +0 -169
- package/cursor-rules/12-ci-cd-integration.mdc +0 -150
- package/dist/DataTable-LRJL4IRV.js +0 -15
- package/dist/eslint-rules/rules/compliance.cjs +0 -348
- package/dist/eslint-rules/rules/components.cjs +0 -113
- package/dist/eslint-rules/rules/imports.cjs +0 -102
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -604
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-standards-compliance.md +0 -188
- package/docs/standards/03-solid-principles.md +0 -39
- package/docs/standards/04-testing-standards.md +0 -36
- package/docs/standards/05-bug-reports-and-features.md +0 -27
- package/docs/standards/06-code-quality.md +0 -34
- package/docs/standards/07-tech-stack-compliance.md +0 -30
- package/docs/standards/10-error-handling-patterns.md +0 -401
- package/docs/standards/11-performance-optimization.md +0 -348
- package/docs/standards/12-ci-cd-integration.md +0 -370
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
- package/scripts/audit/audit-compliance.cjs +0 -1295
- package/scripts/audit/audit-components.cjs +0 -260
- package/scripts/audit/audit-rbac.cjs +0 -954
- package/scripts/audit/audit-standards.cjs +0 -1268
- package/scripts/audit/index.cjs +0 -1927
- package/src/components/DataTable/components/DataTableBody.tsx +0 -478
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/index.cjs +0 -22
- package/src/eslint-rules/rules/components.cjs +0 -113
- package/src/eslint-rules/rules/imports.cjs +0 -102
- package/src/eslint-rules/rules/rbac.cjs +0 -790
- package/src/eslint-rules/utils/helpers.cjs +0 -42
- package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard 9: Operations Audit
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Audit/Standard9
|
|
5
|
+
*
|
|
6
|
+
* Audits consuming apps for compliance with Standard 9: Operations.
|
|
7
|
+
* Validates error handling patterns, CI/CD configuration, and performance patterns.
|
|
8
|
+
*
|
|
9
|
+
* Reference: packages/core/docs/standards/9-operations-standards.md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const { findSourceFiles, readFileSafe, getRelativePath, directoryExists } = require('../utils/file-utils.cjs');
|
|
15
|
+
const { getLineNumber, getCodeSnippet, isInCommentOrString } = require('../utils/code-utils.cjs');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check error handling patterns (ApiResult usage)
|
|
19
|
+
*/
|
|
20
|
+
function checkErrorHandlingPatterns(consumingAppPath) {
|
|
21
|
+
const issues = [];
|
|
22
|
+
|
|
23
|
+
const srcDir = path.join(consumingAppPath, 'src');
|
|
24
|
+
if (!directoryExists(srcDir)) {
|
|
25
|
+
return issues;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sourceFiles = findSourceFiles(srcDir);
|
|
29
|
+
|
|
30
|
+
// Check for API functions that don't use ApiResult pattern
|
|
31
|
+
sourceFiles.forEach(filePath => {
|
|
32
|
+
const content = readFileSafe(filePath);
|
|
33
|
+
if (!content) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Skip test files
|
|
38
|
+
if (filePath.includes('.test.') || filePath.includes('.spec.')) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const relativePath = getRelativePath(filePath, consumingAppPath);
|
|
43
|
+
|
|
44
|
+
// Look for async functions that might be API functions
|
|
45
|
+
const asyncFunctionPattern = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*:\s*Promise/gi;
|
|
46
|
+
const matches = [...content.matchAll(asyncFunctionPattern)];
|
|
47
|
+
|
|
48
|
+
matches.forEach(match => {
|
|
49
|
+
const functionName = match[1];
|
|
50
|
+
const functionIndex = match.index;
|
|
51
|
+
|
|
52
|
+
// Skip if it's a hook
|
|
53
|
+
if (functionName.startsWith('use')) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if function uses ApiResult
|
|
58
|
+
const functionBody = content.substring(functionIndex, Math.min(functionIndex + 500, content.length));
|
|
59
|
+
const usesApiResult = /ApiResult|ok:\s*(true|false)/.test(functionBody);
|
|
60
|
+
|
|
61
|
+
if (!usesApiResult) {
|
|
62
|
+
// Check if it's likely an API function (returns Promise, might throw errors)
|
|
63
|
+
const returnsPromise = /:\s*Promise/.test(match[0]);
|
|
64
|
+
const hasErrorHandling = /catch|throw|error/i.test(functionBody);
|
|
65
|
+
|
|
66
|
+
if (returnsPromise && hasErrorHandling) {
|
|
67
|
+
issues.push({
|
|
68
|
+
type: 'errorHandling',
|
|
69
|
+
file: relativePath,
|
|
70
|
+
line: getLineNumber(content, functionIndex),
|
|
71
|
+
message: `API function '${functionName}' does not use ApiResult pattern. Consider using ApiResult<T> for consistent error handling.`,
|
|
72
|
+
code: getCodeSnippet(content, functionIndex),
|
|
73
|
+
severity: 'info',
|
|
74
|
+
fix: 'Use ApiResult<T> type: type ApiResult<T> = { ok: true; data: T } | { ok: false; error: ApiError }',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return issues;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check CI/CD configuration
|
|
86
|
+
*/
|
|
87
|
+
function checkCICDConfig(consumingAppPath) {
|
|
88
|
+
const issues = [];
|
|
89
|
+
|
|
90
|
+
// Check for .github/workflows directory
|
|
91
|
+
const workflowsDir = path.join(consumingAppPath, '.github', 'workflows');
|
|
92
|
+
|
|
93
|
+
if (!directoryExists(workflowsDir)) {
|
|
94
|
+
issues.push({
|
|
95
|
+
type: 'cicd',
|
|
96
|
+
file: '.github/workflows/ (not found)',
|
|
97
|
+
line: 0,
|
|
98
|
+
message: 'CI/CD workflows directory not found. Consider setting up GitHub Actions for automated testing and deployment.',
|
|
99
|
+
severity: 'info',
|
|
100
|
+
fix: 'Create .github/workflows/ directory and add CI/CD workflow files',
|
|
101
|
+
});
|
|
102
|
+
return issues;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check for common workflow files
|
|
106
|
+
const workflowFiles = fs.existsSync(workflowsDir)
|
|
107
|
+
? fs.readdirSync(workflowsDir).filter(file => file.endsWith('.yml') || file.endsWith('.yaml'))
|
|
108
|
+
: [];
|
|
109
|
+
|
|
110
|
+
if (workflowFiles.length === 0) {
|
|
111
|
+
issues.push({
|
|
112
|
+
type: 'cicd',
|
|
113
|
+
file: '.github/workflows/',
|
|
114
|
+
line: 0,
|
|
115
|
+
message: 'No CI/CD workflow files found. Consider setting up automated testing and deployment.',
|
|
116
|
+
severity: 'info',
|
|
117
|
+
fix: 'Create workflow files in .github/workflows/ (e.g., ci.yml, deploy.yml)',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return issues;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check for error boundary usage
|
|
126
|
+
*/
|
|
127
|
+
function checkErrorBoundaries(consumingAppPath) {
|
|
128
|
+
const issues = [];
|
|
129
|
+
|
|
130
|
+
const srcDir = path.join(consumingAppPath, 'src');
|
|
131
|
+
if (!directoryExists(srcDir)) {
|
|
132
|
+
return issues;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const sourceFiles = findSourceFiles(srcDir);
|
|
136
|
+
|
|
137
|
+
// Check if ErrorBoundary is used
|
|
138
|
+
let hasErrorBoundary = false;
|
|
139
|
+
sourceFiles.forEach(filePath => {
|
|
140
|
+
const content = readFileSafe(filePath);
|
|
141
|
+
if (!content) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (/ErrorBoundary|errorBoundary/i.test(content)) {
|
|
146
|
+
hasErrorBoundary = true;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Check main.tsx or App.tsx for ErrorBoundary
|
|
151
|
+
const mainFiles = [
|
|
152
|
+
path.join(consumingAppPath, 'src', 'main.tsx'),
|
|
153
|
+
path.join(consumingAppPath, 'src', 'App.tsx'),
|
|
154
|
+
path.join(consumingAppPath, 'App.tsx'),
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
let hasErrorBoundaryInApp = false;
|
|
158
|
+
mainFiles.forEach(filePath => {
|
|
159
|
+
if (fs.existsSync(filePath)) {
|
|
160
|
+
const content = readFileSafe(filePath);
|
|
161
|
+
if (content && /ErrorBoundary|errorBoundary/i.test(content)) {
|
|
162
|
+
hasErrorBoundaryInApp = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (!hasErrorBoundaryInApp && hasErrorBoundary) {
|
|
168
|
+
issues.push({
|
|
169
|
+
type: 'errorBoundary',
|
|
170
|
+
file: 'src/main.tsx or src/App.tsx',
|
|
171
|
+
line: 1,
|
|
172
|
+
message: 'ErrorBoundary component exists but is not used in main.tsx or App.tsx. Should wrap the app root to catch React errors.',
|
|
173
|
+
severity: 'warning',
|
|
174
|
+
fix: 'Wrap app root with ErrorBoundary in main.tsx or App.tsx',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return issues;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Run audit for Standard 9: Operations
|
|
183
|
+
* @param {string} consumingAppPath - Path to consuming app
|
|
184
|
+
* @returns {object} - Audit results with issues array
|
|
185
|
+
*/
|
|
186
|
+
function runStandard9Audit(consumingAppPath) {
|
|
187
|
+
const issues = [];
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
issues.push(...checkErrorHandlingPatterns(consumingAppPath));
|
|
191
|
+
issues.push(...checkCICDConfig(consumingAppPath));
|
|
192
|
+
issues.push(...checkErrorBoundaries(consumingAppPath));
|
|
193
|
+
} catch (error) {
|
|
194
|
+
return {
|
|
195
|
+
standard: '09-operations',
|
|
196
|
+
issues: [],
|
|
197
|
+
error: error.message,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
standard: '09-operations',
|
|
203
|
+
issues,
|
|
204
|
+
error: null,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = { runStandard9Audit };
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Comprehensive Audit Script for Consuming Apps
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit
|
|
7
|
+
*
|
|
8
|
+
* Audits consuming apps against pace-core standards and generates a markdown report.
|
|
9
|
+
* Organized by the 10-file standards structure (Standards 1-9).
|
|
10
|
+
*
|
|
11
|
+
* This is Layer 4 of the quality enforcement strategy:
|
|
12
|
+
* - Layer 1: Standards Documents (Source of Truth)
|
|
13
|
+
* - Layer 2: Cursor Rules (Real-time Guidance)
|
|
14
|
+
* - Layer 3: ESLint (Fast, Local Static Analysis)
|
|
15
|
+
* - Layer 4: Audit Tool (Deep, System-Level Analysis) ← You are here
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node packages/core/audit-tool/index.cjs [path-to-consuming-app] [--output report.md]
|
|
19
|
+
* npm run audit:pace-core [path-to-consuming-app] [--output report.md]
|
|
20
|
+
*
|
|
21
|
+
* If no path provided, assumes current directory is consuming app.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
// Import dependency audit (runs before standards)
|
|
28
|
+
const { runDependencyAudit } = require('./00-dependencies.cjs');
|
|
29
|
+
|
|
30
|
+
// Import new standard-aligned audit modules
|
|
31
|
+
const { runStandard1Audit } = require('./audits/01-pace-core-compliance.cjs');
|
|
32
|
+
const { runStandard2Audit } = require('./audits/02-project-structure.cjs');
|
|
33
|
+
const { runStandard3Audit } = require('./audits/03-architecture.cjs');
|
|
34
|
+
const { runStandard4Audit } = require('./audits/04-code-quality.cjs');
|
|
35
|
+
const { runStandard5Audit } = require('./audits/05-styling.cjs');
|
|
36
|
+
const { runStandard6Audit } = require('./audits/06-security-rbac.cjs');
|
|
37
|
+
const { runStandard7Audit } = require('./audits/07-api-tech-stack.cjs');
|
|
38
|
+
const { runStandard8Audit } = require('./audits/08-testing-documentation.cjs');
|
|
39
|
+
const { runStandard9Audit } = require('./audits/09-operations.cjs');
|
|
40
|
+
|
|
41
|
+
// Import report utilities
|
|
42
|
+
const { generateMarkdownReport, generateSummary } = require('./utils/report-utils.cjs');
|
|
43
|
+
|
|
44
|
+
// Colors for terminal output
|
|
45
|
+
const colors = {
|
|
46
|
+
reset: '\x1b[0m',
|
|
47
|
+
red: '\x1b[31m',
|
|
48
|
+
green: '\x1b[32m',
|
|
49
|
+
yellow: '\x1b[33m',
|
|
50
|
+
blue: '\x1b[34m',
|
|
51
|
+
cyan: '\x1b[36m',
|
|
52
|
+
bold: '\x1b[1m',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Run all standard audits
|
|
57
|
+
*/
|
|
58
|
+
function runAllAudits(consumingAppPath, showProgress = false) {
|
|
59
|
+
const results = {};
|
|
60
|
+
|
|
61
|
+
const standardNames = {
|
|
62
|
+
'01-pace-core-compliance': 'pace-core Compliance',
|
|
63
|
+
'02-project-structure': 'Project Structure',
|
|
64
|
+
'03-architecture': 'Architecture',
|
|
65
|
+
'04-code-quality': 'Code Quality',
|
|
66
|
+
'05-styling': 'Styling',
|
|
67
|
+
'06-security-rbac': 'Security & RBAC',
|
|
68
|
+
'07-api-tech-stack': 'API & Tech Stack',
|
|
69
|
+
'08-testing-documentation': 'Testing & Documentation',
|
|
70
|
+
'09-operations': 'Operations',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const auditFunctions = [
|
|
74
|
+
{ key: '01-pace-core-compliance', name: standardNames['01-pace-core-compliance'], fn: runStandard1Audit },
|
|
75
|
+
{ key: '02-project-structure', name: standardNames['02-project-structure'], fn: runStandard2Audit },
|
|
76
|
+
{ key: '03-architecture', name: standardNames['03-architecture'], fn: runStandard3Audit },
|
|
77
|
+
{ key: '04-code-quality', name: standardNames['04-code-quality'], fn: runStandard4Audit },
|
|
78
|
+
{ key: '05-styling', name: standardNames['05-styling'], fn: runStandard5Audit },
|
|
79
|
+
{ key: '06-security-rbac', name: standardNames['06-security-rbac'], fn: runStandard6Audit },
|
|
80
|
+
{ key: '07-api-tech-stack', name: standardNames['07-api-tech-stack'], fn: runStandard7Audit },
|
|
81
|
+
{ key: '08-testing-documentation', name: standardNames['08-testing-documentation'], fn: runStandard8Audit },
|
|
82
|
+
{ key: '09-operations', name: standardNames['09-operations'], fn: runStandard9Audit },
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
auditFunctions.forEach(({ key, name, fn }) => {
|
|
86
|
+
if (showProgress) {
|
|
87
|
+
console.log(`${colors.blue}Running ${name} audit...${colors.reset}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const result = fn(consumingAppPath);
|
|
92
|
+
results[key] = result;
|
|
93
|
+
|
|
94
|
+
if (result.error && showProgress) {
|
|
95
|
+
console.warn(`${colors.yellow}Warning: ${name} audit failed: ${result.error}${colors.reset}`);
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (showProgress) {
|
|
99
|
+
console.warn(`${colors.yellow}Warning: ${name} audit encountered an error: ${error.message}${colors.reset}`);
|
|
100
|
+
}
|
|
101
|
+
results[key] = {
|
|
102
|
+
standard: key,
|
|
103
|
+
issues: [],
|
|
104
|
+
error: error.message,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return results;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Main function
|
|
114
|
+
*/
|
|
115
|
+
function main() {
|
|
116
|
+
const args = process.argv.slice(2);
|
|
117
|
+
const outputArg = args.find(arg => arg.startsWith('--output'));
|
|
118
|
+
const outputPath = outputArg ? (outputArg.includes('=') ? outputArg.split('=')[1] : args[args.indexOf(outputArg) + 1] || 'audit-report.md') : null;
|
|
119
|
+
const consumingAppPath = args.find(arg => !arg.startsWith('--') && arg !== outputPath) || process.cwd();
|
|
120
|
+
|
|
121
|
+
console.log(`${colors.bold}${colors.cyan}pace-core Comprehensive Audit${colors.reset}\n`);
|
|
122
|
+
console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
|
|
123
|
+
|
|
124
|
+
// Get project info from package.json
|
|
125
|
+
const packageJsonPath = path.join(consumingAppPath, 'package.json');
|
|
126
|
+
let projectName = 'unknown';
|
|
127
|
+
let paceCoreVersion = 'unknown';
|
|
128
|
+
|
|
129
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
130
|
+
try {
|
|
131
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
132
|
+
projectName = packageJson.name || 'unknown';
|
|
133
|
+
|
|
134
|
+
// Find pace-core version
|
|
135
|
+
const allDeps = {
|
|
136
|
+
...(packageJson.dependencies || {}),
|
|
137
|
+
...(packageJson.devDependencies || {}),
|
|
138
|
+
};
|
|
139
|
+
paceCoreVersion = allDeps['@jmruthers/pace-core'] || 'not found in package.json';
|
|
140
|
+
} catch (error) {
|
|
141
|
+
// Skip if package.json can't be parsed
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(`Project: ${colors.bold}${projectName}${colors.reset}`);
|
|
146
|
+
console.log(`pace-core: ${colors.bold}${paceCoreVersion}${colors.reset}\n`);
|
|
147
|
+
|
|
148
|
+
// Run dependency audit
|
|
149
|
+
console.log(`${colors.blue}Running dependency audit...${colors.reset}`);
|
|
150
|
+
const dependencyResult = runDependencyAudit(consumingAppPath);
|
|
151
|
+
|
|
152
|
+
// Run all standard audits
|
|
153
|
+
const standardResults = runAllAudits(consumingAppPath, true);
|
|
154
|
+
|
|
155
|
+
// Combine results
|
|
156
|
+
const allResults = {
|
|
157
|
+
...standardResults,
|
|
158
|
+
dependencies: dependencyResult,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Generate summary
|
|
162
|
+
const summary = generateSummary(standardResults);
|
|
163
|
+
|
|
164
|
+
// Display audit results summary
|
|
165
|
+
console.log(`\n${colors.bold}Audit Results:${colors.reset}\n`);
|
|
166
|
+
|
|
167
|
+
// Display dependency audit first
|
|
168
|
+
if (dependencyResult.error) {
|
|
169
|
+
console.log(` ${colors.red}❌ Dependency Audit: Error - ${dependencyResult.error}${colors.reset}`);
|
|
170
|
+
} else {
|
|
171
|
+
const depIssues = dependencyResult.issues || {};
|
|
172
|
+
const depCount = (depIssues.includedDeps?.length || 0) +
|
|
173
|
+
(depIssues.missingRequired?.length || 0) +
|
|
174
|
+
(depIssues.versionIssues?.length || 0) +
|
|
175
|
+
(depIssues.wrongLocation?.length || 0) +
|
|
176
|
+
(depIssues.missingDevDeps?.length || 0) +
|
|
177
|
+
(depIssues.devVersionIssues?.length || 0);
|
|
178
|
+
|
|
179
|
+
if (depCount === 0) {
|
|
180
|
+
console.log(` ${colors.green}✅ Dependency Audit: 0 issues${colors.reset}`);
|
|
181
|
+
} else {
|
|
182
|
+
console.log(` ${colors.red}❌ Dependency Audit: ${depCount} issue(s)${colors.reset}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Display each standard
|
|
187
|
+
Object.entries(standardResults).forEach(([key, result]) => {
|
|
188
|
+
const standardNames = {
|
|
189
|
+
'01-pace-core-compliance': 'pace-core Compliance',
|
|
190
|
+
'02-project-structure': 'Project Structure',
|
|
191
|
+
'03-architecture': 'Architecture',
|
|
192
|
+
'04-code-quality': 'Code Quality',
|
|
193
|
+
'05-styling': 'Styling',
|
|
194
|
+
'06-security-rbac': 'Security & RBAC',
|
|
195
|
+
'07-api-tech-stack': 'API & Tech Stack',
|
|
196
|
+
'08-testing-documentation': 'Testing & Documentation',
|
|
197
|
+
'09-operations': 'Operations',
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const name = standardNames[key] || key;
|
|
201
|
+
const issues = Array.isArray(result.issues) ? result.issues : Object.values(result.issues || {}).flat();
|
|
202
|
+
const count = issues.length;
|
|
203
|
+
|
|
204
|
+
if (count === 0) {
|
|
205
|
+
console.log(` ${colors.green}✅ ${name}: 0 issues${colors.reset}`);
|
|
206
|
+
} else {
|
|
207
|
+
console.log(` ${colors.red}❌ ${name}: ${count} issue(s)${colors.reset}`);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Total summary
|
|
212
|
+
const totalIssues = summary.total + (dependencyResult.error ? 0 :
|
|
213
|
+
((dependencyResult.issues?.includedDeps?.length || 0) +
|
|
214
|
+
(dependencyResult.issues?.missingRequired?.length || 0) +
|
|
215
|
+
(dependencyResult.issues?.versionIssues?.length || 0) +
|
|
216
|
+
(dependencyResult.issues?.wrongLocation?.length || 0) +
|
|
217
|
+
(dependencyResult.issues?.missingDevDeps?.length || 0) +
|
|
218
|
+
(dependencyResult.issues?.devVersionIssues?.length || 0)));
|
|
219
|
+
|
|
220
|
+
console.log(`\n${colors.bold}Total Issues: ${totalIssues === 0 ? colors.green + '0 (All checks passed!)' : colors.red + totalIssues}${colors.reset}\n`);
|
|
221
|
+
|
|
222
|
+
// Generate and save markdown report
|
|
223
|
+
const markdownReport = generateMarkdownReport(standardResults, consumingAppPath, dependencyResult);
|
|
224
|
+
|
|
225
|
+
// Generate timestamp in yyyymmddHHMM format
|
|
226
|
+
const now = new Date();
|
|
227
|
+
const year = now.getFullYear();
|
|
228
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
229
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
230
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
231
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
232
|
+
const timestamp = `${year}${month}${day}${hours}${minutes}`;
|
|
233
|
+
|
|
234
|
+
// Helper function to add timestamp to filename
|
|
235
|
+
function addTimestampToFilename(filePath) {
|
|
236
|
+
const dir = path.dirname(filePath);
|
|
237
|
+
const ext = path.extname(filePath);
|
|
238
|
+
const name = path.basename(filePath, ext);
|
|
239
|
+
return path.join(dir, `${name}-${timestamp}${ext}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Determine report path
|
|
243
|
+
let reportPath;
|
|
244
|
+
if (outputPath) {
|
|
245
|
+
reportPath = addTimestampToFilename(path.join(consumingAppPath, outputPath));
|
|
246
|
+
} else {
|
|
247
|
+
// Default: save to audit/ directory
|
|
248
|
+
const auditDir = path.join(consumingAppPath, 'audit');
|
|
249
|
+
if (!fs.existsSync(auditDir)) {
|
|
250
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
251
|
+
}
|
|
252
|
+
reportPath = path.join(auditDir, `pace-core-audit-${timestamp}.md`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Save report
|
|
256
|
+
fs.writeFileSync(reportPath, markdownReport, 'utf8');
|
|
257
|
+
|
|
258
|
+
// Display report location
|
|
259
|
+
const relativeReportPath = path.relative(consumingAppPath, reportPath);
|
|
260
|
+
console.log(`${colors.bold}Report saved to:${colors.reset} ${colors.cyan}${relativeReportPath}${colors.reset}\n`);
|
|
261
|
+
|
|
262
|
+
// Exit with error code if issues found
|
|
263
|
+
process.exit(totalIssues > 0 ? 1 : 0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Export for programmatic usage
|
|
267
|
+
module.exports = {
|
|
268
|
+
runAudit: main,
|
|
269
|
+
runAllAudits,
|
|
270
|
+
runStandardAudit: (standardNumber, consumingAppPath) => {
|
|
271
|
+
const auditFunctions = {
|
|
272
|
+
'1': runStandard1Audit,
|
|
273
|
+
'2': runStandard2Audit,
|
|
274
|
+
'3': runStandard3Audit,
|
|
275
|
+
'4': runStandard4Audit,
|
|
276
|
+
'5': runStandard5Audit,
|
|
277
|
+
'6': runStandard6Audit,
|
|
278
|
+
'7': runStandard7Audit,
|
|
279
|
+
'8': runStandard8Audit,
|
|
280
|
+
'9': runStandard9Audit,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const fn = auditFunctions[standardNumber];
|
|
284
|
+
if (!fn) {
|
|
285
|
+
throw new Error(`Invalid standard number: ${standardNumber}. Must be 1-9.`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return fn(consumingAppPath);
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// Run if called directly
|
|
293
|
+
if (require.main === module) {
|
|
294
|
+
main();
|
|
295
|
+
}
|