@zuvia-software-solutions/code-mapper 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +215 -0
- package/dist/cli/ai-context.d.ts +19 -0
- package/dist/cli/ai-context.js +168 -0
- package/dist/cli/analyze.d.ts +7 -0
- package/dist/cli/analyze.js +325 -0
- package/dist/cli/augment.d.ts +7 -0
- package/dist/cli/augment.js +27 -0
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +56 -0
- package/dist/cli/eval-server.d.ts +25 -0
- package/dist/cli/eval-server.js +365 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +102 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +19 -0
- package/dist/cli/list.d.ts +2 -0
- package/dist/cli/list.js +27 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +35 -0
- package/dist/cli/refresh.d.ts +12 -0
- package/dist/cli/refresh.js +165 -0
- package/dist/cli/serve.d.ts +5 -0
- package/dist/cli/serve.js +8 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +218 -0
- package/dist/cli/status.d.ts +2 -0
- package/dist/cli/status.js +33 -0
- package/dist/cli/tool.d.ts +28 -0
- package/dist/cli/tool.js +87 -0
- package/dist/config/ignore-service.d.ts +32 -0
- package/dist/config/ignore-service.js +282 -0
- package/dist/config/supported-languages.d.ts +23 -0
- package/dist/config/supported-languages.js +52 -0
- package/dist/core/augmentation/engine.d.ts +22 -0
- package/dist/core/augmentation/engine.js +232 -0
- package/dist/core/embeddings/embedder.d.ts +35 -0
- package/dist/core/embeddings/embedder.js +171 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
- package/dist/core/embeddings/embedding-pipeline.js +402 -0
- package/dist/core/embeddings/index.d.ts +5 -0
- package/dist/core/embeddings/index.js +6 -0
- package/dist/core/embeddings/text-generator.d.ts +20 -0
- package/dist/core/embeddings/text-generator.js +159 -0
- package/dist/core/embeddings/types.d.ts +60 -0
- package/dist/core/embeddings/types.js +23 -0
- package/dist/core/graph/graph.d.ts +4 -0
- package/dist/core/graph/graph.js +65 -0
- package/dist/core/graph/types.d.ts +69 -0
- package/dist/core/graph/types.js +3 -0
- package/dist/core/incremental/child-process.d.ts +8 -0
- package/dist/core/incremental/child-process.js +649 -0
- package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
- package/dist/core/incremental/refresh-coordinator.js +147 -0
- package/dist/core/incremental/types.d.ts +78 -0
- package/dist/core/incremental/types.js +153 -0
- package/dist/core/incremental/watcher.d.ts +63 -0
- package/dist/core/incremental/watcher.js +338 -0
- package/dist/core/ingestion/ast-cache.d.ts +12 -0
- package/dist/core/ingestion/ast-cache.js +34 -0
- package/dist/core/ingestion/call-processor.d.ts +34 -0
- package/dist/core/ingestion/call-processor.js +937 -0
- package/dist/core/ingestion/call-routing.d.ts +40 -0
- package/dist/core/ingestion/call-routing.js +97 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
- package/dist/core/ingestion/cluster-enricher.js +151 -0
- package/dist/core/ingestion/community-processor.d.ts +26 -0
- package/dist/core/ingestion/community-processor.js +272 -0
- package/dist/core/ingestion/constants.d.ts +5 -0
- package/dist/core/ingestion/constants.js +8 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
- package/dist/core/ingestion/entry-point-scoring.js +317 -0
- package/dist/core/ingestion/export-detection.d.ts +11 -0
- package/dist/core/ingestion/export-detection.js +203 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
- package/dist/core/ingestion/filesystem-walker.js +64 -0
- package/dist/core/ingestion/framework-detection.d.ts +42 -0
- package/dist/core/ingestion/framework-detection.js +405 -0
- package/dist/core/ingestion/heritage-processor.d.ts +15 -0
- package/dist/core/ingestion/heritage-processor.js +237 -0
- package/dist/core/ingestion/import-processor.d.ts +31 -0
- package/dist/core/ingestion/import-processor.js +416 -0
- package/dist/core/ingestion/language-config.d.ts +32 -0
- package/dist/core/ingestion/language-config.js +161 -0
- package/dist/core/ingestion/mro-processor.d.ts +32 -0
- package/dist/core/ingestion/mro-processor.js +343 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
- package/dist/core/ingestion/named-binding-extraction.js +343 -0
- package/dist/core/ingestion/parsing-processor.d.ts +20 -0
- package/dist/core/ingestion/parsing-processor.js +282 -0
- package/dist/core/ingestion/pipeline.d.ts +3 -0
- package/dist/core/ingestion/pipeline.js +416 -0
- package/dist/core/ingestion/process-processor.d.ts +42 -0
- package/dist/core/ingestion/process-processor.js +357 -0
- package/dist/core/ingestion/resolution-context.d.ts +40 -0
- package/dist/core/ingestion/resolution-context.js +171 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
- package/dist/core/ingestion/resolvers/csharp.js +101 -0
- package/dist/core/ingestion/resolvers/go.d.ts +8 -0
- package/dist/core/ingestion/resolvers/go.js +33 -0
- package/dist/core/ingestion/resolvers/index.d.ts +14 -0
- package/dist/core/ingestion/resolvers/index.js +10 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
- package/dist/core/ingestion/resolvers/jvm.js +74 -0
- package/dist/core/ingestion/resolvers/php.d.ts +7 -0
- package/dist/core/ingestion/resolvers/php.js +30 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
- package/dist/core/ingestion/resolvers/ruby.js +13 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
- package/dist/core/ingestion/resolvers/rust.js +62 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
- package/dist/core/ingestion/resolvers/standard.js +144 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
- package/dist/core/ingestion/resolvers/utils.js +113 -0
- package/dist/core/ingestion/structure-processor.d.ts +4 -0
- package/dist/core/ingestion/structure-processor.js +39 -0
- package/dist/core/ingestion/symbol-table.d.ts +34 -0
- package/dist/core/ingestion/symbol-table.js +48 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
- package/dist/core/ingestion/tree-sitter-queries.js +691 -0
- package/dist/core/ingestion/type-env.d.ts +52 -0
- package/dist/core/ingestion/type-env.js +349 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/csharp.js +224 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/go.js +261 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
- package/dist/core/ingestion/type-extractors/index.js +30 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/jvm.js +386 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/php.js +280 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/python.js +175 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
- package/dist/core/ingestion/type-extractors/ruby.js +218 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/rust.js +290 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
- package/dist/core/ingestion/type-extractors/shared.js +322 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/swift.js +140 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
- package/dist/core/ingestion/type-extractors/types.js +4 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/typescript.js +227 -0
- package/dist/core/ingestion/utils.d.ts +73 -0
- package/dist/core/ingestion/utils.js +992 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
- package/dist/core/ingestion/workers/parse-worker.js +1055 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
- package/dist/core/ingestion/workers/worker-pool.js +123 -0
- package/dist/core/lbug/csv-generator.d.ts +28 -0
- package/dist/core/lbug/csv-generator.js +355 -0
- package/dist/core/lbug/lbug-adapter.d.ts +96 -0
- package/dist/core/lbug/lbug-adapter.js +753 -0
- package/dist/core/lbug/schema.d.ts +46 -0
- package/dist/core/lbug/schema.js +402 -0
- package/dist/core/search/bm25-index.d.ts +20 -0
- package/dist/core/search/bm25-index.js +123 -0
- package/dist/core/search/hybrid-search.d.ts +32 -0
- package/dist/core/search/hybrid-search.js +131 -0
- package/dist/core/search/query-cache.d.ts +18 -0
- package/dist/core/search/query-cache.js +47 -0
- package/dist/core/search/query-expansion.d.ts +19 -0
- package/dist/core/search/query-expansion.js +75 -0
- package/dist/core/search/reranker.d.ts +29 -0
- package/dist/core/search/reranker.js +122 -0
- package/dist/core/search/types.d.ts +154 -0
- package/dist/core/search/types.js +51 -0
- package/dist/core/semantic/tsgo-service.d.ts +67 -0
- package/dist/core/semantic/tsgo-service.js +355 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
- package/dist/core/tree-sitter/parser-loader.js +71 -0
- package/dist/lib/memory-guard.d.ts +35 -0
- package/dist/lib/memory-guard.js +70 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.js +6 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
- package/dist/mcp/compatible-stdio-transport.js +209 -0
- package/dist/mcp/core/embedder.d.ts +24 -0
- package/dist/mcp/core/embedder.js +168 -0
- package/dist/mcp/core/lbug-adapter.d.ts +29 -0
- package/dist/mcp/core/lbug-adapter.js +330 -0
- package/dist/mcp/local/local-backend.d.ts +188 -0
- package/dist/mcp/local/local-backend.js +2759 -0
- package/dist/mcp/resources.d.ts +22 -0
- package/dist/mcp/resources.js +379 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +217 -0
- package/dist/mcp/staleness.d.ts +10 -0
- package/dist/mcp/staleness.js +25 -0
- package/dist/mcp/tools.d.ts +21 -0
- package/dist/mcp/tools.js +202 -0
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +340 -0
- package/dist/server/mcp-http.d.ts +7 -0
- package/dist/server/mcp-http.js +95 -0
- package/dist/storage/git.d.ts +6 -0
- package/dist/storage/git.js +35 -0
- package/dist/storage/repo-manager.d.ts +87 -0
- package/dist/storage/repo-manager.js +249 -0
- package/dist/types/pipeline.d.ts +35 -0
- package/dist/types/pipeline.js +20 -0
- package/hooks/claude/code-mapper-hook.cjs +238 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/models/mlx-embedder.py +185 -0
- package/package.json +100 -0
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
// code-mapper/src/core/incremental/watcher.ts
|
|
2
|
+
/**
|
|
3
|
+
* @file File system watcher for incremental refresh
|
|
4
|
+
* @description Monitors source file changes using fs.watch (recursive) on macOS/Windows,
|
|
5
|
+
* falling back to interval-based polling on platforms where recursive watching is
|
|
6
|
+
* unavailable (Linux Node <19). Maintains a debounced dirty-file map that the
|
|
7
|
+
* refresh scheduler drains on each tick
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { toRelativeFilePath } from './types.js';
|
|
12
|
+
import { SUPPORTED_EXTENSIONS } from '../../config/supported-languages.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Constants
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
/** Debounce window in ms — editors emit multiple fs events per save */
|
|
17
|
+
const DEBOUNCE_MS = 500;
|
|
18
|
+
/** Polling interval when recursive watch is unavailable or errors out */
|
|
19
|
+
const POLL_INTERVAL_MS = 2_000;
|
|
20
|
+
/** Directory names to ignore at any depth */
|
|
21
|
+
const IGNORED_DIRS = new Set([
|
|
22
|
+
'node_modules',
|
|
23
|
+
'.git',
|
|
24
|
+
'dist',
|
|
25
|
+
'build',
|
|
26
|
+
'.code-mapper',
|
|
27
|
+
'__pycache__',
|
|
28
|
+
'.next',
|
|
29
|
+
'target',
|
|
30
|
+
'vendor',
|
|
31
|
+
'.venv',
|
|
32
|
+
]);
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Helpers
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
/** Check whether any segment in a relative path matches an ignored directory */
|
|
37
|
+
function isIgnoredPath(relativePath) {
|
|
38
|
+
const segments = relativePath.split('/');
|
|
39
|
+
return segments.some((seg) => IGNORED_DIRS.has(seg));
|
|
40
|
+
}
|
|
41
|
+
/** Check whether a filename has a supported source extension */
|
|
42
|
+
function hasSupportedExtension(filename) {
|
|
43
|
+
const ext = path.extname(filename).toLowerCase();
|
|
44
|
+
return SUPPORTED_EXTENSIONS.has(ext);
|
|
45
|
+
}
|
|
46
|
+
/** Classify a file change by stat-ing the absolute path */
|
|
47
|
+
async function classifyChange(absolutePath, knownBefore) {
|
|
48
|
+
try {
|
|
49
|
+
await fs.promises.stat(absolutePath);
|
|
50
|
+
// File exists now
|
|
51
|
+
return knownBefore ? 'modified' : 'created';
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// File no longer exists
|
|
55
|
+
return 'deleted';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// FileSystemWatcher
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
export class FileSystemWatcher {
|
|
62
|
+
repoRoot;
|
|
63
|
+
dirtyFiles = new Map();
|
|
64
|
+
// Debounce timers keyed by relative path
|
|
65
|
+
debounceTimers = new Map();
|
|
66
|
+
// Snapshot state used only by poll-based fallback
|
|
67
|
+
previousSnapshot = null;
|
|
68
|
+
// Active watcher / poll handle
|
|
69
|
+
fsWatcher = null;
|
|
70
|
+
pollTimer = null;
|
|
71
|
+
// Track known files so we can distinguish created vs modified
|
|
72
|
+
knownFiles = new Set();
|
|
73
|
+
running = false;
|
|
74
|
+
constructor(repoRoot) {
|
|
75
|
+
this.repoRoot = repoRoot;
|
|
76
|
+
}
|
|
77
|
+
// -------------------------------------------------------------------------
|
|
78
|
+
// Public API
|
|
79
|
+
// -------------------------------------------------------------------------
|
|
80
|
+
/** Begin watching the repository for source file changes */
|
|
81
|
+
async start() {
|
|
82
|
+
if (this.running)
|
|
83
|
+
return;
|
|
84
|
+
this.running = true;
|
|
85
|
+
// Seed knownFiles from the initial filesystem scan
|
|
86
|
+
await this.seedKnownFiles(this.repoRoot);
|
|
87
|
+
try {
|
|
88
|
+
this.startRecursiveWatch();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Recursive watch unsupported or failed — fall back to polling
|
|
92
|
+
this.startPolling();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Stop watching and release all resources */
|
|
96
|
+
stop() {
|
|
97
|
+
this.running = false;
|
|
98
|
+
if (this.fsWatcher) {
|
|
99
|
+
this.fsWatcher.close();
|
|
100
|
+
this.fsWatcher = null;
|
|
101
|
+
}
|
|
102
|
+
if (this.pollTimer) {
|
|
103
|
+
clearInterval(this.pollTimer);
|
|
104
|
+
this.pollTimer = null;
|
|
105
|
+
}
|
|
106
|
+
// Clear any pending debounce timers
|
|
107
|
+
for (const timer of this.debounceTimers.values()) {
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
}
|
|
110
|
+
this.debounceTimers.clear();
|
|
111
|
+
}
|
|
112
|
+
/** Returns true when there is at least one dirty file pending */
|
|
113
|
+
hasDirtyFiles() {
|
|
114
|
+
return this.dirtyFiles.size > 0 || this.debounceTimers.size > 0;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Flush all pending debounce timers so their changes become immediately
|
|
118
|
+
* visible in the dirty file map. Must be called before drain() to avoid
|
|
119
|
+
* missing edits that happened within the debounce window.
|
|
120
|
+
*/
|
|
121
|
+
async flush() {
|
|
122
|
+
if (this.debounceTimers.size === 0)
|
|
123
|
+
return;
|
|
124
|
+
const pending = [];
|
|
125
|
+
for (const [relPath, timer] of this.debounceTimers) {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
pending.push(this.recordChange(relPath));
|
|
128
|
+
}
|
|
129
|
+
this.debounceTimers.clear();
|
|
130
|
+
await Promise.all(pending);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Re-inject dirty file entries that were previously drained but whose
|
|
134
|
+
* refresh failed. Ensures the next ensureFresh() retries them.
|
|
135
|
+
* Existing entries (from new edits since the drain) are NOT overwritten.
|
|
136
|
+
*/
|
|
137
|
+
inject(entries) {
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
// Don't overwrite a newer change that arrived after the failed drain
|
|
140
|
+
if (!this.dirtyFiles.has(entry.relativePath)) {
|
|
141
|
+
this.dirtyFiles.set(entry.relativePath, entry);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Return the current dirty file map and clear it
|
|
147
|
+
*
|
|
148
|
+
* Returns null if there are no dirty files, allowing callers to
|
|
149
|
+
* short-circuit without allocating
|
|
150
|
+
*/
|
|
151
|
+
drain() {
|
|
152
|
+
if (this.dirtyFiles.size === 0)
|
|
153
|
+
return null;
|
|
154
|
+
const snapshot = new Map(this.dirtyFiles);
|
|
155
|
+
this.dirtyFiles.clear();
|
|
156
|
+
return snapshot;
|
|
157
|
+
}
|
|
158
|
+
// -------------------------------------------------------------------------
|
|
159
|
+
// Recursive fs.watch implementation (macOS / Windows)
|
|
160
|
+
// -------------------------------------------------------------------------
|
|
161
|
+
startRecursiveWatch() {
|
|
162
|
+
const watcher = fs.watch(this.repoRoot, { recursive: true }, (eventType, filename) => {
|
|
163
|
+
if (!filename)
|
|
164
|
+
return;
|
|
165
|
+
this.handleRawEvent(filename, eventType);
|
|
166
|
+
});
|
|
167
|
+
watcher.on('error', (err) => {
|
|
168
|
+
// EMFILE = too many open files — degrade gracefully to polling
|
|
169
|
+
if (err.code === 'EMFILE' || err.code === 'ENOSPC') {
|
|
170
|
+
watcher.close();
|
|
171
|
+
this.fsWatcher = null;
|
|
172
|
+
this.startPolling();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// For other errors, log and attempt to continue
|
|
176
|
+
// The watcher may still be functional after transient errors
|
|
177
|
+
});
|
|
178
|
+
this.fsWatcher = watcher;
|
|
179
|
+
}
|
|
180
|
+
// -------------------------------------------------------------------------
|
|
181
|
+
// Raw event handling + debounce
|
|
182
|
+
// -------------------------------------------------------------------------
|
|
183
|
+
handleRawEvent(filename, _eventType) {
|
|
184
|
+
// Normalize to forward-slash relative path
|
|
185
|
+
const relativePath = filename.replace(/\\/g, '/');
|
|
186
|
+
if (isIgnoredPath(relativePath))
|
|
187
|
+
return;
|
|
188
|
+
if (!hasSupportedExtension(relativePath))
|
|
189
|
+
return;
|
|
190
|
+
const relPath = toRelativeFilePath(relativePath);
|
|
191
|
+
// Cancel any existing debounce timer for this path
|
|
192
|
+
const existing = this.debounceTimers.get(relPath);
|
|
193
|
+
if (existing)
|
|
194
|
+
clearTimeout(existing);
|
|
195
|
+
const timer = setTimeout(() => {
|
|
196
|
+
this.debounceTimers.delete(relPath);
|
|
197
|
+
void this.recordChange(relPath);
|
|
198
|
+
}, DEBOUNCE_MS);
|
|
199
|
+
this.debounceTimers.set(relPath, timer);
|
|
200
|
+
}
|
|
201
|
+
async recordChange(relPath) {
|
|
202
|
+
const absolutePath = path.join(this.repoRoot, relPath);
|
|
203
|
+
const wasKnown = this.knownFiles.has(relPath);
|
|
204
|
+
const changeKind = await classifyChange(absolutePath, wasKnown);
|
|
205
|
+
// Update known-file tracking
|
|
206
|
+
if (changeKind === 'deleted') {
|
|
207
|
+
this.knownFiles.delete(relPath);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
this.knownFiles.add(relPath);
|
|
211
|
+
}
|
|
212
|
+
const entry = {
|
|
213
|
+
relativePath: relPath,
|
|
214
|
+
changeKind,
|
|
215
|
+
};
|
|
216
|
+
this.dirtyFiles.set(relPath, entry);
|
|
217
|
+
}
|
|
218
|
+
// -------------------------------------------------------------------------
|
|
219
|
+
// Poll-based fallback (Linux Node <19, or after EMFILE)
|
|
220
|
+
// -------------------------------------------------------------------------
|
|
221
|
+
startPolling() {
|
|
222
|
+
if (this.pollTimer)
|
|
223
|
+
return;
|
|
224
|
+
// Take an initial snapshot synchronously so the first poll detects diffs
|
|
225
|
+
this.previousSnapshot = this.takeSnapshotSync(this.repoRoot);
|
|
226
|
+
this.pollTimer = setInterval(() => {
|
|
227
|
+
void this.pollOnce();
|
|
228
|
+
}, POLL_INTERVAL_MS);
|
|
229
|
+
}
|
|
230
|
+
async pollOnce() {
|
|
231
|
+
if (!this.running)
|
|
232
|
+
return;
|
|
233
|
+
const current = this.takeSnapshotSync(this.repoRoot);
|
|
234
|
+
const previous = this.previousSnapshot ?? new Map();
|
|
235
|
+
// Detect created and modified files
|
|
236
|
+
for (const [relPath, snap] of current) {
|
|
237
|
+
const prev = previous.get(relPath);
|
|
238
|
+
if (!prev) {
|
|
239
|
+
// New file
|
|
240
|
+
this.enqueuePollChange(relPath, 'created');
|
|
241
|
+
}
|
|
242
|
+
else if (prev.mtimeMs !== snap.mtimeMs || prev.size !== snap.size) {
|
|
243
|
+
// Modified file
|
|
244
|
+
this.enqueuePollChange(relPath, 'modified');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Detect deleted files
|
|
248
|
+
for (const relPath of previous.keys()) {
|
|
249
|
+
if (!current.has(relPath)) {
|
|
250
|
+
this.enqueuePollChange(relPath, 'deleted');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
this.previousSnapshot = current;
|
|
254
|
+
}
|
|
255
|
+
enqueuePollChange(relativePath, changeKind) {
|
|
256
|
+
const relPath = toRelativeFilePath(relativePath);
|
|
257
|
+
// Update known-file tracking
|
|
258
|
+
if (changeKind === 'deleted') {
|
|
259
|
+
this.knownFiles.delete(relPath);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
this.knownFiles.add(relPath);
|
|
263
|
+
}
|
|
264
|
+
const entry = {
|
|
265
|
+
relativePath: relPath,
|
|
266
|
+
changeKind,
|
|
267
|
+
};
|
|
268
|
+
this.dirtyFiles.set(relPath, entry);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Walk the repo tree synchronously and collect mtime+size for every
|
|
272
|
+
* supported source file
|
|
273
|
+
*
|
|
274
|
+
* Uses synchronous I/O because this runs on a short interval and needs
|
|
275
|
+
* a consistent snapshot — async walk would race with itself
|
|
276
|
+
*/
|
|
277
|
+
takeSnapshotSync(dir, prefix = '') {
|
|
278
|
+
const result = new Map();
|
|
279
|
+
let entries;
|
|
280
|
+
try {
|
|
281
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Permission denied or directory vanished — skip gracefully
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
for (const entry of entries) {
|
|
288
|
+
if (IGNORED_DIRS.has(entry.name))
|
|
289
|
+
continue;
|
|
290
|
+
const fullPath = path.join(dir, entry.name);
|
|
291
|
+
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
292
|
+
if (entry.isDirectory()) {
|
|
293
|
+
const sub = this.takeSnapshotSync(fullPath, relPath);
|
|
294
|
+
for (const [k, v] of sub) {
|
|
295
|
+
result.set(k, v);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else if (entry.isFile() && hasSupportedExtension(entry.name)) {
|
|
299
|
+
try {
|
|
300
|
+
const stat = fs.statSync(fullPath);
|
|
301
|
+
result.set(relPath, { mtimeMs: stat.mtimeMs, size: stat.size });
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// File vanished between readdir and stat — skip
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
// -------------------------------------------------------------------------
|
|
311
|
+
// Seed known files
|
|
312
|
+
// -------------------------------------------------------------------------
|
|
313
|
+
/**
|
|
314
|
+
* Walk the repo tree and populate knownFiles so the first batch of
|
|
315
|
+
* watcher events can distinguish 'created' from 'modified'
|
|
316
|
+
*/
|
|
317
|
+
async seedKnownFiles(dir, prefix = '') {
|
|
318
|
+
let entries;
|
|
319
|
+
try {
|
|
320
|
+
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
for (const entry of entries) {
|
|
326
|
+
if (IGNORED_DIRS.has(entry.name))
|
|
327
|
+
continue;
|
|
328
|
+
const fullPath = path.join(dir, entry.name);
|
|
329
|
+
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
330
|
+
if (entry.isDirectory()) {
|
|
331
|
+
await this.seedKnownFiles(fullPath, relPath);
|
|
332
|
+
}
|
|
333
|
+
else if (entry.isFile() && hasSupportedExtension(entry.name)) {
|
|
334
|
+
this.knownFiles.add(toRelativeFilePath(relPath));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** @file ast-cache.ts @description LRU cache for parsed tree-sitter AST trees with automatic WASM memory cleanup on eviction */
|
|
2
|
+
import Parser from 'tree-sitter';
|
|
3
|
+
export interface ASTCache {
|
|
4
|
+
get: (filePath: string) => Parser.Tree | undefined;
|
|
5
|
+
set: (filePath: string, tree: Parser.Tree) => void;
|
|
6
|
+
clear: () => void;
|
|
7
|
+
stats: () => {
|
|
8
|
+
size: number;
|
|
9
|
+
maxSize: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare const createASTCache: (maxSize?: number) => ASTCache;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/ast-cache.ts
|
|
2
|
+
/** @file ast-cache.ts @description LRU cache for parsed tree-sitter AST trees with automatic WASM memory cleanup on eviction */
|
|
3
|
+
import { LRUCache } from 'lru-cache';
|
|
4
|
+
export const createASTCache = (maxSize = 50) => {
|
|
5
|
+
const effectiveMax = Math.max(maxSize, 1);
|
|
6
|
+
// Dispose handler runs automatically on eviction to free WASM memory
|
|
7
|
+
const cache = new LRUCache({
|
|
8
|
+
max: effectiveMax,
|
|
9
|
+
dispose: (tree) => {
|
|
10
|
+
try {
|
|
11
|
+
// web-tree-sitter uses tree.delete(); native trees are GC-managed
|
|
12
|
+
tree.delete?.();
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
console.warn('Failed to delete tree from WASM memory', e);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
get: (filePath) => {
|
|
21
|
+
return cache.get(filePath);
|
|
22
|
+
},
|
|
23
|
+
set: (filePath, tree) => {
|
|
24
|
+
cache.set(filePath, tree);
|
|
25
|
+
},
|
|
26
|
+
clear: () => {
|
|
27
|
+
cache.clear();
|
|
28
|
+
},
|
|
29
|
+
stats: () => ({
|
|
30
|
+
size: cache.size,
|
|
31
|
+
maxSize: effectiveMax
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** @file call-processor.ts @description Resolves function/method call sites to target symbol nodes, supporting sequential AST-based and pre-extracted worker-based paths */
|
|
2
|
+
import { KnowledgeGraph } from '../graph/types.js';
|
|
3
|
+
import { ASTCache } from './ast-cache.js';
|
|
4
|
+
import type { ResolutionContext } from './resolution-context.js';
|
|
5
|
+
import type { ExtractedCall, ExtractedHeritage, ExtractedRoute, FileConstructorBindings } from './workers/parse-worker.js';
|
|
6
|
+
export declare const processCalls: (graph: KnowledgeGraph, files: {
|
|
7
|
+
path: string;
|
|
8
|
+
content: string;
|
|
9
|
+
}[], astCache: ASTCache, ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<ExtractedHeritage[]>;
|
|
10
|
+
export declare const extractReturnTypeName: (raw: string, depth?: number) => string | undefined;
|
|
11
|
+
/** Resolve pre-extracted call sites from workers (no AST parsing needed) */
|
|
12
|
+
export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void, constructorBindings?: FileConstructorBindings[]) => Promise<void>;
|
|
13
|
+
/** Resolve pre-extracted Laravel routes to CALLS edges from route files to controller methods */
|
|
14
|
+
export declare const processRoutesFromExtracted: (graph: KnowledgeGraph, extractedRoutes: ExtractedRoute[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Create DEPENDS_ON edges from constructor parameter types.
|
|
17
|
+
*
|
|
18
|
+
* For each Constructor (or constructor-like method like Python __init__),
|
|
19
|
+
* resolves its parameterTypes and creates DEPENDS_ON edges from the owning
|
|
20
|
+
* class to each resolved Class/Interface/Trait/Struct parameter type.
|
|
21
|
+
*
|
|
22
|
+
* This bridges DI: BookingService(IPaymentGateway) → DEPENDS_ON → IPaymentGateway
|
|
23
|
+
*/
|
|
24
|
+
export declare const createDependsOnEdges: (graph: KnowledgeGraph, ctx: ResolutionContext) => Promise<number>;
|
|
25
|
+
/**
|
|
26
|
+
* Create PROVIDES edges from factory method return types.
|
|
27
|
+
*
|
|
28
|
+
* For methods/functions whose return type resolves to an Interface or Trait,
|
|
29
|
+
* creates a PROVIDES edge from the method to the interface. Heuristic filter:
|
|
30
|
+
* only short methods (< 20 lines) OR methods with DI-related framework annotations.
|
|
31
|
+
*
|
|
32
|
+
* This captures @Bean factories, provider methods, and factory functions.
|
|
33
|
+
*/
|
|
34
|
+
export declare const createProvidesEdges: (graph: KnowledgeGraph, ctx: ResolutionContext) => Promise<number>;
|