@colbymchenry/codegraph-win32-x64 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dist/bin/codegraph.d.ts +1 -1
- package/lib/dist/bin/codegraph.d.ts.map +1 -1
- package/lib/dist/bin/codegraph.js +232 -61
- package/lib/dist/bin/codegraph.js.map +1 -1
- package/lib/dist/bin/command-supervision.d.ts.map +1 -1
- package/lib/dist/bin/command-supervision.js +4 -1
- package/lib/dist/bin/command-supervision.js.map +1 -1
- package/lib/dist/db/index.d.ts +17 -5
- package/lib/dist/db/index.d.ts.map +1 -1
- package/lib/dist/db/index.js +68 -12
- package/lib/dist/db/index.js.map +1 -1
- package/lib/dist/db/migrations.d.ts +1 -1
- package/lib/dist/db/migrations.d.ts.map +1 -1
- package/lib/dist/db/migrations.js +19 -1
- package/lib/dist/db/migrations.js.map +1 -1
- package/lib/dist/db/queries.d.ts +76 -1
- package/lib/dist/db/queries.d.ts.map +1 -1
- package/lib/dist/db/queries.js +180 -0
- package/lib/dist/db/queries.js.map +1 -1
- package/lib/dist/db/schema.sql +19 -0
- package/lib/dist/directory.d.ts +9 -5
- package/lib/dist/directory.d.ts.map +1 -1
- package/lib/dist/directory.js +210 -19
- package/lib/dist/directory.js.map +1 -1
- package/lib/dist/extraction/cfml-extractor.d.ts +107 -0
- package/lib/dist/extraction/cfml-extractor.d.ts.map +1 -0
- package/lib/dist/extraction/cfml-extractor.js +494 -0
- package/lib/dist/extraction/cfml-extractor.js.map +1 -0
- package/lib/dist/extraction/grammars.d.ts +9 -0
- package/lib/dist/extraction/grammars.d.ts.map +1 -1
- package/lib/dist/extraction/grammars.js +115 -3
- package/lib/dist/extraction/grammars.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +46 -1
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js +338 -34
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/extraction/languages/arkts.d.ts +3 -0
- package/lib/dist/extraction/languages/arkts.d.ts.map +1 -0
- package/lib/dist/extraction/languages/arkts.js +127 -0
- package/lib/dist/extraction/languages/arkts.js.map +1 -0
- package/lib/dist/extraction/languages/c-cpp.d.ts +42 -0
- package/lib/dist/extraction/languages/c-cpp.d.ts.map +1 -1
- package/lib/dist/extraction/languages/c-cpp.js +369 -4
- package/lib/dist/extraction/languages/c-cpp.js.map +1 -1
- package/lib/dist/extraction/languages/cfquery.d.ts +12 -0
- package/lib/dist/extraction/languages/cfquery.d.ts.map +1 -0
- package/lib/dist/extraction/languages/cfquery.js +28 -0
- package/lib/dist/extraction/languages/cfquery.js.map +1 -0
- package/lib/dist/extraction/languages/cfscript.d.ts +3 -0
- package/lib/dist/extraction/languages/cfscript.d.ts.map +1 -0
- package/lib/dist/extraction/languages/cfscript.js +73 -0
- package/lib/dist/extraction/languages/cfscript.js.map +1 -0
- package/lib/dist/extraction/languages/cobol.d.ts +33 -0
- package/lib/dist/extraction/languages/cobol.d.ts.map +1 -0
- package/lib/dist/extraction/languages/cobol.js +499 -0
- package/lib/dist/extraction/languages/cobol.js.map +1 -0
- package/lib/dist/extraction/languages/erlang.d.ts +3 -0
- package/lib/dist/extraction/languages/erlang.d.ts.map +1 -0
- package/lib/dist/extraction/languages/erlang.js +350 -0
- package/lib/dist/extraction/languages/erlang.js.map +1 -0
- package/lib/dist/extraction/languages/index.d.ts.map +1 -1
- package/lib/dist/extraction/languages/index.js +18 -0
- package/lib/dist/extraction/languages/index.js.map +1 -1
- package/lib/dist/extraction/languages/nix.d.ts +3 -0
- package/lib/dist/extraction/languages/nix.d.ts.map +1 -0
- package/lib/dist/extraction/languages/nix.js +294 -0
- package/lib/dist/extraction/languages/nix.js.map +1 -0
- package/lib/dist/extraction/languages/solidity.d.ts +3 -0
- package/lib/dist/extraction/languages/solidity.d.ts.map +1 -0
- package/lib/dist/extraction/languages/solidity.js +293 -0
- package/lib/dist/extraction/languages/solidity.js.map +1 -0
- package/lib/dist/extraction/languages/terraform.d.ts +3 -0
- package/lib/dist/extraction/languages/terraform.d.ts.map +1 -0
- package/lib/dist/extraction/languages/terraform.js +641 -0
- package/lib/dist/extraction/languages/terraform.js.map +1 -0
- package/lib/dist/extraction/languages/vbnet.d.ts +11 -0
- package/lib/dist/extraction/languages/vbnet.d.ts.map +1 -0
- package/lib/dist/extraction/languages/vbnet.js +141 -0
- package/lib/dist/extraction/languages/vbnet.js.map +1 -0
- package/lib/dist/extraction/mybatis-extractor.d.ts +30 -10
- package/lib/dist/extraction/mybatis-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/mybatis-extractor.js +140 -38
- package/lib/dist/extraction/mybatis-extractor.js.map +1 -1
- package/lib/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter-helpers.js +1 -0
- package/lib/dist/extraction/tree-sitter-helpers.js.map +1 -1
- package/lib/dist/extraction/tree-sitter-types.d.ts +3 -1
- package/lib/dist/extraction/tree-sitter-types.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter.d.ts +38 -0
- package/lib/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/lib/dist/extraction/tree-sitter.js +753 -10
- package/lib/dist/extraction/tree-sitter.js.map +1 -1
- package/lib/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
- package/lib/dist/index.d.ts +63 -1
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +327 -5
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/installer/index.d.ts.map +1 -1
- package/lib/dist/installer/index.js +3 -1
- package/lib/dist/installer/index.js.map +1 -1
- package/lib/dist/mcp/daemon.d.ts +25 -3
- package/lib/dist/mcp/daemon.d.ts.map +1 -1
- package/lib/dist/mcp/daemon.js +62 -8
- package/lib/dist/mcp/daemon.js.map +1 -1
- package/lib/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
- package/lib/dist/mcp/dynamic-boundaries.js +2 -1
- package/lib/dist/mcp/dynamic-boundaries.js.map +1 -1
- package/lib/dist/mcp/early-ppid.d.ts +26 -0
- package/lib/dist/mcp/early-ppid.d.ts.map +1 -0
- package/lib/dist/mcp/early-ppid.js +29 -0
- package/lib/dist/mcp/early-ppid.js.map +1 -0
- package/lib/dist/mcp/index.d.ts.map +1 -1
- package/lib/dist/mcp/index.js +21 -4
- package/lib/dist/mcp/index.js.map +1 -1
- package/lib/dist/mcp/proxy.d.ts.map +1 -1
- package/lib/dist/mcp/proxy.js +29 -3
- package/lib/dist/mcp/proxy.js.map +1 -1
- package/lib/dist/mcp/query-pool.d.ts +14 -0
- package/lib/dist/mcp/query-pool.d.ts.map +1 -1
- package/lib/dist/mcp/query-pool.js +18 -0
- package/lib/dist/mcp/query-pool.js.map +1 -1
- package/lib/dist/mcp/session.d.ts.map +1 -1
- package/lib/dist/mcp/session.js +6 -0
- package/lib/dist/mcp/session.js.map +1 -1
- package/lib/dist/mcp/startup-handshake.d.ts +44 -0
- package/lib/dist/mcp/startup-handshake.d.ts.map +1 -0
- package/lib/dist/mcp/startup-handshake.js +73 -0
- package/lib/dist/mcp/startup-handshake.js.map +1 -0
- package/lib/dist/mcp/tools.d.ts +22 -0
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +88 -18
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/mcp/transport.d.ts.map +1 -1
- package/lib/dist/mcp/transport.js +18 -0
- package/lib/dist/mcp/transport.js.map +1 -1
- package/lib/dist/project-config.d.ts +38 -0
- package/lib/dist/project-config.d.ts.map +1 -1
- package/lib/dist/project-config.js +101 -2
- package/lib/dist/project-config.js.map +1 -1
- package/lib/dist/resolution/c-fnptr-synthesizer.d.ts +2 -1
- package/lib/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
- package/lib/dist/resolution/c-fnptr-synthesizer.js +170 -146
- package/lib/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
- package/lib/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/lib/dist/resolution/callback-synthesizer.js +982 -81
- package/lib/dist/resolution/callback-synthesizer.js.map +1 -1
- package/lib/dist/resolution/frameworks/cics.d.ts +20 -0
- package/lib/dist/resolution/frameworks/cics.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/cics.js +90 -0
- package/lib/dist/resolution/frameworks/cics.js.map +1 -0
- package/lib/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/index.js +6 -0
- package/lib/dist/resolution/frameworks/index.js.map +1 -1
- package/lib/dist/resolution/frameworks/java.d.ts.map +1 -1
- package/lib/dist/resolution/frameworks/java.js +14 -6
- package/lib/dist/resolution/frameworks/java.js.map +1 -1
- package/lib/dist/resolution/frameworks/terraform.d.ts +38 -0
- package/lib/dist/resolution/frameworks/terraform.d.ts.map +1 -0
- package/lib/dist/resolution/frameworks/terraform.js +277 -0
- package/lib/dist/resolution/frameworks/terraform.js.map +1 -0
- package/lib/dist/resolution/goframe-synthesizer.d.ts +2 -1
- package/lib/dist/resolution/goframe-synthesizer.d.ts.map +1 -1
- package/lib/dist/resolution/goframe-synthesizer.js +8 -3
- package/lib/dist/resolution/goframe-synthesizer.js.map +1 -1
- package/lib/dist/resolution/import-resolver.d.ts +7 -0
- package/lib/dist/resolution/import-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/import-resolver.js +129 -4
- package/lib/dist/resolution/import-resolver.js.map +1 -1
- package/lib/dist/resolution/index.d.ts +49 -6
- package/lib/dist/resolution/index.d.ts.map +1 -1
- package/lib/dist/resolution/index.js +342 -65
- package/lib/dist/resolution/index.js.map +1 -1
- package/lib/dist/resolution/name-matcher.d.ts +0 -3
- package/lib/dist/resolution/name-matcher.d.ts.map +1 -1
- package/lib/dist/resolution/name-matcher.js +225 -33
- package/lib/dist/resolution/name-matcher.js.map +1 -1
- package/lib/dist/resolution/strip-comments.d.ts +1 -1
- package/lib/dist/resolution/strip-comments.d.ts.map +1 -1
- package/lib/dist/resolution/strip-comments.js +49 -0
- package/lib/dist/resolution/strip-comments.js.map +1 -1
- package/lib/dist/resolution/types.d.ts +29 -0
- package/lib/dist/resolution/types.d.ts.map +1 -1
- package/lib/dist/resolution/workspace-packages.d.ts +10 -0
- package/lib/dist/resolution/workspace-packages.d.ts.map +1 -1
- package/lib/dist/resolution/workspace-packages.js +142 -4
- package/lib/dist/resolution/workspace-packages.js.map +1 -1
- package/lib/dist/search/identifier-segments.d.ts +60 -0
- package/lib/dist/search/identifier-segments.d.ts.map +1 -0
- package/lib/dist/search/identifier-segments.js +176 -0
- package/lib/dist/search/identifier-segments.js.map +1 -0
- package/lib/dist/sync/git-hooks.d.ts.map +1 -1
- package/lib/dist/sync/git-hooks.js +2 -0
- package/lib/dist/sync/git-hooks.js.map +1 -1
- package/lib/dist/sync/watcher.d.ts +10 -5
- package/lib/dist/sync/watcher.d.ts.map +1 -1
- package/lib/dist/sync/watcher.js +51 -14
- package/lib/dist/sync/watcher.js.map +1 -1
- package/lib/dist/sync/worktree.d.ts.map +1 -1
- package/lib/dist/sync/worktree.js +5 -0
- package/lib/dist/sync/worktree.js.map +1 -1
- package/lib/dist/types.d.ts +19 -1
- package/lib/dist/types.d.ts.map +1 -1
- package/lib/dist/types.js +10 -0
- package/lib/dist/types.js.map +1 -1
- package/lib/node_modules/.package-lock.json +1 -1
- package/lib/package.json +1 -1
- package/package.json +1 -1
- package/lib/dist/reasoning/config.d.ts +0 -45
- package/lib/dist/reasoning/config.d.ts.map +0 -1
- package/lib/dist/reasoning/config.js +0 -171
- package/lib/dist/reasoning/config.js.map +0 -1
- package/lib/dist/reasoning/credentials.d.ts +0 -5
- package/lib/dist/reasoning/credentials.d.ts.map +0 -1
- package/lib/dist/reasoning/credentials.js +0 -83
- package/lib/dist/reasoning/credentials.js.map +0 -1
- package/lib/dist/reasoning/login.d.ts +0 -21
- package/lib/dist/reasoning/login.d.ts.map +0 -1
- package/lib/dist/reasoning/login.js +0 -85
- package/lib/dist/reasoning/login.js.map +0 -1
- package/lib/dist/reasoning/reasoner.d.ts +0 -43
- package/lib/dist/reasoning/reasoner.d.ts.map +0 -1
- package/lib/dist/reasoning/reasoner.js +0 -308
- package/lib/dist/reasoning/reasoner.js.map +0 -1
|
@@ -46,6 +46,7 @@ exports.hashContent = hashContent;
|
|
|
46
46
|
exports.buildDefaultIgnore = buildDefaultIgnore;
|
|
47
47
|
exports.buildScopeIgnore = buildScopeIgnore;
|
|
48
48
|
exports.discoverEmbeddedRepoRoots = discoverEmbeddedRepoRoots;
|
|
49
|
+
exports.findUnindexedIgnoredRepos = findUnindexedIgnoredRepos;
|
|
49
50
|
exports.scanDirectory = scanDirectory;
|
|
50
51
|
exports.scanDirectoryAsync = scanDirectoryAsync;
|
|
51
52
|
const fs = __importStar(require("fs"));
|
|
@@ -63,6 +64,7 @@ const errors_1 = require("../errors");
|
|
|
63
64
|
const utils_1 = require("../utils");
|
|
64
65
|
const ignore_1 = __importDefault(require("ignore"));
|
|
65
66
|
const frameworks_1 = require("../resolution/frameworks");
|
|
67
|
+
const cooperative_yield_1 = require("../resolution/cooperative-yield");
|
|
66
68
|
/**
|
|
67
69
|
* Number of files to read in parallel during indexing.
|
|
68
70
|
* File reads are I/O-bound; batching overlaps I/O wait with CPU parse work.
|
|
@@ -302,6 +304,160 @@ function loadExcludeMatcher(rootDir) {
|
|
|
302
304
|
const patterns = (0, project_config_1.loadExcludePatterns)(rootDir);
|
|
303
305
|
return patterns.length > 0 ? (0, ignore_1.default)().add(patterns) : null;
|
|
304
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Matcher for the project's `codegraph.json` `include` patterns — first-party
|
|
309
|
+
* source to force INTO the index even when `.gitignore` drops it (the general
|
|
310
|
+
* whitelist `includeIgnored` never was — that one only revives *embedded git
|
|
311
|
+
* repos*). The case it exists for: a project under a second VCS (SVN/Perforce)
|
|
312
|
+
* `.gitignore`s its own real source so it stays out of Git, yet we still want it
|
|
313
|
+
* indexed. Returns `null` when nothing is force-included (the zero-config
|
|
314
|
+
* default → no overhead, no extra walk). Built once per scan/sync/scope
|
|
315
|
+
* operation from the scan root.
|
|
316
|
+
*/
|
|
317
|
+
function loadIncludeMatcher(rootDir) {
|
|
318
|
+
const patterns = (0, project_config_1.loadIncludePatterns)(rootDir);
|
|
319
|
+
return patterns.length > 0 ? (0, ignore_1.default)().add(patterns) : null;
|
|
320
|
+
}
|
|
321
|
+
/** Glob metacharacters that end the static (literal) prefix of an `include` pattern. */
|
|
322
|
+
const GLOB_META = /[*?[\]{}!]/;
|
|
323
|
+
/**
|
|
324
|
+
* The static directory prefix of each `include` pattern — the literal leading
|
|
325
|
+
* path up to the first glob segment — trailing-slashed, used to (a) walk only
|
|
326
|
+
* the opted-in subtrees in `collectIncludedFiles` and (b) let `ScopeIgnore` keep
|
|
327
|
+
* the watcher descending toward them. `Tools/` stays `Tools/`; a recursive
|
|
328
|
+
* `Tools/**` glob yields `Tools/`; `src/local/file.ts` yields `src/local/` (the
|
|
329
|
+
* file's dir); a pattern that starts with a glob (like a leading `**`) yields
|
|
330
|
+
* `''`, meaning "no static root — walk the whole tree". Duplicates and roots
|
|
331
|
+
* nested under a broader root are collapsed so each subtree is walked once.
|
|
332
|
+
*/
|
|
333
|
+
function includeStaticRoots(patterns) {
|
|
334
|
+
const roots = new Set();
|
|
335
|
+
for (const pattern of patterns) {
|
|
336
|
+
let p = pattern.replace(/^\/+/, '');
|
|
337
|
+
const trailingSlash = p.endsWith('/');
|
|
338
|
+
if (trailingSlash)
|
|
339
|
+
p = p.slice(0, -1);
|
|
340
|
+
const segs = p.split('/').filter(Boolean);
|
|
341
|
+
const lead = [];
|
|
342
|
+
for (const s of segs) {
|
|
343
|
+
if (GLOB_META.test(s))
|
|
344
|
+
break;
|
|
345
|
+
lead.push(s);
|
|
346
|
+
}
|
|
347
|
+
const hadWildcard = lead.length < segs.length;
|
|
348
|
+
// A wholly-literal pattern with no trailing slash names a file (or a dir we
|
|
349
|
+
// can't tell apart) — drop its last segment so we walk the containing dir
|
|
350
|
+
// and let the matcher pick the file. A trailing slash or a glob means the
|
|
351
|
+
// remaining `lead` is already the directory to walk.
|
|
352
|
+
if (!hadWildcard && !trailingSlash && lead.length > 0)
|
|
353
|
+
lead.pop();
|
|
354
|
+
if (lead.length === 0) {
|
|
355
|
+
roots.clear();
|
|
356
|
+
roots.add('');
|
|
357
|
+
return ['']; // a top-level glob forces a whole-tree walk; nothing narrower matters
|
|
358
|
+
}
|
|
359
|
+
roots.add(lead.join('/') + '/');
|
|
360
|
+
}
|
|
361
|
+
// Collapse roots nested under a broader one (e.g. drop `a/b/` if `a/` is present).
|
|
362
|
+
const all = [...roots];
|
|
363
|
+
return all.filter((r) => !all.some((other) => other !== r && r.startsWith(other)));
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Actively discover the source files an `include` whitelist forces in. `git
|
|
367
|
+
* ls-files` never lists gitignored files, so a filtered filesystem walk of just
|
|
368
|
+
* the opted-in subtrees (`includeStaticRoots`) is the only way to find them.
|
|
369
|
+
* Returns project-root-relative, normalized source-file paths.
|
|
370
|
+
*
|
|
371
|
+
* A file is collected when it MATCHES `include`, is NOT hit by `exclude` (an
|
|
372
|
+
* explicit exclude always wins), is a recognized source file, and does not live
|
|
373
|
+
* under a built-in default-ignored dir (`node_modules`, `dist`, …), `.git`, or
|
|
374
|
+
* CodeGraph's data dir — those are never resurfaced, mirroring `ScopeIgnore`.
|
|
375
|
+
* `.gitignore` is deliberately NOT consulted: overriding it is the whole point.
|
|
376
|
+
*/
|
|
377
|
+
function collectIncludedFiles(rootDir, include, exclude, roots, overrides) {
|
|
378
|
+
const out = new Set();
|
|
379
|
+
const defaults = defaultsOnlyIgnore();
|
|
380
|
+
const visited = new Set();
|
|
381
|
+
const consider = (abs, rel, isDir) => {
|
|
382
|
+
if (isDir) {
|
|
383
|
+
if (defaults.ignores(rel + '/'))
|
|
384
|
+
return; // never node_modules/dist/… via include
|
|
385
|
+
// An explicit `exclude` always wins over `include`; prune the whole subtree
|
|
386
|
+
// here so a large excluded dir (a committed frontend's own vendored deps,
|
|
387
|
+
// build output, …) is never walked — the per-file guard below still catches
|
|
388
|
+
// anything a directory pattern doesn't, so this is a pure efficiency win.
|
|
389
|
+
if (exclude && exclude.ignores(rel + '/'))
|
|
390
|
+
return;
|
|
391
|
+
walk(abs);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
if (defaults.ignores(rel))
|
|
395
|
+
return;
|
|
396
|
+
if (!include.ignores(rel))
|
|
397
|
+
return;
|
|
398
|
+
if (exclude && exclude.ignores(rel))
|
|
399
|
+
return;
|
|
400
|
+
if (!(0, grammars_1.isSourceFile)(rel, overrides))
|
|
401
|
+
return;
|
|
402
|
+
out.add(rel);
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
function walk(absDir) {
|
|
406
|
+
let realDir;
|
|
407
|
+
try {
|
|
408
|
+
realDir = fs.realpathSync(absDir);
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (visited.has(realDir))
|
|
414
|
+
return; // symlink-cycle guard
|
|
415
|
+
visited.add(realDir);
|
|
416
|
+
let entries;
|
|
417
|
+
try {
|
|
418
|
+
entries = fs.readdirSync(absDir, { withFileTypes: true });
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
for (const entry of entries) {
|
|
424
|
+
if (entry.name === '.git' || (0, directory_1.isCodeGraphDataDir)(entry.name))
|
|
425
|
+
continue;
|
|
426
|
+
const abs = path.join(absDir, entry.name);
|
|
427
|
+
const rel = (0, utils_1.normalizePath)(path.relative(rootDir, abs));
|
|
428
|
+
if (!rel || rel.startsWith('..'))
|
|
429
|
+
continue;
|
|
430
|
+
if (entry.isSymbolicLink()) {
|
|
431
|
+
try {
|
|
432
|
+
const st = fs.statSync(fs.realpathSync(abs));
|
|
433
|
+
consider(abs, rel, st.isDirectory());
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
// broken symlink — skip
|
|
437
|
+
}
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
consider(abs, rel, entry.isDirectory());
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
for (const root of roots) {
|
|
444
|
+
walk(root === '' ? rootDir : path.join(rootDir, root));
|
|
445
|
+
}
|
|
446
|
+
return out;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* The included source files (`codegraph.json` `include`) for a scan root, or an
|
|
450
|
+
* empty set when nothing is force-included. Centralizes loading the matcher,
|
|
451
|
+
* roots, exclude, and overrides so both enumeration paths (git and filesystem
|
|
452
|
+
* walk) add the same files.
|
|
453
|
+
*/
|
|
454
|
+
function collectIncludedFilesForRoot(rootDir) {
|
|
455
|
+
const include = loadIncludeMatcher(rootDir);
|
|
456
|
+
if (!include)
|
|
457
|
+
return new Set();
|
|
458
|
+
const roots = includeStaticRoots((0, project_config_1.loadIncludePatterns)(rootDir));
|
|
459
|
+
return collectIncludedFiles(rootDir, include, loadExcludeMatcher(rootDir), roots, (0, project_config_1.loadExtensionOverrides)(rootDir));
|
|
460
|
+
}
|
|
305
461
|
/**
|
|
306
462
|
* `git ls-files --directory` collapses a wholly-untracked/ignored directory into
|
|
307
463
|
* one entry — and when the command's own cwd is such a directory (the indexed
|
|
@@ -442,6 +598,8 @@ function findNestedGitRepos(absDir, relPrefix) {
|
|
|
442
598
|
class ScopeIgnore {
|
|
443
599
|
rootMatcher;
|
|
444
600
|
exclude;
|
|
601
|
+
include;
|
|
602
|
+
includeRoots;
|
|
445
603
|
embedded;
|
|
446
604
|
defaults = defaultsOnlyIgnore();
|
|
447
605
|
constructor(rootMatcher, embedded,
|
|
@@ -450,9 +608,21 @@ class ScopeIgnore {
|
|
|
450
608
|
* full root-relative path. Wins over everything else — an explicit user
|
|
451
609
|
* exclude applies even to tracked files and even inside embedded repos.
|
|
452
610
|
*/
|
|
453
|
-
exclude = null
|
|
611
|
+
exclude = null,
|
|
612
|
+
/**
|
|
613
|
+
* Project `codegraph.json` `include` patterns — first-party source forced
|
|
614
|
+
* INTO the index despite `.gitignore`. When a path matches, it is NOT
|
|
615
|
+
* ignored (so the watcher watches it), overriding `.gitignore`/`rootMatcher`
|
|
616
|
+
* — but never `exclude` (checked first) and never a built-in default-ignored
|
|
617
|
+
* dir. `includeRoots` are the static prefixes so a gitignored ANCESTOR
|
|
618
|
+
* directory of an included subtree still isn't pruned by the directory
|
|
619
|
+
* walker/watcher.
|
|
620
|
+
*/
|
|
621
|
+
include = null, includeRoots = []) {
|
|
454
622
|
this.rootMatcher = rootMatcher;
|
|
455
623
|
this.exclude = exclude;
|
|
624
|
+
this.include = include;
|
|
625
|
+
this.includeRoots = includeRoots;
|
|
456
626
|
// Longest root first so paths in nested embedded repos hit the innermost matcher.
|
|
457
627
|
this.embedded = [...embedded].sort((a, b) => b.root.length - a.root.length);
|
|
458
628
|
}
|
|
@@ -462,6 +632,20 @@ class ScopeIgnore {
|
|
|
462
632
|
// everywhere, including ancestors of embedded repos.
|
|
463
633
|
if (this.exclude && this.exclude.ignores(rel))
|
|
464
634
|
return true;
|
|
635
|
+
// User `include`: force first-party source in despite `.gitignore`. Never
|
|
636
|
+
// resurfaces a built-in default-ignored dir (node_modules/dist/…), so an
|
|
637
|
+
// include pattern can't accidentally pull in dependency/build trees.
|
|
638
|
+
if (this.include && !this.defaults.ignores(rel)) {
|
|
639
|
+
if (rel.endsWith('/')) {
|
|
640
|
+
// A directory on (or leading to) an included subtree must stay walkable
|
|
641
|
+
// so the watcher/walker descends to reach the forced-in files.
|
|
642
|
+
if (this.includeRoots.some((r) => r.startsWith(rel) || rel.startsWith(r)))
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
else if (this.include.ignores(rel)) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
465
649
|
for (const { root, matcher } of this.embedded) {
|
|
466
650
|
if (rel.startsWith(root)) {
|
|
467
651
|
const inner = rel.slice(root.length);
|
|
@@ -488,7 +672,8 @@ exports.ScopeIgnore = ScopeIgnore;
|
|
|
488
672
|
*/
|
|
489
673
|
function buildScopeIgnore(rootDir, embeddedRoots) {
|
|
490
674
|
const roots = embeddedRoots ? [...embeddedRoots] : discoverEmbeddedRepoRoots(rootDir);
|
|
491
|
-
|
|
675
|
+
const include = loadIncludeMatcher(rootDir);
|
|
676
|
+
return new ScopeIgnore(buildDefaultIgnore(rootDir), roots.map((root) => ({ root, matcher: buildDefaultIgnore(path.join(rootDir, root)) })), loadExcludeMatcher(rootDir), include, include ? includeStaticRoots((0, project_config_1.loadIncludePatterns)(rootDir)) : []);
|
|
492
677
|
}
|
|
493
678
|
/**
|
|
494
679
|
* Whether an embedded repo found as a tracked gitlink (mode 160000, #1031/#1033)
|
|
@@ -584,6 +769,50 @@ function discoverEmbeddedRepoRoots(rootDir) {
|
|
|
584
769
|
visit(rootDir, '');
|
|
585
770
|
return out;
|
|
586
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Cap on how many skipped gitignored repos the CLI hint enumerates — a huge
|
|
774
|
+
* gitignored data dir full of clones must never turn the hint scan into a long
|
|
775
|
+
* walk. Enough to make the point; the caller says "+N more" past this.
|
|
776
|
+
*/
|
|
777
|
+
const UNINDEXED_IGNORED_REPO_HINT_CAP = 100;
|
|
778
|
+
/**
|
|
779
|
+
* The INVERSE of the gitignored side of {@link discoverEmbeddedRepoRoots}:
|
|
780
|
+
* nested git repositories under a gitignored directory that the project has NOT
|
|
781
|
+
* opted into via `codegraph.json` `includeIgnored`. These are real repos the
|
|
782
|
+
* default `init`/`index` deliberately skips because `.gitignore` excludes them
|
|
783
|
+
* (#970, #976) — most visibly the "super-repo `.gitignore`s its child repos"
|
|
784
|
+
* layout (#1156), where `init` at the parent correctly indexes ~nothing while
|
|
785
|
+
* `init` inside each child works. The CLI uses this to turn that silent empty
|
|
786
|
+
* index into an actionable hint: it names the skipped repos and offers to opt
|
|
787
|
+
* them in. Paths are `rootDir`-relative and trailing-slashed (valid
|
|
788
|
+
* `includeIgnored` patterns as-is). Returns `[]` for a non-git root (a
|
|
789
|
+
* filesystem walk already descends into nested repos there), skips built-in
|
|
790
|
+
* default-ignored dirs (`node_modules`, …), and is bounded so it never stalls
|
|
791
|
+
* on a giant ignored tree.
|
|
792
|
+
*/
|
|
793
|
+
function findUnindexedIgnoredRepos(rootDir) {
|
|
794
|
+
try {
|
|
795
|
+
(0, child_process_1.execFileSync)('git', ['rev-parse', '--git-dir'], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
796
|
+
}
|
|
797
|
+
catch {
|
|
798
|
+
return [];
|
|
799
|
+
}
|
|
800
|
+
const defaults = defaultsOnlyIgnore();
|
|
801
|
+
const includeIgnored = loadIncludeIgnoredMatcher(rootDir);
|
|
802
|
+
const repos = [];
|
|
803
|
+
for (const dir of listIgnoredDirs(rootDir)) {
|
|
804
|
+
if (defaults.ignores(dir))
|
|
805
|
+
continue; // node_modules etc. — never project code
|
|
806
|
+
if (includeIgnored?.ignores((0, utils_1.normalizePath)(dir)))
|
|
807
|
+
continue; // already opted in — nothing to nag about
|
|
808
|
+
for (const repo of findNestedGitRepos(path.join(rootDir, dir), dir)) {
|
|
809
|
+
repos.push(repo);
|
|
810
|
+
if (repos.length >= UNINDEXED_IGNORED_REPO_HINT_CAP)
|
|
811
|
+
return repos;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return repos;
|
|
815
|
+
}
|
|
587
816
|
/**
|
|
588
817
|
* Discover embedded repos hidden by `repoDir`'s OWN gitignore rules: for each
|
|
589
818
|
* gitignored directory, search for nested `.git` roots. Returns repo paths
|
|
@@ -764,7 +993,15 @@ function getGitVisibleFiles(rootDir) {
|
|
|
764
993
|
// not the parent's: the parent's .gitignore hides the child repo from git,
|
|
765
994
|
// not from the index. (#514)
|
|
766
995
|
const ig = buildScopeIgnore(rootDir, embeddedRoots);
|
|
767
|
-
|
|
996
|
+
const visible = new Set([...files].filter((f) => !ig.ignores(f)));
|
|
997
|
+
// Force-include first-party source the project whitelisted in
|
|
998
|
+
// `codegraph.json` `include`. These are gitignored, so `git ls-files` never
|
|
999
|
+
// listed them above — discover them directly off disk and add them. (The
|
|
1000
|
+
// common SVN+Git dual-VCS case: source committed to SVN, gitignored out of
|
|
1001
|
+
// Git, but still wanted in the graph.)
|
|
1002
|
+
for (const f of collectIncludedFilesForRoot(rootDir))
|
|
1003
|
+
visible.add(f);
|
|
1004
|
+
return visible;
|
|
768
1005
|
}
|
|
769
1006
|
catch {
|
|
770
1007
|
return null;
|
|
@@ -1027,6 +1264,19 @@ function scanDirectoryWalk(rootDir, onProgress) {
|
|
|
1027
1264
|
if (exclude)
|
|
1028
1265
|
baseMatchers.push({ dir: rootDir, ig: exclude });
|
|
1029
1266
|
walk(rootDir, baseMatchers);
|
|
1267
|
+
// Force-include first-party source whitelisted in `codegraph.json` `include`
|
|
1268
|
+
// — the walk above honours `.gitignore`, so anything gitignored was dropped;
|
|
1269
|
+
// add it back here (deduped). Mirrors the git path's union.
|
|
1270
|
+
const included = collectIncludedFilesForRoot(rootDir);
|
|
1271
|
+
if (included.size > 0) {
|
|
1272
|
+
const seen = new Set(files);
|
|
1273
|
+
for (const f of included) {
|
|
1274
|
+
if (!seen.has(f)) {
|
|
1275
|
+
files.push(f);
|
|
1276
|
+
seen.add(f);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1030
1280
|
return files;
|
|
1031
1281
|
}
|
|
1032
1282
|
/**
|
|
@@ -1143,6 +1393,10 @@ class ExtractionOrchestrator {
|
|
|
1143
1393
|
current: 0,
|
|
1144
1394
|
total: 0,
|
|
1145
1395
|
});
|
|
1396
|
+
// Phase attribution to stderr (same opt-in as the synthesis timings):
|
|
1397
|
+
// early-run 5-10s single stalls were observed on 95k-file repos but never
|
|
1398
|
+
// attributed — these labels settle scan vs framework-detect vs grammars.
|
|
1399
|
+
const tScan = Date.now();
|
|
1146
1400
|
const files = await scanDirectoryAsync(this.rootDir, (current, file) => {
|
|
1147
1401
|
onProgress?.({
|
|
1148
1402
|
phase: 'scanning',
|
|
@@ -1151,13 +1405,18 @@ class ExtractionOrchestrator {
|
|
|
1151
1405
|
currentFile: file,
|
|
1152
1406
|
});
|
|
1153
1407
|
});
|
|
1408
|
+
if (process.env.CODEGRAPH_SYNTH_TIMINGS)
|
|
1409
|
+
console.error(`[phase-timing] scan: ${Date.now() - tScan}ms (${files.length} files)`);
|
|
1154
1410
|
// Detect frameworks once per indexAll run using the scanned file list.
|
|
1155
1411
|
// Names are passed to each parse call so framework-specific extractors
|
|
1156
1412
|
// (route nodes, middleware, etc.) run after the tree-sitter pass.
|
|
1157
1413
|
// Framework detection is reset each run so adding e.g. requirements.txt
|
|
1158
1414
|
// between runs is picked up without restarting the process.
|
|
1159
1415
|
this.detectedFrameworkNames = null;
|
|
1416
|
+
const tFw = Date.now();
|
|
1160
1417
|
const frameworkNames = this.ensureDetectedFrameworks(files);
|
|
1418
|
+
if (process.env.CODEGRAPH_SYNTH_TIMINGS)
|
|
1419
|
+
console.error(`[phase-timing] framework-detect: ${Date.now() - tFw}ms`);
|
|
1161
1420
|
if (signal?.aborted) {
|
|
1162
1421
|
return {
|
|
1163
1422
|
success: false,
|
|
@@ -1240,12 +1499,17 @@ class ExtractionOrchestrator {
|
|
|
1240
1499
|
let nextSeq = 0; // file-order sequence assigned at dispatch
|
|
1241
1500
|
let nextToStore = 0; // cursor: next sequence to commit
|
|
1242
1501
|
let aborted = false;
|
|
1243
|
-
|
|
1502
|
+
// Yielder for the in-order commit path: a single giant generated file's
|
|
1503
|
+
// store is otherwise one unyielding multi-second transaction span on the
|
|
1504
|
+
// main thread (5–14s single stalls measured on llvm-project), starving
|
|
1505
|
+
// the #850 watchdog heartbeat on slow hardware.
|
|
1506
|
+
const commitYield = (0, cooperative_yield_1.createYielder)();
|
|
1507
|
+
const storeResult = async (filePath, content, stats, result) => {
|
|
1244
1508
|
processed++;
|
|
1245
1509
|
// Store in database on main thread (SQLite is not thread-safe)
|
|
1246
1510
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1247
1511
|
const language = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1248
|
-
this.storeExtractionResult(filePath, content, language, stats, result);
|
|
1512
|
+
await this.storeExtractionResult(filePath, content, language, stats, result, commitYield);
|
|
1249
1513
|
}
|
|
1250
1514
|
if (result.errors.length > 0) {
|
|
1251
1515
|
for (const err of result.errors) {
|
|
@@ -1289,20 +1553,35 @@ class ExtractionOrchestrator {
|
|
|
1289
1553
|
};
|
|
1290
1554
|
// Commit buffered parses to the DB in file order, advancing the cursor over
|
|
1291
1555
|
// contiguous completed results. Runs after each parse settles (and once more
|
|
1292
|
-
// after the drain). storeResult
|
|
1293
|
-
// so
|
|
1556
|
+
// after the drain). storeResult is now async (it yields between chunked
|
|
1557
|
+
// inserts), so commits are SERIALIZED on a promise chain — concurrent parse
|
|
1558
|
+
// completions append to the chain instead of interleaving mid-store, which
|
|
1559
|
+
// preserves both the file-order commit invariant (#1015: resolution
|
|
1560
|
+
// disambiguates same-named candidates by insertion order) and the
|
|
1561
|
+
// single-writer discipline for SQLite. Errors are recorded and re-thrown
|
|
1562
|
+
// at the drain, matching the old synchronous propagation.
|
|
1563
|
+
let flushChain = Promise.resolve();
|
|
1564
|
+
let flushError = null;
|
|
1294
1565
|
const flushOrdered = () => {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1566
|
+
flushChain = flushChain.then(async () => {
|
|
1567
|
+
if (aborted || flushError)
|
|
1568
|
+
return;
|
|
1569
|
+
try {
|
|
1570
|
+
while (completed.has(nextToStore)) {
|
|
1571
|
+
const item = completed.get(nextToStore);
|
|
1572
|
+
completed.delete(nextToStore);
|
|
1573
|
+
nextToStore++;
|
|
1574
|
+
if (item.ok)
|
|
1575
|
+
await storeResult(item.filePath, item.content, item.stats, item.result);
|
|
1576
|
+
else
|
|
1577
|
+
recordParseFailure(item.filePath, item.err);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
catch (err) {
|
|
1581
|
+
flushError = err;
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
return flushChain;
|
|
1306
1585
|
};
|
|
1307
1586
|
// Dispatch one file's parse (parses run concurrently across the pool), tagged
|
|
1308
1587
|
// with its file-order sequence so flushOrdered commits results in order. The
|
|
@@ -1325,10 +1604,15 @@ class ExtractionOrchestrator {
|
|
|
1325
1604
|
// buffered), not just in-flight: a slow file sitting at the commit cursor
|
|
1326
1605
|
// lets later parses finish and buffer, which would otherwise grow without
|
|
1327
1606
|
// bound. Wait for parses to settle (each may advance the cursor) until the
|
|
1328
|
-
// window has room.
|
|
1329
|
-
//
|
|
1330
|
-
|
|
1331
|
-
|
|
1607
|
+
// window has room. When nothing is in flight but the window is still full,
|
|
1608
|
+
// the async commit chain is what's behind — await it so the cursor
|
|
1609
|
+
// advances (buffered items hold whole file contents, so this bound is
|
|
1610
|
+
// load-bearing for memory).
|
|
1611
|
+
while (nextSeq - nextToStore >= windowSize) {
|
|
1612
|
+
if (inFlight.size > 0)
|
|
1613
|
+
await Promise.race(inFlight);
|
|
1614
|
+
else
|
|
1615
|
+
await flushOrdered();
|
|
1332
1616
|
}
|
|
1333
1617
|
};
|
|
1334
1618
|
for (let i = 0; i < files.length; i += FILE_IO_BATCH_SIZE) {
|
|
@@ -1403,7 +1687,9 @@ class ExtractionOrchestrator {
|
|
|
1403
1687
|
// then commit any results the cursor hasn't reached yet.
|
|
1404
1688
|
if (!aborted) {
|
|
1405
1689
|
await Promise.all(inFlight);
|
|
1406
|
-
flushOrdered();
|
|
1690
|
+
await flushOrdered();
|
|
1691
|
+
if (flushError)
|
|
1692
|
+
throw flushError;
|
|
1407
1693
|
}
|
|
1408
1694
|
if (signal?.aborted || aborted) {
|
|
1409
1695
|
if (pool)
|
|
@@ -1413,6 +1699,7 @@ class ExtractionOrchestrator {
|
|
|
1413
1699
|
filesIndexed,
|
|
1414
1700
|
filesSkipped,
|
|
1415
1701
|
filesErrored,
|
|
1702
|
+
filesDiscovered: total,
|
|
1416
1703
|
nodesCreated: totalNodes,
|
|
1417
1704
|
edgesCreated: totalEdges,
|
|
1418
1705
|
errors: [{ message: 'Aborted', severity: 'error' }, ...errors],
|
|
@@ -1466,7 +1753,7 @@ class ExtractionOrchestrator {
|
|
|
1466
1753
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1467
1754
|
const language = (0, grammars_1.detectLanguage)(filePath, content, overrides);
|
|
1468
1755
|
const stats = await fsp.stat(path.join(this.rootDir, filePath));
|
|
1469
|
-
this.storeExtractionResult(filePath, content, language, stats, result);
|
|
1756
|
+
await this.storeExtractionResult(filePath, content, language, stats, result, commitYield);
|
|
1470
1757
|
const idx = errors.indexOf(errEntry);
|
|
1471
1758
|
if (idx >= 0)
|
|
1472
1759
|
errors.splice(idx, 1);
|
|
@@ -1514,7 +1801,7 @@ class ExtractionOrchestrator {
|
|
|
1514
1801
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1515
1802
|
const language = (0, grammars_1.detectLanguage)(filePath, fullContent, overrides);
|
|
1516
1803
|
const stats = await fsp.stat(path.join(this.rootDir, filePath));
|
|
1517
|
-
this.storeExtractionResult(filePath, fullContent, language, stats, result);
|
|
1804
|
+
await this.storeExtractionResult(filePath, fullContent, language, stats, result, commitYield);
|
|
1518
1805
|
const idx = errors.indexOf(errEntry);
|
|
1519
1806
|
if (idx >= 0)
|
|
1520
1807
|
errors.splice(idx, 1);
|
|
@@ -1535,6 +1822,7 @@ class ExtractionOrchestrator {
|
|
|
1535
1822
|
filesIndexed,
|
|
1536
1823
|
filesSkipped,
|
|
1537
1824
|
filesErrored,
|
|
1825
|
+
filesDiscovered: total,
|
|
1538
1826
|
nodesCreated: totalNodes,
|
|
1539
1827
|
edgesCreated: totalEdges,
|
|
1540
1828
|
errors,
|
|
@@ -1678,14 +1966,21 @@ class ExtractionOrchestrator {
|
|
|
1678
1966
|
const result = (0, tree_sitter_1.extractFromSource)(relativePath, content, language, frameworkNames);
|
|
1679
1967
|
// Store in database
|
|
1680
1968
|
if (result.nodes.length > 0 || result.errors.length === 0) {
|
|
1681
|
-
this.storeExtractionResult(relativePath, content, language, stats, result);
|
|
1969
|
+
await this.storeExtractionResult(relativePath, content, language, stats, result, (0, cooperative_yield_1.createYielder)());
|
|
1682
1970
|
}
|
|
1683
1971
|
return result;
|
|
1684
1972
|
}
|
|
1685
1973
|
/**
|
|
1686
1974
|
* Store extraction result in database
|
|
1687
1975
|
*/
|
|
1688
|
-
storeExtractionResult(filePath, content, language, stats, result) {
|
|
1976
|
+
async storeExtractionResult(filePath, content, language, stats, result, onYield) {
|
|
1977
|
+
// Bulk inserts run in bounded sub-transactions with a yield between, so a
|
|
1978
|
+
// giant generated file (tens of thousands of symbols) can't block the
|
|
1979
|
+
// event loop — and the #850 watchdog heartbeat — for the whole store.
|
|
1980
|
+
// The file was NEVER one atomic transaction (each insert call has its
|
|
1981
|
+
// own), and the files-table record still lands last, so crash recovery
|
|
1982
|
+
// is unchanged: a partially-stored file has no record and re-indexes.
|
|
1983
|
+
const STORE_CHUNK = 2000;
|
|
1689
1984
|
const contentHash = hashContent(content);
|
|
1690
1985
|
// Check if file already exists and hasn't changed
|
|
1691
1986
|
const existingFile = this.queries.getFileByPath(filePath);
|
|
@@ -1719,16 +2014,18 @@ class ExtractionOrchestrator {
|
|
|
1719
2014
|
// This prevents FK violations when edges reference nodes that would
|
|
1720
2015
|
// be silently skipped by insertNode() (see issue #42).
|
|
1721
2016
|
const validNodes = result.nodes.filter((n) => n.id && n.kind && n.name && n.filePath && n.language);
|
|
1722
|
-
// Insert nodes
|
|
1723
|
-
|
|
1724
|
-
this.queries.insertNodes(validNodes);
|
|
2017
|
+
// Insert nodes (chunked — see STORE_CHUNK above)
|
|
2018
|
+
for (let i = 0; i < validNodes.length; i += STORE_CHUNK) {
|
|
2019
|
+
this.queries.insertNodes(validNodes.slice(i, i + STORE_CHUNK));
|
|
2020
|
+
await onYield?.();
|
|
1725
2021
|
}
|
|
1726
2022
|
// Filter edges to only reference nodes that were actually inserted
|
|
1727
2023
|
if (result.edges.length > 0) {
|
|
1728
2024
|
const insertedIds = new Set(validNodes.map((n) => n.id));
|
|
1729
2025
|
const validEdges = result.edges.filter((e) => insertedIds.has(e.source) && insertedIds.has(e.target));
|
|
1730
|
-
|
|
1731
|
-
this.queries.insertEdges(validEdges);
|
|
2026
|
+
for (let i = 0; i < validEdges.length; i += STORE_CHUNK) {
|
|
2027
|
+
this.queries.insertEdges(validEdges.slice(i, i + STORE_CHUNK));
|
|
2028
|
+
await onYield?.();
|
|
1732
2029
|
}
|
|
1733
2030
|
}
|
|
1734
2031
|
// Re-insert cross-file incoming edges snapshotted before the delete,
|
|
@@ -1766,8 +2063,9 @@ class ExtractionOrchestrator {
|
|
|
1766
2063
|
filePath: ref.filePath ?? filePath,
|
|
1767
2064
|
language: ref.language ?? language,
|
|
1768
2065
|
}));
|
|
1769
|
-
|
|
1770
|
-
this.queries.insertUnresolvedRefsBatch(refsWithContext);
|
|
2066
|
+
for (let i = 0; i < refsWithContext.length; i += STORE_CHUNK) {
|
|
2067
|
+
this.queries.insertUnresolvedRefsBatch(refsWithContext.slice(i, i + STORE_CHUNK));
|
|
2068
|
+
await onYield?.();
|
|
1771
2069
|
}
|
|
1772
2070
|
}
|
|
1773
2071
|
// Insert file record
|
|
@@ -1815,10 +2113,16 @@ class ExtractionOrchestrator {
|
|
|
1815
2113
|
// whether or not the project uses git, and crucially also catches committed
|
|
1816
2114
|
// changes from `git pull`/`checkout`/`merge`/`rebase` — which `git status`
|
|
1817
2115
|
// cannot see, because the working tree is clean afterward.
|
|
2116
|
+
const tSyncScan = Date.now();
|
|
1818
2117
|
const currentFiles = await scanDirectoryAsync(this.rootDir);
|
|
2118
|
+
if (process.env.CODEGRAPH_SYNTH_TIMINGS)
|
|
2119
|
+
console.error(`[phase-timing] sync-scan: ${Date.now() - tSyncScan}ms (${currentFiles.length} files)`);
|
|
1819
2120
|
filesChecked = currentFiles.length;
|
|
1820
2121
|
const currentSet = new Set(currentFiles);
|
|
2122
|
+
const tTracked = Date.now();
|
|
1821
2123
|
const trackedFiles = this.queries.getAllFiles();
|
|
2124
|
+
if (process.env.CODEGRAPH_SYNTH_TIMINGS)
|
|
2125
|
+
console.error(`[phase-timing] sync-tracked-load: ${Date.now() - tTracked}ms (${trackedFiles.length} tracked)`);
|
|
1822
2126
|
const trackedMap = new Map();
|
|
1823
2127
|
for (const f of trackedFiles) {
|
|
1824
2128
|
trackedMap.set(f.path, f);
|