@abhinav2203/codeflow-analysis 0.1.1 → 0.1.3

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.
Files changed (38) hide show
  1. package/dist/bin/cli.js +0 -0
  2. package/dist/conflicts.js +1 -1
  3. package/dist/cycles.d.ts +78 -8
  4. package/dist/cycles.d.ts.map +1 -1
  5. package/dist/handlers/conflicts.d.ts +1 -18
  6. package/dist/handlers/conflicts.d.ts.map +1 -1
  7. package/dist/handlers/conflicts.js +3 -3
  8. package/dist/handlers/cycles.d.ts +1 -20
  9. package/dist/handlers/cycles.d.ts.map +1 -1
  10. package/dist/handlers/cycles.js +3 -3
  11. package/dist/handlers/metrics.d.ts +1 -26
  12. package/dist/handlers/metrics.d.ts.map +1 -1
  13. package/dist/handlers/metrics.js +3 -3
  14. package/dist/handlers/refactor-detect.d.ts +1 -6
  15. package/dist/handlers/refactor-detect.d.ts.map +1 -1
  16. package/dist/handlers/refactor-detect.js +3 -3
  17. package/dist/handlers/refactor-heal.d.ts +1 -7
  18. package/dist/handlers/refactor-heal.d.ts.map +1 -1
  19. package/dist/handlers/refactor-heal.js +3 -3
  20. package/dist/handlers/smells.d.ts +1 -17
  21. package/dist/handlers/smells.d.ts.map +1 -1
  22. package/dist/handlers/smells.js +3 -3
  23. package/dist/metrics.d.ts +41 -1
  24. package/dist/metrics.d.ts.map +1 -1
  25. package/dist/metrics.js +2 -2
  26. package/dist/smells.d.ts +51 -13
  27. package/dist/smells.d.ts.map +1 -1
  28. package/dist/smells.js +5 -5
  29. package/package.json +1 -1
  30. package/src/conflicts.ts +1 -1
  31. package/src/handlers/conflicts.ts +4 -4
  32. package/src/handlers/cycles.ts +4 -4
  33. package/src/handlers/metrics.ts +4 -4
  34. package/src/handlers/refactor-detect.ts +4 -4
  35. package/src/handlers/refactor-heal.ts +4 -4
  36. package/src/handlers/smells.ts +4 -4
  37. package/src/metrics.ts +2 -2
  38. package/src/smells.ts +5 -5
package/dist/bin/cli.js CHANGED
File without changes
package/dist/conflicts.js CHANGED
@@ -15,7 +15,7 @@ export const detectGraphConflicts = async (graph, repoPath) => {
15
15
  const conflicts = [];
16
16
  // Only consider code-bearing nodes (not modules, which are structural containers).
17
17
  const repoNodes = repoGraph.nodes.filter((node) => node.kind !== "module");
18
- const blueprintRepoNodes = graph.nodes.filter((node) => node.sourceRefs.some((ref) => ref.kind === "repo"));
18
+ const blueprintRepoNodes = graph.nodes.filter((node) => node.sourceRefs?.some((ref) => ref.kind === "repo") ?? false);
19
19
  const repoMap = new Map(repoNodes.map((node) => [repoKeyForNode(node), node]));
20
20
  const blueprintMap = new Map(blueprintRepoNodes.map((node) => [repoKeyForNode(node), node]));
21
21
  // Check each blueprint node against the repo snapshot.
package/dist/cycles.d.ts CHANGED
@@ -1,28 +1,98 @@
1
1
  import { z } from "zod";
2
2
  import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
3
3
  export declare const cycleSchema: z.ZodObject<{
4
- nodeIds: z.ZodArray<z.ZodString>;
4
+ nodeIds: z.ZodArray<z.ZodString, "many">;
5
5
  edges: z.ZodArray<z.ZodObject<{
6
6
  from: z.ZodString;
7
7
  to: z.ZodString;
8
8
  kind: z.ZodString;
9
- }, z.core.$strip>>;
10
- }, z.core.$strip>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ kind: string;
11
+ from: string;
12
+ to: string;
13
+ }, {
14
+ kind: string;
15
+ from: string;
16
+ to: string;
17
+ }>, "many">;
18
+ }, "strip", z.ZodTypeAny, {
19
+ edges: {
20
+ kind: string;
21
+ from: string;
22
+ to: string;
23
+ }[];
24
+ nodeIds: string[];
25
+ }, {
26
+ edges: {
27
+ kind: string;
28
+ from: string;
29
+ to: string;
30
+ }[];
31
+ nodeIds: string[];
32
+ }>;
11
33
  export type Cycle = z.infer<typeof cycleSchema>;
12
34
  export declare const cycleReportSchema: z.ZodObject<{
13
35
  analyzedAt: z.ZodString;
14
36
  totalCycles: z.ZodNumber;
15
37
  maxCycleLength: z.ZodNumber;
16
38
  cycles: z.ZodArray<z.ZodObject<{
17
- nodeIds: z.ZodArray<z.ZodString>;
39
+ nodeIds: z.ZodArray<z.ZodString, "many">;
18
40
  edges: z.ZodArray<z.ZodObject<{
19
41
  from: z.ZodString;
20
42
  to: z.ZodString;
21
43
  kind: z.ZodString;
22
- }, z.core.$strip>>;
23
- }, z.core.$strip>>;
24
- affectedNodeIds: z.ZodArray<z.ZodString>;
25
- }, z.core.$strip>;
44
+ }, "strip", z.ZodTypeAny, {
45
+ kind: string;
46
+ from: string;
47
+ to: string;
48
+ }, {
49
+ kind: string;
50
+ from: string;
51
+ to: string;
52
+ }>, "many">;
53
+ }, "strip", z.ZodTypeAny, {
54
+ edges: {
55
+ kind: string;
56
+ from: string;
57
+ to: string;
58
+ }[];
59
+ nodeIds: string[];
60
+ }, {
61
+ edges: {
62
+ kind: string;
63
+ from: string;
64
+ to: string;
65
+ }[];
66
+ nodeIds: string[];
67
+ }>, "many">;
68
+ affectedNodeIds: z.ZodArray<z.ZodString, "many">;
69
+ }, "strip", z.ZodTypeAny, {
70
+ analyzedAt: string;
71
+ totalCycles: number;
72
+ maxCycleLength: number;
73
+ cycles: {
74
+ edges: {
75
+ kind: string;
76
+ from: string;
77
+ to: string;
78
+ }[];
79
+ nodeIds: string[];
80
+ }[];
81
+ affectedNodeIds: string[];
82
+ }, {
83
+ analyzedAt: string;
84
+ totalCycles: number;
85
+ maxCycleLength: number;
86
+ cycles: {
87
+ edges: {
88
+ kind: string;
89
+ from: string;
90
+ to: string;
91
+ }[];
92
+ nodeIds: string[];
93
+ }[];
94
+ affectedNodeIds: string[];
95
+ }>;
26
96
  export type CycleReport = z.infer<typeof cycleReportSchema>;
27
97
  /**
28
98
  * Detect all directed cycles in a blueprint graph using Tarjan's strongly-connected
@@ -1 +1 @@
1
- {"version":3,"file":"cycles.d.ts","sourceRoot":"","sources":["../src/cycles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,eAAO,MAAM,WAAW;;;;;;;iBAStB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAmE5D;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,cAAc,KAAG,WA2CpD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,cAAc,KAAG,OAejD,CAAC"}
1
+ {"version":3,"file":"cycles.d.ts","sourceRoot":"","sources":["../src/cycles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAStB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAmE5D;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,cAAc,KAAG,WA2CpD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,cAAc,KAAG,OAejD,CAAC"}
@@ -1,4 +1,3 @@
1
- import { NextResponse } from "next/server";
2
1
  /**
3
2
  * POST /api/conflicts
4
3
  *
@@ -8,21 +7,5 @@ import { NextResponse } from "next/server";
8
7
  * detecting signature mismatches, summary mismatches, missing-in-repo
9
8
  * nodes, and missing-in-blueprint symbols.
10
9
  */
11
- export declare function POST(request: Request): Promise<NextResponse<{
12
- report: {
13
- checkedAt: string;
14
- repoPath: string;
15
- conflicts: {
16
- kind: "missing-in-repo" | "missing-in-blueprint" | "signature-mismatch" | "summary-mismatch";
17
- message: string;
18
- suggestedAction: string;
19
- nodeId?: string | undefined;
20
- path?: string | undefined;
21
- blueprintValue?: string | undefined;
22
- repoValue?: string | undefined;
23
- }[];
24
- };
25
- }> | NextResponse<{
26
- error: string;
27
- }>>;
10
+ export declare function POST(request: globalThis.Request): Promise<Response>;
28
11
  //# sourceMappingURL=conflicts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../../src/handlers/conflicts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;;;;;;;IAc1C"}
1
+ {"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../../src/handlers/conflicts.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAczE"}
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
  import { detectGraphConflicts } from "../conflicts";
3
3
  import { conflictCheckRequestSchema } from "@abhinav2203/codeflow-core/schema";
4
4
  /**
@@ -14,10 +14,10 @@ export async function POST(request) {
14
14
  try {
15
15
  const payload = conflictCheckRequestSchema.parse(await request.json());
16
16
  const report = await detectGraphConflicts(payload.graph, payload.repoPath);
17
- return NextResponse.json({ report });
17
+ return Response.json({ report });
18
18
  }
19
19
  catch (error) {
20
- return NextResponse.json({
20
+ return Response.json({
21
21
  error: error instanceof Error ? error.message : "Failed to analyze graph conflicts.",
22
22
  }, { status: 400 });
23
23
  }
@@ -1,4 +1,3 @@
1
- import { NextResponse } from "next/server";
2
1
  /**
3
2
  * POST /api/analysis/cycles
4
3
  *
@@ -8,23 +7,5 @@ import { NextResponse } from "next/server";
8
7
  * Includes total cycle count, affected node IDs, per-cycle edge details,
9
8
  * and a convenience `hasCycles` boolean.
10
9
  */
11
- export declare function POST(request: Request): Promise<NextResponse<{
12
- report: {
13
- hasCycles: boolean;
14
- analyzedAt: string;
15
- totalCycles: number;
16
- maxCycleLength: number;
17
- cycles: {
18
- nodeIds: string[];
19
- edges: {
20
- from: string;
21
- to: string;
22
- kind: string;
23
- }[];
24
- }[];
25
- affectedNodeIds: string[];
26
- };
27
- }> | NextResponse<{
28
- error: string;
29
- }>>;
10
+ export declare function POST(request: globalThis.Request): Promise<Response>;
30
11
  //# sourceMappingURL=cycles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cycles.d.ts","sourceRoot":"","sources":["../../src/handlers/cycles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;;;;;;;;;IAc1C"}
1
+ {"version":3,"file":"cycles.d.ts","sourceRoot":"","sources":["../../src/handlers/cycles.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAczE"}
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
  import { detectCycles, hasCycles } from "../cycles";
3
3
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
4
4
  /**
@@ -14,10 +14,10 @@ export async function POST(request) {
14
14
  try {
15
15
  const payload = blueprintGraphSchema.parse(await request.json());
16
16
  const report = detectCycles(payload);
17
- return NextResponse.json({ report: { ...report, hasCycles: hasCycles(payload) } });
17
+ return Response.json({ report: { ...report, hasCycles: hasCycles(payload) } });
18
18
  }
19
19
  catch (error) {
20
- return NextResponse.json({
20
+ return Response.json({
21
21
  error: error instanceof Error ? error.message : "Failed to detect dependency cycles.",
22
22
  }, { status: 400 });
23
23
  }
@@ -1,4 +1,3 @@
1
- import { NextResponse } from "next/server";
2
1
  /**
3
2
  * POST /api/analysis/metrics
4
3
  *
@@ -7,29 +6,5 @@ import { NextResponse } from "next/server";
7
6
  * Returns structural graph metrics: node/edge counts, degree statistics,
8
7
  * density, connected components, and contract-level averages.
9
8
  */
10
- export declare function POST(request: Request): Promise<NextResponse<{
11
- metrics: {
12
- analyzedAt: string;
13
- nodeCount: number;
14
- edgeCount: number;
15
- nodesByKind: Record<string, number>;
16
- edgesByKind: Record<string, number>;
17
- nodesByStatus: Record<string, number>;
18
- density: number;
19
- avgDegree: number;
20
- maxInDegree: number;
21
- maxOutDegree: number;
22
- avgMethodsPerNode: number;
23
- avgResponsibilitiesPerNode: number;
24
- totalMethods: number;
25
- totalResponsibilities: number;
26
- connectedComponents: number;
27
- isolatedNodes: number;
28
- leafNodes: number;
29
- maxInDegreeNodeId?: string | undefined;
30
- maxOutDegreeNodeId?: string | undefined;
31
- };
32
- }> | NextResponse<{
33
- error: string;
34
- }>>;
9
+ export declare function POST(request: globalThis.Request): Promise<Response>;
35
10
  //# sourceMappingURL=metrics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/handlers/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;IAc1C"}
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/handlers/metrics.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAczE"}
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
  import { computeGraphMetrics } from "../metrics";
3
3
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
4
4
  /**
@@ -13,10 +13,10 @@ export async function POST(request) {
13
13
  try {
14
14
  const payload = blueprintGraphSchema.parse(await request.json());
15
15
  const metrics = computeGraphMetrics(payload);
16
- return NextResponse.json({ metrics });
16
+ return Response.json({ metrics });
17
17
  }
18
18
  catch (error) {
19
- return NextResponse.json({
19
+ return Response.json({
20
20
  error: error instanceof Error ? error.message : "Failed to compute graph metrics.",
21
21
  }, { status: 400 });
22
22
  }
@@ -1,4 +1,3 @@
1
- import { NextResponse } from "next/server";
2
1
  /**
3
2
  * POST /api/refactor/detect
4
3
  *
@@ -7,9 +6,5 @@ import { NextResponse } from "next/server";
7
6
  * Returns a {@link RefactorReport} describing all detected drift issues:
8
7
  * broken edges, missing edges, and signature drift.
9
8
  */
10
- export declare function POST(request: Request): Promise<NextResponse<{
11
- report: import("..").RefactorReport;
12
- }> | NextResponse<{
13
- error: string;
14
- }>>;
9
+ export declare function POST(request: globalThis.Request): Promise<Response>;
15
10
  //# sourceMappingURL=refactor-detect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"refactor-detect.d.ts","sourceRoot":"","sources":["../../src/handlers/refactor-detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;;;IAc1C"}
1
+ {"version":3,"file":"refactor-detect.d.ts","sourceRoot":"","sources":["../../src/handlers/refactor-detect.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAczE"}
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
  import { detectDrift } from "../refactor";
3
3
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
4
4
  /**
@@ -13,10 +13,10 @@ export async function POST(request) {
13
13
  try {
14
14
  const graph = blueprintGraphSchema.parse(await request.json());
15
15
  const report = detectDrift(graph);
16
- return NextResponse.json({ report });
16
+ return Response.json({ report });
17
17
  }
18
18
  catch (error) {
19
- return NextResponse.json({
19
+ return Response.json({
20
20
  error: error instanceof Error ? error.message : "Failed to detect architectural drift.",
21
21
  }, { status: 400 });
22
22
  }
@@ -1,4 +1,3 @@
1
- import { NextResponse } from "next/server";
2
1
  /**
3
2
  * POST /api/refactor/heal
4
3
  *
@@ -10,10 +9,5 @@ import { NextResponse } from "next/server";
10
9
  *
11
10
  * Returns both the detection report and the healed graph.
12
11
  */
13
- export declare function POST(request: Request): Promise<NextResponse<{
14
- report: import("..").RefactorReport;
15
- result: import("..").HealResult;
16
- }> | NextResponse<{
17
- error: string;
18
- }>>;
12
+ export declare function POST(request: globalThis.Request): Promise<Response>;
19
13
  //# sourceMappingURL=refactor-heal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"refactor-heal.d.ts","sourceRoot":"","sources":["../../src/handlers/refactor-heal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C;;;;;;;;;;GAUG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;;;;IAe1C"}
1
+ {"version":3,"file":"refactor-heal.d.ts","sourceRoot":"","sources":["../../src/handlers/refactor-heal.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAezE"}
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
  import { detectDrift, healGraph } from "../refactor";
3
3
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
4
4
  /**
@@ -17,10 +17,10 @@ export async function POST(request) {
17
17
  const graph = blueprintGraphSchema.parse(await request.json());
18
18
  const report = detectDrift(graph);
19
19
  const result = healGraph(graph, report);
20
- return NextResponse.json({ report, result });
20
+ return Response.json({ report, result });
21
21
  }
22
22
  catch (error) {
23
- return NextResponse.json({
23
+ return Response.json({
24
24
  error: error instanceof Error ? error.message : "Failed to heal architectural drift.",
25
25
  }, { status: 400 });
26
26
  }
@@ -1,4 +1,3 @@
1
- import { NextResponse } from "next/server";
2
1
  /**
3
2
  * POST /api/analysis/smells
4
3
  *
@@ -8,20 +7,5 @@ import { NextResponse } from "next/server";
8
7
  * orphan-node, tight-coupling, unstable-dependency, and scattered-responsibility
9
8
  * detections along with an overall health score.
10
9
  */
11
- export declare function POST(request: Request): Promise<NextResponse<{
12
- report: {
13
- analyzedAt: string;
14
- totalSmells: number;
15
- smells: {
16
- code: string;
17
- severity: "warning" | "info" | "critical";
18
- message: string;
19
- suggestion: string;
20
- nodeId?: string | undefined;
21
- }[];
22
- healthScore: number;
23
- };
24
- }> | NextResponse<{
25
- error: string;
26
- }>>;
10
+ export declare function POST(request: globalThis.Request): Promise<Response>;
27
11
  //# sourceMappingURL=smells.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"smells.d.ts","sourceRoot":"","sources":["../../src/handlers/smells.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;;;;;;IAc1C"}
1
+ {"version":3,"file":"smells.d.ts","sourceRoot":"","sources":["../../src/handlers/smells.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAczE"}
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
  import { detectSmells } from "../smells";
3
3
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
4
4
  /**
@@ -14,10 +14,10 @@ export async function POST(request) {
14
14
  try {
15
15
  const payload = blueprintGraphSchema.parse(await request.json());
16
16
  const report = detectSmells(payload);
17
- return NextResponse.json({ report });
17
+ return Response.json({ report });
18
18
  }
19
19
  catch (error) {
20
- return NextResponse.json({
20
+ return Response.json({
21
21
  error: error instanceof Error ? error.message : "Failed to detect architecture smells.",
22
22
  }, { status: 400 });
23
23
  }
package/dist/metrics.d.ts CHANGED
@@ -20,7 +20,47 @@ export declare const graphMetricsSchema: z.ZodObject<{
20
20
  connectedComponents: z.ZodNumber;
21
21
  isolatedNodes: z.ZodNumber;
22
22
  leafNodes: z.ZodNumber;
23
- }, z.core.$strip>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ analyzedAt: string;
25
+ nodeCount: number;
26
+ edgeCount: number;
27
+ nodesByKind: Record<string, number>;
28
+ edgesByKind: Record<string, number>;
29
+ nodesByStatus: Record<string, number>;
30
+ density: number;
31
+ avgDegree: number;
32
+ maxInDegree: number;
33
+ maxOutDegree: number;
34
+ avgMethodsPerNode: number;
35
+ avgResponsibilitiesPerNode: number;
36
+ totalMethods: number;
37
+ totalResponsibilities: number;
38
+ connectedComponents: number;
39
+ isolatedNodes: number;
40
+ leafNodes: number;
41
+ maxInDegreeNodeId?: string | undefined;
42
+ maxOutDegreeNodeId?: string | undefined;
43
+ }, {
44
+ analyzedAt: string;
45
+ nodeCount: number;
46
+ edgeCount: number;
47
+ nodesByKind: Record<string, number>;
48
+ edgesByKind: Record<string, number>;
49
+ nodesByStatus: Record<string, number>;
50
+ density: number;
51
+ avgDegree: number;
52
+ maxInDegree: number;
53
+ maxOutDegree: number;
54
+ avgMethodsPerNode: number;
55
+ avgResponsibilitiesPerNode: number;
56
+ totalMethods: number;
57
+ totalResponsibilities: number;
58
+ connectedComponents: number;
59
+ isolatedNodes: number;
60
+ leafNodes: number;
61
+ maxInDegreeNodeId?: string | undefined;
62
+ maxOutDegreeNodeId?: string | undefined;
63
+ }>;
24
64
  export type GraphMetrics = z.infer<typeof graphMetricsSchema>;
25
65
  /**
26
66
  * Compute structural metrics for a blueprint graph.
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;iBAoB7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAgE9D;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,cAAc,KAAG,YAuF3D,CAAC"}
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoB7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAgE9D;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,cAAc,KAAG,YAuF3D,CAAC"}
package/dist/metrics.js CHANGED
@@ -120,8 +120,8 @@ export const computeGraphMetrics = (graph) => {
120
120
  }
121
121
  }
122
122
  const avgDegree = nodeCount === 0 ? 0 : (2 * edgeCount) / nodeCount;
123
- const totalMethods = nodes.reduce((sum, n) => sum + n.contract.methods.length, 0);
124
- const totalResponsibilities = nodes.reduce((sum, n) => sum + n.contract.responsibilities.length, 0);
123
+ const totalMethods = nodes.reduce((sum, n) => sum + (n.contract?.methods?.length ?? 0), 0);
124
+ const totalResponsibilities = nodes.reduce((sum, n) => sum + (n.contract?.responsibilities?.length ?? 0), 0);
125
125
  const avgMethodsPerNode = nodeCount === 0 ? 0 : totalMethods / nodeCount;
126
126
  const avgResponsibilitiesPerNode = nodeCount === 0 ? 0 : totalResponsibilities / nodeCount;
127
127
  const nodeIds = nodes.map((n) => n.id);
package/dist/smells.d.ts CHANGED
@@ -2,32 +2,70 @@ import { z } from "zod";
2
2
  import type { BlueprintGraph } from "@abhinav2203/codeflow-core/schema";
3
3
  export declare const smellSchema: z.ZodObject<{
4
4
  code: z.ZodString;
5
- severity: z.ZodEnum<{
6
- warning: "warning";
7
- info: "info";
8
- critical: "critical";
9
- }>;
5
+ severity: z.ZodEnum<["info", "warning", "critical"]>;
10
6
  nodeId: z.ZodOptional<z.ZodString>;
11
7
  message: z.ZodString;
12
8
  suggestion: z.ZodString;
13
- }, z.core.$strip>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ message: string;
11
+ code: string;
12
+ severity: "warning" | "info" | "critical";
13
+ suggestion: string;
14
+ nodeId?: string | undefined;
15
+ }, {
16
+ message: string;
17
+ code: string;
18
+ severity: "warning" | "info" | "critical";
19
+ suggestion: string;
20
+ nodeId?: string | undefined;
21
+ }>;
14
22
  export type Smell = z.infer<typeof smellSchema>;
15
23
  export declare const smellReportSchema: z.ZodObject<{
16
24
  analyzedAt: z.ZodString;
17
25
  totalSmells: z.ZodNumber;
18
26
  smells: z.ZodArray<z.ZodObject<{
19
27
  code: z.ZodString;
20
- severity: z.ZodEnum<{
21
- warning: "warning";
22
- info: "info";
23
- critical: "critical";
24
- }>;
28
+ severity: z.ZodEnum<["info", "warning", "critical"]>;
25
29
  nodeId: z.ZodOptional<z.ZodString>;
26
30
  message: z.ZodString;
27
31
  suggestion: z.ZodString;
28
- }, z.core.$strip>>;
32
+ }, "strip", z.ZodTypeAny, {
33
+ message: string;
34
+ code: string;
35
+ severity: "warning" | "info" | "critical";
36
+ suggestion: string;
37
+ nodeId?: string | undefined;
38
+ }, {
39
+ message: string;
40
+ code: string;
41
+ severity: "warning" | "info" | "critical";
42
+ suggestion: string;
43
+ nodeId?: string | undefined;
44
+ }>, "many">;
29
45
  healthScore: z.ZodNumber;
30
- }, z.core.$strip>;
46
+ }, "strip", z.ZodTypeAny, {
47
+ analyzedAt: string;
48
+ totalSmells: number;
49
+ smells: {
50
+ message: string;
51
+ code: string;
52
+ severity: "warning" | "info" | "critical";
53
+ suggestion: string;
54
+ nodeId?: string | undefined;
55
+ }[];
56
+ healthScore: number;
57
+ }, {
58
+ analyzedAt: string;
59
+ totalSmells: number;
60
+ smells: {
61
+ message: string;
62
+ code: string;
63
+ severity: "warning" | "info" | "critical";
64
+ suggestion: string;
65
+ nodeId?: string | undefined;
66
+ }[];
67
+ healthScore: number;
68
+ }>;
31
69
  export type SmellReport = z.infer<typeof smellReportSchema>;
32
70
  /**
33
71
  * Detect all architecture smells in a blueprint graph.
@@ -1 +1 @@
1
- {"version":3,"file":"smells.d.ts","sourceRoot":"","sources":["../src/smells.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,eAAO,MAAM,WAAW;;;;;;;;;;iBAMtB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;iBAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAmL5D;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,cAAc,KAAG,WAgBpD,CAAC"}
1
+ {"version":3,"file":"smells.d.ts","sourceRoot":"","sources":["../src/smells.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;EAMtB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAmL5D;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,cAAc,KAAG,WAgBpD,CAAC"}
package/dist/smells.js CHANGED
@@ -25,13 +25,13 @@ const WARNING_PENALTY = 8;
25
25
  const INFO_PENALTY = 3;
26
26
  /** Nodes with too many methods AND responsibilities — violates single responsibility. */
27
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)
28
+ .filter((n) => (n.contract?.methods?.length ?? 0) >= GOD_NODE_MIN_METHODS &&
29
+ (n.contract?.responsibilities?.length ?? 0) >= GOD_NODE_MIN_RESPONSIBILITIES)
30
30
  .map((n) => ({
31
31
  code: "god-node",
32
32
  severity: "critical",
33
33
  nodeId: n.id,
34
- message: `Node "${n.name}" has ${n.contract.methods.length} methods and ${n.contract.responsibilities.length} responsibilities.`,
34
+ message: `Node "${n.name}" has ${n.contract?.methods?.length ?? 0} methods and ${n.contract?.responsibilities?.length ?? 0} responsibilities.`,
35
35
  suggestion: "Split this node into smaller, focused modules with single responsibilities.",
36
36
  }));
37
37
  /** Nodes with very high total degree — potential hub that other nodes depend on too heavily. */
@@ -142,12 +142,12 @@ const detectUnstableDependencies = (graph) => {
142
142
  };
143
143
  /** Nodes that declare many side effects — scattered responsibilities across the system. */
144
144
  const detectScatteredResponsibility = (graph) => graph.nodes
145
- .filter((n) => n.contract.sideEffects.length >= SCATTERED_MIN_SIDE_EFFECTS)
145
+ .filter((n) => (n.contract?.sideEffects?.length ?? 0) >= SCATTERED_MIN_SIDE_EFFECTS)
146
146
  .map((n) => ({
147
147
  code: "scattered-responsibility",
148
148
  severity: "info",
149
149
  nodeId: n.id,
150
- message: `Node "${n.name}" declares ${n.contract.sideEffects.length} side effects.`,
150
+ message: `Node "${n.name}" declares ${n.contract?.sideEffects?.length ?? 0} side effects.`,
151
151
  suggestion: "Extract side effects into dedicated service nodes to improve testability and clarity.",
152
152
  }));
153
153
  const computeHealthScore = (smells) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abhinav2203/codeflow-analysis",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/conflicts.ts CHANGED
@@ -30,7 +30,7 @@ export const detectGraphConflicts = async (
30
30
  // Only consider code-bearing nodes (not modules, which are structural containers).
31
31
  const repoNodes = repoGraph.nodes.filter((node) => node.kind !== "module");
32
32
  const blueprintRepoNodes = graph.nodes.filter((node) =>
33
- node.sourceRefs.some((ref) => ref.kind === "repo")
33
+ node.sourceRefs?.some((ref) => ref.kind === "repo") ?? false
34
34
  );
35
35
 
36
36
  const repoMap = new Map(repoNodes.map((node) => [repoKeyForNode(node), node]));
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
 
3
3
  import { detectGraphConflicts } from "../conflicts";
4
4
  import { conflictCheckRequestSchema } from "@abhinav2203/codeflow-core/schema";
@@ -12,14 +12,14 @@ import { conflictCheckRequestSchema } from "@abhinav2203/codeflow-core/schema";
12
12
  * detecting signature mismatches, summary mismatches, missing-in-repo
13
13
  * nodes, and missing-in-blueprint symbols.
14
14
  */
15
- export async function POST(request: Request) {
15
+ export async function POST(request: globalThis.Request): Promise<Response> {
16
16
  try {
17
17
  const payload = conflictCheckRequestSchema.parse(await request.json());
18
18
  const report = await detectGraphConflicts(payload.graph, payload.repoPath);
19
19
 
20
- return NextResponse.json({ report });
20
+ return Response.json({ report });
21
21
  } catch (error) {
22
- return NextResponse.json(
22
+ return Response.json(
23
23
  {
24
24
  error: error instanceof Error ? error.message : "Failed to analyze graph conflicts.",
25
25
  },
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
 
3
3
  import { detectCycles, hasCycles } from "../cycles";
4
4
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
@@ -12,14 +12,14 @@ import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
12
12
  * Includes total cycle count, affected node IDs, per-cycle edge details,
13
13
  * and a convenience `hasCycles` boolean.
14
14
  */
15
- export async function POST(request: Request) {
15
+ export async function POST(request: globalThis.Request): Promise<Response> {
16
16
  try {
17
17
  const payload = blueprintGraphSchema.parse(await request.json());
18
18
  const report = detectCycles(payload);
19
19
 
20
- return NextResponse.json({ report: { ...report, hasCycles: hasCycles(payload) } });
20
+ return Response.json({ report: { ...report, hasCycles: hasCycles(payload) } });
21
21
  } catch (error) {
22
- return NextResponse.json(
22
+ return Response.json(
23
23
  {
24
24
  error: error instanceof Error ? error.message : "Failed to detect dependency cycles.",
25
25
  },
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
 
3
3
  import { computeGraphMetrics } from "../metrics";
4
4
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
@@ -11,14 +11,14 @@ import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
11
11
  * Returns structural graph metrics: node/edge counts, degree statistics,
12
12
  * density, connected components, and contract-level averages.
13
13
  */
14
- export async function POST(request: Request) {
14
+ export async function POST(request: globalThis.Request): Promise<Response> {
15
15
  try {
16
16
  const payload = blueprintGraphSchema.parse(await request.json());
17
17
  const metrics = computeGraphMetrics(payload);
18
18
 
19
- return NextResponse.json({ metrics });
19
+ return Response.json({ metrics });
20
20
  } catch (error) {
21
- return NextResponse.json(
21
+ return Response.json(
22
22
  {
23
23
  error: error instanceof Error ? error.message : "Failed to compute graph metrics.",
24
24
  },
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
 
3
3
  import { detectDrift } from "../refactor";
4
4
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
@@ -11,14 +11,14 @@ import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
11
11
  * Returns a {@link RefactorReport} describing all detected drift issues:
12
12
  * broken edges, missing edges, and signature drift.
13
13
  */
14
- export async function POST(request: Request) {
14
+ export async function POST(request: globalThis.Request): Promise<Response> {
15
15
  try {
16
16
  const graph = blueprintGraphSchema.parse(await request.json());
17
17
  const report = detectDrift(graph);
18
18
 
19
- return NextResponse.json({ report });
19
+ return Response.json({ report });
20
20
  } catch (error) {
21
- return NextResponse.json(
21
+ return Response.json(
22
22
  {
23
23
  error: error instanceof Error ? error.message : "Failed to detect architectural drift.",
24
24
  },
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
 
3
3
  import { detectDrift, healGraph } from "../refactor";
4
4
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
@@ -14,15 +14,15 @@ import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
14
14
  *
15
15
  * Returns both the detection report and the healed graph.
16
16
  */
17
- export async function POST(request: Request) {
17
+ export async function POST(request: globalThis.Request): Promise<Response> {
18
18
  try {
19
19
  const graph = blueprintGraphSchema.parse(await request.json());
20
20
  const report = detectDrift(graph);
21
21
  const result = healGraph(graph, report);
22
22
 
23
- return NextResponse.json({ report, result });
23
+ return Response.json({ report, result });
24
24
  } catch (error) {
25
- return NextResponse.json(
25
+ return Response.json(
26
26
  {
27
27
  error: error instanceof Error ? error.message : "Failed to heal architectural drift.",
28
28
  },
@@ -1,4 +1,4 @@
1
- import { NextResponse } from "next/server";
1
+ // ponytail: native Response.json() replaces NextResponse no next peer dep needed
2
2
 
3
3
  import { detectSmells } from "../smells";
4
4
  import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
@@ -12,14 +12,14 @@ import { blueprintGraphSchema } from "@abhinav2203/codeflow-core/schema";
12
12
  * orphan-node, tight-coupling, unstable-dependency, and scattered-responsibility
13
13
  * detections along with an overall health score.
14
14
  */
15
- export async function POST(request: Request) {
15
+ export async function POST(request: globalThis.Request): Promise<Response> {
16
16
  try {
17
17
  const payload = blueprintGraphSchema.parse(await request.json());
18
18
  const report = detectSmells(payload);
19
19
 
20
- return NextResponse.json({ report });
20
+ return Response.json({ report });
21
21
  } catch (error) {
22
- return NextResponse.json(
22
+ return Response.json(
23
23
  {
24
24
  error: error instanceof Error ? error.message : "Failed to detect architecture smells.",
25
25
  },
package/src/metrics.ts CHANGED
@@ -139,9 +139,9 @@ export const computeGraphMetrics = (graph: BlueprintGraph): GraphMetrics => {
139
139
 
140
140
  const avgDegree = nodeCount === 0 ? 0 : (2 * edgeCount) / nodeCount;
141
141
 
142
- const totalMethods = nodes.reduce((sum, n) => sum + n.contract.methods.length, 0);
142
+ const totalMethods = nodes.reduce((sum, n) => sum + (n.contract?.methods?.length ?? 0), 0);
143
143
  const totalResponsibilities = nodes.reduce(
144
- (sum, n) => sum + n.contract.responsibilities.length,
144
+ (sum, n) => sum + (n.contract?.responsibilities?.length ?? 0),
145
145
  0
146
146
  );
147
147
  const avgMethodsPerNode = nodeCount === 0 ? 0 : totalMethods / nodeCount;
package/src/smells.ts CHANGED
@@ -36,14 +36,14 @@ const detectGodNodes = (graph: BlueprintGraph): Smell[] =>
36
36
  graph.nodes
37
37
  .filter(
38
38
  (n) =>
39
- n.contract.methods.length >= GOD_NODE_MIN_METHODS &&
40
- n.contract.responsibilities.length >= GOD_NODE_MIN_RESPONSIBILITIES
39
+ (n.contract?.methods?.length ?? 0) >= GOD_NODE_MIN_METHODS &&
40
+ (n.contract?.responsibilities?.length ?? 0) >= GOD_NODE_MIN_RESPONSIBILITIES
41
41
  )
42
42
  .map((n) => ({
43
43
  code: "god-node",
44
44
  severity: "critical" as const,
45
45
  nodeId: n.id,
46
- message: `Node "${n.name}" has ${n.contract.methods.length} methods and ${n.contract.responsibilities.length} responsibilities.`,
46
+ message: `Node "${n.name}" has ${n.contract?.methods?.length ?? 0} methods and ${n.contract?.responsibilities?.length ?? 0} responsibilities.`,
47
47
  suggestion:
48
48
  "Split this node into smaller, focused modules with single responsibilities.",
49
49
  }));
@@ -174,12 +174,12 @@ const detectUnstableDependencies = (graph: BlueprintGraph): Smell[] => {
174
174
  /** Nodes that declare many side effects — scattered responsibilities across the system. */
175
175
  const detectScatteredResponsibility = (graph: BlueprintGraph): Smell[] =>
176
176
  graph.nodes
177
- .filter((n) => n.contract.sideEffects.length >= SCATTERED_MIN_SIDE_EFFECTS)
177
+ .filter((n) => (n.contract?.sideEffects?.length ?? 0) >= SCATTERED_MIN_SIDE_EFFECTS)
178
178
  .map((n) => ({
179
179
  code: "scattered-responsibility",
180
180
  severity: "info" as const,
181
181
  nodeId: n.id,
182
- message: `Node "${n.name}" declares ${n.contract.sideEffects.length} side effects.`,
182
+ message: `Node "${n.name}" declares ${n.contract?.sideEffects?.length ?? 0} side effects.`,
183
183
  suggestion:
184
184
  "Extract side effects into dedicated service nodes to improve testability and clarity.",
185
185
  }));