archbyte 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/README.md +282 -0
- package/bin/archbyte.js +213 -0
- package/dist/agents/core/component-detector.d.ts +2 -0
- package/dist/agents/core/component-detector.js +57 -0
- package/dist/agents/core/connection-mapper.d.ts +2 -0
- package/dist/agents/core/connection-mapper.js +77 -0
- package/dist/agents/core/doc-parser.d.ts +2 -0
- package/dist/agents/core/doc-parser.js +64 -0
- package/dist/agents/core/env-detector.d.ts +2 -0
- package/dist/agents/core/env-detector.js +51 -0
- package/dist/agents/core/event-detector.d.ts +2 -0
- package/dist/agents/core/event-detector.js +59 -0
- package/dist/agents/core/infra-analyzer.d.ts +2 -0
- package/dist/agents/core/infra-analyzer.js +72 -0
- package/dist/agents/core/structure-scanner.d.ts +2 -0
- package/dist/agents/core/structure-scanner.js +55 -0
- package/dist/agents/core/validator.d.ts +2 -0
- package/dist/agents/core/validator.js +74 -0
- package/dist/agents/index.d.ts +24 -0
- package/dist/agents/index.js +73 -0
- package/dist/agents/llm/index.d.ts +8 -0
- package/dist/agents/llm/index.js +185 -0
- package/dist/agents/llm/prompt-builder.d.ts +3 -0
- package/dist/agents/llm/prompt-builder.js +251 -0
- package/dist/agents/llm/response-parser.d.ts +6 -0
- package/dist/agents/llm/response-parser.js +174 -0
- package/dist/agents/llm/types.d.ts +31 -0
- package/dist/agents/llm/types.js +2 -0
- package/dist/agents/pipeline/agents/component-identifier.d.ts +3 -0
- package/dist/agents/pipeline/agents/component-identifier.js +102 -0
- package/dist/agents/pipeline/agents/connection-mapper.d.ts +3 -0
- package/dist/agents/pipeline/agents/connection-mapper.js +126 -0
- package/dist/agents/pipeline/agents/flow-detector.d.ts +3 -0
- package/dist/agents/pipeline/agents/flow-detector.js +101 -0
- package/dist/agents/pipeline/agents/service-describer.d.ts +3 -0
- package/dist/agents/pipeline/agents/service-describer.js +100 -0
- package/dist/agents/pipeline/agents/validator.d.ts +3 -0
- package/dist/agents/pipeline/agents/validator.js +102 -0
- package/dist/agents/pipeline/index.d.ts +13 -0
- package/dist/agents/pipeline/index.js +128 -0
- package/dist/agents/pipeline/merger.d.ts +7 -0
- package/dist/agents/pipeline/merger.js +212 -0
- package/dist/agents/pipeline/response-parser.d.ts +5 -0
- package/dist/agents/pipeline/response-parser.js +43 -0
- package/dist/agents/pipeline/types.d.ts +92 -0
- package/dist/agents/pipeline/types.js +3 -0
- package/dist/agents/prompt-data.d.ts +1 -0
- package/dist/agents/prompt-data.js +15 -0
- package/dist/agents/prompts-encode.d.ts +9 -0
- package/dist/agents/prompts-encode.js +26 -0
- package/dist/agents/prompts.d.ts +12 -0
- package/dist/agents/prompts.js +30 -0
- package/dist/agents/providers/anthropic.d.ts +10 -0
- package/dist/agents/providers/anthropic.js +117 -0
- package/dist/agents/providers/google.d.ts +10 -0
- package/dist/agents/providers/google.js +136 -0
- package/dist/agents/providers/ollama.d.ts +9 -0
- package/dist/agents/providers/ollama.js +162 -0
- package/dist/agents/providers/openai.d.ts +9 -0
- package/dist/agents/providers/openai.js +142 -0
- package/dist/agents/providers/router.d.ts +7 -0
- package/dist/agents/providers/router.js +55 -0
- package/dist/agents/runtime/orchestrator.d.ts +34 -0
- package/dist/agents/runtime/orchestrator.js +193 -0
- package/dist/agents/runtime/registry.d.ts +23 -0
- package/dist/agents/runtime/registry.js +56 -0
- package/dist/agents/runtime/types.d.ts +117 -0
- package/dist/agents/runtime/types.js +29 -0
- package/dist/agents/static/code-sampler.d.ts +3 -0
- package/dist/agents/static/code-sampler.js +153 -0
- package/dist/agents/static/component-detector.d.ts +3 -0
- package/dist/agents/static/component-detector.js +404 -0
- package/dist/agents/static/connection-mapper.d.ts +3 -0
- package/dist/agents/static/connection-mapper.js +280 -0
- package/dist/agents/static/doc-parser.d.ts +3 -0
- package/dist/agents/static/doc-parser.js +358 -0
- package/dist/agents/static/env-detector.d.ts +3 -0
- package/dist/agents/static/env-detector.js +73 -0
- package/dist/agents/static/event-detector.d.ts +3 -0
- package/dist/agents/static/event-detector.js +70 -0
- package/dist/agents/static/file-tree-collector.d.ts +3 -0
- package/dist/agents/static/file-tree-collector.js +51 -0
- package/dist/agents/static/index.d.ts +19 -0
- package/dist/agents/static/index.js +307 -0
- package/dist/agents/static/infra-analyzer.d.ts +3 -0
- package/dist/agents/static/infra-analyzer.js +208 -0
- package/dist/agents/static/structure-scanner.d.ts +3 -0
- package/dist/agents/static/structure-scanner.js +195 -0
- package/dist/agents/static/types.d.ts +165 -0
- package/dist/agents/static/types.js +2 -0
- package/dist/agents/static/utils.d.ts +21 -0
- package/dist/agents/static/utils.js +146 -0
- package/dist/agents/static/validator.d.ts +2 -0
- package/dist/agents/static/validator.js +75 -0
- package/dist/agents/tools/claude-code.d.ts +38 -0
- package/dist/agents/tools/claude-code.js +129 -0
- package/dist/agents/tools/local-fs.d.ts +12 -0
- package/dist/agents/tools/local-fs.js +112 -0
- package/dist/agents/tools/tool-definitions.d.ts +6 -0
- package/dist/agents/tools/tool-definitions.js +66 -0
- package/dist/cli/analyze.d.ts +27 -0
- package/dist/cli/analyze.js +586 -0
- package/dist/cli/auth.d.ts +46 -0
- package/dist/cli/auth.js +397 -0
- package/dist/cli/config.d.ts +11 -0
- package/dist/cli/config.js +177 -0
- package/dist/cli/diff.d.ts +10 -0
- package/dist/cli/diff.js +144 -0
- package/dist/cli/export.d.ts +10 -0
- package/dist/cli/export.js +321 -0
- package/dist/cli/gate.d.ts +13 -0
- package/dist/cli/gate.js +131 -0
- package/dist/cli/generate.d.ts +10 -0
- package/dist/cli/generate.js +213 -0
- package/dist/cli/license-gate.d.ts +27 -0
- package/dist/cli/license-gate.js +121 -0
- package/dist/cli/patrol.d.ts +15 -0
- package/dist/cli/patrol.js +212 -0
- package/dist/cli/run.d.ts +11 -0
- package/dist/cli/run.js +24 -0
- package/dist/cli/serve.d.ts +9 -0
- package/dist/cli/serve.js +65 -0
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.js +233 -0
- package/dist/cli/shared.d.ts +68 -0
- package/dist/cli/shared.js +275 -0
- package/dist/cli/stats.d.ts +9 -0
- package/dist/cli/stats.js +158 -0
- package/dist/cli/ui.d.ts +18 -0
- package/dist/cli/ui.js +144 -0
- package/dist/cli/validate.d.ts +54 -0
- package/dist/cli/validate.js +315 -0
- package/dist/cli/workflow.d.ts +10 -0
- package/dist/cli/workflow.js +594 -0
- package/dist/server/src/generator/index.d.ts +123 -0
- package/dist/server/src/generator/index.js +254 -0
- package/dist/server/src/index.d.ts +8 -0
- package/dist/server/src/index.js +1311 -0
- package/package.json +62 -0
- package/ui/dist/assets/index-B66Til39.js +70 -0
- package/ui/dist/assets/index-BE2OWbzu.css +1 -0
- package/ui/dist/index.html +14 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Architecture diagram generator - converts analysis JSON to Architecture format
|
|
3
|
+
*/
|
|
4
|
+
export type LayerType = 'presentation' | 'application' | 'data' | 'external' | 'deployment';
|
|
5
|
+
export type NodeType = 'component' | 'service' | 'database' | 'external';
|
|
6
|
+
export interface ArchNode {
|
|
7
|
+
id: string;
|
|
8
|
+
type: NodeType;
|
|
9
|
+
label: string;
|
|
10
|
+
layer: LayerType;
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
color: string;
|
|
16
|
+
strokeColor?: string;
|
|
17
|
+
environments?: string[];
|
|
18
|
+
path?: string;
|
|
19
|
+
techStack?: string[];
|
|
20
|
+
description?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ArchEdge {
|
|
23
|
+
id: string;
|
|
24
|
+
source: string;
|
|
25
|
+
target: string;
|
|
26
|
+
label?: string;
|
|
27
|
+
color: string;
|
|
28
|
+
environments?: string[];
|
|
29
|
+
animated?: boolean;
|
|
30
|
+
style?: "solid" | "dashed";
|
|
31
|
+
}
|
|
32
|
+
export interface FlowStep {
|
|
33
|
+
edge: string;
|
|
34
|
+
label: string;
|
|
35
|
+
}
|
|
36
|
+
export interface Flow {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
color: string;
|
|
41
|
+
steps: FlowStep[];
|
|
42
|
+
}
|
|
43
|
+
export interface EnvironmentMetadata {
|
|
44
|
+
label: string;
|
|
45
|
+
color?: string;
|
|
46
|
+
source?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface Architecture {
|
|
49
|
+
nodes: ArchNode[];
|
|
50
|
+
edges: ArchEdge[];
|
|
51
|
+
flows: Flow[];
|
|
52
|
+
lastUpdated: string;
|
|
53
|
+
version: number;
|
|
54
|
+
environments?: Record<string, EnvironmentMetadata>;
|
|
55
|
+
}
|
|
56
|
+
export interface ParsedComponent {
|
|
57
|
+
id: string;
|
|
58
|
+
name: string;
|
|
59
|
+
type: "frontend" | "backend" | "worker" | "gateway" | "cli" | "library";
|
|
60
|
+
path: string;
|
|
61
|
+
techStack: string[];
|
|
62
|
+
entryPoints: string[];
|
|
63
|
+
dependencies: string[];
|
|
64
|
+
devDependencies: string[];
|
|
65
|
+
ports?: number[];
|
|
66
|
+
environments?: string[];
|
|
67
|
+
description?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface ParsedDatabase {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
type: "postgresql" | "mysql" | "mongodb" | "redis" | "sqlite" | "other";
|
|
73
|
+
}
|
|
74
|
+
export interface ParsedExternalService {
|
|
75
|
+
id: string;
|
|
76
|
+
name: string;
|
|
77
|
+
type: "api" | "auth" | "storage" | "messaging" | "analytics" | "other";
|
|
78
|
+
provider?: string;
|
|
79
|
+
envVars: string[];
|
|
80
|
+
environments?: string[];
|
|
81
|
+
color?: string;
|
|
82
|
+
strokeColor?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface ParsedConnection {
|
|
85
|
+
from: string;
|
|
86
|
+
to: string;
|
|
87
|
+
type: "http" | "grpc" | "websocket" | "database" | "queue" | "file" | "event" | "pubsub" | "cloudevents" | "import" | "other";
|
|
88
|
+
label?: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
environments?: string[];
|
|
91
|
+
color?: string;
|
|
92
|
+
eventType?: string;
|
|
93
|
+
async?: boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface ParsedProject {
|
|
96
|
+
name: string;
|
|
97
|
+
description?: string;
|
|
98
|
+
components: ParsedComponent[];
|
|
99
|
+
services: Array<{
|
|
100
|
+
name: string;
|
|
101
|
+
componentId?: string;
|
|
102
|
+
}>;
|
|
103
|
+
databases: ParsedDatabase[];
|
|
104
|
+
externalServices: ParsedExternalService[];
|
|
105
|
+
connections: ParsedConnection[];
|
|
106
|
+
metadata: {
|
|
107
|
+
isMonorepo: boolean;
|
|
108
|
+
workspaces: string[];
|
|
109
|
+
hasDocker: boolean;
|
|
110
|
+
hasCi: boolean;
|
|
111
|
+
primaryLanguage?: string;
|
|
112
|
+
frameworks: string[];
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Generate Architecture diagram from parsed project.
|
|
117
|
+
* Layout uses topological sort on connections so nodes flow left-to-right
|
|
118
|
+
* following actual sequence/data flow. Source nodes (gateways, entry-points)
|
|
119
|
+
* appear on the left; downstream nodes cascade rightward.
|
|
120
|
+
* Within each column, nodes are sorted by layer priority so that the vertical
|
|
121
|
+
* order remains visually consistent.
|
|
122
|
+
*/
|
|
123
|
+
export declare function generateArchitecture(project: ParsedProject): Architecture;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Architecture diagram generator - converts analysis JSON to Architecture format
|
|
3
|
+
*/
|
|
4
|
+
// Layer colors
|
|
5
|
+
const LAYER_STYLES = {
|
|
6
|
+
presentation: { stroke: "#1971c2", bg: "#d0ebff" },
|
|
7
|
+
application: { stroke: "#e67700", bg: "#ffe8cc" },
|
|
8
|
+
data: { stroke: "#2f9e44", bg: "#b2f2bb" },
|
|
9
|
+
external: { stroke: "#9c36b5", bg: "#eebefa" },
|
|
10
|
+
deployment: { stroke: "#c92a2a", bg: "#ffe3e3" },
|
|
11
|
+
};
|
|
12
|
+
function getLayerForComponentType(type) {
|
|
13
|
+
switch (type) {
|
|
14
|
+
case "frontend":
|
|
15
|
+
return "presentation";
|
|
16
|
+
case "backend":
|
|
17
|
+
case "worker":
|
|
18
|
+
case "gateway":
|
|
19
|
+
case "cli":
|
|
20
|
+
case "library":
|
|
21
|
+
return "application";
|
|
22
|
+
default:
|
|
23
|
+
return "application";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function getNodeTypeForComponentType(type) {
|
|
27
|
+
switch (type) {
|
|
28
|
+
case "frontend":
|
|
29
|
+
case "backend":
|
|
30
|
+
case "worker":
|
|
31
|
+
case "gateway":
|
|
32
|
+
case "cli":
|
|
33
|
+
case "library":
|
|
34
|
+
return "component";
|
|
35
|
+
default:
|
|
36
|
+
return "component";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Generate Architecture diagram from parsed project.
|
|
41
|
+
* Layout uses topological sort on connections so nodes flow left-to-right
|
|
42
|
+
* following actual sequence/data flow. Source nodes (gateways, entry-points)
|
|
43
|
+
* appear on the left; downstream nodes cascade rightward.
|
|
44
|
+
* Within each column, nodes are sorted by layer priority so that the vertical
|
|
45
|
+
* order remains visually consistent.
|
|
46
|
+
*/
|
|
47
|
+
export function generateArchitecture(project) {
|
|
48
|
+
const nodes = [];
|
|
49
|
+
const edges = [];
|
|
50
|
+
const nodePositions = new Map();
|
|
51
|
+
// Grid settings - all positions snap to this grid
|
|
52
|
+
const GRID_SIZE = 20;
|
|
53
|
+
const snapToGrid = (value) => Math.round(value / GRID_SIZE) * GRID_SIZE;
|
|
54
|
+
// Layout settings
|
|
55
|
+
const SPACING_X = 320; // Horizontal gap between columns
|
|
56
|
+
const SPACING_Y = 180; // Vertical gap between rows within a column
|
|
57
|
+
const NODE_WIDTH = 220;
|
|
58
|
+
const NODE_HEIGHT = 100;
|
|
59
|
+
const START_X = 120;
|
|
60
|
+
const START_Y = 120;
|
|
61
|
+
const allNodes = new Map();
|
|
62
|
+
for (const comp of project.components) {
|
|
63
|
+
const layer = getLayerForComponentType(comp.type);
|
|
64
|
+
const style = LAYER_STYLES[layer];
|
|
65
|
+
allNodes.set(comp.id, {
|
|
66
|
+
id: comp.id,
|
|
67
|
+
layer,
|
|
68
|
+
nodeType: getNodeTypeForComponentType(comp.type),
|
|
69
|
+
data: {
|
|
70
|
+
label: comp.name,
|
|
71
|
+
color: style.bg,
|
|
72
|
+
strokeColor: style.stroke,
|
|
73
|
+
path: comp.path,
|
|
74
|
+
techStack: comp.techStack,
|
|
75
|
+
...(comp.environments && { environments: comp.environments }),
|
|
76
|
+
...(comp.description && { description: comp.description }),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
for (const db of project.databases) {
|
|
81
|
+
const layer = "data";
|
|
82
|
+
const style = LAYER_STYLES[layer];
|
|
83
|
+
allNodes.set(db.id, {
|
|
84
|
+
id: db.id,
|
|
85
|
+
layer,
|
|
86
|
+
nodeType: "database",
|
|
87
|
+
data: { label: db.name, color: style.bg, strokeColor: style.stroke },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
for (const ext of project.externalServices) {
|
|
91
|
+
const layer = "external";
|
|
92
|
+
const style = LAYER_STYLES[layer];
|
|
93
|
+
allNodes.set(ext.id, {
|
|
94
|
+
id: ext.id,
|
|
95
|
+
layer,
|
|
96
|
+
nodeType: "external",
|
|
97
|
+
data: {
|
|
98
|
+
label: ext.name,
|
|
99
|
+
color: ext.color || style.bg,
|
|
100
|
+
strokeColor: ext.strokeColor || style.stroke,
|
|
101
|
+
...(ext.environments && { environments: ext.environments }),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// ── 2. Build directed adjacency & in-degree for topological sort ──
|
|
106
|
+
const inDegree = new Map();
|
|
107
|
+
const adjacency = new Map();
|
|
108
|
+
for (const id of allNodes.keys()) {
|
|
109
|
+
inDegree.set(id, 0);
|
|
110
|
+
adjacency.set(id, []);
|
|
111
|
+
}
|
|
112
|
+
for (const conn of project.connections) {
|
|
113
|
+
if (!allNodes.has(conn.from) || !allNodes.has(conn.to))
|
|
114
|
+
continue;
|
|
115
|
+
adjacency.get(conn.from).push(conn.to);
|
|
116
|
+
inDegree.set(conn.to, (inDegree.get(conn.to) || 0) + 1);
|
|
117
|
+
}
|
|
118
|
+
// ── 3. BFS (Kahn's algorithm) — assign depth (column) to every node ──
|
|
119
|
+
const depth = new Map();
|
|
120
|
+
const queue = [];
|
|
121
|
+
inDegree.forEach((deg, id) => {
|
|
122
|
+
if (deg === 0) {
|
|
123
|
+
queue.push(id);
|
|
124
|
+
depth.set(id, 0);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
// Clone in-degree so we can decrement during traversal
|
|
128
|
+
const remaining = new Map(inDegree);
|
|
129
|
+
while (queue.length > 0) {
|
|
130
|
+
const current = queue.shift();
|
|
131
|
+
const currentDepth = depth.get(current);
|
|
132
|
+
for (const neighbor of adjacency.get(current) || []) {
|
|
133
|
+
const newDepth = currentDepth + 1;
|
|
134
|
+
if (!depth.has(neighbor) || depth.get(neighbor) < newDepth) {
|
|
135
|
+
depth.set(neighbor, newDepth);
|
|
136
|
+
}
|
|
137
|
+
remaining.set(neighbor, remaining.get(neighbor) - 1);
|
|
138
|
+
if (remaining.get(neighbor) === 0)
|
|
139
|
+
queue.push(neighbor);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Any nodes not reached (cycles, disconnected) get depth 0
|
|
143
|
+
for (const id of allNodes.keys()) {
|
|
144
|
+
if (!depth.has(id))
|
|
145
|
+
depth.set(id, 0);
|
|
146
|
+
}
|
|
147
|
+
// ── 4. Group nodes into columns by depth ──
|
|
148
|
+
const columns = new Map();
|
|
149
|
+
for (const [id, entry] of allNodes) {
|
|
150
|
+
const col = depth.get(id);
|
|
151
|
+
if (!columns.has(col))
|
|
152
|
+
columns.set(col, []);
|
|
153
|
+
columns.get(col).push(entry);
|
|
154
|
+
}
|
|
155
|
+
// Layer priority for vertical sort within a column
|
|
156
|
+
const LAYER_ORDER = {
|
|
157
|
+
presentation: 0,
|
|
158
|
+
application: 1,
|
|
159
|
+
data: 2,
|
|
160
|
+
external: 3,
|
|
161
|
+
deployment: 4,
|
|
162
|
+
};
|
|
163
|
+
// ── 5. Position nodes left-to-right by column, sorted vertically by layer ──
|
|
164
|
+
const sortedCols = [...columns.keys()].sort((a, b) => a - b);
|
|
165
|
+
sortedCols.forEach((col, colIndex) => {
|
|
166
|
+
const colNodes = columns.get(col);
|
|
167
|
+
// Sort within column: by layer priority, then alphabetical by label
|
|
168
|
+
colNodes.sort((a, b) => {
|
|
169
|
+
const la = LAYER_ORDER[a.layer] ?? 99;
|
|
170
|
+
const lb = LAYER_ORDER[b.layer] ?? 99;
|
|
171
|
+
if (la !== lb)
|
|
172
|
+
return la - lb;
|
|
173
|
+
return a.data.label.localeCompare(b.data.label);
|
|
174
|
+
});
|
|
175
|
+
colNodes.forEach((entry, rowIndex) => {
|
|
176
|
+
const nodeX = snapToGrid(START_X + colIndex * SPACING_X);
|
|
177
|
+
const nodeY = snapToGrid(START_Y + rowIndex * SPACING_Y);
|
|
178
|
+
nodes.push({
|
|
179
|
+
id: entry.id,
|
|
180
|
+
type: entry.nodeType,
|
|
181
|
+
label: entry.data.label,
|
|
182
|
+
layer: entry.layer,
|
|
183
|
+
x: nodeX,
|
|
184
|
+
y: nodeY,
|
|
185
|
+
width: NODE_WIDTH,
|
|
186
|
+
height: NODE_HEIGHT,
|
|
187
|
+
color: entry.data.color,
|
|
188
|
+
strokeColor: entry.data.strokeColor,
|
|
189
|
+
...(entry.data.path && { path: entry.data.path }),
|
|
190
|
+
...(entry.data.techStack && { techStack: entry.data.techStack }),
|
|
191
|
+
...(entry.data.environments && { environments: entry.data.environments }),
|
|
192
|
+
...(entry.data.description && { description: entry.data.description }),
|
|
193
|
+
});
|
|
194
|
+
nodePositions.set(entry.id, { x: nodeX, y: nodeY, width: NODE_WIDTH, height: NODE_HEIGHT });
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
// Add connections as edges, merging all connections between the same pair
|
|
198
|
+
// of nodes (including bidirectional and multiple eventTypes) into a single edge
|
|
199
|
+
const edgeMap = new Map();
|
|
200
|
+
for (const conn of project.connections) {
|
|
201
|
+
const from = nodePositions.get(conn.from);
|
|
202
|
+
const to = nodePositions.get(conn.to);
|
|
203
|
+
if (!from || !to)
|
|
204
|
+
continue;
|
|
205
|
+
const forwardKey = `${conn.from}::${conn.to}`;
|
|
206
|
+
const reverseKey = `${conn.to}::${conn.from}`;
|
|
207
|
+
if (edgeMap.has(reverseKey)) {
|
|
208
|
+
edgeMap.get(reverseKey).push(conn);
|
|
209
|
+
}
|
|
210
|
+
else if (edgeMap.has(forwardKey)) {
|
|
211
|
+
edgeMap.get(forwardKey).push(conn);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
edgeMap.set(forwardKey, [conn]);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// EDA connection types that should be styled as async/dashed
|
|
218
|
+
const EDA_TYPES = new Set(["event", "pubsub", "cloudevents", "queue"]);
|
|
219
|
+
for (const [key, conns] of edgeMap) {
|
|
220
|
+
const [source, target] = key.split("::");
|
|
221
|
+
// Collect unique labels: prefer explicit labels, fall back to description
|
|
222
|
+
const labels = [...new Set(conns.map(c => c.label || c.description).filter(Boolean))];
|
|
223
|
+
const label = labels.join(" / ") || undefined;
|
|
224
|
+
// Merge environments from all connections
|
|
225
|
+
const envSet = new Set();
|
|
226
|
+
for (const c of conns) {
|
|
227
|
+
if (c.environments)
|
|
228
|
+
c.environments.forEach(e => envSet.add(e));
|
|
229
|
+
}
|
|
230
|
+
const environments = envSet.size > 0 ? [...envSet] : undefined;
|
|
231
|
+
// Determine if any connection is event-driven
|
|
232
|
+
const isEDA = conns.some(c => EDA_TYPES.has(c.type) || c.async);
|
|
233
|
+
const edaColor = "#06b6d4"; // cyan for event-driven
|
|
234
|
+
// Pick first explicit color
|
|
235
|
+
const color = conns.find(c => c.color)?.color || (isEDA ? edaColor : "#868e96");
|
|
236
|
+
const edgeId = `edge-${source}-${target}`;
|
|
237
|
+
edges.push({
|
|
238
|
+
id: edgeId,
|
|
239
|
+
source,
|
|
240
|
+
target,
|
|
241
|
+
label,
|
|
242
|
+
color,
|
|
243
|
+
...(environments?.length && { environments }),
|
|
244
|
+
...(isEDA && { style: "dashed", animated: true }),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
nodes,
|
|
249
|
+
edges,
|
|
250
|
+
flows: [],
|
|
251
|
+
lastUpdated: new Date().toISOString(),
|
|
252
|
+
version: 1,
|
|
253
|
+
};
|
|
254
|
+
}
|