diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..8a97a80 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,15 @@ +{ + "name": "@understand-anything/core", + "version": "0.1.0", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "vitest run" + }, + "devDependencies": { + "typescript": "^5.7.0", + "vitest": "^3.1.0" + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..a0c4ebf --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1 @@ +export * from "./types.js"; diff --git a/packages/core/src/types.test.ts b/packages/core/src/types.test.ts new file mode 100644 index 0000000..e155b8f --- /dev/null +++ b/packages/core/src/types.test.ts @@ -0,0 +1,117 @@ +import { describe, it, expect } from "vitest"; +import type { KnowledgeGraph, GraphNode, GraphEdge } from "./types.js"; + +describe("KnowledgeGraph types", () => { + it("should create a valid empty KnowledgeGraph", () => { + const graph: KnowledgeGraph = { + version: "1.0.0", + project: { + name: "test-project", + languages: [], + frameworks: [], + description: "A test project", + analyzedAt: new Date().toISOString(), + gitCommitHash: "abc123", + }, + nodes: [], + edges: [], + layers: [], + tour: [], + }; + + expect(graph.version).toBe("1.0.0"); + expect(graph.project.name).toBe("test-project"); + expect(graph.nodes).toHaveLength(0); + expect(graph.edges).toHaveLength(0); + expect(graph.layers).toHaveLength(0); + expect(graph.tour).toHaveLength(0); + }); + + it("should create valid GraphNodes with all fields", () => { + const fileNode: GraphNode = { + id: "node-1", + type: "file", + name: "index.ts", + filePath: "src/index.ts", + lineRange: [1, 50], + summary: "Entry point of the application", + tags: ["entry", "typescript"], + complexity: "simple", + languageNotes: "Uses ES module syntax", + }; + + expect(fileNode.id).toBe("node-1"); + expect(fileNode.type).toBe("file"); + expect(fileNode.filePath).toBe("src/index.ts"); + expect(fileNode.lineRange).toEqual([1, 50]); + expect(fileNode.tags).toContain("entry"); + expect(fileNode.complexity).toBe("simple"); + + const functionNode: GraphNode = { + id: "node-2", + type: "function", + name: "processData", + filePath: "src/utils.ts", + lineRange: [10, 25], + summary: "Processes raw data into structured format", + tags: ["utility", "data"], + complexity: "moderate", + }; + + expect(functionNode.type).toBe("function"); + expect(functionNode.complexity).toBe("moderate"); + expect(functionNode.languageNotes).toBeUndefined(); + + const classNode: GraphNode = { + id: "node-3", + type: "class", + name: "DataStore", + summary: "Manages application state", + tags: ["state", "core"], + complexity: "complex", + }; + + expect(classNode.type).toBe("class"); + expect(classNode.filePath).toBeUndefined(); + expect(classNode.lineRange).toBeUndefined(); + }); + + it("should create valid GraphEdges with weight bounds", () => { + const edge: GraphEdge = { + source: "node-1", + target: "node-2", + type: "imports", + direction: "forward", + description: "index.ts imports processData from utils.ts", + weight: 0.8, + }; + + expect(edge.source).toBe("node-1"); + expect(edge.target).toBe("node-2"); + expect(edge.type).toBe("imports"); + expect(edge.direction).toBe("forward"); + expect(edge.weight).toBeGreaterThanOrEqual(0); + expect(edge.weight).toBeLessThanOrEqual(1); + + const minWeightEdge: GraphEdge = { + source: "node-1", + target: "node-3", + type: "related", + direction: "bidirectional", + weight: 0, + }; + + expect(minWeightEdge.weight).toBe(0); + expect(minWeightEdge.description).toBeUndefined(); + + const maxWeightEdge: GraphEdge = { + source: "node-2", + target: "node-3", + type: "calls", + direction: "forward", + weight: 1, + }; + + expect(maxWeightEdge.weight).toBe(1); + }); +}); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts new file mode 100644 index 0000000..d9884e3 --- /dev/null +++ b/packages/core/src/types.ts @@ -0,0 +1,103 @@ +// Edge types (17 total in 5 categories: Structural, Behavioral, Data flow, Dependencies, Semantic) +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 + +// GraphNode with 5 types: file, function, class, module, concept +export interface GraphNode { + id: string; + type: "file" | "function" | "class" | "module" | "concept"; + name: string; + filePath?: string; + lineRange?: [number, number]; + summary: string; + tags: string[]; + complexity: "simple" | "moderate" | "complex"; + languageNotes?: string; +} + +// GraphEdge with rich relationship modeling +export interface GraphEdge { + source: string; + target: string; + type: EdgeType; + direction: "forward" | "backward" | "bidirectional"; + description?: string; + weight: number; // 0-1 +} + +// Layer (logical grouping) +export interface Layer { + id: string; + name: string; + description: string; + nodeIds: string[]; +} + +// TourStep (for learn mode) +export interface TourStep { + order: number; + title: string; + description: string; + nodeIds: string[]; + languageLesson?: string; +} + +// ProjectMeta +export interface ProjectMeta { + name: string; + languages: string[]; + frameworks: string[]; + description: string; + analyzedAt: string; + gitCommitHash: string; +} + +// Root KnowledgeGraph +export interface KnowledgeGraph { + version: string; + project: ProjectMeta; + nodes: GraphNode[]; + edges: GraphEdge[]; + layers: Layer[]; + tour: TourStep[]; +} + +// AnalysisMeta (for persistence) +export interface AnalysisMeta { + lastAnalyzedAt: string; + gitCommitHash: string; + version: string; + analyzedFiles: 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 }>; +} + +export interface ImportResolution { + source: string; + resolvedPath: string; + specifiers: string[]; +} + +export interface CallGraphEntry { + caller: string; + callee: string; + lineNumber: number; +} + +export interface AnalyzerPlugin { + name: string; + languages: string[]; + analyzeFile(filePath: string, content: string): StructuralAnalysis; + resolveImports(filePath: string, content: string): ImportResolution[]; + extractCallGraph?(filePath: string, content: string): CallGraphEntry[]; +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..a086b14 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4b6858..e2e9acb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,15 @@ importers: specifier: ^3.1.0 version: 3.2.4 + packages/core: + devDependencies: + typescript: + specifier: ^5.7.0 + version: 5.9.3 + vitest: + specifier: ^3.1.0 + version: 3.2.4 + packages: '@esbuild/aix-ppc64@0.27.4':