@abhinav2203/codeflow-analysis 0.1.0 → 0.1.2
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/dist/app/api/analysis/cycles/route.test.d.ts +2 -0
- package/dist/app/api/analysis/cycles/route.test.d.ts.map +1 -0
- package/dist/app/api/analysis/cycles/route.test.js +92 -0
- package/dist/app/api/analysis/metrics/route.test.d.ts +2 -0
- package/dist/app/api/analysis/metrics/route.test.d.ts.map +1 -0
- package/dist/app/api/analysis/metrics/route.test.js +82 -0
- package/dist/app/api/analysis/smells/route.test.d.ts +2 -0
- package/dist/app/api/analysis/smells/route.test.d.ts.map +1 -0
- package/dist/app/api/analysis/smells/route.test.js +102 -0
- package/dist/app/api/conflicts/route.test.d.ts +2 -0
- package/dist/app/api/conflicts/route.test.d.ts.map +1 -0
- package/dist/app/api/conflicts/route.test.js +58 -0
- package/dist/app/api/refactor/detect/route.test.d.ts +2 -0
- package/dist/app/api/refactor/detect/route.test.d.ts.map +1 -0
- package/dist/app/api/refactor/detect/route.test.js +61 -0
- package/dist/app/api/refactor/heal/route.test.d.ts +2 -0
- package/dist/app/api/refactor/heal/route.test.d.ts.map +1 -0
- package/dist/app/api/refactor/heal/route.test.js +62 -0
- package/dist/bin/cli.js +5 -41
- package/dist/conflicts.d.ts +12 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +77 -0
- package/dist/conflicts.test.d.ts +2 -0
- package/dist/conflicts.test.d.ts.map +1 -0
- package/dist/conflicts.test.js +98 -0
- package/dist/cycles.d.ts +109 -0
- package/dist/cycles.d.ts.map +1 -0
- package/dist/cycles.js +129 -0
- package/dist/cycles.test.d.ts +2 -0
- package/dist/cycles.test.d.ts.map +1 -0
- package/dist/cycles.test.js +82 -0
- package/dist/handlers/conflicts.d.ts +28 -0
- package/dist/handlers/conflicts.d.ts.map +1 -0
- package/dist/handlers/conflicts.js +24 -0
- package/dist/handlers/cycles.d.ts +30 -0
- package/dist/handlers/cycles.d.ts.map +1 -0
- package/dist/handlers/cycles.js +24 -0
- package/dist/handlers/metrics.d.ts +35 -0
- package/dist/handlers/metrics.d.ts.map +1 -0
- package/dist/handlers/metrics.js +23 -0
- package/dist/handlers/refactor-detect.d.ts +15 -0
- package/dist/handlers/refactor-detect.d.ts.map +1 -0
- package/dist/handlers/refactor-detect.js +23 -0
- package/dist/handlers/refactor-heal.d.ts +19 -0
- package/dist/handlers/refactor-heal.d.ts.map +1 -0
- package/dist/handlers/refactor-heal.js +27 -0
- package/dist/handlers/smells.d.ts +27 -0
- package/dist/handlers/smells.d.ts.map +1 -0
- package/dist/handlers/smells.js +24 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/invoke.d.ts +3 -0
- package/dist/invoke.d.ts.map +1 -0
- package/dist/invoke.js +162 -0
- package/dist/metrics.d.ts +73 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +159 -0
- package/dist/metrics.test.d.ts +2 -0
- package/dist/metrics.test.d.ts.map +1 -0
- package/dist/metrics.test.js +98 -0
- package/dist/refactor.d.ts +84 -0
- package/dist/refactor.d.ts.map +1 -0
- package/dist/refactor.js +183 -0
- package/dist/refactor.test.d.ts +2 -0
- package/dist/refactor.test.d.ts.map +1 -0
- package/dist/refactor.test.js +269 -0
- package/dist/smells.d.ts +77 -0
- package/dist/smells.d.ts.map +1 -0
- package/dist/smells.js +186 -0
- package/dist/smells.test.d.ts +2 -0
- package/dist/smells.test.d.ts.map +1 -0
- package/dist/smells.test.js +120 -0
- package/package.json +3 -3
- package/src/app/api/analysis/cycles/route.test.ts +2 -2
- package/src/app/api/analysis/metrics/route.test.ts +2 -2
- package/src/app/api/analysis/smells/route.test.ts +7 -7
- package/src/app/api/conflicts/route.test.ts +2 -2
- package/src/app/api/refactor/detect/route.test.ts +2 -2
- package/src/app/api/refactor/heal/route.test.ts +2 -2
- package/src/conflicts.test.ts +2 -2
- package/src/conflicts.ts +2 -2
- package/src/cycles.test.ts +2 -2
- package/src/cycles.ts +1 -1
- package/src/handlers/conflicts.ts +1 -1
- package/src/handlers/cycles.ts +1 -1
- package/src/handlers/metrics.ts +1 -1
- package/src/handlers/refactor-detect.ts +1 -1
- package/src/handlers/refactor-heal.ts +1 -1
- package/src/handlers/smells.ts +1 -1
- package/src/invoke.ts +116 -113
- package/src/metrics.test.ts +3 -3
- package/src/metrics.ts +1 -1
- package/src/refactor.test.ts +4 -4
- package/src/refactor.ts +1 -1
- package/src/smells.test.ts +7 -7
- package/src/smells.ts +1 -1
- package/vitest.config.ts +0 -8
package/dist/smells.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const smellSchema = z.object({
|
|
3
|
+
code: z.string(),
|
|
4
|
+
severity: z.enum(["info", "warning", "critical"]),
|
|
5
|
+
nodeId: z.string().optional(),
|
|
6
|
+
message: z.string(),
|
|
7
|
+
suggestion: z.string(),
|
|
8
|
+
});
|
|
9
|
+
export const smellReportSchema = z.object({
|
|
10
|
+
analyzedAt: z.string(),
|
|
11
|
+
totalSmells: z.number(),
|
|
12
|
+
smells: z.array(smellSchema),
|
|
13
|
+
healthScore: z.number(),
|
|
14
|
+
});
|
|
15
|
+
const GOD_NODE_MIN_METHODS = 7;
|
|
16
|
+
const GOD_NODE_MIN_RESPONSIBILITIES = 5;
|
|
17
|
+
const HUB_NODE_MIN_DEGREE = 8;
|
|
18
|
+
const TIGHT_COUPLING_MIN_EDGES = 3;
|
|
19
|
+
const UNSTABLE_DEP_MIN_INCOMING = 1;
|
|
20
|
+
const UNSTABLE_DEP_MIN_OUTGOING = 4;
|
|
21
|
+
const UNSTABLE_DEP_THRESHOLD = 0.8;
|
|
22
|
+
const SCATTERED_MIN_SIDE_EFFECTS = 4;
|
|
23
|
+
const CRITICAL_PENALTY = 15;
|
|
24
|
+
const WARNING_PENALTY = 8;
|
|
25
|
+
const INFO_PENALTY = 3;
|
|
26
|
+
/** Nodes with too many methods AND responsibilities — violates single responsibility. */
|
|
27
|
+
const detectGodNodes = (graph) => graph.nodes
|
|
28
|
+
.filter((n) => n.contract.methods.length >= GOD_NODE_MIN_METHODS &&
|
|
29
|
+
n.contract.responsibilities.length >= GOD_NODE_MIN_RESPONSIBILITIES)
|
|
30
|
+
.map((n) => ({
|
|
31
|
+
code: "god-node",
|
|
32
|
+
severity: "critical",
|
|
33
|
+
nodeId: n.id,
|
|
34
|
+
message: `Node "${n.name}" has ${n.contract.methods.length} methods and ${n.contract.responsibilities.length} responsibilities.`,
|
|
35
|
+
suggestion: "Split this node into smaller, focused modules with single responsibilities.",
|
|
36
|
+
}));
|
|
37
|
+
/** Nodes with very high total degree — potential hub that other nodes depend on too heavily. */
|
|
38
|
+
const detectHubNodes = (graph) => {
|
|
39
|
+
const inDegree = new Map();
|
|
40
|
+
const outDegree = new Map();
|
|
41
|
+
for (const node of graph.nodes) {
|
|
42
|
+
inDegree.set(node.id, 0);
|
|
43
|
+
outDegree.set(node.id, 0);
|
|
44
|
+
}
|
|
45
|
+
for (const edge of graph.edges) {
|
|
46
|
+
inDegree.set(edge.to, (inDegree.get(edge.to) ?? 0) + 1);
|
|
47
|
+
outDegree.set(edge.from, (outDegree.get(edge.from) ?? 0) + 1);
|
|
48
|
+
}
|
|
49
|
+
return graph.nodes
|
|
50
|
+
.filter((n) => (inDegree.get(n.id) ?? 0) + (outDegree.get(n.id) ?? 0) >= HUB_NODE_MIN_DEGREE)
|
|
51
|
+
.map((n) => {
|
|
52
|
+
const total = (inDegree.get(n.id) ?? 0) + (outDegree.get(n.id) ?? 0);
|
|
53
|
+
return {
|
|
54
|
+
code: "hub-node",
|
|
55
|
+
severity: "warning",
|
|
56
|
+
nodeId: n.id,
|
|
57
|
+
message: `Node "${n.name}" has a total degree of ${total} (in: ${inDegree.get(n.id) ?? 0}, out: ${outDegree.get(n.id) ?? 0}).`,
|
|
58
|
+
suggestion: "Introduce an intermediary or facade to reduce direct dependencies on this node.",
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
/** Nodes with no incoming or outgoing edges — may be dead code or missing connections. */
|
|
63
|
+
const detectOrphanNodes = (graph) => {
|
|
64
|
+
const connected = new Set();
|
|
65
|
+
for (const edge of graph.edges) {
|
|
66
|
+
connected.add(edge.from);
|
|
67
|
+
connected.add(edge.to);
|
|
68
|
+
}
|
|
69
|
+
return graph.nodes
|
|
70
|
+
.filter((n) => !connected.has(n.id))
|
|
71
|
+
.map((n) => ({
|
|
72
|
+
code: "orphan-node",
|
|
73
|
+
severity: "info",
|
|
74
|
+
nodeId: n.id,
|
|
75
|
+
message: `Node "${n.name}" has no incoming or outgoing edges.`,
|
|
76
|
+
suggestion: "Verify this node is still needed; it may be dead code or missing connections.",
|
|
77
|
+
}));
|
|
78
|
+
};
|
|
79
|
+
/** Node pairs connected by three or more distinct edges — excessive coupling. */
|
|
80
|
+
const detectTightCoupling = (graph) => {
|
|
81
|
+
const pairCounts = new Map();
|
|
82
|
+
for (const edge of graph.edges) {
|
|
83
|
+
const [a, b] = [edge.from, edge.to].sort();
|
|
84
|
+
const key = `${a}\0${b}`;
|
|
85
|
+
const entry = pairCounts.get(key);
|
|
86
|
+
if (entry) {
|
|
87
|
+
entry.count++;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
pairCounts.set(key, { a, b, count: 1 });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const smells = [];
|
|
94
|
+
for (const { a, b, count } of pairCounts.values()) {
|
|
95
|
+
if (count >= TIGHT_COUPLING_MIN_EDGES) {
|
|
96
|
+
smells.push({
|
|
97
|
+
code: "tight-coupling",
|
|
98
|
+
severity: "warning",
|
|
99
|
+
nodeId: undefined,
|
|
100
|
+
message: `Nodes "${a}" and "${b}" are connected by ${count} edges.`,
|
|
101
|
+
suggestion: "Consider merging these nodes or extracting a shared interface to reduce coupling.",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return smells;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Nodes that are depended upon (incoming edges) but have many outgoing edges —
|
|
109
|
+
* unstable intermediates that are prone to breaking dependents when changed.
|
|
110
|
+
*/
|
|
111
|
+
const detectUnstableDependencies = (graph) => {
|
|
112
|
+
const inCount = new Map();
|
|
113
|
+
const outCount = new Map();
|
|
114
|
+
for (const node of graph.nodes) {
|
|
115
|
+
inCount.set(node.id, 0);
|
|
116
|
+
outCount.set(node.id, 0);
|
|
117
|
+
}
|
|
118
|
+
for (const edge of graph.edges) {
|
|
119
|
+
inCount.set(edge.to, (inCount.get(edge.to) ?? 0) + 1);
|
|
120
|
+
outCount.set(edge.from, (outCount.get(edge.from) ?? 0) + 1);
|
|
121
|
+
}
|
|
122
|
+
return graph.nodes
|
|
123
|
+
.filter((n) => {
|
|
124
|
+
const inc = inCount.get(n.id) ?? 0;
|
|
125
|
+
const out = outCount.get(n.id) ?? 0;
|
|
126
|
+
if (inc < UNSTABLE_DEP_MIN_INCOMING || out < UNSTABLE_DEP_MIN_OUTGOING)
|
|
127
|
+
return false;
|
|
128
|
+
return out / (inc + out) > UNSTABLE_DEP_THRESHOLD;
|
|
129
|
+
})
|
|
130
|
+
.map((n) => {
|
|
131
|
+
const inc = inCount.get(n.id) ?? 0;
|
|
132
|
+
const out = outCount.get(n.id) ?? 0;
|
|
133
|
+
const instability = out / (inc + out);
|
|
134
|
+
return {
|
|
135
|
+
code: "unstable-dependency",
|
|
136
|
+
severity: "warning",
|
|
137
|
+
nodeId: n.id,
|
|
138
|
+
message: `Node "${n.name}" has instability ${instability.toFixed(2)} (in: ${inc}, out: ${out}) and is depended upon.`,
|
|
139
|
+
suggestion: "Stabilize this node by reducing its outgoing dependencies or shielding dependents with an abstraction.",
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
/** Nodes that declare many side effects — scattered responsibilities across the system. */
|
|
144
|
+
const detectScatteredResponsibility = (graph) => graph.nodes
|
|
145
|
+
.filter((n) => n.contract.sideEffects.length >= SCATTERED_MIN_SIDE_EFFECTS)
|
|
146
|
+
.map((n) => ({
|
|
147
|
+
code: "scattered-responsibility",
|
|
148
|
+
severity: "info",
|
|
149
|
+
nodeId: n.id,
|
|
150
|
+
message: `Node "${n.name}" declares ${n.contract.sideEffects.length} side effects.`,
|
|
151
|
+
suggestion: "Extract side effects into dedicated service nodes to improve testability and clarity.",
|
|
152
|
+
}));
|
|
153
|
+
const computeHealthScore = (smells) => {
|
|
154
|
+
let score = 100;
|
|
155
|
+
for (const smell of smells) {
|
|
156
|
+
if (smell.severity === "critical")
|
|
157
|
+
score -= CRITICAL_PENALTY;
|
|
158
|
+
else if (smell.severity === "warning")
|
|
159
|
+
score -= WARNING_PENALTY;
|
|
160
|
+
else
|
|
161
|
+
score -= INFO_PENALTY;
|
|
162
|
+
}
|
|
163
|
+
return Math.max(0, score);
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Detect all architecture smells in a blueprint graph.
|
|
167
|
+
*
|
|
168
|
+
* Smell categories: god-node, hub-node, orphan-node, tight-coupling,
|
|
169
|
+
* unstable-dependency, scattered-responsibility.
|
|
170
|
+
*/
|
|
171
|
+
export const detectSmells = (graph) => {
|
|
172
|
+
const smells = [
|
|
173
|
+
...detectGodNodes(graph),
|
|
174
|
+
...detectHubNodes(graph),
|
|
175
|
+
...detectOrphanNodes(graph),
|
|
176
|
+
...detectTightCoupling(graph),
|
|
177
|
+
...detectUnstableDependencies(graph),
|
|
178
|
+
...detectScatteredResponsibility(graph),
|
|
179
|
+
];
|
|
180
|
+
return {
|
|
181
|
+
analyzedAt: new Date().toISOString(),
|
|
182
|
+
totalSmells: smells.length,
|
|
183
|
+
smells,
|
|
184
|
+
healthScore: computeHealthScore(smells),
|
|
185
|
+
};
|
|
186
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smells.test.d.ts","sourceRoot":"","sources":["../src/smells.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { detectSmells } from "./smells";
|
|
3
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
4
|
+
const node = (id, kind = "module", contractOverrides = {}) => ({
|
|
5
|
+
id,
|
|
6
|
+
kind,
|
|
7
|
+
name: id,
|
|
8
|
+
summary: id,
|
|
9
|
+
contract: { ...emptyContract(), ...contractOverrides },
|
|
10
|
+
sourceRefs: [],
|
|
11
|
+
generatedRefs: [],
|
|
12
|
+
traceRefs: [],
|
|
13
|
+
});
|
|
14
|
+
const edge = (from, to) => ({
|
|
15
|
+
from,
|
|
16
|
+
to,
|
|
17
|
+
kind: "calls",
|
|
18
|
+
required: true,
|
|
19
|
+
confidence: 1,
|
|
20
|
+
});
|
|
21
|
+
const graph = (projectName, nodes, edges) => ({
|
|
22
|
+
projectName,
|
|
23
|
+
mode: "essential",
|
|
24
|
+
generatedAt: "2026-03-14T00:00:00.000Z",
|
|
25
|
+
warnings: [],
|
|
26
|
+
workflows: [],
|
|
27
|
+
nodes,
|
|
28
|
+
edges,
|
|
29
|
+
});
|
|
30
|
+
const makeMethod = (name) => ({
|
|
31
|
+
name,
|
|
32
|
+
summary: `Does ${name}.`,
|
|
33
|
+
inputs: [],
|
|
34
|
+
outputs: [],
|
|
35
|
+
sideEffects: [],
|
|
36
|
+
calls: [],
|
|
37
|
+
});
|
|
38
|
+
describe("detectSmells", () => {
|
|
39
|
+
it("detects a god-node (critical)", () => {
|
|
40
|
+
const report = detectSmells(graph("GodNode", [
|
|
41
|
+
node("god", "class", {
|
|
42
|
+
responsibilities: ["r1", "r2", "r3", "r4", "r5"],
|
|
43
|
+
methods: Array.from({ length: 7 }, (_, i) => makeMethod(`method${i}`)),
|
|
44
|
+
}),
|
|
45
|
+
], []));
|
|
46
|
+
expect(report.smells.some((s) => s.code === "god-node" && s.severity === "critical")).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it("does not flag a node with only methods but few responsibilities", () => {
|
|
49
|
+
const report = detectSmells(graph("MethodsOnly", [
|
|
50
|
+
node("methodsOnly", "class", {
|
|
51
|
+
responsibilities: ["r1"],
|
|
52
|
+
methods: Array.from({ length: 8 }, (_, i) => makeMethod(`m${i}`)),
|
|
53
|
+
}),
|
|
54
|
+
], []));
|
|
55
|
+
expect(report.smells.some((s) => s.code === "god-node")).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
it("does not flag a node with only responsibilities but few methods", () => {
|
|
58
|
+
const report = detectSmells(graph("ResponsibilitiesOnly", [
|
|
59
|
+
node("respOnly", "class", {
|
|
60
|
+
responsibilities: Array.from({ length: 6 }, (_, i) => `r${i}`),
|
|
61
|
+
methods: [makeMethod("single")],
|
|
62
|
+
}),
|
|
63
|
+
], []));
|
|
64
|
+
expect(report.smells.some((s) => s.code === "god-node")).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
it("detects orphan nodes (info)", () => {
|
|
67
|
+
const report = detectSmells(graph("Orphan", [node("lonely", "function")], []));
|
|
68
|
+
expect(report.smells.some((s) => s.code === "orphan-node" && s.severity === "info")).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
it("returns health score 100 for a clean small graph", () => {
|
|
71
|
+
const report = detectSmells(graph("Clean", [node("A", "module"), node("B", "function")], [edge("A", "B")]));
|
|
72
|
+
expect(report.healthScore).toBe(100); // A→B edge means no orphans in connected graph
|
|
73
|
+
});
|
|
74
|
+
it("detects tight coupling between two nodes with 3+ edges", () => {
|
|
75
|
+
const report = detectSmells(graph("TightCoupling", [node("A", "module"), node("B", "module")], [
|
|
76
|
+
edge("A", "B"),
|
|
77
|
+
{ from: "A", to: "B", kind: "imports", required: true, confidence: 1 },
|
|
78
|
+
edge("B", "A"),
|
|
79
|
+
]));
|
|
80
|
+
expect(report.smells.some((s) => s.code === "tight-coupling" && s.severity === "warning")).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
it("does not flag two nodes with fewer than 3 edges as tight coupling", () => {
|
|
83
|
+
const report = detectSmells(graph("NotTight", [node("A", "module"), node("B", "module")], [edge("A", "B"), edge("B", "A")]));
|
|
84
|
+
expect(report.smells.some((s) => s.code === "tight-coupling")).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
it("detects scattered responsibility (info)", () => {
|
|
87
|
+
const report = detectSmells(graph("Scattered", [
|
|
88
|
+
node("scattered", "module", {
|
|
89
|
+
sideEffects: ["db-write", "email", "cache-invalidate", "log"],
|
|
90
|
+
}),
|
|
91
|
+
], []));
|
|
92
|
+
expect(report.smells.some((s) => s.code === "scattered-responsibility" && s.severity === "info")).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
it("health score decreases by correct penalty amounts", () => {
|
|
95
|
+
// god-node (critical = -15) + orphan-node (info = -3) = 82
|
|
96
|
+
const report = detectSmells(graph("Mixed", [
|
|
97
|
+
node("god", "class", {
|
|
98
|
+
responsibilities: ["r1", "r2", "r3", "r4", "r5"],
|
|
99
|
+
methods: Array.from({ length: 8 }, (_, i) => makeMethod(`m${i}`)),
|
|
100
|
+
}),
|
|
101
|
+
node("lonely", "function"),
|
|
102
|
+
], []));
|
|
103
|
+
expect(report.healthScore).toBe(100 - 15 - 3 - 3); // god-node (critical -15) + god IS orphan (-3) + lonely orphan (-3) = 79
|
|
104
|
+
});
|
|
105
|
+
it("totalSmells equals the number of individual smell records", () => {
|
|
106
|
+
const report = detectSmells(graph("Count", [node("A", "module"), node("B", "function")], []));
|
|
107
|
+
expect(report.totalSmells).toBe(report.smells.length);
|
|
108
|
+
});
|
|
109
|
+
it("smell suggestion is always non-empty", () => {
|
|
110
|
+
const report = detectSmells(graph("Suggestions", [
|
|
111
|
+
node("god", "class", {
|
|
112
|
+
responsibilities: ["r1", "r2", "r3", "r4", "r5"],
|
|
113
|
+
methods: Array.from({ length: 8 }, (_, i) => makeMethod(`m${i}`)),
|
|
114
|
+
}),
|
|
115
|
+
], []));
|
|
116
|
+
for (const smell of report.smells) {
|
|
117
|
+
expect(smell.suggestion.length).toBeGreaterThan(0);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abhinav2203/codeflow-analysis",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"build": "tsc --outDir dist --declaration --declarationMap --noEmit false"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@abhinav2203/codeflow-core": "
|
|
25
|
-
"@abhinav2203/codeflow-store": "
|
|
24
|
+
"@abhinav2203/codeflow-core": "^1.1.5",
|
|
25
|
+
"@abhinav2203/codeflow-store": "^1.0.13",
|
|
26
26
|
"zod": "^3.0.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { POST } from "../../../../handlers/cycles";
|
|
4
|
-
import type { BlueprintGraph } from "
|
|
5
|
-
import { emptyContract } from "
|
|
4
|
+
import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
|
|
5
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
6
6
|
|
|
7
7
|
const minimalNode = (id: string): BlueprintGraph["nodes"][number] => ({
|
|
8
8
|
id,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { POST } from "../../../../handlers/metrics";
|
|
4
|
-
import type { BlueprintGraph } from "
|
|
5
|
-
import { emptyContract } from "
|
|
4
|
+
import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
|
|
5
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
6
6
|
|
|
7
7
|
const minimalNode = (
|
|
8
8
|
id: string,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { POST } from "../../../../handlers/smells";
|
|
4
|
-
import type { BlueprintGraph } from "
|
|
5
|
-
import { emptyContract } from "
|
|
4
|
+
import type { BlueprintGraph, MethodSpec } from "@abhinav2203/codeflow-core/schema";
|
|
5
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
6
6
|
|
|
7
7
|
const baseGraph: BlueprintGraph = {
|
|
8
8
|
projectName: "Smells Route Test",
|
|
@@ -14,13 +14,13 @@ const baseGraph: BlueprintGraph = {
|
|
|
14
14
|
edges: [],
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const makeMethod = (name: string) => ({
|
|
17
|
+
const makeMethod = (name: string): MethodSpec => ({
|
|
18
18
|
name,
|
|
19
19
|
summary: `Does ${name}.`,
|
|
20
|
-
inputs: []
|
|
21
|
-
outputs: []
|
|
22
|
-
sideEffects: []
|
|
23
|
-
calls: []
|
|
20
|
+
inputs: [],
|
|
21
|
+
outputs: [],
|
|
22
|
+
sideEffects: [],
|
|
23
|
+
calls: [],
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
describe("POST /api/analysis/smells", () => {
|
|
@@ -3,8 +3,8 @@ import path from "node:path";
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
|
|
5
5
|
import { POST } from "../../../handlers/conflicts";
|
|
6
|
-
import type { BlueprintGraph } from "
|
|
7
|
-
import { emptyContract } from "
|
|
6
|
+
import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
|
|
7
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
8
8
|
|
|
9
9
|
const fixturePath = path.resolve(process.cwd(), "test-fixtures/sample-repo");
|
|
10
10
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { POST } from "../../../../handlers/refactor-detect";
|
|
4
|
-
import type { BlueprintGraph } from "
|
|
5
|
-
import { emptyContract } from "
|
|
4
|
+
import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
|
|
5
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
6
6
|
|
|
7
7
|
const graph: BlueprintGraph = {
|
|
8
8
|
projectName: "Refactor Detect Route",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { POST } from "../../../../handlers/refactor-heal";
|
|
4
|
-
import type { BlueprintGraph } from "
|
|
5
|
-
import { emptyContract } from "
|
|
4
|
+
import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
|
|
5
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
6
6
|
|
|
7
7
|
const graph: BlueprintGraph = {
|
|
8
8
|
projectName: "Refactor Heal Route",
|
package/src/conflicts.test.ts
CHANGED
|
@@ -3,8 +3,8 @@ import path from "node:path";
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
|
|
5
5
|
import { detectGraphConflicts } from "./conflicts";
|
|
6
|
-
import type { BlueprintGraph } from "
|
|
7
|
-
import { emptyContract } from "
|
|
6
|
+
import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
|
|
7
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
8
8
|
|
|
9
9
|
const fixturePath = path.resolve(process.cwd(), "test-fixtures/sample-repo");
|
|
10
10
|
|
package/src/conflicts.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
import { analyzeTypeScriptRepo } from "
|
|
3
|
+
import { analyzeTypeScriptRepo } from "@abhinav2203/codeflow-core/analyzer";
|
|
4
4
|
import type {
|
|
5
5
|
BlueprintGraph,
|
|
6
6
|
BlueprintNode,
|
|
7
7
|
ConflictRecord,
|
|
8
8
|
ConflictReport,
|
|
9
|
-
} from "
|
|
9
|
+
} from "@abhinav2203/codeflow-core/schema";
|
|
10
10
|
|
|
11
11
|
const repoKeyForNode = (node: BlueprintNode): string =>
|
|
12
12
|
`${node.kind}:${node.path ?? ""}:${node.name}`;
|
package/src/cycles.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { detectCycles, hasCycles } from "./cycles";
|
|
4
|
-
import type { BlueprintGraph } from "
|
|
5
|
-
import { emptyContract } from "
|
|
4
|
+
import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
|
|
5
|
+
import { emptyContract } from "@abhinav2203/codeflow-core/schema";
|
|
6
6
|
|
|
7
7
|
const node = (id: string): BlueprintGraph["nodes"][number] => ({
|
|
8
8
|
id,
|
package/src/cycles.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
|
|
3
3
|
import { detectGraphConflicts } from "../conflicts";
|
|
4
|
-
import { conflictCheckRequestSchema } from "
|
|
4
|
+
import { conflictCheckRequestSchema } from "@abhinav2203/codeflow-core/schema";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* POST /api/conflicts
|
package/src/handlers/cycles.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
|
|
3
3
|
import { detectCycles, hasCycles } from "../cycles";
|
|
4
|
-
import { blueprintGraphSchema } from "
|
|
4
|
+
import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* POST /api/analysis/cycles
|
package/src/handlers/metrics.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
|
|
3
3
|
import { computeGraphMetrics } from "../metrics";
|
|
4
|
-
import { blueprintGraphSchema } from "
|
|
4
|
+
import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* POST /api/analysis/metrics
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
|
|
3
3
|
import { detectDrift } from "../refactor";
|
|
4
|
-
import { blueprintGraphSchema } from "
|
|
4
|
+
import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* POST /api/refactor/detect
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
|
|
3
3
|
import { detectDrift, healGraph } from "../refactor";
|
|
4
|
-
import { blueprintGraphSchema } from "
|
|
4
|
+
import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* POST /api/refactor/heal
|
package/src/handlers/smells.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
|
|
3
3
|
import { detectSmells } from "../smells";
|
|
4
|
-
import { blueprintGraphSchema } from "
|
|
4
|
+
import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* POST /api/analysis/smells
|