feat(core): add saveDomainGraph/loadDomainGraph persistence functions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lum1104
2026-04-02 11:29:26 +08:00
co-authored by Claude Opus 4.6
parent 37e56d62b3
commit fe01ee0f85
2 changed files with 98 additions and 0 deletions
@@ -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);
});
});
@@ -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;
}