camouf 0.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/LICENSE +21 -0
- package/README.md +346 -0
- package/dist/cli/commands/analyze.d.ts +8 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +81 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +104 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/report.d.ts +8 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +63 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +9 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +87 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +9 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +93 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +48 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/version.d.ts +16 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +16 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/core/analyzer/architecture-visualizer.d.ts +25 -0
- package/dist/core/analyzer/architecture-visualizer.d.ts.map +1 -0
- package/dist/core/analyzer/architecture-visualizer.js +333 -0
- package/dist/core/analyzer/architecture-visualizer.js.map +1 -0
- package/dist/core/analyzer/dependency-analyzer.d.ts +49 -0
- package/dist/core/analyzer/dependency-analyzer.d.ts.map +1 -0
- package/dist/core/analyzer/dependency-analyzer.js +242 -0
- package/dist/core/analyzer/dependency-analyzer.js.map +1 -0
- package/dist/core/config/config-schema.d.ts +280 -0
- package/dist/core/config/config-schema.d.ts.map +1 -0
- package/dist/core/config/config-schema.js +199 -0
- package/dist/core/config/config-schema.js.map +1 -0
- package/dist/core/config/configuration-manager.d.ts +82 -0
- package/dist/core/config/configuration-manager.d.ts.map +1 -0
- package/dist/core/config/configuration-manager.js +306 -0
- package/dist/core/config/configuration-manager.js.map +1 -0
- package/dist/core/logger.d.ts +27 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +86 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/parsers/go-parser.d.ts +16 -0
- package/dist/core/parsers/go-parser.d.ts.map +1 -0
- package/dist/core/parsers/go-parser.js +117 -0
- package/dist/core/parsers/go-parser.js.map +1 -0
- package/dist/core/parsers/java-parser.d.ts +22 -0
- package/dist/core/parsers/java-parser.d.ts.map +1 -0
- package/dist/core/parsers/java-parser.js +114 -0
- package/dist/core/parsers/java-parser.js.map +1 -0
- package/dist/core/parsers/javascript-parser.d.ts +14 -0
- package/dist/core/parsers/javascript-parser.d.ts.map +1 -0
- package/dist/core/parsers/javascript-parser.js +19 -0
- package/dist/core/parsers/javascript-parser.js.map +1 -0
- package/dist/core/parsers/parser-registry.d.ts +45 -0
- package/dist/core/parsers/parser-registry.d.ts.map +1 -0
- package/dist/core/parsers/parser-registry.js +121 -0
- package/dist/core/parsers/parser-registry.js.map +1 -0
- package/dist/core/parsers/parser.interface.d.ts +49 -0
- package/dist/core/parsers/parser.interface.d.ts.map +1 -0
- package/dist/core/parsers/parser.interface.js +7 -0
- package/dist/core/parsers/parser.interface.js.map +1 -0
- package/dist/core/parsers/python-parser.d.ts +22 -0
- package/dist/core/parsers/python-parser.d.ts.map +1 -0
- package/dist/core/parsers/python-parser.js +136 -0
- package/dist/core/parsers/python-parser.js.map +1 -0
- package/dist/core/parsers/rust-parser.d.ts +16 -0
- package/dist/core/parsers/rust-parser.d.ts.map +1 -0
- package/dist/core/parsers/rust-parser.js +176 -0
- package/dist/core/parsers/rust-parser.js.map +1 -0
- package/dist/core/parsers/typescript-parser.d.ts +29 -0
- package/dist/core/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/core/parsers/typescript-parser.js +251 -0
- package/dist/core/parsers/typescript-parser.js.map +1 -0
- package/dist/core/reporter/report-generator.d.ts +31 -0
- package/dist/core/reporter/report-generator.d.ts.map +1 -0
- package/dist/core/reporter/report-generator.js +417 -0
- package/dist/core/reporter/report-generator.js.map +1 -0
- package/dist/core/reporter/violation-reporter.d.ts +62 -0
- package/dist/core/reporter/violation-reporter.d.ts.map +1 -0
- package/dist/core/reporter/violation-reporter.js +265 -0
- package/dist/core/reporter/violation-reporter.js.map +1 -0
- package/dist/core/rules/builtin/api-versioning.rule.d.ts +28 -0
- package/dist/core/rules/builtin/api-versioning.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/api-versioning.rule.js +103 -0
- package/dist/core/rules/builtin/api-versioning.rule.js.map +1 -0
- package/dist/core/rules/builtin/circular-dependencies.rule.d.ts +26 -0
- package/dist/core/rules/builtin/circular-dependencies.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/circular-dependencies.rule.js +99 -0
- package/dist/core/rules/builtin/circular-dependencies.rule.js.map +1 -0
- package/dist/core/rules/builtin/data-flow-integrity.rule.d.ts +27 -0
- package/dist/core/rules/builtin/data-flow-integrity.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/data-flow-integrity.rule.js +111 -0
- package/dist/core/rules/builtin/data-flow-integrity.rule.js.map +1 -0
- package/dist/core/rules/builtin/ddd-boundaries.rule.d.ts +31 -0
- package/dist/core/rules/builtin/ddd-boundaries.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/ddd-boundaries.rule.js +141 -0
- package/dist/core/rules/builtin/ddd-boundaries.rule.js.map +1 -0
- package/dist/core/rules/builtin/distributed-transactions.rule.d.ts +27 -0
- package/dist/core/rules/builtin/distributed-transactions.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/distributed-transactions.rule.js +134 -0
- package/dist/core/rules/builtin/distributed-transactions.rule.js.map +1 -0
- package/dist/core/rules/builtin/index.d.ts +16 -0
- package/dist/core/rules/builtin/index.d.ts.map +1 -0
- package/dist/core/rules/builtin/index.js +16 -0
- package/dist/core/rules/builtin/index.js.map +1 -0
- package/dist/core/rules/builtin/layer-dependencies.rule.d.ts +30 -0
- package/dist/core/rules/builtin/layer-dependencies.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/layer-dependencies.rule.js +102 -0
- package/dist/core/rules/builtin/layer-dependencies.rule.js.map +1 -0
- package/dist/core/rules/builtin/performance-antipatterns.rule.d.ts +29 -0
- package/dist/core/rules/builtin/performance-antipatterns.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/performance-antipatterns.rule.js +148 -0
- package/dist/core/rules/builtin/performance-antipatterns.rule.js.map +1 -0
- package/dist/core/rules/builtin/resilience-patterns.rule.d.ts +29 -0
- package/dist/core/rules/builtin/resilience-patterns.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/resilience-patterns.rule.js +123 -0
- package/dist/core/rules/builtin/resilience-patterns.rule.js.map +1 -0
- package/dist/core/rules/builtin/security-context.rule.d.ts +32 -0
- package/dist/core/rules/builtin/security-context.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/security-context.rule.js +145 -0
- package/dist/core/rules/builtin/security-context.rule.js.map +1 -0
- package/dist/core/rules/builtin/type-safety.rule.d.ts +32 -0
- package/dist/core/rules/builtin/type-safety.rule.d.ts.map +1 -0
- package/dist/core/rules/builtin/type-safety.rule.js +175 -0
- package/dist/core/rules/builtin/type-safety.rule.js.map +1 -0
- package/dist/core/rules/rule-engine.d.ts +72 -0
- package/dist/core/rules/rule-engine.d.ts.map +1 -0
- package/dist/core/rules/rule-engine.js +225 -0
- package/dist/core/rules/rule-engine.js.map +1 -0
- package/dist/core/rules/rule.interface.d.ts +169 -0
- package/dist/core/rules/rule.interface.d.ts.map +1 -0
- package/dist/core/rules/rule.interface.js +38 -0
- package/dist/core/rules/rule.interface.js.map +1 -0
- package/dist/core/scanner/project-detector.d.ts +51 -0
- package/dist/core/scanner/project-detector.d.ts.map +1 -0
- package/dist/core/scanner/project-detector.js +310 -0
- package/dist/core/scanner/project-detector.js.map +1 -0
- package/dist/core/scanner/project-scanner.d.ts +101 -0
- package/dist/core/scanner/project-scanner.d.ts.map +1 -0
- package/dist/core/scanner/project-scanner.js +321 -0
- package/dist/core/scanner/project-scanner.js.map +1 -0
- package/dist/core/watcher/file-watcher.d.ts +48 -0
- package/dist/core/watcher/file-watcher.d.ts.map +1 -0
- package/dist/core/watcher/file-watcher.js +124 -0
- package/dist/core/watcher/file-watcher.js.map +1 -0
- package/dist/types/config.types.d.ts +163 -0
- package/dist/types/config.types.d.ts.map +1 -0
- package/dist/types/config.types.js +36 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/core.types.d.ts +247 -0
- package/dist/types/core.types.d.ts.map +1 -0
- package/dist/types/core.types.js +7 -0
- package/dist/types/core.types.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Architecture Visualizer
|
|
3
|
+
*
|
|
4
|
+
* Generates visual representations of the architecture.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
export class ArchitectureVisualizer {
|
|
9
|
+
config;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
async generate(analysis, options) {
|
|
14
|
+
await fs.mkdir(options.outputPath, { recursive: true });
|
|
15
|
+
switch (options.format) {
|
|
16
|
+
case 'html':
|
|
17
|
+
await this.generateHtmlVisualization(analysis, options.outputPath);
|
|
18
|
+
break;
|
|
19
|
+
case 'dot':
|
|
20
|
+
await this.generateDotVisualization(analysis, options.outputPath);
|
|
21
|
+
break;
|
|
22
|
+
case 'json':
|
|
23
|
+
await this.generateJsonVisualization(analysis, options.outputPath);
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async generateHtmlVisualization(analysis, outputPath) {
|
|
28
|
+
// Generate interactive HTML visualization using D3.js
|
|
29
|
+
const html = `
|
|
30
|
+
<!DOCTYPE html>
|
|
31
|
+
<html lang="en">
|
|
32
|
+
<head>
|
|
33
|
+
<meta charset="UTF-8">
|
|
34
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
35
|
+
<title>Architecture Visualization - ${this.config.name || 'Project'}</title>
|
|
36
|
+
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
37
|
+
<style>
|
|
38
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
39
|
+
body {
|
|
40
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
41
|
+
background: #1a1a2e;
|
|
42
|
+
color: #eee;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
}
|
|
45
|
+
#container {
|
|
46
|
+
display: flex;
|
|
47
|
+
height: 100vh;
|
|
48
|
+
}
|
|
49
|
+
#sidebar {
|
|
50
|
+
width: 300px;
|
|
51
|
+
background: #16213e;
|
|
52
|
+
padding: 1rem;
|
|
53
|
+
overflow-y: auto;
|
|
54
|
+
}
|
|
55
|
+
#graph {
|
|
56
|
+
flex: 1;
|
|
57
|
+
position: relative;
|
|
58
|
+
}
|
|
59
|
+
h1 { font-size: 1.2rem; margin-bottom: 1rem; }
|
|
60
|
+
h2 { font-size: 1rem; margin: 1rem 0 0.5rem; color: #e94560; }
|
|
61
|
+
.stat { margin: 0.5rem 0; }
|
|
62
|
+
.stat-label { color: #aaa; font-size: 0.85rem; }
|
|
63
|
+
.stat-value { font-size: 1.5rem; font-weight: bold; color: #e94560; }
|
|
64
|
+
.list { list-style: none; }
|
|
65
|
+
.list li {
|
|
66
|
+
padding: 0.5rem;
|
|
67
|
+
margin: 0.25rem 0;
|
|
68
|
+
background: #0f3460;
|
|
69
|
+
border-radius: 4px;
|
|
70
|
+
font-size: 0.85rem;
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
text-overflow: ellipsis;
|
|
74
|
+
}
|
|
75
|
+
.node circle {
|
|
76
|
+
stroke: #fff;
|
|
77
|
+
stroke-width: 1.5px;
|
|
78
|
+
}
|
|
79
|
+
.link {
|
|
80
|
+
stroke: #999;
|
|
81
|
+
stroke-opacity: 0.6;
|
|
82
|
+
}
|
|
83
|
+
.link.violation {
|
|
84
|
+
stroke: #e94560;
|
|
85
|
+
stroke-width: 2px;
|
|
86
|
+
}
|
|
87
|
+
.node text {
|
|
88
|
+
font-size: 10px;
|
|
89
|
+
fill: #eee;
|
|
90
|
+
}
|
|
91
|
+
.tooltip {
|
|
92
|
+
position: absolute;
|
|
93
|
+
background: #16213e;
|
|
94
|
+
padding: 0.5rem 1rem;
|
|
95
|
+
border-radius: 4px;
|
|
96
|
+
font-size: 0.85rem;
|
|
97
|
+
pointer-events: none;
|
|
98
|
+
opacity: 0;
|
|
99
|
+
transition: opacity 0.2s;
|
|
100
|
+
}
|
|
101
|
+
</style>
|
|
102
|
+
</head>
|
|
103
|
+
<body>
|
|
104
|
+
<div id="container">
|
|
105
|
+
<div id="sidebar">
|
|
106
|
+
<h1>🏗️ ${this.config.name || 'Project'}</h1>
|
|
107
|
+
|
|
108
|
+
<h2>📊 Summary</h2>
|
|
109
|
+
<div class="stat">
|
|
110
|
+
<div class="stat-label">Files</div>
|
|
111
|
+
<div class="stat-value">${analysis.summary.totalFiles}</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="stat">
|
|
114
|
+
<div class="stat-label">Dependencies</div>
|
|
115
|
+
<div class="stat-value">${analysis.summary.totalDependencies}</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="stat">
|
|
118
|
+
<div class="stat-label">Circular Deps</div>
|
|
119
|
+
<div class="stat-value">${analysis.summary.circularDependencies}</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="stat">
|
|
122
|
+
<div class="stat-label">Avg Coupling</div>
|
|
123
|
+
<div class="stat-value">${analysis.summary.averageCoupling.toFixed(1)}</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<h2>🔥 Hotspots</h2>
|
|
127
|
+
<ul class="list">
|
|
128
|
+
${analysis.hotspots.slice(0, 10).map(h => `<li title="${h.file}">${this.getFileName(h.file)} (${h.dependents})</li>`).join('')}
|
|
129
|
+
</ul>
|
|
130
|
+
|
|
131
|
+
<h2>💡 Suggestions</h2>
|
|
132
|
+
<ul class="list">
|
|
133
|
+
${analysis.suggestions.map(s => `<li title="${s}">${s}</li>`).join('')}
|
|
134
|
+
</ul>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div id="graph">
|
|
138
|
+
<div class="tooltip" id="tooltip"></div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<script>
|
|
143
|
+
const data = ${JSON.stringify(this.prepareGraphData(analysis))};
|
|
144
|
+
|
|
145
|
+
const container = document.getElementById('graph');
|
|
146
|
+
const width = container.clientWidth;
|
|
147
|
+
const height = container.clientHeight;
|
|
148
|
+
|
|
149
|
+
const svg = d3.select('#graph')
|
|
150
|
+
.append('svg')
|
|
151
|
+
.attr('width', width)
|
|
152
|
+
.attr('height', height);
|
|
153
|
+
|
|
154
|
+
const g = svg.append('g');
|
|
155
|
+
|
|
156
|
+
// Zoom behavior
|
|
157
|
+
const zoom = d3.zoom()
|
|
158
|
+
.scaleExtent([0.1, 4])
|
|
159
|
+
.on('zoom', (event) => g.attr('transform', event.transform));
|
|
160
|
+
|
|
161
|
+
svg.call(zoom);
|
|
162
|
+
|
|
163
|
+
// Force simulation
|
|
164
|
+
const simulation = d3.forceSimulation(data.nodes)
|
|
165
|
+
.force('link', d3.forceLink(data.links).id(d => d.id).distance(100))
|
|
166
|
+
.force('charge', d3.forceManyBody().strength(-300))
|
|
167
|
+
.force('center', d3.forceCenter(width / 2, height / 2))
|
|
168
|
+
.force('collision', d3.forceCollide().radius(30));
|
|
169
|
+
|
|
170
|
+
// Links
|
|
171
|
+
const link = g.append('g')
|
|
172
|
+
.selectAll('line')
|
|
173
|
+
.data(data.links)
|
|
174
|
+
.join('line')
|
|
175
|
+
.attr('class', d => d.violation ? 'link violation' : 'link');
|
|
176
|
+
|
|
177
|
+
// Nodes
|
|
178
|
+
const node = g.append('g')
|
|
179
|
+
.selectAll('.node')
|
|
180
|
+
.data(data.nodes)
|
|
181
|
+
.join('g')
|
|
182
|
+
.attr('class', 'node')
|
|
183
|
+
.call(d3.drag()
|
|
184
|
+
.on('start', dragstarted)
|
|
185
|
+
.on('drag', dragged)
|
|
186
|
+
.on('end', dragended));
|
|
187
|
+
|
|
188
|
+
node.append('circle')
|
|
189
|
+
.attr('r', d => Math.min(5 + d.size * 2, 20))
|
|
190
|
+
.attr('fill', d => d.color);
|
|
191
|
+
|
|
192
|
+
node.append('text')
|
|
193
|
+
.attr('dx', 12)
|
|
194
|
+
.attr('dy', 4)
|
|
195
|
+
.text(d => d.label);
|
|
196
|
+
|
|
197
|
+
// Tooltip
|
|
198
|
+
const tooltip = d3.select('#tooltip');
|
|
199
|
+
|
|
200
|
+
node.on('mouseover', (event, d) => {
|
|
201
|
+
tooltip
|
|
202
|
+
.style('opacity', 1)
|
|
203
|
+
.html(\`<strong>\${d.label}</strong><br/>Deps: \${d.deps}<br/>Dependents: \${d.dependents}\`)
|
|
204
|
+
.style('left', (event.pageX + 10) + 'px')
|
|
205
|
+
.style('top', (event.pageY - 10) + 'px');
|
|
206
|
+
})
|
|
207
|
+
.on('mouseout', () => {
|
|
208
|
+
tooltip.style('opacity', 0);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
simulation.on('tick', () => {
|
|
212
|
+
link
|
|
213
|
+
.attr('x1', d => d.source.x)
|
|
214
|
+
.attr('y1', d => d.source.y)
|
|
215
|
+
.attr('x2', d => d.target.x)
|
|
216
|
+
.attr('y2', d => d.target.y);
|
|
217
|
+
|
|
218
|
+
node.attr('transform', d => \`translate(\${d.x},\${d.y})\`);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
function dragstarted(event) {
|
|
222
|
+
if (!event.active) simulation.alphaTarget(0.3).restart();
|
|
223
|
+
event.subject.fx = event.subject.x;
|
|
224
|
+
event.subject.fy = event.subject.y;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function dragged(event) {
|
|
228
|
+
event.subject.fx = event.x;
|
|
229
|
+
event.subject.fy = event.y;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function dragended(event) {
|
|
233
|
+
if (!event.active) simulation.alphaTarget(0);
|
|
234
|
+
event.subject.fx = null;
|
|
235
|
+
event.subject.fy = null;
|
|
236
|
+
}
|
|
237
|
+
</script>
|
|
238
|
+
</body>
|
|
239
|
+
</html>
|
|
240
|
+
`.trim();
|
|
241
|
+
await fs.writeFile(path.join(outputPath, 'visualization.html'), html);
|
|
242
|
+
}
|
|
243
|
+
async generateDotVisualization(analysis, outputPath) {
|
|
244
|
+
// Generate GraphViz DOT format
|
|
245
|
+
let dot = `digraph Architecture {\n`;
|
|
246
|
+
dot += ` rankdir=LR;\n`;
|
|
247
|
+
dot += ` node [shape=box, style=filled, fillcolor=lightblue];\n`;
|
|
248
|
+
dot += ` edge [color=gray];\n\n`;
|
|
249
|
+
// Add layer subgraphs
|
|
250
|
+
const layerFiles = new Map();
|
|
251
|
+
for (const hotspot of analysis.hotspots) {
|
|
252
|
+
const layer = this.getLayerForFile(hotspot.file);
|
|
253
|
+
if (layer) {
|
|
254
|
+
const files = layerFiles.get(layer) || [];
|
|
255
|
+
files.push(hotspot.file);
|
|
256
|
+
layerFiles.set(layer, files);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const [layer, files] of layerFiles) {
|
|
260
|
+
dot += ` subgraph cluster_${layer} {\n`;
|
|
261
|
+
dot += ` label="${layer}";\n`;
|
|
262
|
+
dot += ` style=filled;\n`;
|
|
263
|
+
dot += ` color=lightgrey;\n`;
|
|
264
|
+
for (const file of files) {
|
|
265
|
+
const safeName = this.sanitizeNodeName(file);
|
|
266
|
+
dot += ` "${safeName}" [label="${this.getFileName(file)}"];\n`;
|
|
267
|
+
}
|
|
268
|
+
dot += ` }\n\n`;
|
|
269
|
+
}
|
|
270
|
+
// Add edges for circular dependencies
|
|
271
|
+
for (const cycle of analysis.circularDependencies) {
|
|
272
|
+
for (let i = 0; i < cycle.cycle.length; i++) {
|
|
273
|
+
const from = this.sanitizeNodeName(cycle.cycle[i]);
|
|
274
|
+
const to = this.sanitizeNodeName(cycle.cycle[(i + 1) % cycle.cycle.length]);
|
|
275
|
+
dot += ` "${from}" -> "${to}" [color=red, style=bold];\n`;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
dot += `}\n`;
|
|
279
|
+
await fs.writeFile(path.join(outputPath, 'architecture.dot'), dot);
|
|
280
|
+
}
|
|
281
|
+
async generateJsonVisualization(analysis, outputPath) {
|
|
282
|
+
const graphData = this.prepareGraphData(analysis);
|
|
283
|
+
await fs.writeFile(path.join(outputPath, 'graph-data.json'), JSON.stringify(graphData, null, 2));
|
|
284
|
+
}
|
|
285
|
+
prepareGraphData(analysis) {
|
|
286
|
+
const layerColors = {
|
|
287
|
+
presentation: '#4ecca3',
|
|
288
|
+
application: '#45aaf2',
|
|
289
|
+
domain: '#f7b731',
|
|
290
|
+
infrastructure: '#fc5c65',
|
|
291
|
+
shared: '#a55eea',
|
|
292
|
+
};
|
|
293
|
+
const nodes = analysis.hotspots.map(h => ({
|
|
294
|
+
id: h.file,
|
|
295
|
+
label: this.getFileName(h.file),
|
|
296
|
+
color: layerColors[this.getLayerForFile(h.file) || 'shared'] || '#999',
|
|
297
|
+
size: h.dependents,
|
|
298
|
+
deps: h.dependencies,
|
|
299
|
+
dependents: h.dependents,
|
|
300
|
+
}));
|
|
301
|
+
const links = [];
|
|
302
|
+
// Add circular dependency links
|
|
303
|
+
for (const cycle of analysis.circularDependencies) {
|
|
304
|
+
for (let i = 0; i < cycle.cycle.length; i++) {
|
|
305
|
+
links.push({
|
|
306
|
+
source: cycle.cycle[i],
|
|
307
|
+
target: cycle.cycle[(i + 1) % cycle.cycle.length],
|
|
308
|
+
violation: true,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return { nodes, links };
|
|
313
|
+
}
|
|
314
|
+
getFileName(filePath) {
|
|
315
|
+
return path.basename(filePath);
|
|
316
|
+
}
|
|
317
|
+
getLayerForFile(filePath) {
|
|
318
|
+
const normalizedPath = filePath.replace(/\\/g, '/').toLowerCase();
|
|
319
|
+
for (const layer of this.config.layers) {
|
|
320
|
+
for (const dir of layer.directories) {
|
|
321
|
+
const normalizedDir = dir.replace(/\\/g, '/').toLowerCase();
|
|
322
|
+
if (normalizedPath.startsWith(normalizedDir + '/') || normalizedPath === normalizedDir) {
|
|
323
|
+
return layer.name;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
sanitizeNodeName(name) {
|
|
330
|
+
return name.replace(/[^a-zA-Z0-9]/g, '_');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=architecture-visualizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"architecture-visualizer.js","sourceRoot":"","sources":["../../../src/core/analyzer/architecture-visualizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes the dependency graph for insights and metrics.
|
|
5
|
+
*/
|
|
6
|
+
import { CamoufConfig } from '../../types/config.types.js';
|
|
7
|
+
import { AnalysisResult } from '../../types/core.types.js';
|
|
8
|
+
import { DependencyGraph } from '../scanner/project-scanner.js';
|
|
9
|
+
interface AnalyzeOptions {
|
|
10
|
+
maxDepth?: number;
|
|
11
|
+
focus?: string;
|
|
12
|
+
includeMetrics?: boolean;
|
|
13
|
+
analyzeCoupling?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class DependencyAnalyzer {
|
|
16
|
+
private config;
|
|
17
|
+
constructor(config: CamoufConfig);
|
|
18
|
+
analyze(graph: DependencyGraph, options?: AnalyzeOptions): Promise<AnalysisResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Find all circular dependencies using DFS
|
|
21
|
+
*/
|
|
22
|
+
private findCircularDependencies;
|
|
23
|
+
/**
|
|
24
|
+
* Find layer violations
|
|
25
|
+
*/
|
|
26
|
+
private findLayerViolations;
|
|
27
|
+
/**
|
|
28
|
+
* Calculate coupling metrics
|
|
29
|
+
*/
|
|
30
|
+
private calculateCouplingMetrics;
|
|
31
|
+
/**
|
|
32
|
+
* Find hotspot files (high coupling)
|
|
33
|
+
*/
|
|
34
|
+
private findHotspots;
|
|
35
|
+
/**
|
|
36
|
+
* Calculate detailed file metrics
|
|
37
|
+
*/
|
|
38
|
+
private calculateFileMetrics;
|
|
39
|
+
/**
|
|
40
|
+
* Generate improvement suggestions
|
|
41
|
+
*/
|
|
42
|
+
private generateSuggestions;
|
|
43
|
+
/**
|
|
44
|
+
* Get layer for a file path
|
|
45
|
+
*/
|
|
46
|
+
private getLayerForFile;
|
|
47
|
+
}
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=dependency-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-analyzer.d.ts","sourceRoot":"","sources":["../../../src/core/analyzer/dependency-analyzer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAA6E,MAAM,2BAA2B,CAAC;AACtI,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGhE,UAAU,cAAc;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,EAAE,YAAY;IAI1B,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAuD5F;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAqDhC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAyC3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAsBhC;;OAEG;IACH,OAAO,CAAC,YAAY;IAyBpB;;OAEG;YACW,oBAAoB;IAmBlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA+D3B;;OAEG;IACH,OAAO,CAAC,eAAe;CAWxB"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes the dependency graph for insights and metrics.
|
|
5
|
+
*/
|
|
6
|
+
import { Logger } from '../logger.js';
|
|
7
|
+
export class DependencyAnalyzer {
|
|
8
|
+
config;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
async analyze(graph, options = {}) {
|
|
13
|
+
Logger.debug('Starting dependency analysis...');
|
|
14
|
+
const nodes = graph.nodes();
|
|
15
|
+
const edges = graph.edges();
|
|
16
|
+
// Calculate basic metrics
|
|
17
|
+
const totalFiles = nodes.length;
|
|
18
|
+
const totalDependencies = edges.length;
|
|
19
|
+
// Find circular dependencies
|
|
20
|
+
const circularDependencies = this.findCircularDependencies(graph);
|
|
21
|
+
// Find layer violations
|
|
22
|
+
const layerViolations = this.findLayerViolations(graph);
|
|
23
|
+
// Calculate coupling metrics
|
|
24
|
+
const couplingMetrics = this.calculateCouplingMetrics(graph);
|
|
25
|
+
// Find hotspots (files with most dependencies/dependents)
|
|
26
|
+
const hotspots = this.findHotspots(graph);
|
|
27
|
+
// Generate file metrics if requested
|
|
28
|
+
const fileMetrics = options.includeMetrics
|
|
29
|
+
? await this.calculateFileMetrics(graph)
|
|
30
|
+
: undefined;
|
|
31
|
+
// Generate suggestions
|
|
32
|
+
const suggestions = this.generateSuggestions({
|
|
33
|
+
totalFiles,
|
|
34
|
+
circularDependencies,
|
|
35
|
+
layerViolations,
|
|
36
|
+
hotspots,
|
|
37
|
+
couplingMetrics,
|
|
38
|
+
});
|
|
39
|
+
const summary = {
|
|
40
|
+
totalFiles,
|
|
41
|
+
totalDependencies,
|
|
42
|
+
circularDependencies: circularDependencies.length,
|
|
43
|
+
averageCoupling: couplingMetrics.average,
|
|
44
|
+
maxCoupling: couplingMetrics.max,
|
|
45
|
+
layerViolations: layerViolations.length,
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
summary,
|
|
49
|
+
hotspots,
|
|
50
|
+
circularDependencies,
|
|
51
|
+
layerViolations,
|
|
52
|
+
suggestions,
|
|
53
|
+
fileMetrics,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Find all circular dependencies using DFS
|
|
58
|
+
*/
|
|
59
|
+
findCircularDependencies(graph) {
|
|
60
|
+
const cycles = [];
|
|
61
|
+
const visited = new Set();
|
|
62
|
+
const recursionStack = new Set();
|
|
63
|
+
const path = [];
|
|
64
|
+
const dfs = (node) => {
|
|
65
|
+
visited.add(node);
|
|
66
|
+
recursionStack.add(node);
|
|
67
|
+
path.push(node);
|
|
68
|
+
const outEdges = graph.outEdges(node) || [];
|
|
69
|
+
for (const edge of outEdges) {
|
|
70
|
+
const neighbor = edge.w;
|
|
71
|
+
if (!visited.has(neighbor)) {
|
|
72
|
+
dfs(neighbor);
|
|
73
|
+
}
|
|
74
|
+
else if (recursionStack.has(neighbor)) {
|
|
75
|
+
// Found a cycle
|
|
76
|
+
const cycleStart = path.indexOf(neighbor);
|
|
77
|
+
if (cycleStart !== -1) {
|
|
78
|
+
const cycle = path.slice(cycleStart);
|
|
79
|
+
// Only add if not a duplicate
|
|
80
|
+
const cycleKey = [...cycle].sort().join(',');
|
|
81
|
+
const exists = cycles.some(c => [...c.cycle].sort().join(',') === cycleKey);
|
|
82
|
+
if (!exists && cycle.length > 1) {
|
|
83
|
+
cycles.push({
|
|
84
|
+
cycle,
|
|
85
|
+
files: cycle,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
path.pop();
|
|
92
|
+
recursionStack.delete(node);
|
|
93
|
+
};
|
|
94
|
+
for (const node of graph.nodes()) {
|
|
95
|
+
if (!visited.has(node)) {
|
|
96
|
+
dfs(node);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return cycles;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Find layer violations
|
|
103
|
+
*/
|
|
104
|
+
findLayerViolations(graph) {
|
|
105
|
+
const violations = [];
|
|
106
|
+
// Build layer map
|
|
107
|
+
const layerMap = new Map();
|
|
108
|
+
const allowedDeps = new Map();
|
|
109
|
+
for (const layer of this.config.layers) {
|
|
110
|
+
for (const dir of layer.directories) {
|
|
111
|
+
const normalizedDir = dir.replace(/\\/g, '/').toLowerCase();
|
|
112
|
+
layerMap.set(normalizedDir, layer.name);
|
|
113
|
+
}
|
|
114
|
+
allowedDeps.set(layer.name, new Set(layer.allowedDependencies));
|
|
115
|
+
}
|
|
116
|
+
// Check each edge
|
|
117
|
+
for (const edge of graph.edges()) {
|
|
118
|
+
const sourceNode = graph.node(edge.v);
|
|
119
|
+
const targetNode = graph.node(edge.w);
|
|
120
|
+
if (!sourceNode || !targetNode)
|
|
121
|
+
continue;
|
|
122
|
+
const sourceLayer = this.getLayerForFile(sourceNode.data.relativePath, layerMap);
|
|
123
|
+
const targetLayer = this.getLayerForFile(targetNode.data.relativePath, layerMap);
|
|
124
|
+
if (!sourceLayer || !targetLayer || sourceLayer === targetLayer)
|
|
125
|
+
continue;
|
|
126
|
+
const allowed = allowedDeps.get(sourceLayer);
|
|
127
|
+
if (!allowed?.has(targetLayer) && !allowed?.has('*')) {
|
|
128
|
+
violations.push({
|
|
129
|
+
sourceLayer,
|
|
130
|
+
targetLayer,
|
|
131
|
+
file: sourceNode.data.relativePath,
|
|
132
|
+
dependency: targetNode.data.relativePath,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return violations;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Calculate coupling metrics
|
|
140
|
+
*/
|
|
141
|
+
calculateCouplingMetrics(graph) {
|
|
142
|
+
const couplings = [];
|
|
143
|
+
for (const node of graph.nodes()) {
|
|
144
|
+
const inDegree = (graph.inEdges(node) || []).length;
|
|
145
|
+
const outDegree = (graph.outEdges(node) || []).length;
|
|
146
|
+
const coupling = inDegree + outDegree;
|
|
147
|
+
couplings.push(coupling);
|
|
148
|
+
}
|
|
149
|
+
if (couplings.length === 0) {
|
|
150
|
+
return { average: 0, max: 0, min: 0 };
|
|
151
|
+
}
|
|
152
|
+
const sum = couplings.reduce((a, b) => a + b, 0);
|
|
153
|
+
const average = sum / couplings.length;
|
|
154
|
+
const max = Math.max(...couplings);
|
|
155
|
+
const min = Math.min(...couplings);
|
|
156
|
+
return { average, max, min };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Find hotspot files (high coupling)
|
|
160
|
+
*/
|
|
161
|
+
findHotspots(graph) {
|
|
162
|
+
const hotspots = [];
|
|
163
|
+
for (const node of graph.nodes()) {
|
|
164
|
+
const inEdges = graph.inEdges(node) || [];
|
|
165
|
+
const outEdges = graph.outEdges(node) || [];
|
|
166
|
+
const dependents = inEdges.length;
|
|
167
|
+
const dependencies = outEdges.length;
|
|
168
|
+
const coupling = dependents + dependencies;
|
|
169
|
+
hotspots.push({
|
|
170
|
+
file: node,
|
|
171
|
+
dependents,
|
|
172
|
+
dependencies,
|
|
173
|
+
coupling,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// Sort by number of dependents (most depended upon)
|
|
177
|
+
hotspots.sort((a, b) => b.dependents - a.dependents);
|
|
178
|
+
return hotspots.slice(0, 20);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Calculate detailed file metrics
|
|
182
|
+
*/
|
|
183
|
+
async calculateFileMetrics(graph) {
|
|
184
|
+
const metrics = [];
|
|
185
|
+
for (const node of graph.nodes()) {
|
|
186
|
+
const nodeData = graph.node(node);
|
|
187
|
+
const inEdges = graph.inEdges(node) || [];
|
|
188
|
+
const outEdges = graph.outEdges(node) || [];
|
|
189
|
+
metrics.push({
|
|
190
|
+
file: node,
|
|
191
|
+
linesOfCode: 0, // Would need to read file to calculate
|
|
192
|
+
dependencies: outEdges.length,
|
|
193
|
+
dependents: inEdges.length,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return metrics;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Generate improvement suggestions
|
|
200
|
+
*/
|
|
201
|
+
generateSuggestions(data) {
|
|
202
|
+
const suggestions = [];
|
|
203
|
+
// Circular dependencies suggestions
|
|
204
|
+
if (data.circularDependencies.length > 0) {
|
|
205
|
+
suggestions.push(`Found ${data.circularDependencies.length} circular dependency chains. Consider:`, ` - Extracting shared code to a common module`, ` - Using dependency injection`, ` - Applying the mediator pattern for complex interactions`);
|
|
206
|
+
}
|
|
207
|
+
// Layer violation suggestions
|
|
208
|
+
if (data.layerViolations.length > 0) {
|
|
209
|
+
suggestions.push(`Found ${data.layerViolations.length} layer violations. Consider:`, ` - Moving shared code to the 'shared' layer`, ` - Using interfaces for cross-layer communication`, ` - Restructuring to follow the defined architecture`);
|
|
210
|
+
}
|
|
211
|
+
// High coupling suggestions
|
|
212
|
+
if (data.couplingMetrics.max > 20) {
|
|
213
|
+
const highCouplingFiles = data.hotspots.filter(h => h.coupling > 15);
|
|
214
|
+
if (highCouplingFiles.length > 0) {
|
|
215
|
+
suggestions.push(`${highCouplingFiles.length} files have high coupling (>15 connections). Consider:`, ` - Breaking large files into smaller, focused modules`, ` - Applying the single responsibility principle`, ` - Creating facade modules to reduce direct dependencies`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Hotspot suggestions
|
|
219
|
+
const superHotspots = data.hotspots.filter(h => h.dependents > 10);
|
|
220
|
+
if (superHotspots.length > 0) {
|
|
221
|
+
suggestions.push(`${superHotspots.length} files are depended upon by >10 other files:`, ...superHotspots.slice(0, 3).map(h => ` - ${h.file} (${h.dependents} dependents)`), ` These might be good candidates for optimization or splitting.`);
|
|
222
|
+
}
|
|
223
|
+
// General suggestions if no issues
|
|
224
|
+
if (suggestions.length === 0) {
|
|
225
|
+
suggestions.push(`✨ Great job! No major architectural issues detected.`, `Continue monitoring as your codebase grows.`);
|
|
226
|
+
}
|
|
227
|
+
return suggestions;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get layer for a file path
|
|
231
|
+
*/
|
|
232
|
+
getLayerForFile(filePath, layerMap) {
|
|
233
|
+
const normalizedPath = filePath.replace(/\\/g, '/').toLowerCase();
|
|
234
|
+
for (const [dir, layer] of layerMap) {
|
|
235
|
+
if (normalizedPath.startsWith(dir + '/') || normalizedPath === dir) {
|
|
236
|
+
return layer;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=dependency-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-analyzer.js","sourceRoot":"","sources":["../../../src/core/analyzer/dependency-analyzer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAStC,MAAM,OAAO,kBAAkB;IACrB,MAAM,CAAe;IAE7B,YAAY,MAAoB;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAsB,EAAE,UAA0B,EAAE;QAChE,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAEhD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAE5B,0BAA0B;QAC1B,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC;QAChC,MAAM,iBAAiB,GAAG,KAAK,CAAC,MAAM,CAAC;QAEvC,6BAA6B;QAC7B,MAAM,oBAAoB,GAAG,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAElE,wBAAwB;QACxB,MAAM,eAAe,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAExD,6BAA6B;QAC7B,MAAM,eAAe,GAAG,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAE7D,0DAA0D;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAE1C,qCAAqC;QACrC,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc;YACxC,CAAC,CAAC,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC;YACxC,CAAC,CAAC,SAAS,CAAC;QAEd,uBAAuB;QACvB,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC;YAC3C,UAAU;YACV,oBAAoB;YACpB,eAAe;YACf,QAAQ;YACR,eAAe;SAChB,CAAC,CAAC;QAEH,MAAM,OAAO,GAAoB;YAC/B,UAAU;YACV,iBAAiB;YACjB,oBAAoB,EAAE,oBAAoB,CAAC,MAAM;YACjD,eAAe,EAAE,eAAe,CAAC,OAAO;YACxC,WAAW,EAAE,eAAe,CAAC,GAAG;YAChC,eAAe,EAAE,eAAe,CAAC,MAAM;SACxC,CAAC;QAEF,OAAO;YACL,OAAO;YACP,QAAQ;YACR,oBAAoB;YACpB,eAAe;YACf,WAAW;YACX,WAAW;SACZ,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,wBAAwB,CAAC,KAAsB;QACrD,MAAM,MAAM,GAAyB,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;QACzC,MAAM,IAAI,GAAa,EAAE,CAAC;QAE1B,MAAM,GAAG,GAAG,CAAC,IAAY,EAAQ,EAAE;YACjC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClB,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEhB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAE5C,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC;gBAExB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3B,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAChB,CAAC;qBAAM,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACxC,gBAAgB;oBAChB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;oBAC1C,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;wBACtB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;wBAErC,8BAA8B;wBAC9B,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;wBAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC7B,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ,CAC3C,CAAC;wBAEF,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BAChC,MAAM,CAAC,IAAI,CAAC;gCACV,KAAK;gCACL,KAAK,EAAE,KAAK;6BACb,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,CAAC,GAAG,EAAE,CAAC;YACX,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,CAAC;YACZ,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,KAAsB;QAChD,MAAM,UAAU,GAAqB,EAAE,CAAC;QAExC,kBAAkB;QAClB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC3C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;QAEnD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACvC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;gBACpC,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;gBAC5D,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1C,CAAC;YACD,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,kBAAkB;QAClB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAEtC,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU;gBAAE,SAAS;YAEzC,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;YACjF,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;YAEjF,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,IAAI,WAAW,KAAK,WAAW;gBAAE,SAAS;YAE1E,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrD,UAAU,CAAC,IAAI,CAAC;oBACd,WAAW;oBACX,WAAW;oBACX,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,YAAY;oBAClC,UAAU,EAAE,UAAU,CAAC,IAAI,CAAC,YAAY;iBACzC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;IAED;;OAEG;IACK,wBAAwB,CAAC,KAAsB;QACrD,MAAM,SAAS,GAAa,EAAE,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YACpD,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YACtD,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;YACtC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QACxC,CAAC;QAED,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;QAEnC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,KAAsB;QACzC,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAE5C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;YAClC,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC;YACrC,MAAM,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;YAE3C,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,IAAI;gBACV,UAAU;gBACV,YAAY;gBACZ,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;QAED,oDAAoD;QACpD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QAErD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB,CAAC,KAAsB;QACvD,MAAM,OAAO,GAAkB,EAAE,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAE5C,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,IAAI;gBACV,WAAW,EAAE,CAAC,EAAE,uCAAuC;gBACvD,YAAY,EAAE,QAAQ,CAAC,MAAM;gBAC7B,UAAU,EAAE,OAAO,CAAC,MAAM;aAC3B,CAAC,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,IAM3B;QACC,MAAM,WAAW,GAAa,EAAE,CAAC;QAEjC,oCAAoC;QACpC,IAAI,IAAI,CAAC,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzC,WAAW,CAAC,IAAI,CACd,SAAS,IAAI,CAAC,oBAAoB,CAAC,MAAM,wCAAwC,EACjF,+CAA+C,EAC/C,gCAAgC,EAChC,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QAED,8BAA8B;QAC9B,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,WAAW,CAAC,IAAI,CACd,SAAS,IAAI,CAAC,eAAe,CAAC,MAAM,8BAA8B,EAClE,8CAA8C,EAC9C,oDAAoD,EACpD,sDAAsD,CACvD,CAAC;QACJ,CAAC;QAED,4BAA4B;QAC5B,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC;YAClC,MAAM,iBAAiB,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;YACrE,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjC,WAAW,CAAC,IAAI,CACd,GAAG,iBAAiB,CAAC,MAAM,wDAAwD,EACnF,wDAAwD,EACxD,kDAAkD,EAClD,2DAA2D,CAC5D,CAAC;YACJ,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;QACnE,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,WAAW,CAAC,IAAI,CACd,GAAG,aAAa,CAAC,MAAM,8CAA8C,EACrE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,UAAU,cAAc,CAAC,EACnF,iEAAiE,CAClE,CAAC;QACJ,CAAC;QAED,mCAAmC;QACnC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,WAAW,CAAC,IAAI,CACd,sDAAsD,EACtD,6CAA6C,CAC9C,CAAC;QACJ,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;OAEG;IACK,eAAe,CAAC,QAAgB,EAAE,QAA6B;QACrE,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAElE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC;YACpC,IAAI,cAAc,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,cAAc,KAAK,GAAG,EAAE,CAAC;gBACnE,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
|