From b1f763adac9ded43bd9df97eef944ef16ce76d04 Mon Sep 17 00:00:00 2001 From: Lum1104 Date: Sat, 28 Mar 2026 18:29:01 +0800 Subject: [PATCH] feat(core): extend GraphNode/EdgeType/StructuralAnalysis for non-code file types Add 8 new node types (config, document, service, table, endpoint, pipeline, schema, resource) and 8 new edge types (deploys, serves, migrates, documents, provisions, routes, defines_schema, triggers). Add StructuralAnalysis sub-interfaces: SectionInfo, DefinitionInfo, ServiceInfo, EndpointInfo, StepInfo, ResourceInfo, ReferenceResolution. Make resolveImports optional on AnalyzerPlugin and add extractReferences. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../packages/core/src/types.test.ts | 77 ++++++++++++++++++- .../packages/core/src/types.ts | 66 ++++++++++++++-- 2 files changed, 137 insertions(+), 6 deletions(-) diff --git a/understand-anything-plugin/packages/core/src/types.test.ts b/understand-anything-plugin/packages/core/src/types.test.ts index e155b8f..c12c106 100644 --- a/understand-anything-plugin/packages/core/src/types.test.ts +++ b/understand-anything-plugin/packages/core/src/types.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import type { KnowledgeGraph, GraphNode, GraphEdge } from "./types.js"; +import type { KnowledgeGraph, GraphNode, GraphEdge, EdgeType, StructuralAnalysis, AnalyzerPlugin, ReferenceResolution } from "./types.js"; describe("KnowledgeGraph types", () => { it("should create a valid empty KnowledgeGraph", () => { @@ -115,3 +115,78 @@ describe("KnowledgeGraph types", () => { expect(maxWeightEdge.weight).toBe(1); }); }); + +describe("Extended types", () => { + it("accepts all 13 node types", () => { + const nodeTypes: GraphNode["type"][] = [ + "file", "function", "class", "module", "concept", + "config", "document", "service", "table", "endpoint", + "pipeline", "schema", "resource", + ]; + expect(nodeTypes).toHaveLength(13); + }); + + it("accepts all 26 edge types", () => { + const edgeTypes: EdgeType[] = [ + "imports", "exports", "contains", "inherits", "implements", + "calls", "subscribes", "publishes", "middleware", + "reads_from", "writes_to", "transforms", "validates", + "depends_on", "tested_by", "configures", + "related", "similar_to", + "deploys", "serves", "migrates", "documents", + "provisions", "routes", "defines_schema", "triggers", + ]; + expect(edgeTypes).toHaveLength(26); + }); + + it("StructuralAnalysis has optional non-code fields", () => { + const analysis: StructuralAnalysis = { + functions: [], classes: [], imports: [], exports: [], + sections: [{ name: "Introduction", level: 1, lineRange: [1, 10] }], + definitions: [{ name: "users", kind: "table", lineRange: [1, 20], fields: ["id", "name"] }], + services: [{ name: "web", image: "node:22", ports: [3000] }], + endpoints: [{ method: "GET", path: "/api/users", lineRange: [5, 15] }], + steps: [{ name: "build", lineRange: [1, 5] }], + resources: [{ name: "aws_s3_bucket.main", kind: "aws_s3_bucket", lineRange: [1, 10] }], + }; + expect(analysis.sections).toHaveLength(1); + expect(analysis.definitions).toHaveLength(1); + expect(analysis.services).toHaveLength(1); + expect(analysis.endpoints).toHaveLength(1); + expect(analysis.steps).toHaveLength(1); + expect(analysis.resources).toHaveLength(1); + }); + + it("StructuralAnalysis is backward compatible (non-code fields are optional)", () => { + const analysis: StructuralAnalysis = { + functions: [], classes: [], imports: [], exports: [], + }; + expect(analysis.sections).toBeUndefined(); + expect(analysis.definitions).toBeUndefined(); + expect(analysis.services).toBeUndefined(); + }); + + it("AnalyzerPlugin allows optional resolveImports", () => { + const plugin: AnalyzerPlugin = { + name: "test-plugin", + languages: ["markdown"], + analyzeFile: () => ({ functions: [], classes: [], imports: [], exports: [] }), + // resolveImports is optional — not provided + }; + expect(plugin.resolveImports).toBeUndefined(); + expect(plugin.analyzeFile).toBeDefined(); + }); + + it("AnalyzerPlugin supports extractReferences", () => { + const refs: ReferenceResolution[] = [ + { source: "README.md", target: "./docs/guide.md", referenceType: "file", line: 5 }, + ]; + const plugin: AnalyzerPlugin = { + name: "test-plugin", + languages: ["markdown"], + analyzeFile: () => ({ functions: [], classes: [], imports: [], exports: [] }), + extractReferences: () => refs, + }; + expect(plugin.extractReferences!("README.md", "")).toEqual(refs); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/types.ts b/understand-anything-plugin/packages/core/src/types.ts index 819f17e..d38a68f 100644 --- a/understand-anything-plugin/packages/core/src/types.ts +++ b/understand-anything-plugin/packages/core/src/types.ts @@ -1,15 +1,19 @@ -// Edge types (18 total in 5 categories: Structural, Behavioral, Data flow, Dependencies, Semantic) +// Edge types (26 total in 6 categories: Structural, Behavioral, Data flow, Dependencies, Semantic, Infrastructure) export type EdgeType = | "imports" | "exports" | "contains" | "inherits" | "implements" // Structural | "calls" | "subscribes" | "publishes" | "middleware" // Behavioral | "reads_from" | "writes_to" | "transforms" | "validates" // Data flow | "depends_on" | "tested_by" | "configures" // Dependencies - | "related" | "similar_to"; // Semantic + | "related" | "similar_to" // Semantic + | "deploys" | "serves" | "migrates" | "documents" // Infrastructure + | "provisions" | "routes" | "defines_schema" | "triggers"; // Infrastructure -// GraphNode with 5 types: file, function, class, module, concept +// GraphNode with 13 types: 5 code + 8 non-code export interface GraphNode { id: string; - type: "file" | "function" | "class" | "module" | "concept"; + type: "file" | "function" | "class" | "module" | "concept" + | "config" | "document" | "service" | "table" | "endpoint" + | "pipeline" | "schema" | "resource"; name: string; filePath?: string; lineRange?: [number, number]; @@ -86,12 +90,63 @@ export interface ProjectConfig { autoUpdate: boolean; } +// Non-code structural sub-interfaces +export interface SectionInfo { + name: string; + level: number; + lineRange: [number, number]; +} + +export interface DefinitionInfo { + name: string; + kind: string; // "table", "message", "type", "schema" + lineRange: [number, number]; + fields: string[]; +} + +export interface ServiceInfo { + name: string; + image?: string; + ports: number[]; +} + +export interface EndpointInfo { + method?: string; + path: string; + lineRange: [number, number]; +} + +export interface StepInfo { + name: string; + lineRange: [number, number]; +} + +export interface ResourceInfo { + name: string; + kind: string; + lineRange: [number, number]; +} + +export interface ReferenceResolution { + source: string; + target: string; + referenceType: string; // "file", "image", "schema", "service" + line?: number; +} + // Plugin interfaces export interface StructuralAnalysis { functions: Array<{ name: string; lineRange: [number, number]; params: string[]; returnType?: string }>; classes: Array<{ name: string; lineRange: [number, number]; methods: string[]; properties: string[] }>; imports: Array<{ source: string; specifiers: string[]; lineNumber: number }>; exports: Array<{ name: string; lineNumber: number }>; + // Non-code structural data (all optional for backward compat) + sections?: SectionInfo[]; + definitions?: DefinitionInfo[]; + services?: ServiceInfo[]; + endpoints?: EndpointInfo[]; + steps?: StepInfo[]; + resources?: ResourceInfo[]; } export interface ImportResolution { @@ -110,6 +165,7 @@ export interface AnalyzerPlugin { name: string; languages: string[]; analyzeFile(filePath: string, content: string): StructuralAnalysis; - resolveImports(filePath: string, content: string): ImportResolution[]; + resolveImports?(filePath: string, content: string): ImportResolution[]; extractCallGraph?(filePath: string, content: string): CallGraphEntry[]; + extractReferences?(filePath: string, content: string): ReferenceResolution[]; }