@jterrats/smart-deployment 1.0.5 → 1.1.0
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/README.md +3 -2
- package/lib/ai/wave-validation-prompt.d.ts +2 -0
- package/lib/ai/wave-validation-prompt.js +56 -0
- package/lib/ai/wave-validation-prompt.js.map +1 -0
- package/lib/ai/wave-validation-report.d.ts +2 -0
- package/lib/ai/wave-validation-report.js +96 -0
- package/lib/ai/wave-validation-report.js.map +1 -0
- package/lib/ai/wave-validation-response-parser.d.ts +8 -0
- package/lib/ai/wave-validation-response-parser.js +112 -0
- package/lib/ai/wave-validation-response-parser.js.map +1 -0
- package/lib/ai/wave-validation-result-synthesis.d.ts +6 -0
- package/lib/ai/wave-validation-result-synthesis.js +49 -0
- package/lib/ai/wave-validation-result-synthesis.js.map +1 -0
- package/lib/ai/wave-validation-service.d.ts +1 -55
- package/lib/ai/wave-validation-service.js +11 -359
- package/lib/ai/wave-validation-service.js.map +1 -1
- package/lib/ai/wave-validation-transport.d.ts +7 -0
- package/lib/ai/wave-validation-transport.js +10 -0
- package/lib/ai/wave-validation-transport.js.map +1 -0
- package/lib/analysis/analysis-reporter.d.ts +29 -0
- package/lib/analysis/analysis-reporter.js +84 -1
- package/lib/analysis/analysis-reporter.js.map +1 -1
- package/lib/analysis/analyze-context-service.d.ts +1 -1
- package/lib/analysis/analyze-context-service.js +2 -0
- package/lib/analysis/analyze-context-service.js.map +1 -1
- package/lib/analysis/project-analysis-service.d.ts +2 -0
- package/lib/analysis/project-analysis-service.js +2 -0
- package/lib/analysis/project-analysis-service.js.map +1 -1
- package/lib/commands/start.d.ts +1 -0
- package/lib/commands/start.js +14 -1
- package/lib/commands/start.js.map +1 -1
- package/lib/commands/status.d.ts +2 -0
- package/lib/commands/status.js +1 -0
- package/lib/commands/status.js.map +1 -1
- package/lib/commands/validate.d.ts +3 -3
- package/lib/commands/validate.js +4 -4
- package/lib/commands/validate.js.map +1 -1
- package/lib/constants/api-version.d.ts +2 -2
- package/lib/constants/api-version.js +2 -2
- package/lib/constants/deployment-order.js +11 -8
- package/lib/constants/deployment-order.js.map +1 -1
- package/lib/dependencies/circular-dependency-detector.d.ts +1 -31
- package/lib/dependencies/circular-dependency-detector.js +7 -156
- package/lib/dependencies/circular-dependency-detector.js.map +1 -1
- package/lib/dependencies/cycle-break-suggestions.d.ts +3 -0
- package/lib/dependencies/cycle-break-suggestions.js +63 -0
- package/lib/dependencies/cycle-break-suggestions.js.map +1 -0
- package/lib/dependencies/cycle-discovery.d.ts +16 -0
- package/lib/dependencies/cycle-discovery.js +70 -0
- package/lib/dependencies/cycle-discovery.js.map +1 -0
- package/lib/dependencies/dependency-graph-builder.d.ts +0 -65
- package/lib/dependencies/dependency-graph-builder.js +19 -233
- package/lib/dependencies/dependency-graph-builder.js.map +1 -1
- package/lib/dependencies/dependency-graph-intake.d.ts +17 -0
- package/lib/dependencies/dependency-graph-intake.js +20 -0
- package/lib/dependencies/dependency-graph-intake.js.map +1 -0
- package/lib/dependencies/dependency-graph-stats.d.ts +4 -0
- package/lib/dependencies/dependency-graph-stats.js +82 -0
- package/lib/dependencies/dependency-graph-stats.js.map +1 -0
- package/lib/dependencies/dependency-graph-validation.d.ts +16 -0
- package/lib/dependencies/dependency-graph-validation.js +78 -0
- package/lib/dependencies/dependency-graph-validation.js.map +1 -0
- package/lib/dependencies/dependency-impact-analyzer.d.ts +1 -22
- package/lib/dependencies/dependency-impact-analyzer.js +12 -110
- package/lib/dependencies/dependency-impact-analyzer.js.map +1 -1
- package/lib/dependencies/dependency-impact-scoring.d.ts +5 -0
- package/lib/dependencies/dependency-impact-scoring.js +24 -0
- package/lib/dependencies/dependency-impact-scoring.js.map +1 -0
- package/lib/dependencies/dependency-impact-traversal.d.ts +18 -0
- package/lib/dependencies/dependency-impact-traversal.js +74 -0
- package/lib/dependencies/dependency-impact-traversal.js.map +1 -0
- package/lib/dependencies/dependency-resolution-classifier.d.ts +19 -0
- package/lib/dependencies/dependency-resolution-classifier.js +67 -0
- package/lib/dependencies/dependency-resolution-classifier.js.map +1 -0
- package/lib/dependencies/dependency-resolver.d.ts +1 -48
- package/lib/dependencies/dependency-resolver.js +12 -181
- package/lib/dependencies/dependency-resolver.js.map +1 -1
- package/lib/dependencies/dynamic-query-dependency-references.d.ts +3 -0
- package/lib/dependencies/dynamic-query-dependency-references.js +41 -0
- package/lib/dependencies/dynamic-query-dependency-references.js.map +1 -0
- package/lib/dependencies/topological-dependency-sorter.d.ts +10 -0
- package/lib/dependencies/topological-dependency-sorter.js +84 -0
- package/lib/dependencies/topological-dependency-sorter.js.map +1 -0
- package/lib/deployment/cycle-remediation-runner.d.ts +1 -0
- package/lib/deployment/cycle-remediation-runner.js +5 -1
- package/lib/deployment/cycle-remediation-runner.js.map +1 -1
- package/lib/deployment/deployment-runner.d.ts +3 -1
- package/lib/deployment/deployment-runner.js +6 -1
- package/lib/deployment/deployment-runner.js.map +1 -1
- package/lib/deployment/deployment-status-service.d.ts +2 -0
- package/lib/deployment/deployment-status-service.js +2 -0
- package/lib/deployment/deployment-status-service.js.map +1 -1
- package/lib/deployment/deployment-validation-service.js +1 -1
- package/lib/deployment/deployment-validation-service.js.map +1 -1
- package/lib/deployment/dynamic-query-target-validator.d.ts +24 -0
- package/lib/deployment/dynamic-query-target-validator.js +65 -0
- package/lib/deployment/dynamic-query-target-validator.js.map +1 -0
- package/lib/deployment/sf-cli-integration.d.ts +1 -0
- package/lib/deployment/sf-cli-integration.js +1 -1
- package/lib/deployment/sf-cli-integration.js.map +1 -1
- package/lib/deployment/sf-cli-metadata-lookup.d.ts +4 -0
- package/lib/deployment/sf-cli-metadata-lookup.js +26 -0
- package/lib/deployment/sf-cli-metadata-lookup.js.map +1 -0
- package/lib/deployment/start-execution-service.d.ts +5 -0
- package/lib/deployment/start-execution-service.js +26 -6
- package/lib/deployment/start-execution-service.js.map +1 -1
- package/lib/deployment/wave-graph-state.d.ts +6 -0
- package/lib/deployment/wave-graph-state.js +89 -0
- package/lib/deployment/wave-graph-state.js.map +1 -0
- package/lib/deployment/wave-manifest-service.d.ts +1 -0
- package/lib/deployment/wave-manifest-service.js +1 -1
- package/lib/deployment/wave-manifest-service.js.map +1 -1
- package/lib/errors/deployment-error.js +1 -1
- package/lib/errors/deployment-error.js.map +1 -1
- package/lib/parsers/apex-class-dependencies.d.ts +4 -0
- package/lib/parsers/apex-class-dependencies.js +122 -0
- package/lib/parsers/apex-class-dependencies.js.map +1 -0
- package/lib/parsers/apex-class-lexical.d.ts +3 -0
- package/lib/parsers/apex-class-lexical.js +101 -0
- package/lib/parsers/apex-class-lexical.js.map +1 -0
- package/lib/parsers/apex-class-names.d.ts +18 -0
- package/lib/parsers/apex-class-names.js +93 -0
- package/lib/parsers/apex-class-names.js.map +1 -0
- package/lib/parsers/apex-class-parser-model.d.ts +59 -0
- package/lib/parsers/apex-class-parser-model.js +2 -0
- package/lib/parsers/apex-class-parser-model.js.map +1 -0
- package/lib/parsers/apex-class-parser.d.ts +3 -29
- package/lib/parsers/apex-class-parser.js +10 -395
- package/lib/parsers/apex-class-parser.js.map +1 -1
- package/lib/parsers/apex-class-symbols.d.ts +2 -0
- package/lib/parsers/apex-class-symbols.js +61 -0
- package/lib/parsers/apex-class-symbols.js.map +1 -0
- package/lib/parsers/apex-dynamic-query-analysis.d.ts +2 -0
- package/lib/parsers/apex-dynamic-query-analysis.js +150 -0
- package/lib/parsers/apex-dynamic-query-analysis.js.map +1 -0
- package/lib/parsers/custom-metadata-dynamic-query-analysis.d.ts +2 -0
- package/lib/parsers/custom-metadata-dynamic-query-analysis.js +50 -0
- package/lib/parsers/custom-metadata-dynamic-query-analysis.js.map +1 -0
- package/lib/parsers/custom-metadata-parser.d.ts +2 -0
- package/lib/parsers/custom-metadata-parser.js +3 -0
- package/lib/parsers/custom-metadata-parser.js.map +1 -1
- package/lib/parsers/custom-object-dependency-builder.d.ts +3 -0
- package/lib/parsers/custom-object-dependency-builder.js +103 -0
- package/lib/parsers/custom-object-dependency-builder.js.map +1 -0
- package/lib/parsers/custom-object-parser.js +1 -208
- package/lib/parsers/custom-object-parser.js.map +1 -1
- package/lib/parsers/custom-object-reference-helpers.d.ts +4 -0
- package/lib/parsers/custom-object-reference-helpers.js +92 -0
- package/lib/parsers/custom-object-reference-helpers.js.map +1 -0
- package/lib/parsers/dynamic-query-reference.d.ts +16 -0
- package/lib/parsers/dynamic-query-reference.js +65 -0
- package/lib/parsers/dynamic-query-reference.js.map +1 -0
- package/lib/parsers/layout-action-analysis.d.ts +6 -0
- package/lib/parsers/layout-action-analysis.js +36 -0
- package/lib/parsers/layout-action-analysis.js.map +1 -0
- package/lib/parsers/layout-parser.js +4 -272
- package/lib/parsers/layout-parser.js.map +1 -1
- package/lib/parsers/layout-reference-analysis.d.ts +9 -0
- package/lib/parsers/layout-reference-analysis.js +70 -0
- package/lib/parsers/layout-reference-analysis.js.map +1 -0
- package/lib/parsers/layout-result-assembly.d.ts +5 -0
- package/lib/parsers/layout-result-assembly.js +35 -0
- package/lib/parsers/layout-result-assembly.js.map +1 -0
- package/lib/parsers/layout-section-analysis.d.ts +7 -0
- package/lib/parsers/layout-section-analysis.js +9 -0
- package/lib/parsers/layout-section-analysis.js.map +1 -0
- package/lib/parsers/lwc-code-analysis.d.ts +10 -0
- package/lib/parsers/lwc-code-analysis.js +161 -0
- package/lib/parsers/lwc-code-analysis.js.map +1 -0
- package/lib/parsers/lwc-metadata-analysis.d.ts +6 -0
- package/lib/parsers/lwc-metadata-analysis.js +112 -0
- package/lib/parsers/lwc-metadata-analysis.js.map +1 -0
- package/lib/parsers/lwc-parser.d.ts +3 -27
- package/lib/parsers/lwc-parser.js +6 -377
- package/lib/parsers/lwc-parser.js.map +1 -1
- package/lib/parsers/lwc-result-assembly.d.ts +4 -0
- package/lib/parsers/lwc-result-assembly.js +47 -0
- package/lib/parsers/lwc-result-assembly.js.map +1 -0
- package/lib/presentation/status-command-presenter.d.ts +1 -0
- package/lib/presentation/status-command-presenter.js +20 -0
- package/lib/presentation/status-command-presenter.js.map +1 -1
- package/lib/scanner/forceignore-parser.d.ts +1 -0
- package/lib/scanner/forceignore-parser.js +14 -3
- package/lib/scanner/forceignore-parser.js.map +1 -1
- package/lib/scanner/structure-validator.js +1 -1
- package/lib/services/metadata-scanner-service.d.ts +2 -0
- package/lib/services/metadata-scanner-service.js +9 -2
- package/lib/services/metadata-scanner-service.js.map +1 -1
- package/lib/services/scanners/additional-metadata-scanner.d.ts +2 -0
- package/lib/services/scanners/additional-metadata-scanner.js +183 -0
- package/lib/services/scanners/additional-metadata-scanner.js.map +1 -0
- package/lib/services/scanners/automation-ai-metadata-scanner.d.ts +1 -0
- package/lib/services/scanners/automation-ai-metadata-scanner.js +11 -0
- package/lib/services/scanners/automation-ai-metadata-scanner.js.map +1 -1
- package/lib/services/scanners/code-metadata-scanner.js +14 -1
- package/lib/services/scanners/code-metadata-scanner.js.map +1 -1
- package/lib/services/scanners/data-metadata-scanner.js +24 -8
- package/lib/services/scanners/data-metadata-scanner.js.map +1 -1
- package/lib/types/metadata.d.ts +1 -1
- package/lib/utils/cache-entry-serializer.d.ts +7 -0
- package/lib/utils/cache-entry-serializer.js +15 -0
- package/lib/utils/cache-entry-serializer.js.map +1 -0
- package/lib/utils/cache-expiry-policy.d.ts +7 -0
- package/lib/utils/cache-expiry-policy.js +34 -0
- package/lib/utils/cache-expiry-policy.js.map +1 -0
- package/lib/utils/cache-key-derivation.d.ts +11 -0
- package/lib/utils/cache-key-derivation.js +28 -0
- package/lib/utils/cache-key-derivation.js.map +1 -0
- package/lib/utils/cache-lock-lifecycle.d.ts +15 -0
- package/lib/utils/cache-lock-lifecycle.js +84 -0
- package/lib/utils/cache-lock-lifecycle.js.map +1 -0
- package/lib/utils/cache-logger.d.ts +8 -0
- package/lib/utils/cache-logger.js +19 -0
- package/lib/utils/cache-logger.js.map +1 -0
- package/lib/utils/cache-manager.d.ts +1 -2
- package/lib/utils/cache-manager.js +7 -253
- package/lib/utils/cache-manager.js.map +1 -1
- package/lib/utils/cache-storage.d.ts +20 -0
- package/lib/utils/cache-storage.js +83 -0
- package/lib/utils/cache-storage.js.map +1 -0
- package/lib/validators/xml-metadata-validator.js +1 -1
- package/lib/waves/priority-wave-generator.js +8 -2
- package/lib/waves/priority-wave-generator.js.map +1 -1
- package/lib/waves/test-optimizer-discovery.d.ts +12 -0
- package/lib/waves/test-optimizer-discovery.js +46 -0
- package/lib/waves/test-optimizer-discovery.js.map +1 -0
- package/lib/waves/test-optimizer-matching.d.ts +6 -0
- package/lib/waves/test-optimizer-matching.js +43 -0
- package/lib/waves/test-optimizer-matching.js.map +1 -0
- package/lib/waves/test-optimizer-model.d.ts +92 -0
- package/lib/waves/test-optimizer-model.js +2 -0
- package/lib/waves/test-optimizer-model.js.map +1 -0
- package/lib/waves/test-optimizer-scoring.d.ts +11 -0
- package/lib/waves/test-optimizer-scoring.js +53 -0
- package/lib/waves/test-optimizer-scoring.js.map +1 -0
- package/lib/waves/test-optimizer.d.ts +2 -102
- package/lib/waves/test-optimizer.js +9 -154
- package/lib/waves/test-optimizer.js.map +1 -1
- package/lib/waves/wave-builder.d.ts +0 -11
- package/lib/waves/wave-builder.js +19 -193
- package/lib/waves/wave-builder.js.map +1 -1
- package/lib/waves/wave-graph.d.ts +25 -0
- package/lib/waves/wave-graph.js +138 -0
- package/lib/waves/wave-graph.js.map +1 -0
- package/lib/waves/wave-metadata.d.ts +4 -0
- package/lib/waves/wave-metadata.js +36 -0
- package/lib/waves/wave-metadata.js.map +1 -0
- package/lib/waves/wave-priority-policy.d.ts +5 -0
- package/lib/waves/wave-priority-policy.js +83 -0
- package/lib/waves/wave-priority-policy.js.map +1 -0
- package/lib/waves/wave-topology.d.ts +28 -0
- package/lib/waves/wave-topology.js +65 -0
- package/lib/waves/wave-topology.js.map +1 -0
- package/messages/start.json +5 -5
- package/messages/validate.json +1 -1
- package/npm-shrinkwrap.json +342 -751
- package/oclif.lock +83 -262
- package/oclif.manifest.json +7 -7
- package/package.json +6 -2
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* @issue #30
|
|
13
13
|
*/
|
|
14
14
|
import { getLogger } from '../utils/logger.js';
|
|
15
|
-
import {
|
|
15
|
+
import { generateBreakSuggestions } from './cycle-break-suggestions.js';
|
|
16
|
+
import { CycleDiscovery } from './cycle-discovery.js';
|
|
16
17
|
const logger = getLogger('CircularDependencyDetector');
|
|
17
18
|
/**
|
|
18
19
|
* Circular Dependency Detector
|
|
@@ -34,6 +35,7 @@ export class CircularDependencyDetector {
|
|
|
34
35
|
graph;
|
|
35
36
|
options;
|
|
36
37
|
ignoredEdges;
|
|
38
|
+
discovery;
|
|
37
39
|
constructor(graph, options = {}) {
|
|
38
40
|
this.graph = graph;
|
|
39
41
|
this.options = {
|
|
@@ -43,80 +45,12 @@ export class CircularDependencyDetector {
|
|
|
43
45
|
};
|
|
44
46
|
// Create set of ignored edges for O(1) lookup
|
|
45
47
|
this.ignoredEdges = new Set(this.options.ignoreEdges.map(({ from, to }) => `${from}->${to}`));
|
|
48
|
+
this.discovery = new CycleDiscovery(this.graph, this.options.maxDepth, this.ignoredEdges);
|
|
46
49
|
logger.debug('Initialized CircularDependencyDetector', {
|
|
47
50
|
nodes: this.graph.size,
|
|
48
51
|
ignoredEdges: this.ignoredEdges.size,
|
|
49
52
|
});
|
|
50
53
|
}
|
|
51
|
-
// Private static helper methods
|
|
52
|
-
/**
|
|
53
|
-
* Calculate priority for breaking an edge
|
|
54
|
-
* Higher = better candidate to break
|
|
55
|
-
*/
|
|
56
|
-
static calculateBreakPriority(from, to) {
|
|
57
|
-
let priority = 50; // Base priority
|
|
58
|
-
// Test classes are good candidates to break (they can be deployed separately)
|
|
59
|
-
if (from.includes('Test') || to.includes('Test')) {
|
|
60
|
-
priority += 30;
|
|
61
|
-
}
|
|
62
|
-
// Utility/helper classes are good candidates
|
|
63
|
-
if (CircularDependencyDetector.isUtilityClass(from) || CircularDependencyDetector.isUtilityClass(to)) {
|
|
64
|
-
priority += 20;
|
|
65
|
-
}
|
|
66
|
-
// Handler -> Service edges are typically safe to break
|
|
67
|
-
if (from.includes('Handler') && to.includes('Service')) {
|
|
68
|
-
priority += 15;
|
|
69
|
-
}
|
|
70
|
-
// Controller -> Service edges can be broken
|
|
71
|
-
if (from.includes('Controller') && to.includes('Service')) {
|
|
72
|
-
priority += 15;
|
|
73
|
-
}
|
|
74
|
-
// Trigger -> Handler edges should not be broken (tightly coupled)
|
|
75
|
-
if (from.includes('Trigger') && to.includes('Handler')) {
|
|
76
|
-
priority -= 20;
|
|
77
|
-
}
|
|
78
|
-
// Core domain classes should not be broken if possible
|
|
79
|
-
if (CircularDependencyDetector.isCoreDomainClass(from) && CircularDependencyDetector.isCoreDomainClass(to)) {
|
|
80
|
-
priority -= 15;
|
|
81
|
-
}
|
|
82
|
-
return Math.max(0, Math.min(100, priority));
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Get human-readable reason for break suggestion
|
|
86
|
-
*/
|
|
87
|
-
static getBreakReason(from, to, priority) {
|
|
88
|
-
if (priority >= 80) {
|
|
89
|
-
return `High priority: ${from} → ${to} is a test or utility dependency`;
|
|
90
|
-
}
|
|
91
|
-
else if (priority >= 65) {
|
|
92
|
-
return `Medium priority: ${from} → ${to} can be broken safely`;
|
|
93
|
-
}
|
|
94
|
-
else if (priority >= 50) {
|
|
95
|
-
return `Low priority: ${from} → ${to} may be tightly coupled`;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
return `Not recommended: ${from} → ${to} appears to be core business logic`;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Check if a class is a utility/helper class
|
|
103
|
-
*/
|
|
104
|
-
static isUtilityClass(nodeId) {
|
|
105
|
-
const name = nodeId.toLowerCase();
|
|
106
|
-
return name.includes('util') || name.includes('helper') || name.includes('constant') || name.includes('logger');
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Check if a class is core domain logic
|
|
110
|
-
*/
|
|
111
|
-
static isCoreDomainClass(nodeId) {
|
|
112
|
-
const name = nodeId.toLowerCase();
|
|
113
|
-
// Core classes typically don't have suffixes like Test, Handler, etc.
|
|
114
|
-
return (!name.includes('test') &&
|
|
115
|
-
!name.includes('handler') &&
|
|
116
|
-
!name.includes('controller') &&
|
|
117
|
-
!name.includes('util') &&
|
|
118
|
-
!name.includes('helper'));
|
|
119
|
-
}
|
|
120
54
|
/**
|
|
121
55
|
* Generate a unique ID for a cycle (order-independent)
|
|
122
56
|
*/
|
|
@@ -143,7 +77,7 @@ export class CircularDependencyDetector {
|
|
|
143
77
|
detectCycles() {
|
|
144
78
|
const startTime = Date.now();
|
|
145
79
|
const visited = new Set();
|
|
146
|
-
const rawCycles = this.
|
|
80
|
+
const rawCycles = this.discovery.discoverAcrossGraph(visited);
|
|
147
81
|
const allCycles = this.materializeUniqueCycles(rawCycles);
|
|
148
82
|
const duration = Date.now() - startTime;
|
|
149
83
|
logger.info('Cycle detection completed', {
|
|
@@ -157,7 +91,7 @@ export class CircularDependencyDetector {
|
|
|
157
91
|
* Detect cycles starting from a specific node
|
|
158
92
|
*/
|
|
159
93
|
detectCyclesFromNode(startNode) {
|
|
160
|
-
const rawCycles = this.
|
|
94
|
+
const rawCycles = this.discovery.discoverFromNode(startNode);
|
|
161
95
|
return this.materializeUniqueCycles(rawCycles);
|
|
162
96
|
}
|
|
163
97
|
/**
|
|
@@ -202,63 +136,10 @@ export class CircularDependencyDetector {
|
|
|
202
136
|
breakSuggestions: [],
|
|
203
137
|
};
|
|
204
138
|
if (this.options.generateSuggestions) {
|
|
205
|
-
detected.breakSuggestions =
|
|
139
|
+
detected.breakSuggestions = generateBreakSuggestions(cycle, closingNode);
|
|
206
140
|
}
|
|
207
141
|
return detected;
|
|
208
142
|
}
|
|
209
|
-
discoverCyclesAcrossGraph(visited) {
|
|
210
|
-
const rawCycles = [];
|
|
211
|
-
const recursionStack = new Set();
|
|
212
|
-
const currentPath = [];
|
|
213
|
-
for (const nodeId of this.graph.keys()) {
|
|
214
|
-
if (!visited.has(nodeId)) {
|
|
215
|
-
this.walkForCycles(nodeId, 0, visited, recursionStack, currentPath, rawCycles, true);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return rawCycles;
|
|
219
|
-
}
|
|
220
|
-
discoverCyclesFromNode(startNode) {
|
|
221
|
-
const rawCycles = [];
|
|
222
|
-
this.walkForCycles(startNode, 0, undefined, new Set(), [], rawCycles, false);
|
|
223
|
-
return rawCycles;
|
|
224
|
-
}
|
|
225
|
-
walkForCycles(nodeId, depth, visited, recursionStack, currentPath, cycles, warnOnDepthLimit) {
|
|
226
|
-
if (depth > this.options.maxDepth) {
|
|
227
|
-
if (warnOnDepthLimit) {
|
|
228
|
-
logger.warn('Max depth reached during cycle detection', { nodeId, depth });
|
|
229
|
-
}
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
visited?.add(nodeId);
|
|
233
|
-
recursionStack.add(nodeId);
|
|
234
|
-
currentPath.push(nodeId);
|
|
235
|
-
for (const depId of this.getTraversableDependencies(nodeId)) {
|
|
236
|
-
if (recursionStack.has(depId)) {
|
|
237
|
-
const cycleStartIndex = currentPath.indexOf(depId);
|
|
238
|
-
cycles.push({
|
|
239
|
-
cycle: currentPath.slice(cycleStartIndex),
|
|
240
|
-
closingNode: depId,
|
|
241
|
-
});
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
if (visited?.has(depId)) {
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
this.walkForCycles(depId, depth + 1, visited, recursionStack, currentPath, cycles, warnOnDepthLimit);
|
|
248
|
-
}
|
|
249
|
-
recursionStack.delete(nodeId);
|
|
250
|
-
currentPath.pop();
|
|
251
|
-
}
|
|
252
|
-
getTraversableDependencies(nodeId) {
|
|
253
|
-
const dependencies = this.graph.get(nodeId) ?? new Set();
|
|
254
|
-
const traversable = [];
|
|
255
|
-
for (const depId of dependencies) {
|
|
256
|
-
if (!this.isEdgeIgnored(nodeId, depId) && shouldTraverseDependencyKind(DEFAULT_GRAPH_DEPENDENCY_KIND)) {
|
|
257
|
-
traversable.push(depId);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return traversable;
|
|
261
|
-
}
|
|
262
143
|
materializeUniqueCycles(rawCycles) {
|
|
263
144
|
const cycles = [];
|
|
264
145
|
for (const rawCycle of rawCycles) {
|
|
@@ -268,36 +149,6 @@ export class CircularDependencyDetector {
|
|
|
268
149
|
}
|
|
269
150
|
return cycles;
|
|
270
151
|
}
|
|
271
|
-
/**
|
|
272
|
-
* @ac US-030-AC-4: Suggest where to break cycle
|
|
273
|
-
*
|
|
274
|
-
* Generate suggestions for breaking the cycle
|
|
275
|
-
* Priority based on:
|
|
276
|
-
* - Test classes (high priority to break)
|
|
277
|
-
* - Utility classes (medium priority)
|
|
278
|
-
* - Core business logic (low priority)
|
|
279
|
-
*/
|
|
280
|
-
static generateBreakSuggestions(cycle, closingNode) {
|
|
281
|
-
const suggestions = [];
|
|
282
|
-
// Add closing edge
|
|
283
|
-
const fullCycle = [...cycle, closingNode];
|
|
284
|
-
// Analyze each edge in the cycle
|
|
285
|
-
for (let i = 0; i < fullCycle.length - 1; i++) {
|
|
286
|
-
const from = fullCycle[i];
|
|
287
|
-
const to = fullCycle[i + 1];
|
|
288
|
-
const priority = CircularDependencyDetector.calculateBreakPriority(from, to);
|
|
289
|
-
const reason = CircularDependencyDetector.getBreakReason(from, to, priority);
|
|
290
|
-
suggestions.push({
|
|
291
|
-
from,
|
|
292
|
-
to,
|
|
293
|
-
reason,
|
|
294
|
-
priority,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
// Sort by priority (highest first)
|
|
298
|
-
suggestions.sort((a, b) => b.priority - a.priority);
|
|
299
|
-
return suggestions;
|
|
300
|
-
}
|
|
301
152
|
/**
|
|
302
153
|
* @ac US-030-AC-5: Support user-defined cycle breaks
|
|
303
154
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circular-dependency-detector.js","sourceRoot":"","sources":["../../src/dependencies/circular-dependency-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"circular-dependency-detector.js","sourceRoot":"","sources":["../../src/dependencies/circular-dependency-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export function generateBreakSuggestions(cycle, closingNode) {
|
|
2
|
+
const fullCycle = [...cycle, closingNode];
|
|
3
|
+
const suggestions = [];
|
|
4
|
+
for (let index = 0; index < fullCycle.length - 1; index++) {
|
|
5
|
+
const from = fullCycle[index];
|
|
6
|
+
const to = fullCycle[index + 1];
|
|
7
|
+
const priority = calculateBreakPriority(from, to);
|
|
8
|
+
suggestions.push({
|
|
9
|
+
from,
|
|
10
|
+
to,
|
|
11
|
+
reason: getBreakReason(from, to, priority),
|
|
12
|
+
priority,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return suggestions.sort((a, b) => b.priority - a.priority);
|
|
16
|
+
}
|
|
17
|
+
function calculateBreakPriority(from, to) {
|
|
18
|
+
let priority = 50;
|
|
19
|
+
if (from.includes('Test') || to.includes('Test')) {
|
|
20
|
+
priority += 30;
|
|
21
|
+
}
|
|
22
|
+
if (isUtilityClass(from) || isUtilityClass(to)) {
|
|
23
|
+
priority += 20;
|
|
24
|
+
}
|
|
25
|
+
if (from.includes('Handler') && to.includes('Service')) {
|
|
26
|
+
priority += 15;
|
|
27
|
+
}
|
|
28
|
+
if (from.includes('Controller') && to.includes('Service')) {
|
|
29
|
+
priority += 15;
|
|
30
|
+
}
|
|
31
|
+
if (from.includes('Trigger') && to.includes('Handler')) {
|
|
32
|
+
priority -= 20;
|
|
33
|
+
}
|
|
34
|
+
if (isCoreDomainClass(from) && isCoreDomainClass(to)) {
|
|
35
|
+
priority -= 15;
|
|
36
|
+
}
|
|
37
|
+
return Math.max(0, Math.min(100, priority));
|
|
38
|
+
}
|
|
39
|
+
function getBreakReason(from, to, priority) {
|
|
40
|
+
if (priority >= 80) {
|
|
41
|
+
return `High priority: ${from} → ${to} is a test or utility dependency`;
|
|
42
|
+
}
|
|
43
|
+
else if (priority >= 65) {
|
|
44
|
+
return `Medium priority: ${from} → ${to} can be broken safely`;
|
|
45
|
+
}
|
|
46
|
+
else if (priority >= 50) {
|
|
47
|
+
return `Low priority: ${from} → ${to} may be tightly coupled`;
|
|
48
|
+
}
|
|
49
|
+
return `Not recommended: ${from} → ${to} appears to be core business logic`;
|
|
50
|
+
}
|
|
51
|
+
function isUtilityClass(nodeId) {
|
|
52
|
+
const name = nodeId.toLowerCase();
|
|
53
|
+
return name.includes('util') || name.includes('helper') || name.includes('constant') || name.includes('logger');
|
|
54
|
+
}
|
|
55
|
+
function isCoreDomainClass(nodeId) {
|
|
56
|
+
const name = nodeId.toLowerCase();
|
|
57
|
+
return (!name.includes('test') &&
|
|
58
|
+
!name.includes('handler') &&
|
|
59
|
+
!name.includes('controller') &&
|
|
60
|
+
!name.includes('util') &&
|
|
61
|
+
!name.includes('helper'));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=cycle-break-suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle-break-suggestions.js","sourceRoot":"","sources":["../../src/dependencies/cycle-break-suggestions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DependencyGraph, NodeId } from '../types/dependency.js';
|
|
2
|
+
export type RawCycle = {
|
|
3
|
+
cycle: NodeId[];
|
|
4
|
+
closingNode: NodeId;
|
|
5
|
+
};
|
|
6
|
+
export declare class CycleDiscovery {
|
|
7
|
+
private readonly graph;
|
|
8
|
+
private readonly maxDepth;
|
|
9
|
+
private readonly ignoredEdges;
|
|
10
|
+
constructor(graph: DependencyGraph, maxDepth: number, ignoredEdges: ReadonlySet<string>);
|
|
11
|
+
discoverAcrossGraph(visited: Set<NodeId>): RawCycle[];
|
|
12
|
+
discoverFromNode(startNode: NodeId): RawCycle[];
|
|
13
|
+
private walkForCycles;
|
|
14
|
+
private getTraversableDependencies;
|
|
15
|
+
private isEdgeIgnored;
|
|
16
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
import { DEFAULT_GRAPH_DEPENDENCY_KIND, shouldTraverseDependencyKind } from './dependency-semantics.js';
|
|
3
|
+
const logger = getLogger('CycleDiscovery');
|
|
4
|
+
export class CycleDiscovery {
|
|
5
|
+
graph;
|
|
6
|
+
maxDepth;
|
|
7
|
+
ignoredEdges;
|
|
8
|
+
constructor(graph, maxDepth, ignoredEdges) {
|
|
9
|
+
this.graph = graph;
|
|
10
|
+
this.maxDepth = maxDepth;
|
|
11
|
+
this.ignoredEdges = ignoredEdges;
|
|
12
|
+
}
|
|
13
|
+
discoverAcrossGraph(visited) {
|
|
14
|
+
const rawCycles = [];
|
|
15
|
+
const recursionStack = new Set();
|
|
16
|
+
const currentPath = [];
|
|
17
|
+
for (const nodeId of this.graph.keys()) {
|
|
18
|
+
if (!visited.has(nodeId)) {
|
|
19
|
+
this.walkForCycles(nodeId, 0, visited, recursionStack, currentPath, rawCycles, true);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return rawCycles;
|
|
23
|
+
}
|
|
24
|
+
discoverFromNode(startNode) {
|
|
25
|
+
const rawCycles = [];
|
|
26
|
+
this.walkForCycles(startNode, 0, undefined, new Set(), [], rawCycles, false);
|
|
27
|
+
return rawCycles;
|
|
28
|
+
}
|
|
29
|
+
walkForCycles(nodeId, depth, visited, recursionStack, currentPath, cycles, warnOnDepthLimit) {
|
|
30
|
+
if (depth > this.maxDepth) {
|
|
31
|
+
if (warnOnDepthLimit) {
|
|
32
|
+
logger.warn('Max depth reached during cycle detection', { nodeId, depth });
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
visited?.add(nodeId);
|
|
37
|
+
recursionStack.add(nodeId);
|
|
38
|
+
currentPath.push(nodeId);
|
|
39
|
+
for (const depId of this.getTraversableDependencies(nodeId)) {
|
|
40
|
+
if (recursionStack.has(depId)) {
|
|
41
|
+
const cycleStartIndex = currentPath.indexOf(depId);
|
|
42
|
+
cycles.push({
|
|
43
|
+
cycle: currentPath.slice(cycleStartIndex),
|
|
44
|
+
closingNode: depId,
|
|
45
|
+
});
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (visited?.has(depId)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
this.walkForCycles(depId, depth + 1, visited, recursionStack, currentPath, cycles, warnOnDepthLimit);
|
|
52
|
+
}
|
|
53
|
+
recursionStack.delete(nodeId);
|
|
54
|
+
currentPath.pop();
|
|
55
|
+
}
|
|
56
|
+
getTraversableDependencies(nodeId) {
|
|
57
|
+
const dependencies = this.graph.get(nodeId) ?? new Set();
|
|
58
|
+
const traversable = [];
|
|
59
|
+
for (const depId of dependencies) {
|
|
60
|
+
if (!this.isEdgeIgnored(nodeId, depId) && shouldTraverseDependencyKind(DEFAULT_GRAPH_DEPENDENCY_KIND)) {
|
|
61
|
+
traversable.push(depId);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return traversable;
|
|
65
|
+
}
|
|
66
|
+
isEdgeIgnored(from, to) {
|
|
67
|
+
return this.ignoredEdges.has(`${from}->${to}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=cycle-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle-discovery.js","sourceRoot":"","sources":["../../src/dependencies/cycle-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependency Graph Builder
|
|
3
|
-
* Builds a dependency graph from parsed metadata components
|
|
4
|
-
*
|
|
5
|
-
* @ac US-028-AC-1: Add nodes for each component
|
|
6
|
-
* @ac US-028-AC-2: Add edges for each dependency
|
|
7
|
-
* @ac US-028-AC-3: Handle bidirectional dependencies
|
|
8
|
-
* @ac US-028-AC-4: Track dependency types (hard, soft)
|
|
9
|
-
* @ac US-028-AC-5: Support incremental graph building
|
|
10
|
-
* @ac US-028-AC-6: Validate graph structure
|
|
11
|
-
*
|
|
12
|
-
* @issue #28
|
|
13
|
-
*/
|
|
14
1
|
import type { MetadataComponent, MetadataDependencyKind } from '../types/metadata.js';
|
|
15
2
|
import type { NodeId, DependencyAnalysisResult } from '../types/dependency.js';
|
|
16
3
|
/**
|
|
@@ -28,21 +15,6 @@ export type GraphBuilderOptions = {
|
|
|
28
15
|
/** Max nodes before warning (performance) */
|
|
29
16
|
maxNodes?: number;
|
|
30
17
|
};
|
|
31
|
-
/**
|
|
32
|
-
* Dependency Graph Builder
|
|
33
|
-
*
|
|
34
|
-
* Performance optimized for 10,000+ nodes:
|
|
35
|
-
* - Uses Map/Set for O(1) lookups
|
|
36
|
-
* - Lazy validation
|
|
37
|
-
* - Incremental building support
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* const builder = new DependencyGraphBuilder();
|
|
41
|
-
* builder.addComponent(apexClassComponent);
|
|
42
|
-
* builder.addComponent(triggerComponent);
|
|
43
|
-
* const result = builder.build();
|
|
44
|
-
* console.log(result.stats.totalComponents); // 2
|
|
45
|
-
*/
|
|
46
18
|
export declare class DependencyGraphBuilder {
|
|
47
19
|
private components;
|
|
48
20
|
private graph;
|
|
@@ -59,10 +31,6 @@ export declare class DependencyGraphBuilder {
|
|
|
59
31
|
* Check if graph is empty
|
|
60
32
|
*/
|
|
61
33
|
get isEmpty(): boolean;
|
|
62
|
-
/**
|
|
63
|
-
* Create a node ID from type and name
|
|
64
|
-
*/
|
|
65
|
-
private static createNodeId;
|
|
66
34
|
/**
|
|
67
35
|
* Add a metadata component to the graph
|
|
68
36
|
*
|
|
@@ -130,10 +98,6 @@ export declare class DependencyGraphBuilder {
|
|
|
130
98
|
* Stage 1a: intake and register the component node.
|
|
131
99
|
*/
|
|
132
100
|
private intakeComponentNode;
|
|
133
|
-
/**
|
|
134
|
-
* Stage 2: expand legacy dependency sets into typed dependency details.
|
|
135
|
-
*/
|
|
136
|
-
private expandTypedDependencies;
|
|
137
101
|
/**
|
|
138
102
|
* Stage 3: assemble graph edges from expanded dependency details.
|
|
139
103
|
*/
|
|
@@ -145,39 +109,10 @@ export declare class DependencyGraphBuilder {
|
|
|
145
109
|
* @ac US-028-AC-6: Validate graph structure
|
|
146
110
|
*/
|
|
147
111
|
private validate;
|
|
148
|
-
/**
|
|
149
|
-
* Detect circular dependencies using DFS
|
|
150
|
-
*/
|
|
151
|
-
private detectCircularDependencies;
|
|
152
|
-
/**
|
|
153
|
-
* Find components with no dependencies or dependents
|
|
154
|
-
*/
|
|
155
|
-
private findIsolatedComponents;
|
|
156
|
-
/**
|
|
157
|
-
* Generate graph statistics
|
|
158
|
-
*/
|
|
159
|
-
private generateStats;
|
|
160
|
-
private collectGraphMetrics;
|
|
161
|
-
private collectComponentsByType;
|
|
162
|
-
/**
|
|
163
|
-
* Calculate maximum dependency depth (simplified BFS)
|
|
164
|
-
*/
|
|
165
|
-
private calculateMaxDepth;
|
|
166
|
-
/**
|
|
167
|
-
* BFS to calculate depth from a starting node
|
|
168
|
-
*/
|
|
169
|
-
private bfsDepth;
|
|
170
|
-
/**
|
|
171
|
-
* Count total edges in the graph
|
|
172
|
-
*/
|
|
173
|
-
private countEdges;
|
|
174
112
|
private warnIfGraphIsLarge;
|
|
175
113
|
private initializeNodeEntries;
|
|
176
114
|
private ensureEdgeEndpoints;
|
|
177
115
|
private initializeOutgoingNode;
|
|
178
116
|
private initializeIncomingNode;
|
|
179
117
|
private reportDanglingReferences;
|
|
180
|
-
private validateGraphStructure;
|
|
181
|
-
private collectGraphCounts;
|
|
182
|
-
private findMaxCountEntry;
|
|
183
118
|
}
|