@monoes/monograph 1.0.0
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.
- package/.monomind/data/ranked-context.json +5 -0
- package/.monomind/sessions/current.json +13 -0
- package/__tests__/export/formats.test.ts +82 -0
- package/__tests__/fixtures/sample.py +11 -0
- package/__tests__/fixtures/sample.ts +17 -0
- package/__tests__/integration/full-pipeline.test.ts +64 -0
- package/__tests__/parsers/loader.test.ts +14 -0
- package/__tests__/parsers/python.test.ts +32 -0
- package/__tests__/parsers/typescript.test.ts +45 -0
- package/__tests__/pipeline/analysis.test.ts +58 -0
- package/__tests__/pipeline/phases.test.ts +41 -0
- package/__tests__/pipeline/runner.test.ts +28 -0
- package/__tests__/setup.ts +1 -0
- package/__tests__/storage/db.test.ts +37 -0
- package/__tests__/storage/stores.test.ts +100 -0
- package/__tests__/types.test.ts +13 -0
- package/__tests__/watch/watcher.test.ts +31 -0
- package/dist/src/export/canvas.d.ts +3 -0
- package/dist/src/export/canvas.d.ts.map +1 -0
- package/dist/src/export/canvas.js +39 -0
- package/dist/src/export/canvas.js.map +1 -0
- package/dist/src/export/cypher.d.ts +3 -0
- package/dist/src/export/cypher.d.ts.map +1 -0
- package/dist/src/export/cypher.js +24 -0
- package/dist/src/export/cypher.js.map +1 -0
- package/dist/src/export/graphml.d.ts +3 -0
- package/dist/src/export/graphml.d.ts.map +1 -0
- package/dist/src/export/graphml.js +38 -0
- package/dist/src/export/graphml.js.map +1 -0
- package/dist/src/export/html.d.ts +3 -0
- package/dist/src/export/html.d.ts.map +1 -0
- package/dist/src/export/html.js +54 -0
- package/dist/src/export/html.js.map +1 -0
- package/dist/src/export/index.d.ts +8 -0
- package/dist/src/export/index.d.ts.map +1 -0
- package/dist/src/export/index.js +8 -0
- package/dist/src/export/index.js.map +1 -0
- package/dist/src/export/json.d.ts +3 -0
- package/dist/src/export/json.d.ts.map +1 -0
- package/dist/src/export/json.js +31 -0
- package/dist/src/export/json.js.map +1 -0
- package/dist/src/export/obsidian.d.ts +3 -0
- package/dist/src/export/obsidian.d.ts.map +1 -0
- package/dist/src/export/obsidian.js +38 -0
- package/dist/src/export/obsidian.js.map +1 -0
- package/dist/src/export/svg.d.ts +3 -0
- package/dist/src/export/svg.d.ts.map +1 -0
- package/dist/src/export/svg.js +46 -0
- package/dist/src/export/svg.js.map +1 -0
- package/dist/src/graph/analyzer.d.ts +8 -0
- package/dist/src/graph/analyzer.d.ts.map +1 -0
- package/dist/src/graph/analyzer.js +25 -0
- package/dist/src/graph/analyzer.js.map +1 -0
- package/dist/src/graph/diff.d.ts +19 -0
- package/dist/src/graph/diff.d.ts.map +1 -0
- package/dist/src/graph/diff.js +44 -0
- package/dist/src/graph/diff.js.map +1 -0
- package/dist/src/graph/loader.d.ts +5 -0
- package/dist/src/graph/loader.d.ts.map +1 -0
- package/dist/src/graph/loader.js +42 -0
- package/dist/src/graph/loader.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp/resources.d.ts +11 -0
- package/dist/src/mcp/resources.d.ts.map +1 -0
- package/dist/src/mcp/resources.js +69 -0
- package/dist/src/mcp/resources.js.map +1 -0
- package/dist/src/parsers/extractor.d.ts +5 -0
- package/dist/src/parsers/extractor.d.ts.map +1 -0
- package/dist/src/parsers/extractor.js +142 -0
- package/dist/src/parsers/extractor.js.map +1 -0
- package/dist/src/parsers/go.d.ts +3 -0
- package/dist/src/parsers/go.d.ts.map +1 -0
- package/dist/src/parsers/go.js +25 -0
- package/dist/src/parsers/go.js.map +1 -0
- package/dist/src/parsers/java.d.ts +3 -0
- package/dist/src/parsers/java.d.ts.map +1 -0
- package/dist/src/parsers/java.js +25 -0
- package/dist/src/parsers/java.js.map +1 -0
- package/dist/src/parsers/language-config.d.ts +27 -0
- package/dist/src/parsers/language-config.d.ts.map +1 -0
- package/dist/src/parsers/language-config.js +2 -0
- package/dist/src/parsers/language-config.js.map +1 -0
- package/dist/src/parsers/loader.d.ts +15 -0
- package/dist/src/parsers/loader.d.ts.map +1 -0
- package/dist/src/parsers/loader.js +73 -0
- package/dist/src/parsers/loader.js.map +1 -0
- package/dist/src/parsers/python.d.ts +3 -0
- package/dist/src/parsers/python.d.ts.map +1 -0
- package/dist/src/parsers/python.js +24 -0
- package/dist/src/parsers/python.js.map +1 -0
- package/dist/src/parsers/rust.d.ts +3 -0
- package/dist/src/parsers/rust.d.ts.map +1 -0
- package/dist/src/parsers/rust.js +23 -0
- package/dist/src/parsers/rust.js.map +1 -0
- package/dist/src/parsers/typescript.d.ts +3 -0
- package/dist/src/parsers/typescript.d.ts.map +1 -0
- package/dist/src/parsers/typescript.js +39 -0
- package/dist/src/parsers/typescript.js.map +1 -0
- package/dist/src/pipeline/orchestrator.d.ts +8 -0
- package/dist/src/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/src/pipeline/orchestrator.js +37 -0
- package/dist/src/pipeline/orchestrator.js.map +1 -0
- package/dist/src/pipeline/phases/communities.d.ts +7 -0
- package/dist/src/pipeline/phases/communities.d.ts.map +1 -0
- package/dist/src/pipeline/phases/communities.js +34 -0
- package/dist/src/pipeline/phases/communities.js.map +1 -0
- package/dist/src/pipeline/phases/cross-file.d.ts +7 -0
- package/dist/src/pipeline/phases/cross-file.d.ts.map +1 -0
- package/dist/src/pipeline/phases/cross-file.js +36 -0
- package/dist/src/pipeline/phases/cross-file.js.map +1 -0
- package/dist/src/pipeline/phases/god-nodes.d.ts +7 -0
- package/dist/src/pipeline/phases/god-nodes.d.ts.map +1 -0
- package/dist/src/pipeline/phases/god-nodes.js +28 -0
- package/dist/src/pipeline/phases/god-nodes.js.map +1 -0
- package/dist/src/pipeline/phases/mro.d.ts +7 -0
- package/dist/src/pipeline/phases/mro.d.ts.map +1 -0
- package/dist/src/pipeline/phases/mro.js +41 -0
- package/dist/src/pipeline/phases/mro.js.map +1 -0
- package/dist/src/pipeline/phases/parse.d.ts +9 -0
- package/dist/src/pipeline/phases/parse.d.ts.map +1 -0
- package/dist/src/pipeline/phases/parse.js +51 -0
- package/dist/src/pipeline/phases/parse.js.map +1 -0
- package/dist/src/pipeline/phases/scan.d.ts +7 -0
- package/dist/src/pipeline/phases/scan.d.ts.map +1 -0
- package/dist/src/pipeline/phases/scan.js +60 -0
- package/dist/src/pipeline/phases/scan.js.map +1 -0
- package/dist/src/pipeline/phases/structure.d.ts +9 -0
- package/dist/src/pipeline/phases/structure.d.ts.map +1 -0
- package/dist/src/pipeline/phases/structure.js +56 -0
- package/dist/src/pipeline/phases/structure.js.map +1 -0
- package/dist/src/pipeline/phases/suggest.d.ts +7 -0
- package/dist/src/pipeline/phases/suggest.d.ts.map +1 -0
- package/dist/src/pipeline/phases/suggest.js +59 -0
- package/dist/src/pipeline/phases/suggest.js.map +1 -0
- package/dist/src/pipeline/phases/surprises.d.ts +7 -0
- package/dist/src/pipeline/phases/surprises.d.ts.map +1 -0
- package/dist/src/pipeline/phases/surprises.js +54 -0
- package/dist/src/pipeline/phases/surprises.js.map +1 -0
- package/dist/src/pipeline/runner.d.ts +8 -0
- package/dist/src/pipeline/runner.d.ts.map +1 -0
- package/dist/src/pipeline/runner.js +53 -0
- package/dist/src/pipeline/runner.js.map +1 -0
- package/dist/src/pipeline/types.d.ts +24 -0
- package/dist/src/pipeline/types.d.ts.map +1 -0
- package/dist/src/pipeline/types.js +8 -0
- package/dist/src/pipeline/types.js.map +1 -0
- package/dist/src/storage/db.d.ts +7 -0
- package/dist/src/storage/db.d.ts.map +1 -0
- package/dist/src/storage/db.js +52 -0
- package/dist/src/storage/db.js.map +1 -0
- package/dist/src/storage/edge-store.d.ts +9 -0
- package/dist/src/storage/edge-store.d.ts.map +1 -0
- package/dist/src/storage/edge-store.js +46 -0
- package/dist/src/storage/edge-store.js.map +1 -0
- package/dist/src/storage/fts-store.d.ts +11 -0
- package/dist/src/storage/fts-store.d.ts.map +1 -0
- package/dist/src/storage/fts-store.js +32 -0
- package/dist/src/storage/fts-store.js.map +1 -0
- package/dist/src/storage/node-store.d.ts +9 -0
- package/dist/src/storage/node-store.d.ts.map +1 -0
- package/dist/src/storage/node-store.js +52 -0
- package/dist/src/storage/node-store.js.map +1 -0
- package/dist/src/storage/schema.d.ts +8 -0
- package/dist/src/storage/schema.d.ts.map +1 -0
- package/dist/src/storage/schema.js +72 -0
- package/dist/src/storage/schema.js.map +1 -0
- package/dist/src/types.d.ts +76 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +37 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/watch/watcher.d.ts +16 -0
- package/dist/src/watch/watcher.d.ts.map +1 -0
- package/dist/src/watch/watcher.js +59 -0
- package/dist/src/watch/watcher.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +44 -0
- package/src/export/canvas.ts +42 -0
- package/src/export/cypher.ts +33 -0
- package/src/export/graphml.ts +48 -0
- package/src/export/html.ts +61 -0
- package/src/export/index.ts +7 -0
- package/src/export/json.ts +36 -0
- package/src/export/obsidian.ts +44 -0
- package/src/export/svg.ts +55 -0
- package/src/graph/analyzer.ts +29 -0
- package/src/graph/diff.ts +65 -0
- package/src/graph/loader.ts +41 -0
- package/src/index.ts +17 -0
- package/src/mcp/resources.ts +69 -0
- package/src/parsers/extractor.ts +180 -0
- package/src/parsers/go.ts +27 -0
- package/src/parsers/java.ts +27 -0
- package/src/parsers/language-config.ts +28 -0
- package/src/parsers/loader.ts +85 -0
- package/src/parsers/python.ts +26 -0
- package/src/parsers/rust.ts +25 -0
- package/src/parsers/typescript.ts +41 -0
- package/src/pipeline/orchestrator.ts +46 -0
- package/src/pipeline/phases/communities.ts +45 -0
- package/src/pipeline/phases/cross-file.ts +48 -0
- package/src/pipeline/phases/god-nodes.ts +40 -0
- package/src/pipeline/phases/mro.ts +52 -0
- package/src/pipeline/phases/parse.ts +62 -0
- package/src/pipeline/phases/scan.ts +58 -0
- package/src/pipeline/phases/structure.ts +68 -0
- package/src/pipeline/phases/suggest.ts +71 -0
- package/src/pipeline/phases/surprises.ts +70 -0
- package/src/pipeline/runner.ts +61 -0
- package/src/pipeline/types.ts +33 -0
- package/src/storage/db.ts +54 -0
- package/src/storage/edge-store.ts +61 -0
- package/src/storage/fts-store.ts +52 -0
- package/src/storage/node-store.ts +72 -0
- package/src/storage/schema.ts +77 -0
- package/src/types.ts +131 -0
- package/src/watch/watcher.ts +66 -0
- package/tsconfig.json +10 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "session-1777578175215",
|
|
3
|
+
"startedAt": "2026-04-30T19:42:55.215Z",
|
|
4
|
+
"platform": "darwin",
|
|
5
|
+
"cwd": "/Users/morteza/Desktop/tools/monobrain/packages/@monomind/monograph",
|
|
6
|
+
"context": {},
|
|
7
|
+
"metrics": {
|
|
8
|
+
"edits": 9,
|
|
9
|
+
"commands": 0,
|
|
10
|
+
"tasks": 0,
|
|
11
|
+
"errors": 0
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { toJson } from '../../src/export/json.js';
|
|
3
|
+
import { toCypher } from '../../src/export/cypher.js';
|
|
4
|
+
import type { MonographNode, MonographEdge } from '../../src/types.js';
|
|
5
|
+
|
|
6
|
+
let nodes: MonographNode[];
|
|
7
|
+
let edges: MonographEdge[];
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
nodes = [
|
|
11
|
+
{ id: 'n1', label: 'Class', name: 'Foo', normLabel: 'foo', isExported: true },
|
|
12
|
+
{ id: 'n2', label: 'Method', name: 'bar', normLabel: 'bar', isExported: false, filePath: 'foo.ts' },
|
|
13
|
+
];
|
|
14
|
+
edges = [
|
|
15
|
+
{ id: 'e1', sourceId: 'n1', targetId: 'n2', relation: 'CONTAINS', confidence: 'EXTRACTED', confidenceScore: 1.0 },
|
|
16
|
+
{ id: 'e2', sourceId: 'n2', targetId: 'n1', relation: 'CALLS', confidence: 'INFERRED', confidenceScore: 0.5 },
|
|
17
|
+
];
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('json export', () => {
|
|
21
|
+
it('produces nodes and edges fields', () => {
|
|
22
|
+
const result = toJson(nodes, edges);
|
|
23
|
+
const parsed = JSON.parse(result);
|
|
24
|
+
expect(parsed.nodes).toHaveLength(2);
|
|
25
|
+
expect(parsed.edges).toHaveLength(2);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('transforms node properties correctly', () => {
|
|
29
|
+
const result = toJson(nodes, edges);
|
|
30
|
+
const parsed = JSON.parse(result);
|
|
31
|
+
const firstNode = parsed.nodes[0];
|
|
32
|
+
expect(firstNode.id).toBe('n1');
|
|
33
|
+
expect(firstNode.label).toBe('Class');
|
|
34
|
+
expect(firstNode.norm_label).toBe('foo');
|
|
35
|
+
expect(firstNode.is_exported).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('includes links array', () => {
|
|
39
|
+
const result = toJson(nodes, edges);
|
|
40
|
+
const parsed = JSON.parse(result);
|
|
41
|
+
expect(parsed.links).toBeDefined();
|
|
42
|
+
expect(parsed.links).toHaveLength(2);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('cypher export', () => {
|
|
47
|
+
it('does not include destructive operations', () => {
|
|
48
|
+
const result = toCypher(nodes, edges);
|
|
49
|
+
expect(result).toContain('CREATE');
|
|
50
|
+
// Verify no standalone destructive operations (DROP/DELETE statements, not in comments)
|
|
51
|
+
const lines = result.split('\n').filter(l => !l.trim().startsWith('//'));
|
|
52
|
+
expect(lines.some(l => l.includes('DROP'))).toBe(false);
|
|
53
|
+
expect(lines.some(l => l.includes('DELETE'))).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('includes comment header', () => {
|
|
57
|
+
const result = toCypher(nodes, edges);
|
|
58
|
+
expect(result).toContain('// Generated by Monograph');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('creates nodes and edges separately', () => {
|
|
62
|
+
const result = toCypher(nodes, edges);
|
|
63
|
+
const lines = result.split('\n');
|
|
64
|
+
const createNodeLines = lines.filter(l => l.includes('CREATE') && l.includes(':'));
|
|
65
|
+
expect(createNodeLines.length).toBeGreaterThan(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('handles special characters safely', () => {
|
|
69
|
+
const nodeWithSpecial = {
|
|
70
|
+
...nodes[0],
|
|
71
|
+
name: 'Foo"Bar"Baz',
|
|
72
|
+
id: 'n-special_id',
|
|
73
|
+
};
|
|
74
|
+
const result = toCypher([nodeWithSpecial], []);
|
|
75
|
+
// Verify escaped quotes in output
|
|
76
|
+
expect(result).toContain('\\"');
|
|
77
|
+
// Verify it doesn't have unescaped double quotes in the middle
|
|
78
|
+
const lines = result.split('\n');
|
|
79
|
+
const createLines = lines.filter(l => l.includes('CREATE') && l.includes('name:'));
|
|
80
|
+
expect(createLines.length).toBeGreaterThan(0);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
export interface UserService {
|
|
4
|
+
getUser(id: string): Promise<User>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class UserServiceImpl implements UserService {
|
|
8
|
+
constructor(private db: Database) {}
|
|
9
|
+
|
|
10
|
+
async getUser(id: string): Promise<User> {
|
|
11
|
+
return this.db.find(id);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function helperFn(x: number): number {
|
|
16
|
+
return x * 2;
|
|
17
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { tmpdir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'fs';
|
|
4
|
+
import { buildAsync } from '../../src/pipeline/orchestrator.js';
|
|
5
|
+
import { openDb, closeDb } from '../../src/storage/db.js';
|
|
6
|
+
import { countNodes } from '../../src/storage/node-store.js';
|
|
7
|
+
import { countEdges } from '../../src/storage/edge-store.js';
|
|
8
|
+
import { ftsSearch } from '../../src/storage/fts-store.js';
|
|
9
|
+
|
|
10
|
+
const tmpRepo = join(tmpdir(), `monograph-integration-${Date.now()}`);
|
|
11
|
+
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
mkdirSync(join(tmpRepo, 'src'), { recursive: true });
|
|
14
|
+
writeFileSync(join(tmpRepo, 'src', 'auth.ts'), `
|
|
15
|
+
export interface AuthService {
|
|
16
|
+
login(username: string, password: string): Promise<boolean>;
|
|
17
|
+
}
|
|
18
|
+
export class AuthServiceImpl implements AuthService {
|
|
19
|
+
async login(username: string, password: string): Promise<boolean> {
|
|
20
|
+
return username.length > 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
`);
|
|
24
|
+
writeFileSync(join(tmpRepo, 'src', 'user.ts'), `
|
|
25
|
+
import { AuthService } from './auth';
|
|
26
|
+
export class UserController {
|
|
27
|
+
constructor(private auth: AuthService) {}
|
|
28
|
+
async authenticate(u: string, p: string) {
|
|
29
|
+
return this.auth.login(u, p);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterAll(() => rmSync(tmpRepo, { recursive: true, force: true }));
|
|
36
|
+
|
|
37
|
+
describe('full pipeline integration', () => {
|
|
38
|
+
it('builds without errors', async () => {
|
|
39
|
+
await expect(buildAsync(tmpRepo)).resolves.not.toThrow();
|
|
40
|
+
}, 30000);
|
|
41
|
+
|
|
42
|
+
it('creates the SQLite database', () => {
|
|
43
|
+
const dbPath = join(tmpRepo, '.monomind', 'monograph.db');
|
|
44
|
+
expect(existsSync(dbPath)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('indexes nodes and edges', () => {
|
|
48
|
+
const dbPath = join(tmpRepo, '.monomind', 'monograph.db');
|
|
49
|
+
const db = openDb(dbPath);
|
|
50
|
+
const nodes = countNodes(db);
|
|
51
|
+
const edges = countEdges(db);
|
|
52
|
+
closeDb(db);
|
|
53
|
+
expect(nodes).toBeGreaterThan(2);
|
|
54
|
+
expect(edges).toBeGreaterThan(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('FTS search finds AuthService', () => {
|
|
58
|
+
const dbPath = join(tmpRepo, '.monomind', 'monograph.db');
|
|
59
|
+
const db = openDb(dbPath);
|
|
60
|
+
const results = ftsSearch(db, 'AuthService', 10);
|
|
61
|
+
closeDb(db);
|
|
62
|
+
expect(results.some(r => r.name === 'AuthService' || r.name === 'AuthServiceImpl')).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
}, 60000);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getParser, isSupportedExtension } from '../../src/parsers/loader.js';
|
|
2
|
+
|
|
3
|
+
describe('isSupportedExtension', () => {
|
|
4
|
+
it('recognises .ts', () => expect(isSupportedExtension('.ts')).toBe(true));
|
|
5
|
+
it('recognises .py', () => expect(isSupportedExtension('.py')).toBe(true));
|
|
6
|
+
it('rejects .css', () => expect(isSupportedExtension('.css')).toBe(false));
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
describe('getParser', () => {
|
|
10
|
+
it('returns a parser for TypeScript', async () => {
|
|
11
|
+
const parser = await getParser('.ts');
|
|
12
|
+
expect(parser).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { parseFile } from '../../src/parsers/loader.js';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const fixturePath = join(__dirname, '../fixtures/sample.py');
|
|
8
|
+
|
|
9
|
+
describe('Python parser', () => {
|
|
10
|
+
let result: Awaited<ReturnType<typeof parseFile>>;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const source = readFileSync(fixturePath, 'utf-8');
|
|
14
|
+
result = await parseFile(fixturePath, source, 'src/sample.py');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('extracts the class', () => {
|
|
18
|
+
expect(result.nodes.some(n => n.label === 'Class' && n.name === 'UserService')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('extracts the method', () => {
|
|
22
|
+
expect(result.nodes.some(n => n.label === 'Method' && n.name === 'get_user')).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('extracts the function', () => {
|
|
26
|
+
expect(result.nodes.some(n => n.label === 'Function' && n.name === 'helper_fn')).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('extracts IMPORTS edge for os.path', () => {
|
|
30
|
+
expect(result.edges.some(e => e.relation === 'IMPORTS')).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { parseFile } from '../../src/parsers/loader.js';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const fixturePath = join(__dirname, '../fixtures/sample.ts');
|
|
8
|
+
|
|
9
|
+
describe('TypeScript parser', () => {
|
|
10
|
+
let result: Awaited<ReturnType<typeof parseFile>>;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const source = readFileSync(fixturePath, 'utf-8');
|
|
14
|
+
result = await parseFile(fixturePath, source, 'src/sample.ts');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('extracts the interface node', () => {
|
|
18
|
+
expect(result.nodes.some(n => n.label === 'Interface' && n.name === 'UserService')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('extracts the class node', () => {
|
|
22
|
+
expect(result.nodes.some(n => n.label === 'Class' && n.name === 'UserServiceImpl')).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('extracts the function node', () => {
|
|
26
|
+
expect(result.nodes.some(n => n.label === 'Function' && n.name === 'helperFn')).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('extracts the method node', () => {
|
|
30
|
+
expect(result.nodes.some(n => n.label === 'Method' && n.name === 'getUser')).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('extracts the IMPLEMENTS edge', () => {
|
|
34
|
+
expect(result.edges.some(e => e.relation === 'IMPLEMENTS')).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('extracts the IMPORTS edge for fs/promises', () => {
|
|
38
|
+
expect(result.edges.some(e => e.relation === 'IMPORTS' && e.targetId.includes('fs'))).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('marks exported symbols', () => {
|
|
42
|
+
const cls = result.nodes.find(n => n.name === 'UserServiceImpl');
|
|
43
|
+
expect(cls?.isExported).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import type { PipelineContext } from '../../src/pipeline/types.js';
|
|
3
|
+
import { godNodesPhase } from '../../src/pipeline/phases/god-nodes.js';
|
|
4
|
+
import { suggestPhase } from '../../src/pipeline/phases/suggest.js';
|
|
5
|
+
|
|
6
|
+
describe('god-nodes phase', () => {
|
|
7
|
+
it('returns top nodes by degree, excluding File/Folder nodes', async () => {
|
|
8
|
+
const ctx = { onProgress: vi.fn() } as unknown as PipelineContext;
|
|
9
|
+
const deps = new Map<string, unknown>();
|
|
10
|
+
deps.set('cross-file', {
|
|
11
|
+
resolvedEdges: [
|
|
12
|
+
{ id: 'e1', sourceId: 'cls_a', targetId: 'fn_b', relation: 'CALLS', confidence: 'EXTRACTED', confidenceScore: 1.0 },
|
|
13
|
+
{ id: 'e2', sourceId: 'cls_a', targetId: 'fn_c', relation: 'CALLS', confidence: 'EXTRACTED', confidenceScore: 1.0 },
|
|
14
|
+
]
|
|
15
|
+
});
|
|
16
|
+
deps.set('parse', {
|
|
17
|
+
symbolNodes: [
|
|
18
|
+
{ id: 'cls_a', label: 'Class', name: 'A', normLabel: 'a', isExported: true },
|
|
19
|
+
{ id: 'fn_b', label: 'Function', name: 'b', normLabel: 'b', isExported: false },
|
|
20
|
+
{ id: 'fn_c', label: 'Function', name: 'c', normLabel: 'c', isExported: false },
|
|
21
|
+
{ id: 'file_x', label: 'File', name: 'x.ts', normLabel: 'x.ts', isExported: false },
|
|
22
|
+
],
|
|
23
|
+
allEdges: []
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const result = await godNodesPhase.execute(ctx, deps) as { godNodes: unknown[] };
|
|
27
|
+
const ids = result.godNodes.map((n: any) => n.id);
|
|
28
|
+
expect(ids).toContain('cls_a');
|
|
29
|
+
expect(ids).not.toContain('file_x');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('suggest phase', () => {
|
|
34
|
+
it('returns at least one signal type', async () => {
|
|
35
|
+
const ctx = { onProgress: vi.fn() } as unknown as PipelineContext;
|
|
36
|
+
const deps = new Map<string, unknown>();
|
|
37
|
+
|
|
38
|
+
deps.set('parse', {
|
|
39
|
+
symbolNodes: [
|
|
40
|
+
{ id: 'n1', label: 'Class', name: 'A', normLabel: 'a', isExported: false },
|
|
41
|
+
{ id: 'n2', label: 'Class', name: 'B', normLabel: 'b', isExported: false },
|
|
42
|
+
],
|
|
43
|
+
allEdges: [{
|
|
44
|
+
id: 'e1', sourceId: 'n1', targetId: 'n2',
|
|
45
|
+
relation: 'CALLS', confidence: 'AMBIGUOUS', confidenceScore: 0.2
|
|
46
|
+
}]
|
|
47
|
+
});
|
|
48
|
+
deps.set('cross-file', { resolvedEdges: [] });
|
|
49
|
+
deps.set('mro', { mroEdges: [] });
|
|
50
|
+
deps.set('communities', { memberships: new Map(), communityLabels: new Map() });
|
|
51
|
+
deps.set('god-nodes', { godNodes: [] });
|
|
52
|
+
deps.set('surprises', { surprises: [] });
|
|
53
|
+
|
|
54
|
+
const result = await suggestPhase.execute(ctx, deps) as { questions: unknown[] };
|
|
55
|
+
expect(result.questions.length).toBeGreaterThan(0);
|
|
56
|
+
expect((result.questions[0] as any).type).toBe('ambiguous_edge');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { tmpdir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
4
|
+
import { scanPhase } from '../../src/pipeline/phases/scan.js';
|
|
5
|
+
import { structurePhase } from '../../src/pipeline/phases/structure.js';
|
|
6
|
+
import type { PipelineContext } from '../../src/pipeline/types.js';
|
|
7
|
+
|
|
8
|
+
const tmpRepo = join(tmpdir(), `monograph-test-repo-${Date.now()}`);
|
|
9
|
+
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
mkdirSync(join(tmpRepo, 'src'), { recursive: true });
|
|
12
|
+
writeFileSync(join(tmpRepo, 'src', 'index.ts'), 'export const x = 1;');
|
|
13
|
+
writeFileSync(join(tmpRepo, 'src', 'util.py'), 'def helper(): pass');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterAll(() => rmSync(tmpRepo, { recursive: true, force: true }));
|
|
17
|
+
|
|
18
|
+
const ctx = {
|
|
19
|
+
repoPath: tmpRepo,
|
|
20
|
+
onProgress: vi.fn(),
|
|
21
|
+
options: { codeOnly: false, ignore: [], maxFileSizeBytes: 524288, workerPoolThreshold: 15, workerChunkBudgetBytes: 20971520 },
|
|
22
|
+
} as unknown as PipelineContext;
|
|
23
|
+
|
|
24
|
+
describe('scan phase', () => {
|
|
25
|
+
it('finds source files', async () => {
|
|
26
|
+
const outputs = new Map<string, unknown>();
|
|
27
|
+
const result = await scanPhase.execute(ctx, outputs) as { filePaths: string[] };
|
|
28
|
+
expect(result.filePaths.length).toBeGreaterThan(0);
|
|
29
|
+
expect(result.filePaths.some(p => p.endsWith('index.ts'))).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('structure phase', () => {
|
|
34
|
+
it('creates File and Folder nodes', async () => {
|
|
35
|
+
const outputs = new Map<string, unknown>();
|
|
36
|
+
const scanOutput = await scanPhase.execute(ctx, outputs);
|
|
37
|
+
outputs.set('scan', scanOutput);
|
|
38
|
+
const result = await structurePhase.execute(ctx, outputs) as { fileNodes: unknown[] };
|
|
39
|
+
expect(result.fileNodes.length).toBeGreaterThan(0);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PipelineRunner } from '../../src/pipeline/runner.js';
|
|
2
|
+
import type { PipelinePhase, PipelineContext } from '../../src/pipeline/types.js';
|
|
3
|
+
|
|
4
|
+
describe('PipelineRunner', () => {
|
|
5
|
+
it('executes phases in dependency order', async () => {
|
|
6
|
+
const order: string[] = [];
|
|
7
|
+
|
|
8
|
+
const phases: PipelinePhase<unknown>[] = [
|
|
9
|
+
{ name: 'c', deps: ['a', 'b'], execute: async () => { order.push('c'); return 'c'; } },
|
|
10
|
+
{ name: 'a', deps: [], execute: async () => { order.push('a'); return 'a'; } },
|
|
11
|
+
{ name: 'b', deps: ['a'], execute: async () => { order.push('b'); return 'b'; } },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const runner = new PipelineRunner(phases);
|
|
15
|
+
await runner.run({} as PipelineContext);
|
|
16
|
+
|
|
17
|
+
expect(order.indexOf('a')).toBeLessThan(order.indexOf('b'));
|
|
18
|
+
expect(order.indexOf('b')).toBeLessThan(order.indexOf('c'));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('throws on cycle detection', () => {
|
|
22
|
+
const phases: PipelinePhase<unknown>[] = [
|
|
23
|
+
{ name: 'a', deps: ['b'], execute: async () => null },
|
|
24
|
+
{ name: 'b', deps: ['a'], execute: async () => null },
|
|
25
|
+
];
|
|
26
|
+
expect(() => new PipelineRunner(phases)).toThrow(/cycle/i);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Vitest setup file - placeholder for future test configuration
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { tmpdir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { unlinkSync, existsSync } from 'fs';
|
|
4
|
+
import { openDb, closeDb } from '../../src/storage/db.js';
|
|
5
|
+
|
|
6
|
+
describe('openDb', () => {
|
|
7
|
+
const dbPath = join(tmpdir(), `monograph-test-${Date.now()}.db`);
|
|
8
|
+
|
|
9
|
+
afterEach(() => { if (existsSync(dbPath)) unlinkSync(dbPath); });
|
|
10
|
+
|
|
11
|
+
it('creates a new database file', () => {
|
|
12
|
+
const db = openDb(dbPath);
|
|
13
|
+
closeDb(db);
|
|
14
|
+
expect(existsSync(dbPath)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('creates the nodes table', () => {
|
|
18
|
+
const db = openDb(dbPath);
|
|
19
|
+
const row = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='nodes'").get();
|
|
20
|
+
closeDb(db);
|
|
21
|
+
expect(row).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('creates the edges table', () => {
|
|
25
|
+
const db = openDb(dbPath);
|
|
26
|
+
const row = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='edges'").get();
|
|
27
|
+
closeDb(db);
|
|
28
|
+
expect(row).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('creates the nodes_fts virtual table', () => {
|
|
32
|
+
const db = openDb(dbPath);
|
|
33
|
+
const row = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='nodes_fts'").get();
|
|
34
|
+
closeDb(db);
|
|
35
|
+
expect(row).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { tmpdir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { unlinkSync, existsSync } from 'fs';
|
|
4
|
+
import { openDb, closeDb } from '../../src/storage/db.js';
|
|
5
|
+
import { insertNode, getNode, deleteNodesForFile } from '../../src/storage/node-store.js';
|
|
6
|
+
import { insertEdge, getEdgesForSource } from '../../src/storage/edge-store.js';
|
|
7
|
+
import { ftsSearch } from '../../src/storage/fts-store.js';
|
|
8
|
+
import type { MonographNode, MonographEdge } from '../../src/types.js';
|
|
9
|
+
|
|
10
|
+
const dbPath = join(tmpdir(), `monograph-stores-${Date.now()}.db`);
|
|
11
|
+
let db: ReturnType<typeof openDb>;
|
|
12
|
+
|
|
13
|
+
beforeAll(() => {
|
|
14
|
+
db = openDb(dbPath);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(() => {
|
|
18
|
+
closeDb(db);
|
|
19
|
+
if (existsSync(dbPath)) {
|
|
20
|
+
unlinkSync(dbPath);
|
|
21
|
+
}
|
|
22
|
+
// Also cleanup WAL files
|
|
23
|
+
if (existsSync(dbPath + '-wal')) unlinkSync(dbPath + '-wal');
|
|
24
|
+
if (existsSync(dbPath + '-shm')) unlinkSync(dbPath + '-shm');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('node-store', () => {
|
|
28
|
+
const node: MonographNode = {
|
|
29
|
+
id: 'foo_bar',
|
|
30
|
+
label: 'Function',
|
|
31
|
+
name: 'bar',
|
|
32
|
+
normLabel: 'bar',
|
|
33
|
+
filePath: 'src/foo.ts',
|
|
34
|
+
startLine: 10,
|
|
35
|
+
endLine: 20,
|
|
36
|
+
isExported: true,
|
|
37
|
+
language: 'typescript',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
it('inserts and retrieves a node', () => {
|
|
41
|
+
insertNode(db, node);
|
|
42
|
+
const fetched = getNode(db, 'foo_bar');
|
|
43
|
+
expect(fetched?.name).toBe('bar');
|
|
44
|
+
expect(fetched?.isExported).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('deletes nodes by file path', () => {
|
|
48
|
+
deleteNodesForFile(db, 'src/foo.ts');
|
|
49
|
+
expect(getNode(db, 'foo_bar')).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('edge-store', () => {
|
|
54
|
+
beforeAll(() => {
|
|
55
|
+
insertNode(db, {
|
|
56
|
+
id: 'n1',
|
|
57
|
+
label: 'Class',
|
|
58
|
+
name: 'A',
|
|
59
|
+
normLabel: 'a',
|
|
60
|
+
isExported: false,
|
|
61
|
+
});
|
|
62
|
+
insertNode(db, {
|
|
63
|
+
id: 'n2',
|
|
64
|
+
label: 'Class',
|
|
65
|
+
name: 'B',
|
|
66
|
+
normLabel: 'b',
|
|
67
|
+
isExported: false,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('inserts and retrieves edges by source', () => {
|
|
72
|
+
const edge: MonographEdge = {
|
|
73
|
+
id: 'e1',
|
|
74
|
+
sourceId: 'n1',
|
|
75
|
+
targetId: 'n2',
|
|
76
|
+
relation: 'EXTENDS',
|
|
77
|
+
confidence: 'EXTRACTED',
|
|
78
|
+
confidenceScore: 1.0,
|
|
79
|
+
};
|
|
80
|
+
insertEdge(db, edge);
|
|
81
|
+
const edges = getEdgesForSource(db, 'n1');
|
|
82
|
+
expect(edges).toHaveLength(1);
|
|
83
|
+
expect(edges[0].relation).toBe('EXTENDS');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('fts-store', () => {
|
|
88
|
+
it('finds nodes by keyword', () => {
|
|
89
|
+
insertNode(db, {
|
|
90
|
+
id: 'auth_service',
|
|
91
|
+
label: 'Class',
|
|
92
|
+
name: 'AuthService',
|
|
93
|
+
normLabel: 'authservice',
|
|
94
|
+
filePath: 'src/auth.ts',
|
|
95
|
+
isExported: true,
|
|
96
|
+
});
|
|
97
|
+
const results = ftsSearch(db, 'auth', 10);
|
|
98
|
+
expect(results.some((r) => r.id === 'auth_service')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CONFIDENCE_SCORE } from '../src/types.js';
|
|
2
|
+
|
|
3
|
+
describe('CONFIDENCE_SCORE', () => {
|
|
4
|
+
it('assigns 1.0 to EXTRACTED', () => {
|
|
5
|
+
expect(CONFIDENCE_SCORE.EXTRACTED).toBe(1.0);
|
|
6
|
+
});
|
|
7
|
+
it('assigns 0.5 to INFERRED', () => {
|
|
8
|
+
expect(CONFIDENCE_SCORE.INFERRED).toBe(0.5);
|
|
9
|
+
});
|
|
10
|
+
it('assigns 0.2 to AMBIGUOUS', () => {
|
|
11
|
+
expect(CONFIDENCE_SCORE.AMBIGUOUS).toBe(0.2);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { tmpdir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { mkdirSync, writeFileSync, rmSync, appendFileSync } from 'fs';
|
|
4
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
5
|
+
import { MonographWatcher } from '../../src/watch/watcher.js';
|
|
6
|
+
|
|
7
|
+
const tmpRepo = join(tmpdir(), `monograph-watch-test-${Date.now()}`);
|
|
8
|
+
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
mkdirSync(join(tmpRepo, 'src'), { recursive: true });
|
|
11
|
+
writeFileSync(join(tmpRepo, 'src', 'index.ts'), 'export const x = 1;');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterAll(() => rmSync(tmpRepo, { recursive: true, force: true }));
|
|
15
|
+
|
|
16
|
+
describe('MonographWatcher', () => {
|
|
17
|
+
it('emits monograph:updated within 5s after a file change', async () => {
|
|
18
|
+
const updates: string[] = [];
|
|
19
|
+
const watcher = new MonographWatcher(tmpRepo, { debounceMs: 200 });
|
|
20
|
+
watcher.on('monograph:updated', (paths: string[]) => updates.push(...paths));
|
|
21
|
+
await watcher.start();
|
|
22
|
+
|
|
23
|
+
// Trigger a file change
|
|
24
|
+
await new Promise(r => setTimeout(r, 300));
|
|
25
|
+
appendFileSync(join(tmpRepo, 'src', 'index.ts'), '\nexport const y = 2;');
|
|
26
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
27
|
+
|
|
28
|
+
await watcher.stop();
|
|
29
|
+
expect(updates.length).toBeGreaterThan(0);
|
|
30
|
+
}, 15000);
|
|
31
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/export/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,MAAM,CAqB/E"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function toCanvas(nodes, edges) {
|
|
2
|
+
const canvasNodes = nodes.map((n, i) => ({
|
|
3
|
+
id: n.id,
|
|
4
|
+
type: 'text',
|
|
5
|
+
text: `**${n.label}**\n${n.name}`,
|
|
6
|
+
x: (i % 20) * 200,
|
|
7
|
+
y: Math.floor(i / 20) * 120,
|
|
8
|
+
width: 180,
|
|
9
|
+
height: 80,
|
|
10
|
+
color: labelColor(n.label),
|
|
11
|
+
}));
|
|
12
|
+
const canvasEdges = edges.map(e => ({
|
|
13
|
+
id: e.id,
|
|
14
|
+
fromNode: e.sourceId,
|
|
15
|
+
toNode: e.targetId,
|
|
16
|
+
label: e.relation,
|
|
17
|
+
color: confidenceColor(e.confidence),
|
|
18
|
+
}));
|
|
19
|
+
return JSON.stringify({ nodes: canvasNodes, edges: canvasEdges }, null, 2);
|
|
20
|
+
}
|
|
21
|
+
function labelColor(label) {
|
|
22
|
+
const map = {
|
|
23
|
+
Class: '1',
|
|
24
|
+
Function: '2',
|
|
25
|
+
Interface: '3',
|
|
26
|
+
Module: '4',
|
|
27
|
+
Namespace: '5',
|
|
28
|
+
Method: '6',
|
|
29
|
+
};
|
|
30
|
+
return map[label] ?? '6';
|
|
31
|
+
}
|
|
32
|
+
function confidenceColor(c) {
|
|
33
|
+
if (c === 'EXTRACTED')
|
|
34
|
+
return '#22c55e';
|
|
35
|
+
if (c === 'INFERRED')
|
|
36
|
+
return '#f59e0b';
|
|
37
|
+
return '#ef4444';
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=canvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/export/canvas.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,QAAQ,CAAC,KAAsB,EAAE,KAAsB;IACrE,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,IAAI,EAAE;QACjC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG;QACjB,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG;QAC3B,KAAK,EAAE,GAAG;QACV,MAAM,EAAE,EAAE;QACV,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;KAC3B,CAAC,CAAC,CAAC;IAEJ,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClC,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,MAAM,EAAE,CAAC,CAAC,QAAQ;QAClB,KAAK,EAAE,CAAC,CAAC,QAAQ;QACjB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC;KACrC,CAAC,CAAC,CAAC;IAEJ,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,GAAG,GAA2B;QAClC,KAAK,EAAE,GAAG;QACV,QAAQ,EAAE,GAAG;QACb,SAAS,EAAE,GAAG;QACd,MAAM,EAAE,GAAG;QACX,SAAS,EAAE,GAAG;QACd,MAAM,EAAE,GAAG;KACZ,CAAC;IACF,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;AAC3B,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,IAAI,CAAC,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IACvC,OAAO,SAAS,CAAC;AACnB,CAAC"}
|