From fe01ee0f85c4f8611c3dad0778309539c4181655 Mon Sep 17 00:00:00 2001 From: Lum1104 Date: Thu, 2 Apr 2026 11:29:26 +0800 Subject: [PATCH] feat(core): add saveDomainGraph/loadDomainGraph persistence functions Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/__tests__/domain-persistence.test.ts | 64 +++++++++++++++++++ .../packages/core/src/persistence/index.ts | 34 ++++++++++ 2 files changed, 98 insertions(+) create mode 100644 understand-anything-plugin/packages/core/src/__tests__/domain-persistence.test.ts diff --git a/understand-anything-plugin/packages/core/src/__tests__/domain-persistence.test.ts b/understand-anything-plugin/packages/core/src/__tests__/domain-persistence.test.ts new file mode 100644 index 0000000..20845c3 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/__tests__/domain-persistence.test.ts @@ -0,0 +1,64 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { mkdirSync, rmSync, existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { saveDomainGraph, loadDomainGraph } from "../persistence/index.js"; +import type { KnowledgeGraph } from "../types.js"; + +const testRoot = join(tmpdir(), "ua-domain-persist-test"); + +const domainGraph: KnowledgeGraph = { + version: "1.0.0", + project: { + name: "test", + languages: ["typescript"], + frameworks: [], + description: "test", + analyzedAt: "2026-04-01T00:00:00.000Z", + gitCommitHash: "abc123", + }, + nodes: [ + { + id: "domain:orders", + type: "domain" as any, + name: "Orders", + summary: "Order management", + tags: [], + complexity: "moderate", + }, + ], + edges: [], + layers: [], + tour: [], +}; + +describe("domain graph persistence", () => { + beforeEach(() => { + if (existsSync(testRoot)) rmSync(testRoot, { recursive: true }); + mkdirSync(testRoot, { recursive: true }); + }); + + afterEach(() => { + if (existsSync(testRoot)) rmSync(testRoot, { recursive: true }); + }); + + it("saves and loads domain graph", () => { + saveDomainGraph(testRoot, domainGraph); + const loaded = loadDomainGraph(testRoot); + expect(loaded).not.toBeNull(); + expect(loaded!.nodes[0].id).toBe("domain:orders"); + }); + + it("returns null when no domain graph exists", () => { + const loaded = loadDomainGraph(testRoot); + expect(loaded).toBeNull(); + }); + + it("saves to domain-graph.json, not knowledge-graph.json", () => { + saveDomainGraph(testRoot, domainGraph); + const domainPath = join(testRoot, ".understand-anything", "domain-graph.json"); + const structuralPath = join(testRoot, ".understand-anything", "knowledge-graph.json"); + expect(existsSync(domainPath)).toBe(true); + expect(existsSync(structuralPath)).toBe(false); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/persistence/index.ts b/understand-anything-plugin/packages/core/src/persistence/index.ts index cf1cab6..f14b011 100644 --- a/understand-anything-plugin/packages/core/src/persistence/index.ts +++ b/understand-anything-plugin/packages/core/src/persistence/index.ts @@ -146,3 +146,37 @@ export function loadConfig(projectRoot: string): ProjectConfig { return { ...DEFAULT_CONFIG }; } } + +const DOMAIN_GRAPH_FILE = "domain-graph.json"; + +export function saveDomainGraph(projectRoot: string, graph: KnowledgeGraph): void { + const dir = ensureDir(projectRoot); + const sanitised = sanitiseFilePaths(graph, projectRoot); + writeFileSync( + join(dir, DOMAIN_GRAPH_FILE), + JSON.stringify(sanitised, null, 2), + "utf-8", + ); +} + +export function loadDomainGraph( + projectRoot: string, + options?: { validate?: boolean }, +): KnowledgeGraph | null { + const filePath = join(projectRoot, UA_DIR, DOMAIN_GRAPH_FILE); + if (!existsSync(filePath)) return null; + + const data = JSON.parse(readFileSync(filePath, "utf-8")); + + if (options?.validate !== false) { + const result = validateGraph(data); + if (!result.success) { + throw new Error( + `Invalid domain graph: ${result.fatal ?? "unknown error"}`, + ); + } + return result.data as KnowledgeGraph; + } + + return data as KnowledgeGraph; +}