rho-graph 0.1.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.
Files changed (118) hide show
  1. package/README.md +277 -0
  2. package/bin/rho-graph.js +2 -0
  3. package/dist/cli/commands/index-cmd.d.ts +2 -0
  4. package/dist/cli/commands/index-cmd.js +45 -0
  5. package/dist/cli/commands/index-cmd.js.map +1 -0
  6. package/dist/cli/commands/init.d.ts +3 -0
  7. package/dist/cli/commands/init.js +55 -0
  8. package/dist/cli/commands/init.js.map +1 -0
  9. package/dist/cli/commands/install-hook.d.ts +3 -0
  10. package/dist/cli/commands/install-hook.js +37 -0
  11. package/dist/cli/commands/install-hook.js.map +1 -0
  12. package/dist/cli/commands/install-mcp.d.ts +3 -0
  13. package/dist/cli/commands/install-mcp.js +32 -0
  14. package/dist/cli/commands/install-mcp.js.map +1 -0
  15. package/dist/cli/commands/query.d.ts +2 -0
  16. package/dist/cli/commands/query.js +92 -0
  17. package/dist/cli/commands/query.js.map +1 -0
  18. package/dist/cli/commands/setup.d.ts +2 -0
  19. package/dist/cli/commands/setup.js +15 -0
  20. package/dist/cli/commands/setup.js.map +1 -0
  21. package/dist/cli/commands/status.d.ts +2 -0
  22. package/dist/cli/commands/status.js +40 -0
  23. package/dist/cli/commands/status.js.map +1 -0
  24. package/dist/cli/commands/visualize.d.ts +2 -0
  25. package/dist/cli/commands/visualize.js +45 -0
  26. package/dist/cli/commands/visualize.js.map +1 -0
  27. package/dist/cli/index.d.ts +1 -0
  28. package/dist/cli/index.js +32 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/config.d.ts +22 -0
  31. package/dist/config.js +95 -0
  32. package/dist/config.js.map +1 -0
  33. package/dist/db/connection.d.ts +13 -0
  34. package/dist/db/connection.js +25 -0
  35. package/dist/db/connection.js.map +1 -0
  36. package/dist/db/queries.d.ts +106 -0
  37. package/dist/db/queries.js +247 -0
  38. package/dist/db/queries.js.map +1 -0
  39. package/dist/db/schema.d.ts +2 -0
  40. package/dist/db/schema.js +22 -0
  41. package/dist/db/schema.js.map +1 -0
  42. package/dist/docker/neo4j.d.ts +5 -0
  43. package/dist/docker/neo4j.js +85 -0
  44. package/dist/docker/neo4j.js.map +1 -0
  45. package/dist/indexer/batch-writer.d.ts +35 -0
  46. package/dist/indexer/batch-writer.js +202 -0
  47. package/dist/indexer/batch-writer.js.map +1 -0
  48. package/dist/indexer/extractor.d.ts +35 -0
  49. package/dist/indexer/extractor.js +141 -0
  50. package/dist/indexer/extractor.js.map +1 -0
  51. package/dist/indexer/graph-writer.d.ts +12 -0
  52. package/dist/indexer/graph-writer.js +75 -0
  53. package/dist/indexer/graph-writer.js.map +1 -0
  54. package/dist/indexer/import-resolver.d.ts +8 -0
  55. package/dist/indexer/import-resolver.js +80 -0
  56. package/dist/indexer/import-resolver.js.map +1 -0
  57. package/dist/indexer/index.d.ts +21 -0
  58. package/dist/indexer/index.js +262 -0
  59. package/dist/indexer/index.js.map +1 -0
  60. package/dist/indexer/language-map.json +101 -0
  61. package/dist/indexer/parallel-pipeline.d.ts +41 -0
  62. package/dist/indexer/parallel-pipeline.js +82 -0
  63. package/dist/indexer/parallel-pipeline.js.map +1 -0
  64. package/dist/indexer/parser.d.ts +9 -0
  65. package/dist/indexer/parser.js +85 -0
  66. package/dist/indexer/parser.js.map +1 -0
  67. package/dist/indexer/staleness.d.ts +12 -0
  68. package/dist/indexer/staleness.js +60 -0
  69. package/dist/indexer/staleness.js.map +1 -0
  70. package/dist/mcp/index.d.ts +1 -0
  71. package/dist/mcp/index.js +38 -0
  72. package/dist/mcp/index.js.map +1 -0
  73. package/dist/mcp/staleness-check.d.ts +7 -0
  74. package/dist/mcp/staleness-check.js +31 -0
  75. package/dist/mcp/staleness-check.js.map +1 -0
  76. package/dist/mcp/tools/get-callees.d.ts +3 -0
  77. package/dist/mcp/tools/get-callees.js +34 -0
  78. package/dist/mcp/tools/get-callees.js.map +1 -0
  79. package/dist/mcp/tools/get-callers.d.ts +3 -0
  80. package/dist/mcp/tools/get-callers.js +34 -0
  81. package/dist/mcp/tools/get-callers.js.map +1 -0
  82. package/dist/mcp/tools/get-class.d.ts +3 -0
  83. package/dist/mcp/tools/get-class.js +42 -0
  84. package/dist/mcp/tools/get-class.js.map +1 -0
  85. package/dist/mcp/tools/get-dependencies.d.ts +3 -0
  86. package/dist/mcp/tools/get-dependencies.js +26 -0
  87. package/dist/mcp/tools/get-dependencies.js.map +1 -0
  88. package/dist/mcp/tools/get-dependents.d.ts +3 -0
  89. package/dist/mcp/tools/get-dependents.js +26 -0
  90. package/dist/mcp/tools/get-dependents.js.map +1 -0
  91. package/dist/mcp/tools/get-file-structure.d.ts +3 -0
  92. package/dist/mcp/tools/get-file-structure.js +33 -0
  93. package/dist/mcp/tools/get-file-structure.js.map +1 -0
  94. package/dist/mcp/tools/get-function.d.ts +3 -0
  95. package/dist/mcp/tools/get-function.js +34 -0
  96. package/dist/mcp/tools/get-function.js.map +1 -0
  97. package/dist/mcp/tools/get-repo-structure.d.ts +3 -0
  98. package/dist/mcp/tools/get-repo-structure.js +39 -0
  99. package/dist/mcp/tools/get-repo-structure.js.map +1 -0
  100. package/dist/mcp/tools/reindex.d.ts +4 -0
  101. package/dist/mcp/tools/reindex.js +27 -0
  102. package/dist/mcp/tools/reindex.js.map +1 -0
  103. package/dist/mcp/tools/search-code.d.ts +3 -0
  104. package/dist/mcp/tools/search-code.js +43 -0
  105. package/dist/mcp/tools/search-code.js.map +1 -0
  106. package/dist/visualize/public/graph.js +445 -0
  107. package/dist/visualize/public/index.html +88 -0
  108. package/dist/visualize/queries.d.ts +14 -0
  109. package/dist/visualize/queries.js +84 -0
  110. package/dist/visualize/queries.js.map +1 -0
  111. package/dist/visualize/server.d.ts +19 -0
  112. package/dist/visualize/server.js +293 -0
  113. package/dist/visualize/server.js.map +1 -0
  114. package/docker-compose.yml +16 -0
  115. package/package.json +69 -0
  116. package/src/indexer/language-map.json +128 -0
  117. package/src/visualize/public/graph.js +445 -0
  118. package/src/visualize/public/index.html +88 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Attempt to resolve a raw import source string (as extracted from the AST)
3
+ * to an absolute file path that exists in the indexed file set.
4
+ *
5
+ * Returns null if the import cannot be resolved (e.g., stdlib, third-party,
6
+ * or a language we don't know how to resolve).
7
+ */
8
+ export declare function resolveImport(source: string, fromFilePath: string, language: string, filePathSet: Set<string>): string | null;
@@ -0,0 +1,80 @@
1
+ import { dirname, join, resolve } from "node:path";
2
+ /**
3
+ * Attempt to resolve a raw import source string (as extracted from the AST)
4
+ * to an absolute file path that exists in the indexed file set.
5
+ *
6
+ * Returns null if the import cannot be resolved (e.g., stdlib, third-party,
7
+ * or a language we don't know how to resolve).
8
+ */
9
+ export function resolveImport(source, fromFilePath, language, filePathSet) {
10
+ if (language === "python") {
11
+ return resolvePythonImport(source, fromFilePath, filePathSet);
12
+ }
13
+ if (language === "typescript" || language === "tsx" || language === "javascript") {
14
+ return resolveJsImport(source, fromFilePath, filePathSet);
15
+ }
16
+ return null;
17
+ }
18
+ function resolvePythonImport(source, fromFilePath, filePathSet) {
19
+ // Count leading dots (relative import markers)
20
+ let dots = 0;
21
+ let rest = source;
22
+ while (rest.startsWith(".")) {
23
+ dots++;
24
+ rest = rest.slice(1);
25
+ }
26
+ const relPath = rest ? rest.split(".").join("/") : "";
27
+ if (dots > 0) {
28
+ // Relative import: 1 dot = same package dir, 2 dots = parent, etc.
29
+ let base = dirname(fromFilePath);
30
+ for (let i = 1; i < dots; i++) {
31
+ base = dirname(base);
32
+ }
33
+ return tryPythonCandidates(base, relPath, filePathSet);
34
+ }
35
+ // Absolute import: try file's directory first, then walk up toward repo root.
36
+ // filePathSet is scoped to the repo so no false matches outside it.
37
+ let dir = dirname(fromFilePath);
38
+ while (true) {
39
+ const result = tryPythonCandidates(dir, relPath, filePathSet);
40
+ if (result !== null)
41
+ return result;
42
+ const parent = dirname(dir);
43
+ if (parent === dir)
44
+ break; // reached filesystem root
45
+ dir = parent;
46
+ }
47
+ return null;
48
+ }
49
+ function tryPythonCandidates(base, relPath, filePathSet) {
50
+ const candidates = relPath
51
+ ? [join(base, relPath + ".py"), join(base, relPath, "__init__.py")]
52
+ : [join(base, "__init__.py")];
53
+ for (const candidate of candidates) {
54
+ if (filePathSet.has(candidate))
55
+ return candidate;
56
+ }
57
+ return null;
58
+ }
59
+ function resolveJsImport(source, fromFilePath, filePathSet) {
60
+ if (!source.startsWith("./") && !source.startsWith("../"))
61
+ return null;
62
+ const base = resolve(dirname(fromFilePath), source);
63
+ // Try the path as-is (already has extension)
64
+ if (filePathSet.has(base))
65
+ return base;
66
+ // Try common extensions
67
+ for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
68
+ const candidate = base + ext;
69
+ if (filePathSet.has(candidate))
70
+ return candidate;
71
+ }
72
+ // Try as a directory index file
73
+ for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
74
+ const candidate = join(base, "index" + ext);
75
+ if (filePathSet.has(candidate))
76
+ return candidate;
77
+ }
78
+ return null;
79
+ }
80
+ //# sourceMappingURL=import-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-resolver.js","sourceRoot":"","sources":["../../src/indexer/import-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { DbConnection } from "../db/connection.js";
2
+ import type { IndexConfig } from "../config.js";
3
+ export declare function discoverFiles(rootPath: string, config: IndexConfig): Promise<string[]>;
4
+ export interface IndexResult {
5
+ filesIndexed: number;
6
+ functionsFound: number;
7
+ classesFound: number;
8
+ orphansRemoved: number;
9
+ errors: Array<{
10
+ file: string;
11
+ error: string;
12
+ }>;
13
+ }
14
+ export declare function indexRepository(db: DbConnection, repoPath: string, config: IndexConfig, options?: {
15
+ changedOnly?: boolean;
16
+ specificPath?: string;
17
+ concurrency?: number;
18
+ maxMemoryMB?: number;
19
+ onProgress?: (current: number, total: number, file: string) => void;
20
+ onFlushProgress?: (completed: number, total: number) => void;
21
+ }): Promise<IndexResult>;
@@ -0,0 +1,262 @@
1
+ import { resolve, relative } from "node:path";
2
+ import { execFileSync } from "node:child_process";
3
+ import { glob } from "glob";
4
+ import { minimatch } from "minimatch";
5
+ import { initParser, parseFile, detectLanguage } from "./parser.js";
6
+ import { extractGraphEntities } from "./extractor.js";
7
+ import { writeRepoOnce } from "./graph-writer.js";
8
+ import { BatchGraphWriter } from "./batch-writer.js";
9
+ import { getRepositoryCommit, upsertRepositoryWithCommit, deleteFileAndRelationships, getAllFilePathsUnderPrefix, batchDeleteOrphanFiles, } from "../db/queries.js";
10
+ import { computeFileHash, getFileMtime, isFileStale, getChangedFilesSinceCommit, getCurrentCommitSha } from "./staleness.js";
11
+ import { runParallelPipeline } from "./parallel-pipeline.js";
12
+ /**
13
+ * Attempt to list files via git, which natively respects .gitignore.
14
+ * Returns null if not a git repo or git is unavailable.
15
+ */
16
+ function getGitFiles(absRoot) {
17
+ try {
18
+ const output = execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], { cwd: absRoot, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
19
+ return output.split("\0").filter(Boolean).map((f) => resolve(absRoot, f));
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ export async function discoverFiles(rootPath, config) {
26
+ const absRoot = resolve(rootPath);
27
+ const excludePatterns = config.exclude.map((e) => e.includes("/") ? e : `**/${e}/**`);
28
+ // Prefer git ls-files: it natively respects .gitignore, so node_modules
29
+ // and virtual environments are excluded without needing explicit config.
30
+ const gitFiles = getGitFiles(absRoot);
31
+ if (gitFiles !== null) {
32
+ return gitFiles.filter((absPath) => {
33
+ const rel = relative(absRoot, absPath);
34
+ const matchesInclude = config.include.some((p) => minimatch(rel, p, { dot: true }));
35
+ const matchesExclude = excludePatterns.some((p) => minimatch(rel, p, { dot: true }));
36
+ return matchesInclude && !matchesExclude;
37
+ });
38
+ }
39
+ // Fallback for non-git directories: use glob with manual exclude patterns.
40
+ const allFiles = [];
41
+ for (const pattern of config.include) {
42
+ const matches = await glob(pattern, {
43
+ cwd: absRoot,
44
+ absolute: true,
45
+ ignore: excludePatterns,
46
+ nodir: true,
47
+ });
48
+ allFiles.push(...matches);
49
+ }
50
+ return [...new Set(allFiles)];
51
+ }
52
+ /**
53
+ * Remove File nodes (and their child Function/Class nodes) that exist in the
54
+ * graph under `pathPrefix` but are no longer in the discovered file set.
55
+ *
56
+ * This is the "self-healing" pass: if a previous index run wrote files that
57
+ * shouldn't have been indexed (e.g., venv contents written before .gitignore
58
+ * was respected, or a file that was deleted on disk), this removes them on
59
+ * the next full re-index.
60
+ *
61
+ * Safety: if discovery returned zero files but the graph has files indexed
62
+ * under this prefix, we refuse to delete. That guards against a botched
63
+ * discovery (wrong path, broken git, transient I/O failure) silently
64
+ * nuking the entire repo's graph.
65
+ */
66
+ const ORPHAN_DELETE_CHUNK_SIZE = 500;
67
+ async function cleanOrphanedFiles(db, pathPrefix, discoveredFiles) {
68
+ const session = db.session();
69
+ try {
70
+ const listQ = getAllFilePathsUnderPrefix({ pathPrefix });
71
+ const result = await session.run(listQ.cypher, listQ.params);
72
+ const indexedPaths = result.records.map((r) => r.get("path"));
73
+ const orphans = indexedPaths.filter((p) => !discoveredFiles.has(p));
74
+ if (orphans.length === 0)
75
+ return 0;
76
+ if (discoveredFiles.size === 0) {
77
+ console.warn(`[indexer] orphan cleanup skipped: discovery returned 0 files but ${indexedPaths.length} are indexed under ${pathPrefix}. ` +
78
+ `Refusing to delete to avoid wiping the graph on a botched discovery. ` +
79
+ `If this is intentional, remove the repo manually.`);
80
+ return 0;
81
+ }
82
+ // Chunk the deletes: a single UNWIND of thousands of DETACH DELETEs can
83
+ // exceed Neo4j's per-transaction memory budget (MemoryPoolOutOfMemoryError).
84
+ // 500 per chunk keeps each tx small while still cutting round-trips ~500x
85
+ // vs. one-by-one. The trade-off: a partial run leaves some orphans, which
86
+ // the next index will pick up — acceptable for an idempotent cleanup.
87
+ let deleted = 0;
88
+ for (let i = 0; i < orphans.length; i += ORPHAN_DELETE_CHUNK_SIZE) {
89
+ const chunk = orphans.slice(i, i + ORPHAN_DELETE_CHUNK_SIZE);
90
+ const deleteQ = batchDeleteOrphanFiles(chunk);
91
+ await session.run(deleteQ.cypher, deleteQ.params);
92
+ deleted += chunk.length;
93
+ }
94
+ return deleted;
95
+ }
96
+ finally {
97
+ await session.close();
98
+ }
99
+ }
100
+ export async function indexRepository(db, repoPath, config, options = {}) {
101
+ const absRoot = resolve(repoPath);
102
+ await initParser();
103
+ let files;
104
+ if (options.specificPath) {
105
+ files = await discoverFiles(resolve(absRoot, options.specificPath), config);
106
+ }
107
+ else {
108
+ files = await discoverFiles(absRoot, config);
109
+ }
110
+ // Filter to supported languages
111
+ files = files.filter((f) => detectLanguage(f) !== null);
112
+ // Build the full set of discovered file paths for import resolution
113
+ const filePathSet = new Set(files);
114
+ // If changedOnly, check staleness against existing graph
115
+ if (options.changedOnly) {
116
+ // Try git-based incremental first
117
+ const session = db.session();
118
+ let usedGitBased = false;
119
+ try {
120
+ const commitQ = getRepositoryCommit({ path: absRoot });
121
+ const result = await session.run(commitQ.cypher, commitQ.params);
122
+ const lastCommit = result.records[0]?.get("lastIndexedCommit") ?? null;
123
+ if (lastCommit) {
124
+ const diff = getChangedFilesSinceCommit(absRoot, lastCommit, { includeDeleted: true });
125
+ if (diff.error) {
126
+ // Git failed (invalid SHA, shallow clone, etc.) — fall through to hash/mtime
127
+ }
128
+ else {
129
+ // Remove deleted files from the index
130
+ for (const relPath of diff.deleted) {
131
+ const absPath = resolve(absRoot, relPath);
132
+ const deleteQ = deleteFileAndRelationships({ filePath: absPath });
133
+ await session.run(deleteQ.cypher, deleteQ.params); // let errors propagate
134
+ }
135
+ // Filter files to only changed ones
136
+ const changedAbsPaths = new Set(diff.changed.map((f) => resolve(absRoot, f)));
137
+ files = files.filter((f) => changedAbsPaths.has(f));
138
+ usedGitBased = true;
139
+ }
140
+ }
141
+ }
142
+ finally {
143
+ await session.close();
144
+ }
145
+ // Fall back to mtime/hash-based staleness if git-based wasn't used
146
+ if (!usedGitBased) {
147
+ const session2 = db.session();
148
+ try {
149
+ const result = await session2.run("MATCH (f:File) WHERE f.path STARTS WITH $repoPath RETURN f.path AS path, f.hash AS hash, f.lastModified AS lastModified", { repoPath: absRoot });
150
+ const indexed = new Map(result.records.map((r) => [
151
+ r.get("path"),
152
+ { hash: r.get("hash"), lastModified: r.get("lastModified") },
153
+ ]));
154
+ files = files.filter((f) => {
155
+ const existing = indexed.get(f);
156
+ if (!existing)
157
+ return true;
158
+ return isFileStale(f, existing.hash, existing.lastModified);
159
+ });
160
+ }
161
+ finally {
162
+ await session2.close();
163
+ }
164
+ }
165
+ }
166
+ const result = {
167
+ filesIndexed: 0,
168
+ functionsFound: 0,
169
+ classesFound: 0,
170
+ orphansRemoved: 0,
171
+ errors: [],
172
+ };
173
+ // Snapshot of what discovery returned, for orphan cleanup later. We capture
174
+ // this before any post-discovery filtering so the cleanup compares against
175
+ // the full set of files we *intend* to manage on this run.
176
+ const discoveredSet = options.changedOnly ? null : new Set(files);
177
+ await writeRepoOnce(db, absRoot);
178
+ const concurrency = options.concurrency ?? 8;
179
+ const maxMemoryBytes = (options.maxMemoryMB ?? 8192) * 1024 * 1024;
180
+ if (concurrency > 1) {
181
+ const batchWriter = new BatchGraphWriter(db, { filePathSet });
182
+ const pipelineResult = await runParallelPipeline({
183
+ files,
184
+ absRoot,
185
+ concurrency,
186
+ maxMemoryBytes,
187
+ parseFn: parseFile,
188
+ extractFn: extractGraphEntities,
189
+ batchWriter,
190
+ computeHashFn: computeFileHash,
191
+ getMtimeFn: getFileMtime,
192
+ onProgress: options.onProgress,
193
+ onFlushProgress: options.onFlushProgress,
194
+ });
195
+ result.filesIndexed = pipelineResult.filesIndexed;
196
+ result.functionsFound = pipelineResult.functionsFound;
197
+ result.classesFound = pipelineResult.classesFound;
198
+ result.errors = pipelineResult.errors;
199
+ }
200
+ else {
201
+ // Sequential fallback
202
+ const batchWriter = new BatchGraphWriter(db, { filePathSet });
203
+ for (let i = 0; i < files.length; i++) {
204
+ const file = files[i];
205
+ options.onProgress?.(i + 1, files.length, file);
206
+ try {
207
+ const parseResult = await parseFile(file);
208
+ if (!parseResult)
209
+ continue;
210
+ let entities;
211
+ try {
212
+ entities = extractGraphEntities(parseResult.tree, parseResult.language, parseResult.source, file);
213
+ }
214
+ finally {
215
+ parseResult.tree.delete();
216
+ }
217
+ batchWriter.add(entities, {
218
+ filePath: file, relativePath: relative(absRoot, file), repoPath: absRoot,
219
+ language: parseResult.language,
220
+ hash: computeFileHash(file, parseResult.source),
221
+ lastModified: getFileMtime(file),
222
+ });
223
+ result.filesIndexed++;
224
+ result.functionsFound += entities.functions.length;
225
+ result.classesFound += entities.classes.length;
226
+ }
227
+ catch (error) {
228
+ result.errors.push({ file, error: error instanceof Error ? error.message : String(error) });
229
+ }
230
+ }
231
+ await batchWriter.waitForPendingFlush();
232
+ await batchWriter.flush();
233
+ }
234
+ // Orphan cleanup: remove File nodes that exist in the graph under our scope
235
+ // but weren't in this run's discovered set. Skipped for changedOnly because
236
+ // the git-diff path above already handles deletions for the incremental case.
237
+ if (discoveredSet !== null) {
238
+ const cleanupRoot = options.specificPath
239
+ ? resolve(absRoot, options.specificPath)
240
+ : absRoot;
241
+ result.orphansRemoved = await cleanOrphanedFiles(db, cleanupRoot, discoveredSet);
242
+ }
243
+ if (result.errors.length === 0) {
244
+ const commitSha = getCurrentCommitSha(absRoot);
245
+ if (commitSha) {
246
+ const session = db.session();
247
+ try {
248
+ const q = upsertRepositoryWithCommit({
249
+ path: absRoot,
250
+ name: absRoot.split("/").pop() || absRoot,
251
+ lastIndexedCommit: commitSha,
252
+ });
253
+ await session.run(q.cypher, q.params);
254
+ }
255
+ finally {
256
+ await session.close();
257
+ }
258
+ }
259
+ }
260
+ return result;
261
+ }
262
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ {
2
+ "typescript": {
3
+ "function": ["function_declaration", "arrow_function", "method_definition"],
4
+ "class": ["class_declaration"],
5
+ "import": ["import_statement"],
6
+ "call": ["call_expression"],
7
+ "name_field": "name",
8
+ "body_field": "body",
9
+ "parameters_field": "parameters"
10
+ },
11
+ "tsx": {
12
+ "function": ["function_declaration", "arrow_function", "method_definition"],
13
+ "class": ["class_declaration"],
14
+ "import": ["import_statement"],
15
+ "call": ["call_expression"],
16
+ "name_field": "name",
17
+ "body_field": "body",
18
+ "parameters_field": "parameters"
19
+ },
20
+ "javascript": {
21
+ "function": ["function_declaration", "arrow_function", "method_definition"],
22
+ "class": ["class_declaration"],
23
+ "import": ["import_statement"],
24
+ "call": ["call_expression"],
25
+ "name_field": "name",
26
+ "body_field": "body",
27
+ "parameters_field": "parameters"
28
+ },
29
+ "python": {
30
+ "function": ["function_definition"],
31
+ "class": ["class_definition"],
32
+ "import": ["import_from_statement", "import_statement"],
33
+ "call": ["call"],
34
+ "name_field": "name",
35
+ "body_field": "body",
36
+ "parameters_field": "parameters"
37
+ },
38
+ "go": {
39
+ "function": ["function_declaration", "method_declaration"],
40
+ "class": ["type_declaration"],
41
+ "import": ["import_declaration", "import_spec"],
42
+ "call": ["call_expression"],
43
+ "name_field": "name",
44
+ "body_field": "body",
45
+ "parameters_field": "parameters"
46
+ },
47
+ "java": {
48
+ "function": ["method_declaration", "constructor_declaration"],
49
+ "class": ["class_declaration", "interface_declaration", "enum_declaration"],
50
+ "import": ["import_declaration"],
51
+ "call": ["method_invocation"],
52
+ "name_field": "name",
53
+ "body_field": "body",
54
+ "parameters_field": "parameters"
55
+ },
56
+ "rust": {
57
+ "function": ["function_item"],
58
+ "class": ["struct_item", "enum_item", "trait_item", "impl_item"],
59
+ "import": ["use_declaration"],
60
+ "call": ["call_expression"],
61
+ "name_field": "name",
62
+ "body_field": "body",
63
+ "parameters_field": "parameters"
64
+ },
65
+ "c": {
66
+ "function": ["function_definition"],
67
+ "class": ["struct_specifier"],
68
+ "import": ["preproc_include"],
69
+ "call": ["call_expression"],
70
+ "name_field": "declarator",
71
+ "body_field": "body",
72
+ "parameters_field": "parameters"
73
+ },
74
+ "cpp": {
75
+ "function": ["function_definition"],
76
+ "class": ["class_specifier", "struct_specifier"],
77
+ "import": ["preproc_include"],
78
+ "call": ["call_expression"],
79
+ "name_field": "declarator",
80
+ "body_field": "body",
81
+ "parameters_field": "parameters"
82
+ },
83
+ "ruby": {
84
+ "function": ["method", "singleton_method"],
85
+ "class": ["class", "module"],
86
+ "import": ["call"],
87
+ "call": ["call", "method_call"],
88
+ "name_field": "name",
89
+ "body_field": "body",
90
+ "parameters_field": "parameters"
91
+ },
92
+ "graphql": {
93
+ "function": ["operation_definition", "fragment_definition"],
94
+ "class": ["object_type_definition", "input_object_type_definition", "interface_type_definition", "enum_type_definition"],
95
+ "import": [],
96
+ "call": [],
97
+ "name_field": "name",
98
+ "body_field": "body",
99
+ "parameters_field": "arguments_definition"
100
+ }
101
+ }
@@ -0,0 +1,41 @@
1
+ import type { BatchGraphWriter } from "./batch-writer.js";
2
+ export declare class Semaphore {
3
+ private readonly maxConcurrency;
4
+ private queue;
5
+ private running;
6
+ constructor(maxConcurrency: number);
7
+ acquire(): Promise<() => void>;
8
+ private release;
9
+ }
10
+ export interface PipelineOptions {
11
+ files: string[];
12
+ absRoot: string;
13
+ concurrency: number;
14
+ maxMemoryBytes: number;
15
+ parseFn: (filePath: string) => Promise<{
16
+ tree: any;
17
+ language: string;
18
+ source: string;
19
+ } | null>;
20
+ extractFn: (tree: any, language: string, source: string, filePath: string) => {
21
+ functions: any[];
22
+ classes: any[];
23
+ imports: any[];
24
+ calls: any[];
25
+ };
26
+ batchWriter: BatchGraphWriter;
27
+ computeHashFn: (filePath: string, content?: string) => string;
28
+ getMtimeFn: (filePath: string) => number;
29
+ onProgress?: (current: number, total: number, file: string) => void;
30
+ onFlushProgress?: (completed: number, total: number) => void;
31
+ }
32
+ export interface PipelineResult {
33
+ filesIndexed: number;
34
+ functionsFound: number;
35
+ classesFound: number;
36
+ errors: Array<{
37
+ file: string;
38
+ error: string;
39
+ }>;
40
+ }
41
+ export declare function runParallelPipeline(options: PipelineOptions): Promise<PipelineResult>;
@@ -0,0 +1,82 @@
1
+ import { relative } from "node:path";
2
+ export class Semaphore {
3
+ maxConcurrency;
4
+ queue = [];
5
+ running = 0;
6
+ constructor(maxConcurrency) {
7
+ this.maxConcurrency = maxConcurrency;
8
+ }
9
+ async acquire() {
10
+ if (this.running < this.maxConcurrency) {
11
+ this.running++;
12
+ return () => this.release();
13
+ }
14
+ return new Promise((resolve) => {
15
+ this.queue.push(() => {
16
+ this.running++;
17
+ resolve(() => this.release());
18
+ });
19
+ });
20
+ }
21
+ release() {
22
+ this.running--;
23
+ const next = this.queue.shift();
24
+ if (next)
25
+ next();
26
+ }
27
+ }
28
+ export async function runParallelPipeline(options) {
29
+ const { files, absRoot, concurrency, maxMemoryBytes, parseFn, extractFn, batchWriter, computeHashFn, getMtimeFn, onProgress, onFlushProgress } = options;
30
+ if (onFlushProgress)
31
+ batchWriter.onFlushProgress = onFlushProgress;
32
+ const sem = new Semaphore(concurrency);
33
+ const result = { filesIndexed: 0, functionsFound: 0, classesFound: 0, errors: [] };
34
+ let progressCounter = 0;
35
+ const tasks = files.map((file) => async () => {
36
+ // Backpressure: flush BEFORE acquiring slot — fatal errors propagate out
37
+ while (batchWriter.estimatedMemoryBytes >= maxMemoryBytes) {
38
+ await batchWriter.flush();
39
+ }
40
+ const release = await sem.acquire();
41
+ try {
42
+ const parseResult = await parseFn(file);
43
+ if (parseResult) {
44
+ let entities;
45
+ try {
46
+ entities = extractFn(parseResult.tree, parseResult.language, parseResult.source, file);
47
+ }
48
+ finally {
49
+ parseResult.tree.delete();
50
+ }
51
+ batchWriter.add(entities, {
52
+ filePath: file,
53
+ relativePath: relative(absRoot, file),
54
+ repoPath: absRoot,
55
+ language: parseResult.language,
56
+ hash: computeHashFn(file, parseResult.source),
57
+ lastModified: getMtimeFn(file),
58
+ });
59
+ // Backpressure: flush if memory limit exceeded after add
60
+ if (batchWriter.estimatedMemoryBytes >= maxMemoryBytes) {
61
+ await batchWriter.flush();
62
+ }
63
+ result.filesIndexed++;
64
+ result.functionsFound += entities.functions.length;
65
+ result.classesFound += entities.classes.length;
66
+ }
67
+ }
68
+ catch (error) {
69
+ result.errors.push({ file, error: error instanceof Error ? error.message : String(error) });
70
+ }
71
+ finally {
72
+ progressCounter++;
73
+ onProgress?.(progressCounter, files.length, file);
74
+ release();
75
+ }
76
+ });
77
+ await Promise.all(tasks.map((t) => t()));
78
+ await batchWriter.waitForPendingFlush();
79
+ await batchWriter.flush();
80
+ return result;
81
+ }
82
+ //# sourceMappingURL=parallel-pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel-pipeline.js","sourceRoot":"","sources":["../../src/indexer/parallel-pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import Parser from "web-tree-sitter";
2
+ export declare function detectLanguage(filePath: string): string | null;
3
+ export declare function initParser(): Promise<void>;
4
+ export interface ParseResult {
5
+ tree: Parser.Tree;
6
+ language: string;
7
+ source: string;
8
+ }
9
+ export declare function parseFile(filePath: string): Promise<ParseResult | null>;