@mgamil/mapx 0.2.4

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.
Files changed (203) hide show
  1. package/LICENSE +194 -0
  2. package/README.md +488 -0
  3. package/VERSION +1 -0
  4. package/dist/agents/generator.d.ts +74 -0
  5. package/dist/agents/generator.js +375 -0
  6. package/dist/agents/templates.d.ts +29 -0
  7. package/dist/agents/templates.js +459 -0
  8. package/dist/cli.d.ts +16 -0
  9. package/dist/cli.js +1835 -0
  10. package/dist/core/cluster-engine.d.ts +32 -0
  11. package/dist/core/cluster-engine.js +314 -0
  12. package/dist/core/config.d.ts +29 -0
  13. package/dist/core/config.js +178 -0
  14. package/dist/core/context-builder.d.ts +61 -0
  15. package/dist/core/context-builder.js +252 -0
  16. package/dist/core/flow-tracer.d.ts +63 -0
  17. package/dist/core/flow-tracer.js +366 -0
  18. package/dist/core/git-tracker.d.ts +20 -0
  19. package/dist/core/git-tracker.js +159 -0
  20. package/dist/core/graph.d.ts +42 -0
  21. package/dist/core/graph.js +186 -0
  22. package/dist/core/metrics.d.ts +24 -0
  23. package/dist/core/metrics.js +87 -0
  24. package/dist/core/scanner.d.ts +53 -0
  25. package/dist/core/scanner.js +949 -0
  26. package/dist/core/store-bun.d.ts +13 -0
  27. package/dist/core/store-bun.js +34 -0
  28. package/dist/core/store-interface.d.ts +15 -0
  29. package/dist/core/store-interface.js +7 -0
  30. package/dist/core/store-node.d.ts +13 -0
  31. package/dist/core/store-node.js +35 -0
  32. package/dist/core/store.d.ts +132 -0
  33. package/dist/core/store.js +614 -0
  34. package/dist/core/workspace-manager.d.ts +9 -0
  35. package/dist/core/workspace-manager.js +64 -0
  36. package/dist/exporters/dot-exporter.d.ts +16 -0
  37. package/dist/exporters/dot-exporter.js +179 -0
  38. package/dist/exporters/graph-exporter.d.ts +14 -0
  39. package/dist/exporters/graph-exporter.js +85 -0
  40. package/dist/exporters/index.d.ts +9 -0
  41. package/dist/exporters/index.js +12 -0
  42. package/dist/exporters/llm-exporter.d.ts +18 -0
  43. package/dist/exporters/llm-exporter.js +224 -0
  44. package/dist/exporters/svg-exporter.d.ts +19 -0
  45. package/dist/exporters/svg-exporter.js +319 -0
  46. package/dist/exporters/toon-exporter.d.ts +16 -0
  47. package/dist/exporters/toon-exporter.js +246 -0
  48. package/dist/frameworks/detectors/aspnet.d.ts +11 -0
  49. package/dist/frameworks/detectors/aspnet.js +52 -0
  50. package/dist/frameworks/detectors/django.d.ts +14 -0
  51. package/dist/frameworks/detectors/django.js +135 -0
  52. package/dist/frameworks/detectors/drupal.d.ts +13 -0
  53. package/dist/frameworks/detectors/drupal.js +94 -0
  54. package/dist/frameworks/detectors/express.d.ts +12 -0
  55. package/dist/frameworks/detectors/express.js +234 -0
  56. package/dist/frameworks/detectors/fastapi.d.ts +12 -0
  57. package/dist/frameworks/detectors/fastapi.js +203 -0
  58. package/dist/frameworks/detectors/flask.d.ts +12 -0
  59. package/dist/frameworks/detectors/flask.js +244 -0
  60. package/dist/frameworks/detectors/go.d.ts +11 -0
  61. package/dist/frameworks/detectors/go.js +75 -0
  62. package/dist/frameworks/detectors/laravel.d.ts +11 -0
  63. package/dist/frameworks/detectors/laravel.js +462 -0
  64. package/dist/frameworks/detectors/nestjs.d.ts +12 -0
  65. package/dist/frameworks/detectors/nestjs.js +155 -0
  66. package/dist/frameworks/detectors/nextjs.d.ts +11 -0
  67. package/dist/frameworks/detectors/nextjs.js +118 -0
  68. package/dist/frameworks/detectors/rails.d.ts +12 -0
  69. package/dist/frameworks/detectors/rails.js +76 -0
  70. package/dist/frameworks/detectors/react-router.d.ts +11 -0
  71. package/dist/frameworks/detectors/react-router.js +115 -0
  72. package/dist/frameworks/detectors/rust.d.ts +11 -0
  73. package/dist/frameworks/detectors/rust.js +59 -0
  74. package/dist/frameworks/detectors/spring.d.ts +11 -0
  75. package/dist/frameworks/detectors/spring.js +56 -0
  76. package/dist/frameworks/detectors/sveltekit.d.ts +11 -0
  77. package/dist/frameworks/detectors/sveltekit.js +154 -0
  78. package/dist/frameworks/detectors/symfony.d.ts +13 -0
  79. package/dist/frameworks/detectors/symfony.js +175 -0
  80. package/dist/frameworks/detectors/tanstack-router.d.ts +12 -0
  81. package/dist/frameworks/detectors/tanstack-router.js +80 -0
  82. package/dist/frameworks/detectors/vapor.d.ts +11 -0
  83. package/dist/frameworks/detectors/vapor.js +52 -0
  84. package/dist/frameworks/detectors/vue-router.d.ts +12 -0
  85. package/dist/frameworks/detectors/vue-router.js +237 -0
  86. package/dist/frameworks/detectors/wordpress.d.ts +13 -0
  87. package/dist/frameworks/detectors/wordpress.js +141 -0
  88. package/dist/frameworks/detectors/yii.d.ts +11 -0
  89. package/dist/frameworks/detectors/yii.js +131 -0
  90. package/dist/frameworks/framework-registry.d.ts +13 -0
  91. package/dist/frameworks/framework-registry.js +77 -0
  92. package/dist/frameworks/route-registry.d.ts +26 -0
  93. package/dist/frameworks/route-registry.js +102 -0
  94. package/dist/index.d.ts +19 -0
  95. package/dist/index.js +30 -0
  96. package/dist/languages/index.d.ts +2 -0
  97. package/dist/languages/index.js +7 -0
  98. package/dist/languages/installer.d.ts +13 -0
  99. package/dist/languages/installer.js +103 -0
  100. package/dist/languages/registry.d.ts +19 -0
  101. package/dist/languages/registry.js +427 -0
  102. package/dist/main.d.ts +2 -0
  103. package/dist/main.js +20 -0
  104. package/dist/mcp.d.ts +11 -0
  105. package/dist/mcp.js +1699 -0
  106. package/dist/parsers/common-methods.d.ts +3 -0
  107. package/dist/parsers/common-methods.js +33 -0
  108. package/dist/parsers/fallback-parser.d.ts +10 -0
  109. package/dist/parsers/fallback-parser.js +18 -0
  110. package/dist/parsers/generic-wasm-parser.d.ts +23 -0
  111. package/dist/parsers/generic-wasm-parser.js +168 -0
  112. package/dist/parsers/ignored-symbols.d.ts +26 -0
  113. package/dist/parsers/ignored-symbols.js +77 -0
  114. package/dist/parsers/index.d.ts +9 -0
  115. package/dist/parsers/index.js +13 -0
  116. package/dist/parsers/languages/javascript.d.ts +11 -0
  117. package/dist/parsers/languages/javascript.js +28 -0
  118. package/dist/parsers/languages/php.d.ts +15 -0
  119. package/dist/parsers/languages/php.js +648 -0
  120. package/dist/parsers/languages/typescript.d.ts +10 -0
  121. package/dist/parsers/languages/typescript.js +9 -0
  122. package/dist/parsers/languages/vue.d.ts +13 -0
  123. package/dist/parsers/languages/vue.js +63 -0
  124. package/dist/parsers/parse-worker.d.ts +2 -0
  125. package/dist/parsers/parse-worker.js +185 -0
  126. package/dist/parsers/parser-interface.d.ts +9 -0
  127. package/dist/parsers/parser-interface.js +0 -0
  128. package/dist/parsers/parser-registry.d.ts +8 -0
  129. package/dist/parsers/parser-registry.js +52 -0
  130. package/dist/parsers/wasm-parser.d.ts +16 -0
  131. package/dist/parsers/wasm-parser.js +110 -0
  132. package/dist/types.d.ts +172 -0
  133. package/dist/types.js +0 -0
  134. package/dist/ui/index.html +270 -0
  135. package/dist/ui/main.js +581 -0
  136. package/dist/ui/main.js.map +7 -0
  137. package/dist/ui/styles.css +573 -0
  138. package/dist/ui-events.d.ts +36 -0
  139. package/dist/ui-events.js +61 -0
  140. package/dist/ui-server.d.ts +12 -0
  141. package/dist/ui-server.js +504 -0
  142. package/package.json +179 -0
  143. package/queries/bash/references.scm +22 -0
  144. package/queries/bash/symbols.scm +15 -0
  145. package/queries/c/references.scm +14 -0
  146. package/queries/c/symbols.scm +30 -0
  147. package/queries/c-sharp/references.scm +26 -0
  148. package/queries/c-sharp/symbols.scm +57 -0
  149. package/queries/cpp/references.scm +21 -0
  150. package/queries/cpp/symbols.scm +44 -0
  151. package/queries/dart/references.scm +33 -0
  152. package/queries/dart/symbols.scm +38 -0
  153. package/queries/elixir/references.scm +45 -0
  154. package/queries/elixir/symbols.scm +41 -0
  155. package/queries/go/references.scm +22 -0
  156. package/queries/go/symbols.scm +53 -0
  157. package/queries/java/references.scm +32 -0
  158. package/queries/java/symbols.scm +41 -0
  159. package/queries/javascript/references.scm +14 -0
  160. package/queries/javascript/symbols.scm +23 -0
  161. package/queries/kotlin/references.scm +31 -0
  162. package/queries/kotlin/symbols.scm +24 -0
  163. package/queries/lua/references.scm +19 -0
  164. package/queries/lua/symbols.scm +29 -0
  165. package/queries/pascal/references.scm +29 -0
  166. package/queries/pascal/symbols.scm +45 -0
  167. package/queries/php/references.scm +109 -0
  168. package/queries/php/symbols.scm +33 -0
  169. package/queries/python/references.scm +50 -0
  170. package/queries/python/symbols.scm +21 -0
  171. package/queries/ruby/references.scm +48 -0
  172. package/queries/ruby/symbols.scm +24 -0
  173. package/queries/rust/references.scm +31 -0
  174. package/queries/rust/symbols.scm +35 -0
  175. package/queries/scala/references.scm +30 -0
  176. package/queries/scala/symbols.scm +35 -0
  177. package/queries/svelte/references.scm +20 -0
  178. package/queries/svelte/symbols.scm +30 -0
  179. package/queries/swift/references.scm +22 -0
  180. package/queries/swift/symbols.scm +37 -0
  181. package/queries/typescript/references.scm +25 -0
  182. package/queries/typescript/symbols.scm +35 -0
  183. package/queries/vue/references.scm +20 -0
  184. package/queries/vue/symbols.scm +28 -0
  185. package/queries/zig/references.scm +20 -0
  186. package/queries/zig/symbols.scm +22 -0
  187. package/wasm/tree-sitter-c.wasm +0 -0
  188. package/wasm/tree-sitter-c_sharp.wasm +0 -0
  189. package/wasm/tree-sitter-cpp.wasm +0 -0
  190. package/wasm/tree-sitter-dart.wasm +0 -0
  191. package/wasm/tree-sitter-go.wasm +0 -0
  192. package/wasm/tree-sitter-java.wasm +0 -0
  193. package/wasm/tree-sitter-javascript.wasm +0 -0
  194. package/wasm/tree-sitter-kotlin.wasm +0 -0
  195. package/wasm/tree-sitter-php.wasm +0 -0
  196. package/wasm/tree-sitter-python.wasm +0 -0
  197. package/wasm/tree-sitter-ruby.wasm +0 -0
  198. package/wasm/tree-sitter-rust.wasm +0 -0
  199. package/wasm/tree-sitter-scala.wasm +0 -0
  200. package/wasm/tree-sitter-swift.wasm +0 -0
  201. package/wasm/tree-sitter-tsx.wasm +0 -0
  202. package/wasm/tree-sitter-typescript.wasm +0 -0
  203. package/wasm/tree-sitter-vue.wasm +0 -0
@@ -0,0 +1,614 @@
1
+ import { createRequire } from "node:module";
2
+ const dynamicRequire = createRequire(import.meta.url);
3
+ const CURRENT_SCHEMA_VERSION = 6;
4
+ const INITIAL_SCHEMA = `
5
+ CREATE TABLE IF NOT EXISTS files (
6
+ path TEXT PRIMARY KEY,
7
+ repo TEXT NOT NULL,
8
+ language TEXT NOT NULL,
9
+ git_blob_hash TEXT,
10
+ last_scanned TEXT,
11
+ size_bytes INTEGER DEFAULT 0,
12
+ lines INTEGER DEFAULT 0
13
+ );
14
+
15
+ CREATE TABLE IF NOT EXISTS symbols (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ file_path TEXT NOT NULL,
18
+ repo TEXT NOT NULL,
19
+ name TEXT NOT NULL,
20
+ kind TEXT NOT NULL,
21
+ scope TEXT,
22
+ signature TEXT DEFAULT '',
23
+ start_line INTEGER,
24
+ end_line INTEGER,
25
+ metadata TEXT DEFAULT '{}',
26
+ FOREIGN KEY (file_path) REFERENCES files(path)
27
+ );
28
+
29
+ CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
30
+ CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_path);
31
+ CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind);
32
+ CREATE INDEX IF NOT EXISTS idx_symbols_scope ON symbols(scope);
33
+
34
+ CREATE TABLE IF NOT EXISTS edges (
35
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
36
+ source_file TEXT NOT NULL,
37
+ target_file TEXT NOT NULL,
38
+ source_symbol TEXT,
39
+ target_symbol TEXT,
40
+ edge_type TEXT NOT NULL,
41
+ repo TEXT NOT NULL,
42
+ weight REAL DEFAULT 1.0
43
+ );
44
+
45
+ CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_file);
46
+ CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_file);
47
+ CREATE INDEX IF NOT EXISTS idx_edges_type ON edges(edge_type);
48
+
49
+ CREATE TABLE IF NOT EXISTS snapshots (
50
+ commit_sha TEXT PRIMARY KEY,
51
+ parent_sha TEXT,
52
+ timestamp TEXT,
53
+ files_added TEXT DEFAULT '[]',
54
+ files_modified TEXT DEFAULT '[]',
55
+ files_removed TEXT DEFAULT '[]',
56
+ symbols_delta TEXT DEFAULT '{}'
57
+ );
58
+
59
+ CREATE TABLE IF NOT EXISTS meta (
60
+ key TEXT PRIMARY KEY,
61
+ value TEXT
62
+ );
63
+ `;
64
+ const MIGRATIONS = [
65
+ {
66
+ version: 2,
67
+ description: "Add content_hash column to files table",
68
+ up: [
69
+ `ALTER TABLE files ADD COLUMN content_hash TEXT`
70
+ ]
71
+ },
72
+ {
73
+ version: 3,
74
+ description: "Add verifiability column to edges table",
75
+ up: [
76
+ `ALTER TABLE edges ADD COLUMN verifiability TEXT NOT NULL DEFAULT 'verified'`,
77
+ `CREATE INDEX IF NOT EXISTS idx_edges_verifiability ON edges (verifiability)`
78
+ ]
79
+ },
80
+ {
81
+ version: 4,
82
+ description: "Add metadata column to edges and files tables",
83
+ up: [
84
+ `ALTER TABLE edges ADD COLUMN metadata TEXT DEFAULT '{}'`,
85
+ `ALTER TABLE files ADD COLUMN metadata TEXT DEFAULT '{}'`
86
+ ]
87
+ },
88
+ {
89
+ version: 5,
90
+ description: "Add clusters and cluster_membership tables, namespace column in files",
91
+ up: [
92
+ `ALTER TABLE files ADD COLUMN namespace TEXT`,
93
+ `CREATE TABLE IF NOT EXISTS clusters (
94
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
95
+ repo TEXT NOT NULL,
96
+ name TEXT NOT NULL,
97
+ label TEXT NOT NULL,
98
+ source TEXT NOT NULL,
99
+ parent_name TEXT,
100
+ depth INTEGER DEFAULT 0,
101
+ file_count INTEGER DEFAULT 0,
102
+ UNIQUE(repo, name)
103
+ )`,
104
+ `CREATE INDEX IF NOT EXISTS idx_clusters_repo ON clusters(repo)`,
105
+ `CREATE INDEX IF NOT EXISTS idx_clusters_parent ON clusters(parent_name)`,
106
+ `CREATE TABLE IF NOT EXISTS cluster_membership (
107
+ file_path TEXT NOT NULL,
108
+ cluster_name TEXT NOT NULL,
109
+ repo TEXT NOT NULL,
110
+ is_primary INTEGER DEFAULT 1,
111
+ PRIMARY KEY (file_path, cluster_name, repo)
112
+ )`,
113
+ `CREATE INDEX IF NOT EXISTS idx_membership_file ON cluster_membership(file_path)`,
114
+ `CREATE INDEX IF NOT EXISTS idx_membership_cluster ON cluster_membership(cluster_name)`
115
+ ]
116
+ },
117
+ {
118
+ version: 6,
119
+ description: "Add target_repo column to edges table",
120
+ up: [
121
+ `ALTER TABLE edges ADD COLUMN target_repo TEXT`,
122
+ `CREATE INDEX IF NOT EXISTS idx_edges_target_repo ON edges(target_repo)`
123
+ ]
124
+ }
125
+ ];
126
+ function createStoreBackend(dbPath) {
127
+ const isBun = typeof globalThis.Bun !== "undefined";
128
+ if (isBun) {
129
+ const { BunStore } = dynamicRequire("./store-bun.js");
130
+ return new BunStore(dbPath);
131
+ }
132
+ const { NodeStore } = dynamicRequire("./store-node.js");
133
+ return new NodeStore(dbPath);
134
+ }
135
+ class Store {
136
+ backend;
137
+ constructor(dbPath) {
138
+ this.backend = createStoreBackend(dbPath);
139
+ this.backend.exec(INITIAL_SCHEMA);
140
+ this.runMigrations();
141
+ }
142
+ runMigrations() {
143
+ const currentVersion = this.getSchemaVersion();
144
+ if (currentVersion >= CURRENT_SCHEMA_VERSION) return;
145
+ const pending = MIGRATIONS.filter((m) => m.version > currentVersion);
146
+ if (pending.length === 0) {
147
+ this.setMeta("schema_version", String(CURRENT_SCHEMA_VERSION));
148
+ return;
149
+ }
150
+ this.backend.inTransaction(() => {
151
+ for (const migration of pending) {
152
+ for (const sql of migration.up) {
153
+ this.backend.exec(sql);
154
+ }
155
+ this.setMeta("schema_version", String(migration.version));
156
+ }
157
+ });
158
+ }
159
+ getSchemaVersion() {
160
+ try {
161
+ const version = this.getMeta("schema_version");
162
+ if (version !== null) return parseInt(version, 10) || 0;
163
+ } catch {
164
+ }
165
+ const hasContentHash = this.columnExists("files", "content_hash");
166
+ if (hasContentHash) {
167
+ this.setMeta("schema_version", "2");
168
+ return 2;
169
+ }
170
+ return 1;
171
+ }
172
+ columnExists(table, column) {
173
+ try {
174
+ const rows = this.backend.prepare(`PRAGMA table_info(${table})`).all();
175
+ return rows.some((row) => row.name === column);
176
+ } catch {
177
+ return false;
178
+ }
179
+ }
180
+ get raw() {
181
+ return this.backend;
182
+ }
183
+ setMeta(key, value) {
184
+ this.backend.prepare(
185
+ "INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)"
186
+ ).run(key, value);
187
+ }
188
+ getMeta(key) {
189
+ const row = this.backend.prepare("SELECT value FROM meta WHERE key = ?").get(key);
190
+ return row ? row.value : null;
191
+ }
192
+ upsertFile(file) {
193
+ this.backend.prepare(`
194
+ INSERT OR REPLACE INTO files (path, repo, language, git_blob_hash, content_hash, last_scanned, size_bytes, lines, metadata, namespace)
195
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
196
+ `).run(
197
+ file.path,
198
+ file.repo,
199
+ file.language,
200
+ file.gitBlobHash,
201
+ file.contentHash,
202
+ file.lastScanned,
203
+ file.sizeBytes,
204
+ file.lines,
205
+ file.metadata ? JSON.stringify(file.metadata) : "{}",
206
+ file.namespace ?? null
207
+ );
208
+ }
209
+ updateFileMetadata(filePath, metadata) {
210
+ const file = this.getFile(filePath);
211
+ let merged = { ...metadata };
212
+ if (file && file.metadata) {
213
+ try {
214
+ merged = { ...JSON.parse(file.metadata), ...metadata };
215
+ } catch {
216
+ }
217
+ }
218
+ const namespace = metadata.namespace || null;
219
+ if (namespace) {
220
+ this.backend.prepare("UPDATE files SET metadata = ?, namespace = ? WHERE path = ?").run(JSON.stringify(merged), namespace, filePath);
221
+ } else {
222
+ this.backend.prepare("UPDATE files SET metadata = ? WHERE path = ?").run(JSON.stringify(merged), filePath);
223
+ }
224
+ }
225
+ deleteFile(filePath) {
226
+ this.backend.prepare("DELETE FROM symbols WHERE file_path = ?").run(filePath);
227
+ this.backend.prepare("DELETE FROM edges WHERE source_file = ? OR target_file = ?").run(filePath, filePath);
228
+ this.backend.prepare("DELETE FROM files WHERE path = ?").run(filePath);
229
+ }
230
+ getFile(filePath) {
231
+ return this.backend.prepare("SELECT * FROM files WHERE path = ?").get(filePath);
232
+ }
233
+ getAllFiles(repo) {
234
+ if (repo) {
235
+ return this.backend.prepare("SELECT * FROM files WHERE repo = ?").all(repo);
236
+ }
237
+ return this.backend.prepare("SELECT * FROM files").all();
238
+ }
239
+ insertSymbol(sym) {
240
+ this.backend.prepare(`
241
+ INSERT INTO symbols (file_path, repo, name, kind, scope, signature, start_line, end_line, metadata)
242
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
243
+ `).run(sym.filePath, sym.repo, sym.name, sym.kind, sym.scope, sym.signature, sym.startLine, sym.endLine, sym.metadata);
244
+ }
245
+ deleteSymbolsForFile(filePath) {
246
+ this.backend.prepare("DELETE FROM symbols WHERE file_path = ?").run(filePath);
247
+ }
248
+ searchSymbols(namePattern, repo) {
249
+ if (repo) {
250
+ return this.backend.prepare(
251
+ "SELECT * FROM symbols WHERE name LIKE ? AND repo = ? ORDER BY kind, name"
252
+ ).all(`%${namePattern}%`, repo);
253
+ }
254
+ return this.backend.prepare(
255
+ "SELECT * FROM symbols WHERE name LIKE ? ORDER BY kind, name"
256
+ ).all(`%${namePattern}%`);
257
+ }
258
+ getSymbolsForFile(filePath) {
259
+ return this.backend.prepare(
260
+ "SELECT * FROM symbols WHERE file_path = ? ORDER BY start_line"
261
+ ).all(filePath);
262
+ }
263
+ getAllSymbols(repo) {
264
+ if (repo) {
265
+ return this.backend.prepare("SELECT * FROM symbols WHERE repo = ? ORDER BY file_path, start_line").all(repo);
266
+ }
267
+ return this.backend.prepare("SELECT * FROM symbols ORDER BY file_path, start_line").all();
268
+ }
269
+ insertEdge(edge) {
270
+ this.backend.prepare(`
271
+ INSERT INTO edges (source_file, target_file, source_symbol, target_symbol, edge_type, repo, weight, verifiability, metadata, target_repo)
272
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
273
+ `).run(
274
+ edge.sourceFile,
275
+ edge.targetFile,
276
+ edge.sourceSymbol,
277
+ edge.targetSymbol,
278
+ edge.edgeType,
279
+ edge.repo,
280
+ edge.weight,
281
+ edge.verifiability ?? "verified",
282
+ edge.metadata ? JSON.stringify(edge.metadata) : "{}",
283
+ edge.targetRepo ?? null
284
+ );
285
+ }
286
+ deleteEdgesForFile(filePath) {
287
+ this.backend.prepare("DELETE FROM edges WHERE source_file = ?").run(filePath);
288
+ }
289
+ deleteFrameworkEdgesForRepo(repoName) {
290
+ this.backend.prepare(
291
+ "DELETE FROM edges WHERE repo = ? AND edge_type IN ('route', 'middleware', 'hook', 'graphql_resolver', 'message_handler', 'websocket_handler')"
292
+ ).run(repoName);
293
+ }
294
+ getEdgesForFile(filePath) {
295
+ return this.backend.prepare(
296
+ "SELECT * FROM edges WHERE source_file = ? ORDER BY edge_type"
297
+ ).all(filePath);
298
+ }
299
+ getReverseEdges(filePath) {
300
+ return this.backend.prepare(
301
+ "SELECT * FROM edges WHERE target_file = ? ORDER BY edge_type"
302
+ ).all(filePath);
303
+ }
304
+ getAllEdges(repo) {
305
+ if (repo) {
306
+ return this.backend.prepare("SELECT * FROM edges WHERE repo = ?").all(repo);
307
+ }
308
+ return this.backend.prepare("SELECT * FROM edges").all();
309
+ }
310
+ queryEdges(options) {
311
+ let sql = "SELECT * FROM edges WHERE 1=1";
312
+ const params = [];
313
+ if (options.repo) {
314
+ sql += " AND repo = ?";
315
+ params.push(options.repo);
316
+ }
317
+ if (options.type) {
318
+ sql += " AND edge_type = ?";
319
+ params.push(options.type);
320
+ }
321
+ if (options.from) {
322
+ sql += " AND source_file LIKE ?";
323
+ params.push(`%${options.from}%`);
324
+ }
325
+ if (options.to) {
326
+ sql += " AND target_file LIKE ?";
327
+ params.push(`%${options.to}%`);
328
+ }
329
+ return this.backend.prepare(sql).all(...params);
330
+ }
331
+ getFileCount(repo) {
332
+ const row = repo ? this.backend.prepare("SELECT COUNT(*) as cnt FROM files WHERE repo = ?").get(repo) : this.backend.prepare("SELECT COUNT(*) as cnt FROM files").get();
333
+ return row ? row.cnt : 0;
334
+ }
335
+ getSymbolCount(repo) {
336
+ const row = repo ? this.backend.prepare("SELECT COUNT(*) as cnt FROM symbols WHERE repo = ?").get(repo) : this.backend.prepare("SELECT COUNT(*) as cnt FROM symbols").get();
337
+ return row ? row.cnt : 0;
338
+ }
339
+ getEdgeCount(repo) {
340
+ const row = repo ? this.backend.prepare("SELECT COUNT(*) as cnt FROM edges WHERE repo = ?").get(repo) : this.backend.prepare("SELECT COUNT(*) as cnt FROM edges").get();
341
+ return row ? row.cnt : 0;
342
+ }
343
+ getLanguageBreakdown(repo) {
344
+ const rows = repo ? this.backend.prepare("SELECT language, COUNT(*) as cnt FROM files WHERE repo = ? GROUP BY language").all(repo) : this.backend.prepare("SELECT language, COUNT(*) as cnt FROM files GROUP BY language").all();
345
+ const result = {};
346
+ for (const row of rows) {
347
+ result[row.language] = row.cnt;
348
+ }
349
+ return result;
350
+ }
351
+ upsertSnapshot(snap) {
352
+ this.backend.prepare(`
353
+ INSERT OR REPLACE INTO snapshots (commit_sha, parent_sha, timestamp, files_added, files_modified, files_removed, symbols_delta)
354
+ VALUES (?, ?, ?, ?, ?, ?, ?)
355
+ `).run(snap.commitSha, snap.parentSha, snap.timestamp, snap.filesAdded, snap.filesModified, snap.filesRemoved, snap.symbolsDelta);
356
+ }
357
+ getLatestSnapshot() {
358
+ return this.backend.prepare("SELECT * FROM snapshots ORDER BY timestamp DESC LIMIT 1").get();
359
+ }
360
+ clearClusters(repo) {
361
+ this.backend.prepare("DELETE FROM cluster_membership WHERE repo = ?").run(repo);
362
+ this.backend.prepare("DELETE FROM clusters WHERE repo = ?").run(repo);
363
+ }
364
+ insertCluster(cluster) {
365
+ this.backend.prepare(`
366
+ INSERT OR REPLACE INTO clusters (repo, name, label, source, parent_name, depth, file_count)
367
+ VALUES (?, ?, ?, ?, ?, ?, ?)
368
+ `).run(
369
+ cluster.repo,
370
+ cluster.name,
371
+ cluster.label,
372
+ cluster.source,
373
+ cluster.parentName,
374
+ cluster.depth,
375
+ cluster.fileCount
376
+ );
377
+ }
378
+ insertClusterMembership(membership) {
379
+ this.backend.prepare(`
380
+ INSERT OR REPLACE INTO cluster_membership (file_path, cluster_name, repo, is_primary)
381
+ VALUES (?, ?, ?, ?)
382
+ `).run(
383
+ membership.filePath,
384
+ membership.clusterName,
385
+ membership.repo,
386
+ membership.isPrimary
387
+ );
388
+ }
389
+ getClusters(repo) {
390
+ if (repo) {
391
+ return this.backend.prepare("SELECT * FROM clusters WHERE repo = ?").all(repo);
392
+ }
393
+ return this.backend.prepare("SELECT * FROM clusters").all();
394
+ }
395
+ getClusterMemberships(repo) {
396
+ if (repo) {
397
+ return this.backend.prepare("SELECT * FROM cluster_membership WHERE repo = ?").all(repo);
398
+ }
399
+ return this.backend.prepare("SELECT * FROM cluster_membership").all();
400
+ }
401
+ getClusterFiles(clusterName, repo) {
402
+ const rows = this.backend.prepare(`
403
+ SELECT file_path FROM cluster_membership
404
+ WHERE cluster_name = ? AND repo = ? AND is_primary = 1
405
+ `).all(clusterName, repo);
406
+ return rows.map((row) => row.file_path);
407
+ }
408
+ getClusterEdges(clusterName, repo) {
409
+ const rows = this.backend.prepare(`
410
+ SELECT
411
+ src_cm.cluster_name AS sourceCluster,
412
+ tgt_cm.cluster_name AS targetCluster,
413
+ COUNT(*) AS edgeCount,
414
+ edge_type AS dominantType
415
+ FROM edges e
416
+ JOIN cluster_membership src_cm ON e.source_file = src_cm.file_path AND src_cm.is_primary = 1 AND src_cm.repo = e.repo
417
+ JOIN cluster_membership tgt_cm ON e.target_file = tgt_cm.file_path AND tgt_cm.is_primary = 1 AND tgt_cm.repo = e.repo
418
+ WHERE e.repo = ? AND (src_cm.cluster_name = ? OR tgt_cm.cluster_name = ?) AND src_cm.cluster_name != tgt_cm.cluster_name
419
+ GROUP BY src_cm.cluster_name, tgt_cm.cluster_name, e.edge_type
420
+ `).all(repo, clusterName, clusterName);
421
+ const grouped = /* @__PURE__ */ new Map();
422
+ for (const r of rows) {
423
+ const key = `${r.sourceCluster}->${r.targetCluster}`;
424
+ if (!grouped.has(key)) {
425
+ grouped.set(key, {
426
+ sourceCluster: r.sourceCluster,
427
+ targetCluster: r.targetCluster,
428
+ edgeCount: 0,
429
+ types: {}
430
+ });
431
+ }
432
+ const entry = grouped.get(key);
433
+ entry.edgeCount += r.edgeCount;
434
+ entry.types[r.dominantType] = (entry.types[r.dominantType] || 0) + r.edgeCount;
435
+ }
436
+ return Array.from(grouped.values()).map((g) => {
437
+ let dominantType = "";
438
+ let maxCount = -1;
439
+ for (const [t, c] of Object.entries(g.types)) {
440
+ if (c > maxCount) {
441
+ maxCount = c;
442
+ dominantType = t;
443
+ }
444
+ }
445
+ return {
446
+ sourceCluster: g.sourceCluster,
447
+ targetCluster: g.targetCluster,
448
+ edgeCount: g.edgeCount,
449
+ dominantType
450
+ };
451
+ });
452
+ }
453
+ deleteRepo(repoName) {
454
+ this.inTransaction(() => {
455
+ this.backend.prepare("DELETE FROM symbols WHERE repo = ?").run(repoName);
456
+ this.backend.prepare("DELETE FROM edges WHERE repo = ? OR target_repo = ?").run(repoName, repoName);
457
+ this.backend.prepare("DELETE FROM files WHERE repo = ?").run(repoName);
458
+ this.backend.prepare("DELETE FROM meta WHERE key LIKE ? OR key LIKE ?").run(`last_scan_commit:${repoName}`, `last_scan_time:${repoName}`);
459
+ });
460
+ }
461
+ searchSymbolsFiltered(options) {
462
+ const limit = options.limit ?? 20;
463
+ let sql = "SELECT * FROM symbols WHERE ";
464
+ const params = [];
465
+ if (options.exact) {
466
+ sql += "(name = ? OR file_path = ?)";
467
+ params.push(options.term, options.term);
468
+ } else {
469
+ sql += "(name LIKE ? OR file_path LIKE ?)";
470
+ params.push(`%${options.term}%`, `%${options.term}%`);
471
+ }
472
+ if (options.kind) {
473
+ sql += " AND kind = ?";
474
+ params.push(options.kind);
475
+ }
476
+ if (options.filePrefix) {
477
+ sql += " AND file_path LIKE ?";
478
+ params.push(`${options.filePrefix}%`);
479
+ }
480
+ if (options.repo) {
481
+ sql += " AND repo = ?";
482
+ params.push(options.repo);
483
+ }
484
+ sql += " ORDER BY kind, name LIMIT ?";
485
+ params.push(limit);
486
+ return this.backend.prepare(sql).all(...params);
487
+ }
488
+ getSymbolByName(fullName, repo) {
489
+ if (fullName.includes("::")) {
490
+ const [scope, name] = fullName.split("::");
491
+ if (repo) {
492
+ return this.backend.prepare("SELECT * FROM symbols WHERE scope = ? AND name = ? AND repo = ? LIMIT 1").get(scope, name, repo);
493
+ }
494
+ return this.backend.prepare("SELECT * FROM symbols WHERE scope = ? AND name = ? LIMIT 1").get(scope, name);
495
+ } else {
496
+ if (repo) {
497
+ return this.backend.prepare("SELECT * FROM symbols WHERE name = ? AND repo = ? LIMIT 1").get(fullName, repo);
498
+ }
499
+ return this.backend.prepare("SELECT * FROM symbols WHERE name = ? LIMIT 1").get(fullName);
500
+ }
501
+ }
502
+ getFilesFiltered(options) {
503
+ const limit = options.limit ?? 50;
504
+ let sql = "SELECT * FROM files WHERE 1=1";
505
+ const params = [];
506
+ if (options.pathPrefix) {
507
+ sql += " AND path LIKE ?";
508
+ params.push(`${options.pathPrefix}%`);
509
+ }
510
+ if (options.lang) {
511
+ sql += " AND LOWER(language) = ?";
512
+ params.push(options.lang.toLowerCase());
513
+ }
514
+ if (options.repo) {
515
+ sql += " AND repo = ?";
516
+ params.push(options.repo);
517
+ }
518
+ if (options.sort === "lines") {
519
+ sql += " ORDER BY lines DESC";
520
+ } else if (options.sort === "path") {
521
+ sql += " ORDER BY path ASC";
522
+ }
523
+ sql += " LIMIT ?";
524
+ params.push(limit);
525
+ return this.backend.prepare(sql).all(...params);
526
+ }
527
+ getCallersOfSymbol(fullName, repo) {
528
+ let symbols = [];
529
+ if (fullName.includes("::")) {
530
+ const [scope, name] = fullName.split("::");
531
+ let sql = "SELECT * FROM symbols WHERE scope = ? AND name = ?";
532
+ const params = [scope, name];
533
+ if (repo) {
534
+ sql += " AND repo = ?";
535
+ params.push(repo);
536
+ }
537
+ symbols = this.backend.prepare(sql).all(...params);
538
+ } else {
539
+ let sql = "SELECT * FROM symbols WHERE name = ?";
540
+ const params = [fullName];
541
+ if (repo) {
542
+ sql += " AND repo = ?";
543
+ params.push(repo);
544
+ }
545
+ symbols = this.backend.prepare(sql).all(...params);
546
+ }
547
+ if (symbols.length === 0) {
548
+ return [];
549
+ }
550
+ const results = [];
551
+ for (const sym of symbols) {
552
+ let sql = "SELECT * FROM edges WHERE target_file = ? AND (target_symbol = ? OR target_symbol = ?)";
553
+ const params = [sym.file_path, sym.name, `${sym.scope}::${sym.name}`];
554
+ if (repo) {
555
+ sql += " AND repo = ?";
556
+ params.push(repo);
557
+ }
558
+ const edges = this.backend.prepare(sql).all(...params);
559
+ results.push(...edges);
560
+ }
561
+ return results;
562
+ }
563
+ getCalleesOfSymbol(fullName, repo) {
564
+ let symbols = [];
565
+ if (fullName.includes("::")) {
566
+ const [scope, name] = fullName.split("::");
567
+ let sql = "SELECT * FROM symbols WHERE scope = ? AND name = ?";
568
+ const params = [scope, name];
569
+ if (repo) {
570
+ sql += " AND repo = ?";
571
+ params.push(repo);
572
+ }
573
+ symbols = this.backend.prepare(sql).all(...params);
574
+ } else {
575
+ let sql = "SELECT * FROM symbols WHERE name = ?";
576
+ const params = [fullName];
577
+ if (repo) {
578
+ sql += " AND repo = ?";
579
+ params.push(repo);
580
+ }
581
+ symbols = this.backend.prepare(sql).all(...params);
582
+ }
583
+ if (symbols.length === 0) {
584
+ return [];
585
+ }
586
+ const results = [];
587
+ for (const sym of symbols) {
588
+ let sql = "SELECT * FROM edges WHERE source_file = ? AND (source_symbol = ? OR source_symbol = ?)";
589
+ const params = [sym.file_path, sym.name, `${sym.scope}::${sym.name}`];
590
+ if (repo) {
591
+ sql += " AND repo = ?";
592
+ params.push(repo);
593
+ }
594
+ const edges = this.backend.prepare(sql).all(...params);
595
+ results.push(...edges);
596
+ }
597
+ return results;
598
+ }
599
+ getTopFilesByPageRank(graph, limit = 5) {
600
+ return graph.getRankedFiles().slice(0, limit);
601
+ }
602
+ getTopSymbolsByPageRank(graph, limit = 5) {
603
+ return graph.getRankedSymbols().slice(0, limit);
604
+ }
605
+ inTransaction(fn) {
606
+ return this.backend.inTransaction(fn);
607
+ }
608
+ close() {
609
+ this.backend.close();
610
+ }
611
+ }
612
+ export {
613
+ Store
614
+ };
@@ -0,0 +1,9 @@
1
+ import { SubmoduleInfo } from '../types.js';
2
+
3
+ declare class WorkspaceManager {
4
+ static discoverSubmodules(repoRoot: string): SubmoduleInfo[];
5
+ static discoverPeerRepos(workspaceRoot: string): SubmoduleInfo[];
6
+ static discoverVSCodeWorkspace(workspaceFile: string, workspaceRoot: string): SubmoduleInfo[];
7
+ }
8
+
9
+ export { WorkspaceManager };
@@ -0,0 +1,64 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { resolve, join, dirname, basename, relative } from "node:path";
3
+ import { discoverSubmodules } from "./git-tracker.js";
4
+ class WorkspaceManager {
5
+ static discoverSubmodules(repoRoot) {
6
+ return discoverSubmodules(repoRoot);
7
+ }
8
+ static discoverPeerRepos(workspaceRoot) {
9
+ const parentDir = dirname(resolve(workspaceRoot));
10
+ if (!existsSync(parentDir)) return [];
11
+ const peers = [];
12
+ try {
13
+ const entries = readdirSync(parentDir, { withFileTypes: true });
14
+ for (const entry of entries) {
15
+ if (!entry.isDirectory()) continue;
16
+ if (entry.name === basename(workspaceRoot)) continue;
17
+ const siblingPath = join(parentDir, entry.name);
18
+ const gitPath = join(siblingPath, ".git");
19
+ if (existsSync(gitPath)) {
20
+ peers.push({
21
+ name: entry.name,
22
+ path: `../${entry.name}`,
23
+ url: "",
24
+ isInitialized: true
25
+ });
26
+ }
27
+ }
28
+ } catch {
29
+ }
30
+ return peers;
31
+ }
32
+ static discoverVSCodeWorkspace(workspaceFile, workspaceRoot) {
33
+ if (!existsSync(workspaceFile)) return [];
34
+ const repos = [];
35
+ try {
36
+ const content = readFileSync(workspaceFile, "utf-8");
37
+ const ws = JSON.parse(content);
38
+ const wsDir = dirname(resolve(workspaceFile));
39
+ if (ws && Array.isArray(ws.folders)) {
40
+ for (const folder of ws.folders) {
41
+ if (typeof folder.path === "string") {
42
+ const absFolder = resolve(wsDir, folder.path);
43
+ if (absFolder === resolve(workspaceRoot)) continue;
44
+ const gitPath = join(absFolder, ".git");
45
+ if (existsSync(gitPath)) {
46
+ const relPath = relative(resolve(workspaceRoot), absFolder);
47
+ repos.push({
48
+ name: folder.name || basename(absFolder),
49
+ path: relPath,
50
+ url: "",
51
+ isInitialized: true
52
+ });
53
+ }
54
+ }
55
+ }
56
+ }
57
+ } catch {
58
+ }
59
+ return repos;
60
+ }
61
+ }
62
+ export {
63
+ WorkspaceManager
64
+ };
@@ -0,0 +1,16 @@
1
+ import { Store } from '../core/store.js';
2
+ import { MapxGraph } from '../core/graph.js';
3
+ import '../core/store-interface.js';
4
+ import '../types.js';
5
+
6
+ declare class DotExporter {
7
+ private store;
8
+ private graph;
9
+ constructor(store: Store, graph: MapxGraph);
10
+ export(repo?: string, filesFilter?: string[], opts?: {
11
+ cluster?: 'none' | 'auto';
12
+ depth?: number;
13
+ }): string;
14
+ }
15
+
16
+ export { DotExporter };