@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,504 @@
1
+ import { createServer } from "node:http";
2
+ import { readFileSync, existsSync, statSync } from "node:fs";
3
+ import { resolve, join, extname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Config } from "./core/config.js";
6
+ import { Store } from "./core/store.js";
7
+ import { MapxGraph } from "./core/graph.js";
8
+ import { calculateMetrics, calculateGraphMetrics } from "./core/metrics.js";
9
+ import { ContextBuilder } from "./core/context-builder.js";
10
+ import { RouteRegistry } from "./frameworks/route-registry.js";
11
+ import { UiEventBus, getToolCallsLogPath } from "./ui-events.js";
12
+ import { getChangedFiles, isGitRepo } from "./core/git-tracker.js";
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = resolve(__filename, "..");
15
+ function findUiDir() {
16
+ if (existsSync(__filename)) {
17
+ const root = resolve(__dirname, "..");
18
+ if (existsSync(join(root, "dist/ui/main.js"))) {
19
+ return join(root, "dist/ui");
20
+ }
21
+ if (existsSync(join(root, "ui/main.js"))) {
22
+ return join(root, "ui");
23
+ }
24
+ }
25
+ const binDir = resolve(process.execPath, "..");
26
+ const candidates = [
27
+ join(binDir, "ui"),
28
+ // next to binary
29
+ resolve(binDir, "..", "share", "mapx", "ui"),
30
+ // XDG system
31
+ join(process.env["HOME"] ?? "", ".local", "share", "mapx", "ui")
32
+ // XDG user
33
+ ];
34
+ for (const dir of candidates) {
35
+ if (existsSync(join(dir, "main.js"))) {
36
+ return dir;
37
+ }
38
+ }
39
+ return join(__dirname, "ui");
40
+ }
41
+ function startUiServer(opts) {
42
+ const { port, host, token, dir } = opts;
43
+ const uiDir = findUiDir();
44
+ const rateLimitMap = /* @__PURE__ */ new Map();
45
+ const isRateLimited = (ip) => {
46
+ const now = Date.now();
47
+ const windowStart = now - 6e4;
48
+ let timestamps = rateLimitMap.get(ip) || [];
49
+ timestamps = timestamps.filter((t) => t > windowStart);
50
+ if (timestamps.length >= 10) {
51
+ return true;
52
+ }
53
+ timestamps.push(now);
54
+ rateLimitMap.set(ip, timestamps);
55
+ return false;
56
+ };
57
+ const getClientIp = (req) => {
58
+ return req.headers["x-forwarded-for"] || req.socket.remoteAddress || "unknown";
59
+ };
60
+ const checkAuth = (req) => {
61
+ if (!token) return true;
62
+ const auth = req.headers["authorization"] || req.headers["Authorization"];
63
+ if (typeof auth !== "string") return false;
64
+ return auth.trim() === `Bearer ${token}`;
65
+ };
66
+ const setCorsHeaders = (req, res) => {
67
+ const origin = req.headers.origin;
68
+ if (origin && /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin)) {
69
+ res.setHeader("Access-Control-Allow-Origin", origin);
70
+ } else {
71
+ res.setHeader("Access-Control-Allow-Origin", "*");
72
+ }
73
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
74
+ res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
75
+ };
76
+ const server = createServer(async (req, res) => {
77
+ setCorsHeaders(req, res);
78
+ if (req.method === "OPTIONS") {
79
+ res.writeHead(204);
80
+ res.end();
81
+ return;
82
+ }
83
+ const ip = getClientIp(req);
84
+ const parsedUrl = new URL(req.url || "/", `http://${host}:${port}`);
85
+ const pathname = parsedUrl.pathname;
86
+ if (pathname.startsWith("/api") || pathname === "/events") {
87
+ if (!checkAuth(req)) {
88
+ res.writeHead(401, { "Content-Type": "application/json" });
89
+ res.end(JSON.stringify({ error: "Unauthorized: invalid or missing Bearer token" }));
90
+ return;
91
+ }
92
+ }
93
+ if (pathname === "/api/context" || pathname === "/api/graph") {
94
+ if (isRateLimited(ip)) {
95
+ res.writeHead(429, { "Content-Type": "application/json" });
96
+ res.end(JSON.stringify({ error: "Rate limit exceeded: max 10 requests per minute" }));
97
+ return;
98
+ }
99
+ }
100
+ try {
101
+ if (pathname === "/api/status") {
102
+ const configPath = resolve(dir, ".mapx", "config.json");
103
+ if (!existsSync(configPath)) {
104
+ res.writeHead(400, { "Content-Type": "application/json" });
105
+ res.end(JSON.stringify({ error: "Mapx not initialized" }));
106
+ return;
107
+ }
108
+ const dbPath = resolve(dir, ".mapx", "mapx.db");
109
+ const store = new Store(dbPath);
110
+ try {
111
+ const fileCount = store.getFileCount();
112
+ const symbolCount = store.getSymbolCount();
113
+ const edgeCount = store.getEdgeCount();
114
+ const lastScan = store.getMeta("last_scan_time") || "never";
115
+ const config = await Config.load(dir);
116
+ res.writeHead(200, { "Content-Type": "application/json" });
117
+ res.end(JSON.stringify({
118
+ repoName: config.repo.name,
119
+ lastScan,
120
+ fileCount,
121
+ symbolCount,
122
+ edgeCount,
123
+ languages: store.getLanguageBreakdown()
124
+ }));
125
+ } finally {
126
+ store.close();
127
+ }
128
+ return;
129
+ }
130
+ if (pathname === "/api/graph") {
131
+ const dbPath = resolve(dir, ".mapx", "mapx.db");
132
+ const store = new Store(dbPath);
133
+ try {
134
+ const files = store.getAllFiles();
135
+ const edges = store.getAllEdges();
136
+ const elements = [];
137
+ for (const f of files) {
138
+ const fPath = f.path;
139
+ elements.push({
140
+ data: {
141
+ id: fPath,
142
+ label: fPath.split("/").pop() || fPath,
143
+ type: "file",
144
+ language: f.language,
145
+ size: f.size_bytes,
146
+ lines: f.lines
147
+ }
148
+ });
149
+ }
150
+ for (const e of edges) {
151
+ elements.push({
152
+ data: {
153
+ id: `edge-${e.source_file}-${e.target_file}`,
154
+ source: e.source_file,
155
+ target: e.target_file,
156
+ type: e.edge_type,
157
+ verifiability: e.verifiability
158
+ }
159
+ });
160
+ }
161
+ const payload = JSON.stringify(elements);
162
+ if (payload.length > 10 * 1024 * 1024) {
163
+ res.writeHead(500, { "Content-Type": "application/json" });
164
+ res.end(JSON.stringify({ error: "Graph too large (exceeded 10MB limit)" }));
165
+ return;
166
+ }
167
+ res.writeHead(200, { "Content-Type": "application/json" });
168
+ res.end(payload);
169
+ } finally {
170
+ store.close();
171
+ }
172
+ return;
173
+ }
174
+ if (pathname === "/api/symbols") {
175
+ const dbPath = resolve(dir, ".mapx", "mapx.db");
176
+ const store = new Store(dbPath);
177
+ try {
178
+ const term = parsedUrl.searchParams.get("q") || "";
179
+ const limit = parseInt(parsedUrl.searchParams.get("limit") || "100", 10);
180
+ const results = store.searchSymbolsFiltered({ term, limit });
181
+ res.writeHead(200, { "Content-Type": "application/json" });
182
+ res.end(JSON.stringify(results));
183
+ } finally {
184
+ store.close();
185
+ }
186
+ return;
187
+ }
188
+ if (pathname.startsWith("/api/symbol/")) {
189
+ const dbPath = resolve(dir, ".mapx", "mapx.db");
190
+ const store = new Store(dbPath);
191
+ try {
192
+ const symName = decodeURIComponent(pathname.substring("/api/symbol/".length));
193
+ const sym = store.getSymbolByName(symName);
194
+ if (!sym) {
195
+ res.writeHead(404, { "Content-Type": "application/json" });
196
+ res.end(JSON.stringify({ error: "Symbol not found" }));
197
+ return;
198
+ }
199
+ const callers = store.getCallersOfSymbol(symName);
200
+ const callees = store.getCalleesOfSymbol(symName);
201
+ let sourceCode = "";
202
+ if (sym.file_path) {
203
+ try {
204
+ const fullFilePath = resolve(dir, sym.file_path);
205
+ if (existsSync(fullFilePath)) {
206
+ const fileContent = readFileSync(fullFilePath, "utf-8");
207
+ const lines = fileContent.split("\n");
208
+ const start = Math.max(0, (sym.start_line || 1) - 1);
209
+ const end = Math.min(lines.length, sym.end_line || lines.length);
210
+ sourceCode = lines.slice(start, end).join("\n");
211
+ }
212
+ } catch {
213
+ }
214
+ }
215
+ res.writeHead(200, { "Content-Type": "application/json" });
216
+ res.end(JSON.stringify({
217
+ symbol: sym,
218
+ callers,
219
+ callees,
220
+ sourceCode
221
+ }));
222
+ } finally {
223
+ store.close();
224
+ }
225
+ return;
226
+ }
227
+ if (pathname === "/api/metrics") {
228
+ const dbPath = resolve(dir, ".mapx", "mapx.db");
229
+ const store = new Store(dbPath);
230
+ try {
231
+ const config = await Config.load(dir);
232
+ const graph = new MapxGraph(config.repo.name);
233
+ for (const file of store.getAllFiles()) {
234
+ graph.addFileNode(file.path, file.language, file.size_bytes, file.lines);
235
+ }
236
+ for (const sym of store.getAllSymbols()) {
237
+ graph.addSymbolNode(sym.name, sym.file_path, sym.name, sym.kind, sym.start_line, sym.end_line, sym.scope);
238
+ }
239
+ for (const edge of store.getAllEdges()) {
240
+ graph.addDependencyEdge({
241
+ sourceFile: edge.source_file,
242
+ targetFile: edge.target_file,
243
+ sourceSymbol: edge.source_symbol,
244
+ targetSymbol: edge.target_symbol,
245
+ edgeType: edge.edge_type,
246
+ repo: edge.repo,
247
+ weight: edge.weight,
248
+ verifiability: edge.verifiability,
249
+ targetRepo: edge.target_repo
250
+ });
251
+ }
252
+ const fileCount = store.getFileCount();
253
+ const symbolCount = store.getSymbolCount();
254
+ const edgeCount = store.getEdgeCount();
255
+ const fileMetrics = calculateMetrics(store, { repo: config.repo.name });
256
+ const graphMetrics = calculateGraphMetrics(store, config.repo.name);
257
+ const topFiles = store.getTopFilesByPageRank(graph, 10);
258
+ const topSymbols = store.getTopSymbolsByPageRank(graph, 10);
259
+ const verifiedEdges = store.raw.prepare("SELECT COUNT(*) as cnt FROM edges WHERE verifiability = 'verified'").get()?.cnt || 0;
260
+ const inferredEdges = store.raw.prepare("SELECT COUNT(*) as cnt FROM edges WHERE verifiability = 'inferred'").get()?.cnt || 0;
261
+ const languages = store.getLanguageBreakdown();
262
+ const symbolKinds = store.raw.prepare(
263
+ "SELECT kind, COUNT(*) as cnt FROM symbols GROUP BY kind ORDER BY cnt DESC"
264
+ ).all();
265
+ const edgeTypes = store.raw.prepare(
266
+ "SELECT edge_type, COUNT(*) as cnt FROM edges GROUP BY edge_type ORDER BY cnt DESC"
267
+ ).all();
268
+ let dbSize = 0;
269
+ try {
270
+ dbSize = statSync(dbPath).size;
271
+ } catch {
272
+ }
273
+ const repoRoot = resolve(dir, config.repo.path);
274
+ const isGit = isGitRepo(repoRoot);
275
+ const lastCommit = store.getMeta("last_scan_commit:" + config.repo.name) || store.getMeta("last_scan_commit");
276
+ const gitChanges = isGit ? getChangedFiles(repoRoot, lastCommit || void 0) : [];
277
+ res.writeHead(200, { "Content-Type": "application/json" });
278
+ res.end(JSON.stringify({
279
+ totalFiles: fileCount,
280
+ totalSymbols: symbolCount,
281
+ totalEdges: edgeCount,
282
+ verifiedEdges,
283
+ inferredEdges,
284
+ languages,
285
+ symbolKinds,
286
+ edgeTypes,
287
+ avgEdgesPerFile: fileCount > 0 ? (edgeCount / fileCount).toFixed(2) : "0",
288
+ dbSize,
289
+ density: `${(graphMetrics.density * 100).toFixed(4)}%`,
290
+ transitivity: graphMetrics.transitivity.toFixed(4),
291
+ git: {
292
+ isGit,
293
+ changesCount: gitChanges.length,
294
+ changes: gitChanges
295
+ },
296
+ fileMetrics,
297
+ topFiles,
298
+ topSymbols
299
+ }));
300
+ } finally {
301
+ store.close();
302
+ }
303
+ return;
304
+ }
305
+ if (pathname === "/api/context") {
306
+ if (req.method !== "POST") {
307
+ res.writeHead(405, { "Content-Type": "application/json" });
308
+ res.end(JSON.stringify({ error: "Method not allowed" }));
309
+ return;
310
+ }
311
+ let body = "";
312
+ req.on("data", (chunk) => {
313
+ body += chunk;
314
+ });
315
+ req.on("end", async () => {
316
+ let store = null;
317
+ try {
318
+ const data = JSON.parse(body);
319
+ const task = data.task || "";
320
+ const dbPath = resolve(dir, ".mapx", "mapx.db");
321
+ store = new Store(dbPath);
322
+ const config = await Config.load(dir);
323
+ const graph = new MapxGraph(config.repo.name);
324
+ for (const file of store.getAllFiles()) {
325
+ graph.addFileNode(file.path, file.language, file.size_bytes, file.lines);
326
+ }
327
+ for (const sym of store.getAllSymbols()) {
328
+ graph.addSymbolNode(sym.name, sym.file_path, sym.name, sym.kind, sym.start_line, sym.end_line, sym.scope);
329
+ }
330
+ for (const edge of store.getAllEdges()) {
331
+ graph.addDependencyEdge({
332
+ sourceFile: edge.source_file,
333
+ targetFile: edge.target_file,
334
+ sourceSymbol: edge.source_symbol,
335
+ targetSymbol: edge.target_symbol,
336
+ edgeType: edge.edge_type,
337
+ repo: edge.repo,
338
+ weight: edge.weight,
339
+ verifiability: edge.verifiability,
340
+ targetRepo: edge.target_repo
341
+ });
342
+ }
343
+ const builder = new ContextBuilder(store, graph);
344
+ const context = await builder.buildContext({ task, tokens: 15e3 });
345
+ res.writeHead(200, { "Content-Type": "application/json" });
346
+ res.end(JSON.stringify(context));
347
+ } catch (err) {
348
+ res.writeHead(500, { "Content-Type": "application/json" });
349
+ res.end(JSON.stringify({ error: err.message }));
350
+ } finally {
351
+ if (store) {
352
+ store.close();
353
+ }
354
+ }
355
+ });
356
+ return;
357
+ }
358
+ if (pathname === "/api/routes") {
359
+ const routeRegistry = new RouteRegistry();
360
+ await routeRegistry.load(dir);
361
+ res.writeHead(200, { "Content-Type": "application/json" });
362
+ res.end(JSON.stringify({
363
+ routes: routeRegistry.getRoutes(),
364
+ hooks: routeRegistry.getHooks()
365
+ }));
366
+ return;
367
+ }
368
+ if (pathname === "/api/tool-calls") {
369
+ const mapxDir = resolve(dir, ".mapx");
370
+ const logPath = getToolCallsLogPath(mapxDir);
371
+ const events = [];
372
+ if (existsSync(logPath)) {
373
+ try {
374
+ const content = readFileSync(logPath, "utf-8");
375
+ const lines = content.split("\n").filter(Boolean);
376
+ const recent = lines.slice(-100).reverse();
377
+ for (const line of recent) {
378
+ try {
379
+ events.push(JSON.parse(line));
380
+ } catch {
381
+ }
382
+ }
383
+ } catch {
384
+ }
385
+ }
386
+ res.writeHead(200, { "Content-Type": "application/json" });
387
+ res.end(JSON.stringify(events));
388
+ return;
389
+ }
390
+ if (pathname === "/events") {
391
+ res.writeHead(200, {
392
+ "Content-Type": "text/event-stream",
393
+ "Cache-Control": "no-cache",
394
+ "Connection": "keep-alive"
395
+ });
396
+ const eventBus = UiEventBus.getInstance();
397
+ const sendEvent = (name, data) => {
398
+ res.write(`event: ${name}
399
+ data: ${JSON.stringify(data)}
400
+
401
+ `);
402
+ };
403
+ const onToolCall = (event) => sendEvent("tool-call", event);
404
+ const onScanProgress = (event) => sendEvent("scan-progress", event);
405
+ const onScanComplete = (event) => sendEvent("scan-complete", event);
406
+ eventBus.on("tool-call", onToolCall);
407
+ eventBus.on("scan-progress", onScanProgress);
408
+ eventBus.on("scan-complete", onScanComplete);
409
+ const mapxDir = resolve(dir, ".mapx");
410
+ const logPath = getToolCallsLogPath(mapxDir);
411
+ let lastSize = 0;
412
+ try {
413
+ lastSize = existsSync(logPath) ? statSync(logPath).size : 0;
414
+ } catch {
415
+ }
416
+ const pollLogFile = () => {
417
+ try {
418
+ if (!existsSync(logPath)) return;
419
+ const currentSize = statSync(logPath).size;
420
+ if (currentSize <= lastSize) {
421
+ if (currentSize < lastSize) lastSize = currentSize;
422
+ return;
423
+ }
424
+ const { openSync, readSync, closeSync } = require("node:fs");
425
+ const fd = openSync(logPath, "r");
426
+ const buf = Buffer.alloc(currentSize - lastSize);
427
+ readSync(fd, buf, 0, buf.length, lastSize);
428
+ closeSync(fd);
429
+ lastSize = currentSize;
430
+ const newLines = buf.toString("utf-8").split("\n").filter(Boolean);
431
+ for (const line of newLines) {
432
+ try {
433
+ const event = JSON.parse(line);
434
+ sendEvent("tool-call", event);
435
+ } catch {
436
+ }
437
+ }
438
+ } catch {
439
+ }
440
+ };
441
+ let logWatcher = null;
442
+ try {
443
+ if (existsSync(logPath)) {
444
+ const { watch } = require("node:fs");
445
+ logWatcher = watch(logPath, (eventType) => {
446
+ if (eventType === "change") {
447
+ pollLogFile();
448
+ }
449
+ });
450
+ }
451
+ } catch {
452
+ }
453
+ const pollInterval = setInterval(pollLogFile, 500);
454
+ req.on("close", () => {
455
+ eventBus.off("tool-call", onToolCall);
456
+ eventBus.off("scan-progress", onScanProgress);
457
+ eventBus.off("scan-complete", onScanComplete);
458
+ if (logWatcher) {
459
+ try {
460
+ logWatcher.close();
461
+ } catch {
462
+ }
463
+ }
464
+ clearInterval(pollInterval);
465
+ });
466
+ return;
467
+ }
468
+ const rawAssetPath = pathname === "/" ? "/index.html" : pathname;
469
+ const safeAssetPath = join(uiDir, rawAssetPath);
470
+ if (!safeAssetPath.startsWith(uiDir)) {
471
+ res.writeHead(403, { "Content-Type": "text/plain" });
472
+ res.end("Forbidden: Path traversal rejected");
473
+ return;
474
+ }
475
+ if (existsSync(safeAssetPath)) {
476
+ const ext = extname(safeAssetPath);
477
+ const contentTypes = {
478
+ ".html": "text/html",
479
+ ".js": "application/javascript",
480
+ ".css": "text/css",
481
+ ".svg": "image/svg+xml",
482
+ ".json": "application/json",
483
+ ".png": "image/png"
484
+ };
485
+ const contentType = contentTypes[ext] || "application/octet-stream";
486
+ res.writeHead(200, { "Content-Type": contentType });
487
+ res.end(readFileSync(safeAssetPath));
488
+ } else {
489
+ res.writeHead(404, { "Content-Type": "text/plain" });
490
+ res.end("Not Found");
491
+ }
492
+ } catch (err) {
493
+ res.writeHead(500, { "Content-Type": "application/json" });
494
+ res.end(JSON.stringify({ error: err.message }));
495
+ }
496
+ });
497
+ server.listen(port, host, () => {
498
+ console.log(`Mapx UI Server running at http://${host}:${port}`);
499
+ });
500
+ return server;
501
+ }
502
+ export {
503
+ startUiServer
504
+ };
package/package.json ADDED
@@ -0,0 +1,179 @@
1
+ {
2
+ "name": "@mgamil/mapx",
3
+ "version": "0.2.4",
4
+ "license": "Apache-2.0",
5
+ "description": "Multi-language code graph memory system for LLMs",
6
+ "author": {
7
+ "email": "mgamil.dev@gmail.com",
8
+ "name": "Mohamed Gamil",
9
+ "url": "https://gamil.io"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/MohamedGamil/mapx.git"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/MohamedGamil/mapx/issues"
17
+ },
18
+ "homepage": "https://github.com/MohamedGamil/mapx",
19
+ "type": "module",
20
+ "main": "./dist/index.js",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "bin": {
31
+ "mapx": "./dist/main.js"
32
+ },
33
+ "files": [
34
+ "dist/",
35
+ "wasm/",
36
+ "queries/",
37
+ "VERSION",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "engines": {
42
+ "node": ">=20.18.0"
43
+ },
44
+ "scripts": {
45
+ "version-sync": "tsx scripts/sync-version.ts",
46
+ "dev": "tsx watch src/main.ts",
47
+ "build:wasm": "tsx scripts/build-wasm.ts",
48
+ "build:linux": "bun build --compile --minify --bytecode --target=bun-linux-x64 ./src/main.ts --outfile dist/mapx-linux-x64",
49
+ "build:linux-arm": "bun build --compile --minify --bytecode --target=bun-linux-arm64 ./src/main.ts --outfile dist/mapx-linux-arm64",
50
+ "build:mac-arm": "bun build --compile --minify --bytecode --target=bun-darwin-arm64 ./src/main.ts --outfile dist/mapx-darwin-arm64",
51
+ "build:mac-x64": "bun build --compile --minify --bytecode --target=bun-darwin-x64 ./src/main.ts --outfile dist/mapx-darwin-x64",
52
+ "build:win": "bun build --compile --minify --bytecode --target=bun-windows-x64 ./src/main.ts --outfile dist/mapx-windows-x64.exe",
53
+ "build:all": "tsx scripts/build-all.ts",
54
+ "build:npm": "tsup && tsx scripts/build-ui.ts",
55
+ "prepare": "npm run build:npm",
56
+ "init": "tsx src/main.ts init",
57
+ "uninit": "tsx src/main.ts uninit",
58
+ "scan": "tsx src/main.ts scan",
59
+ "update": "tsx src/main.ts update",
60
+ "status": "tsx src/main.ts status",
61
+ "export": "tsx src/main.ts export",
62
+ "export:json": "tsx src/main.ts export --format=json",
63
+ "export:dot": "tsx src/main.ts export --format=dot",
64
+ "export:svg": "tsx src/main.ts export --format=svg",
65
+ "export:toon": "tsx src/main.ts export --format=toon",
66
+ "query": "tsx src/main.ts query",
67
+ "search": "tsx src/main.ts search",
68
+ "deps": "tsx src/main.ts deps",
69
+ "trace": "tsx src/main.ts trace",
70
+ "callers": "tsx src/main.ts callers",
71
+ "callees": "tsx src/main.ts callees",
72
+ "impact": "tsx src/main.ts impact",
73
+ "node": "tsx src/main.ts node",
74
+ "files": "tsx src/main.ts files",
75
+ "clusters": "tsx src/main.ts clusters",
76
+ "summary": "tsx src/main.ts summary",
77
+ "ui": "tsx src/main.ts ui",
78
+ "serve": "tsx src/main.ts serve",
79
+ "serve:sse": "tsx src/main.ts serve --sse",
80
+ "lang:list": "tsx src/main.ts lang list",
81
+ "lang:install": "tsx src/main.ts lang install",
82
+ "lang:uninstall": "tsx src/main.ts lang uninstall",
83
+ "agents:list": "tsx src/main.ts agents list",
84
+ "agents:generate": "tsx src/main.ts agents generate",
85
+ "agents:mcp": "tsx src/main.ts agents mcp",
86
+ "agents:mcp-detect": "tsx src/main.ts agents mcp --detect",
87
+ "routes": "tsx src/main.ts routes",
88
+ "hooks": "tsx src/main.ts hooks",
89
+ "workspaces:list": "tsx src/main.ts workspaces list",
90
+ "workspaces:discover": "tsx src/main.ts workspaces discover",
91
+ "workspaces:sync": "tsx src/main.ts workspaces sync",
92
+ "bench": "tsx benchmarks/run.ts",
93
+ "bench:json": "tsx benchmarks/run.ts --json",
94
+ "test": "tsx src/main.ts scan && tsx src/main.ts export"
95
+ },
96
+ "dependencies": {
97
+ "better-sqlite3": "^12.10.0",
98
+ "commander": "^13.1.0",
99
+ "cytoscape": "^3.33.4",
100
+ "graphology": "^0.26.0",
101
+ "graphology-metrics": "^2.4.0",
102
+ "minimatch": "^10.2.5",
103
+ "tree-sitter-c-sharp": "0.23.5",
104
+ "tree-sitter-go": "0.25.0",
105
+ "tree-sitter-java": "0.23.5",
106
+ "tree-sitter-javascript": "^0.25.0",
107
+ "tree-sitter-php": "^0.24.2",
108
+ "tree-sitter-python": "0.25.0",
109
+ "tree-sitter-rust": "0.24.0",
110
+ "tree-sitter-typescript": "^0.23.2",
111
+ "uplot": "^1.6.32",
112
+ "web-tree-sitter": "^0.26.9",
113
+ "zod": "^3.25.0",
114
+ "@modelcontextprotocol/sdk": "^1.29.0"
115
+ },
116
+ "optionalDependencies": {},
117
+ "devDependencies": {
118
+ "@types/better-sqlite3": "^7.6.13",
119
+ "@types/cytoscape": "^3.21.9",
120
+ "@types/node": "^22.19.19",
121
+ "esbuild": "^0.28.0",
122
+ "graphology-types": "^0.24.8",
123
+ "tree-sitter-c": "^0.23.4",
124
+ "tree-sitter-cpp": "^0.23.4",
125
+ "tree-sitter-dart": "^1.0.0",
126
+ "tree-sitter-kotlin": "^0.3.8",
127
+ "tree-sitter-ruby": "^0.23.1",
128
+ "tree-sitter-scala": "^0.24.0",
129
+ "tree-sitter-swift": "^0.7.1",
130
+ "tree-sitter-vue": "^0.2.1",
131
+ "tsup": "^8.5.1",
132
+ "tsx": "^4.20.0",
133
+ "typescript": "^5.8.0"
134
+ },
135
+ "publishConfig": {
136
+ "access": "public",
137
+ "registry": "https://registry.npmjs.org/"
138
+ },
139
+ "keywords": [
140
+ "mapx",
141
+ "ai",
142
+ "llm",
143
+ "agent",
144
+ "mcp",
145
+ "mcp-server",
146
+ "agentic",
147
+ "code-memory",
148
+ "code-graph",
149
+ "code-understanding",
150
+ "code-knowledge",
151
+ "code-intelligence",
152
+ "code-analysis",
153
+ "multi-language",
154
+ "llm-tool",
155
+ "semantic-analysis",
156
+ "dependency-graph",
157
+ "code-navigation",
158
+ "refactoring",
159
+ "code-search",
160
+ "typescript",
161
+ "python",
162
+ "javascript",
163
+ "java",
164
+ "go",
165
+ "c#",
166
+ "c++",
167
+ "kotlin",
168
+ "swift",
169
+ "rust",
170
+ "php",
171
+ "vue",
172
+ "react",
173
+ "flask",
174
+ "django",
175
+ "laravel",
176
+ "ai-coding",
177
+ "tree-sitter"
178
+ ]
179
+ }