@optave/codegraph 1.1.0 → 1.4.1

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/src/db.js CHANGED
@@ -1,117 +1,129 @@
1
- import Database from 'better-sqlite3';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import { warn, debug, info } from './logger.js';
5
-
6
- // ─── Schema Migrations ─────────────────────────────────────────────────
7
- export const MIGRATIONS = [
8
- {
9
- version: 1,
10
- up: `
11
- CREATE TABLE IF NOT EXISTS nodes (
12
- id INTEGER PRIMARY KEY AUTOINCREMENT,
13
- name TEXT NOT NULL,
14
- kind TEXT NOT NULL,
15
- file TEXT NOT NULL,
16
- line INTEGER,
17
- end_line INTEGER,
18
- UNIQUE(name, kind, file, line)
19
- );
20
- CREATE TABLE IF NOT EXISTS edges (
21
- id INTEGER PRIMARY KEY AUTOINCREMENT,
22
- source_id INTEGER NOT NULL,
23
- target_id INTEGER NOT NULL,
24
- kind TEXT NOT NULL,
25
- confidence REAL DEFAULT 1.0,
26
- dynamic INTEGER DEFAULT 0,
27
- FOREIGN KEY(source_id) REFERENCES nodes(id),
28
- FOREIGN KEY(target_id) REFERENCES nodes(id)
29
- );
30
- CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
31
- CREATE INDEX IF NOT EXISTS idx_nodes_file ON nodes(file);
32
- CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
33
- CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);
34
- CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);
35
- CREATE INDEX IF NOT EXISTS idx_edges_kind ON edges(kind);
36
- `
37
- },
38
- {
39
- version: 2,
40
- up: `
41
- CREATE INDEX IF NOT EXISTS idx_nodes_name_kind_file ON nodes(name, kind, file);
42
- CREATE INDEX IF NOT EXISTS idx_nodes_file_kind ON nodes(file, kind);
43
- CREATE INDEX IF NOT EXISTS idx_edges_source_kind ON edges(source_id, kind);
44
- CREATE INDEX IF NOT EXISTS idx_edges_target_kind ON edges(target_id, kind);
45
- `
46
- },
47
- {
48
- version: 3,
49
- up: `
50
- CREATE TABLE IF NOT EXISTS file_hashes (
51
- file TEXT PRIMARY KEY,
52
- hash TEXT NOT NULL,
53
- mtime INTEGER NOT NULL
54
- );
55
- `
56
- }
57
- ];
58
-
59
- export function openDb(dbPath) {
60
- const dir = path.dirname(dbPath);
61
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
62
- const db = new Database(dbPath);
63
- db.pragma('journal_mode = WAL');
64
- return db;
65
- }
66
-
67
- export function initSchema(db) {
68
- db.exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL DEFAULT 0)`);
69
-
70
- const row = db.prepare('SELECT version FROM schema_version').get();
71
- let currentVersion = row ? row.version : 0;
72
-
73
- if (!row) {
74
- db.prepare('INSERT INTO schema_version (version) VALUES (0)').run();
75
- }
76
-
77
- for (const migration of MIGRATIONS) {
78
- if (migration.version > currentVersion) {
79
- debug(`Running migration v${migration.version}`);
80
- db.exec(migration.up);
81
- db.prepare('UPDATE schema_version SET version = ?').run(migration.version);
82
- currentVersion = migration.version;
83
- }
84
- }
85
-
86
- try { db.exec('ALTER TABLE nodes ADD COLUMN end_line INTEGER'); } catch { /* already exists */ }
87
- try { db.exec('ALTER TABLE edges ADD COLUMN confidence REAL DEFAULT 1.0'); } catch { /* already exists */ }
88
- try { db.exec('ALTER TABLE edges ADD COLUMN dynamic INTEGER DEFAULT 0'); } catch { /* already exists */ }
89
- }
90
-
91
- export function findDbPath(customPath) {
92
- if (customPath) return path.resolve(customPath);
93
- let dir = process.cwd();
94
- while (true) {
95
- const candidate = path.join(dir, '.codegraph', 'graph.db');
96
- if (fs.existsSync(candidate)) return candidate;
97
- const parent = path.dirname(dir);
98
- if (parent === dir) break;
99
- dir = parent;
100
- }
101
- return path.join(process.cwd(), '.codegraph', 'graph.db');
102
- }
103
-
104
- /**
105
- * Open a database in readonly mode, with a user-friendly error if the DB doesn't exist.
106
- */
107
- export function openReadonlyOrFail(customPath) {
108
- const dbPath = findDbPath(customPath);
109
- if (!fs.existsSync(dbPath)) {
110
- console.error(
111
- `No codegraph database found at ${dbPath}.\n` +
112
- `Run "codegraph build" first to analyze your codebase.`
113
- );
114
- process.exit(1);
115
- }
116
- return new Database(dbPath, { readonly: true });
117
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import Database from 'better-sqlite3';
4
+ import { debug } from './logger.js';
5
+
6
+ // ─── Schema Migrations ─────────────────────────────────────────────────
7
+ export const MIGRATIONS = [
8
+ {
9
+ version: 1,
10
+ up: `
11
+ CREATE TABLE IF NOT EXISTS nodes (
12
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
13
+ name TEXT NOT NULL,
14
+ kind TEXT NOT NULL,
15
+ file TEXT NOT NULL,
16
+ line INTEGER,
17
+ end_line INTEGER,
18
+ UNIQUE(name, kind, file, line)
19
+ );
20
+ CREATE TABLE IF NOT EXISTS edges (
21
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
22
+ source_id INTEGER NOT NULL,
23
+ target_id INTEGER NOT NULL,
24
+ kind TEXT NOT NULL,
25
+ confidence REAL DEFAULT 1.0,
26
+ dynamic INTEGER DEFAULT 0,
27
+ FOREIGN KEY(source_id) REFERENCES nodes(id),
28
+ FOREIGN KEY(target_id) REFERENCES nodes(id)
29
+ );
30
+ CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
31
+ CREATE INDEX IF NOT EXISTS idx_nodes_file ON nodes(file);
32
+ CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
33
+ CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);
34
+ CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);
35
+ CREATE INDEX IF NOT EXISTS idx_edges_kind ON edges(kind);
36
+ `,
37
+ },
38
+ {
39
+ version: 2,
40
+ up: `
41
+ CREATE INDEX IF NOT EXISTS idx_nodes_name_kind_file ON nodes(name, kind, file);
42
+ CREATE INDEX IF NOT EXISTS idx_nodes_file_kind ON nodes(file, kind);
43
+ CREATE INDEX IF NOT EXISTS idx_edges_source_kind ON edges(source_id, kind);
44
+ CREATE INDEX IF NOT EXISTS idx_edges_target_kind ON edges(target_id, kind);
45
+ `,
46
+ },
47
+ {
48
+ version: 3,
49
+ up: `
50
+ CREATE TABLE IF NOT EXISTS file_hashes (
51
+ file TEXT PRIMARY KEY,
52
+ hash TEXT NOT NULL,
53
+ mtime INTEGER NOT NULL
54
+ );
55
+ `,
56
+ },
57
+ ];
58
+
59
+ export function openDb(dbPath) {
60
+ const dir = path.dirname(dbPath);
61
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
62
+ const db = new Database(dbPath);
63
+ db.pragma('journal_mode = WAL');
64
+ return db;
65
+ }
66
+
67
+ export function initSchema(db) {
68
+ db.exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL DEFAULT 0)`);
69
+
70
+ const row = db.prepare('SELECT version FROM schema_version').get();
71
+ let currentVersion = row ? row.version : 0;
72
+
73
+ if (!row) {
74
+ db.prepare('INSERT INTO schema_version (version) VALUES (0)').run();
75
+ }
76
+
77
+ for (const migration of MIGRATIONS) {
78
+ if (migration.version > currentVersion) {
79
+ debug(`Running migration v${migration.version}`);
80
+ db.exec(migration.up);
81
+ db.prepare('UPDATE schema_version SET version = ?').run(migration.version);
82
+ currentVersion = migration.version;
83
+ }
84
+ }
85
+
86
+ try {
87
+ db.exec('ALTER TABLE nodes ADD COLUMN end_line INTEGER');
88
+ } catch {
89
+ /* already exists */
90
+ }
91
+ try {
92
+ db.exec('ALTER TABLE edges ADD COLUMN confidence REAL DEFAULT 1.0');
93
+ } catch {
94
+ /* already exists */
95
+ }
96
+ try {
97
+ db.exec('ALTER TABLE edges ADD COLUMN dynamic INTEGER DEFAULT 0');
98
+ } catch {
99
+ /* already exists */
100
+ }
101
+ }
102
+
103
+ export function findDbPath(customPath) {
104
+ if (customPath) return path.resolve(customPath);
105
+ let dir = process.cwd();
106
+ while (true) {
107
+ const candidate = path.join(dir, '.codegraph', 'graph.db');
108
+ if (fs.existsSync(candidate)) return candidate;
109
+ const parent = path.dirname(dir);
110
+ if (parent === dir) break;
111
+ dir = parent;
112
+ }
113
+ return path.join(process.cwd(), '.codegraph', 'graph.db');
114
+ }
115
+
116
+ /**
117
+ * Open a database in readonly mode, with a user-friendly error if the DB doesn't exist.
118
+ */
119
+ export function openReadonlyOrFail(customPath) {
120
+ const dbPath = findDbPath(customPath);
121
+ if (!fs.existsSync(dbPath)) {
122
+ console.error(
123
+ `No codegraph database found at ${dbPath}.\n` +
124
+ `Run "codegraph build" first to analyze your codebase.`,
125
+ );
126
+ process.exit(1);
127
+ }
128
+ return new Database(dbPath, { readonly: true });
129
+ }