grepmax 0.17.21 → 0.17.23

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.
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.testFind = void 0;
46
46
  const commander_1 = require("commander");
47
47
  const impact_1 = require("../lib/graph/impact");
48
+ const test_hits_1 = require("../lib/graph/test-hits");
48
49
  const vector_db_1 = require("../lib/store/vector-db");
49
50
  const exit_1 = require("../lib/utils/exit");
50
51
  const project_registry_1 = require("../lib/utils/project-registry");
@@ -91,25 +92,18 @@ exports.testFind = new commander_1.Command("test")
91
92
  return;
92
93
  }
93
94
  const rel = (p) => p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
95
+ // One line per test file: the file is what the reader runs; caller
96
+ // symbols inside it (often internal helpers) are detail, not the lead.
97
+ const grouped = (0, test_hits_1.groupTestHitsByFile)(tests);
94
98
  if (opts.agent) {
95
- for (const t of tests) {
96
- const hopLabel = t.hops === -1
97
- ? "via-import"
98
- : t.hops === 0
99
- ? "direct"
100
- : `${t.hops}-hop`;
101
- console.log(`${rel(t.file)}:${t.line + 1}\t${t.symbol}\t${hopLabel}`);
99
+ for (const t of grouped) {
100
+ console.log(`${rel(t.file)}:${t.line + 1}\t${(0, test_hits_1.hopLabelAgent)(t.hops)}${(0, test_hits_1.formatViaAgent)(t.via)}`);
102
101
  }
103
102
  }
104
103
  else {
105
104
  console.log(`Tests for ${target}:\n`);
106
- for (const t of tests) {
107
- const hopLabel = t.hops === -1
108
- ? "via import"
109
- : t.hops === 0
110
- ? "calls directly"
111
- : `${t.hops} hop${t.hops > 1 ? "s" : ""} away`;
112
- console.log(` ${rel(t.file)}:${t.line + 1} ${t.symbol} (${hopLabel})`);
105
+ for (const t of grouped) {
106
+ console.log(` ${rel(t.file)}:${t.line + 1} (${(0, test_hits_1.hopLabelHuman)(t.hops)}${(0, test_hits_1.formatViaHuman)(t.via)})`);
113
107
  }
114
108
  }
115
109
  }
@@ -43,8 +43,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.trace = void 0;
46
- const fs = __importStar(require("node:fs"));
47
46
  const commander_1 = require("commander");
47
+ const callsites_1 = require("../lib/graph/callsites");
48
48
  const graph_builder_1 = require("../lib/graph/graph-builder");
49
49
  const formatter_1 = require("../lib/output/formatter");
50
50
  const vector_db_1 = require("../lib/store/vector-db");
@@ -78,33 +78,6 @@ function formatTraceAgent(graph, projectRoot) {
78
78
  }
79
79
  return lines.join("\n");
80
80
  }
81
- function findCallSiteSnippet(fileCache, callerFile, callerLine, targetSymbol) {
82
- if (!callerFile)
83
- return null;
84
- let lines = fileCache.get(callerFile);
85
- if (!lines) {
86
- try {
87
- lines = fs.readFileSync(callerFile, "utf-8").split("\n");
88
- }
89
- catch (_a) {
90
- return null;
91
- }
92
- fileCache.set(callerFile, lines);
93
- }
94
- // Search a bounded window starting at the caller's definition line.
95
- const start = Math.max(0, callerLine);
96
- const end = Math.min(lines.length, callerLine + 200);
97
- const wordRe = new RegExp(`\\b${targetSymbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
98
- for (let i = start; i < end; i++) {
99
- if (wordRe.test(lines[i])) {
100
- return { snippet: lines[i].trim(), snippetLine: i };
101
- }
102
- }
103
- // Window didn't contain the symbol — chunker rolled the reference up to a
104
- // parent scope. Skip the snippet rather than showing a misleading default;
105
- // the caller's file:line is still emitted.
106
- return null;
107
- }
108
81
  function buildInboundTree(callerTree, targetSymbol, fileCache, withSnippets, limit) {
109
82
  var _a, _b, _c;
110
83
  const out = [];
@@ -114,7 +87,7 @@ function buildInboundTree(callerTree, targetSymbol, fileCache, withSnippets, lim
114
87
  const seen = new Set();
115
88
  for (const t of callerTree) {
116
89
  const snippet = withSnippets
117
- ? findCallSiteSnippet(fileCache, t.node.file, t.node.line, targetSymbol)
90
+ ? (0, callsites_1.findCallSiteSnippet)(fileCache, t.node.file, t.node.line, targetSymbol)
118
91
  : null;
119
92
  const dedupeKey = `${t.node.file}:${(_a = snippet === null || snippet === void 0 ? void 0 : snippet.snippetLine) !== null && _a !== void 0 ? _a : t.node.line}`;
120
93
  if (seen.has(dedupeKey))
package/dist/config.js CHANGED
@@ -76,6 +76,11 @@ exports.CONFIG = {
76
76
  EMBED_BATCH_SIZE: 24,
77
77
  WORKER_THREADS: DEFAULT_WORKER_THREADS,
78
78
  QUERY_PREFIX: "",
79
+ // Bump when chunk metadata semantics change in a way that requires a full
80
+ // reindex to take effect. v2: sub-chunk symbol lists scoped to the chunk's
81
+ // own content (previously every split sub-chunk inherited the parent's
82
+ // full defined/referenced symbol lists, fabricating graph edges).
83
+ CHUNKER_VERSION: 2,
79
84
  };
80
85
  exports.WORKER_TIMEOUT_MS = Number.parseInt(process.env.GMAX_WORKER_TIMEOUT_MS || "60000", 10);
81
86
  exports.WORKER_BOOT_TIMEOUT_MS = Number.parseInt(process.env.GMAX_WORKER_BOOT_TIMEOUT_MS || "300000", 10);
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * Regression probe for the @lancedb 0.27.x LIKE + limit deadlock.
4
+ *
5
+ * A `content LIKE '%X%'` scan with `.limit(N)` never resolves when more than
6
+ * N rows match — the limit-pushdown cancellation loses the completion (same
7
+ * family as lancedb/lancedb#2189). gmax works around it by scanning without a
8
+ * limit and capping in JS; this probe tells us when an upstream lancedb
9
+ * upgrade makes the limited shape safe again (so the workaround can go).
10
+ *
11
+ * Exit 0 = limited LIKE scan resolved (upstream fixed, or matches < limit).
12
+ * Exit 1 = timed out — the deadlock is still present, keep the workarounds.
13
+ *
14
+ * Run: `npx tsx src/eval-like-limit-probe.ts [needle]` (default: "Daemon",
15
+ * which matches >100 chunks in this repo's index).
16
+ */
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ const config_1 = require("./config");
28
+ const vector_db_1 = require("./lib/store/vector-db");
29
+ const filter_builder_1 = require("./lib/utils/filter-builder");
30
+ const query_timeout_1 = require("./lib/utils/query-timeout");
31
+ const needle = process.argv[2] || "Daemon";
32
+ const TIMEOUT_MS = 10000;
33
+ function main() {
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ const db = new vector_db_1.VectorDB(config_1.PATHS.lancedbDir);
36
+ const table = yield db.ensureTable();
37
+ const where = `content LIKE '%${(0, filter_builder_1.escapeSqlString)(needle)}%'`;
38
+ const all = yield (0, query_timeout_1.withQueryTimeout)(table.query().select(["path"]).where(where).toArray(), "unlimited LIKE scan", TIMEOUT_MS);
39
+ console.log(`unlimited scan: ${all.length} matches for '%${needle}%'`);
40
+ if (all.length < 2) {
41
+ console.log("needle too rare to exercise the bug — pick a common one");
42
+ process.exit(0);
43
+ }
44
+ const limit = Math.max(1, Math.floor(all.length / 2));
45
+ const t0 = Date.now();
46
+ try {
47
+ const rows = yield (0, query_timeout_1.withQueryTimeout)(table.query().select(["path"]).where(where).limit(limit).toArray(), `LIKE scan with limit ${limit} < ${all.length} matches`, TIMEOUT_MS);
48
+ console.log(`limited scan OK: ${rows.length} rows in ${Date.now() - t0}ms — ` +
49
+ `upstream deadlock not reproduced; LIKE+limit workarounds may be removable`);
50
+ process.exit(0);
51
+ }
52
+ catch (err) {
53
+ console.error(String(err));
54
+ console.error("LIKE+limit deadlock still present — keep the unlimited-scan workarounds");
55
+ process.exit(1);
56
+ }
57
+ });
58
+ }
59
+ main().catch((err) => {
60
+ console.error(err);
61
+ process.exit(1);
62
+ });
@@ -62,8 +62,7 @@ const config_1 = require("../../config");
62
62
  const batch_processor_1 = require("../index/batch-processor");
63
63
  const syncer_1 = require("../index/syncer");
64
64
  const watcher_1 = require("../index/watcher");
65
- const searcher_1 = require("../search/searcher");
66
- const retriever_1 = require("../skeleton/retriever");
65
+ const search_handler_1 = require("./search-handler");
67
66
  const meta_cache_1 = require("../store/meta-cache");
68
67
  const vector_db_1 = require("../store/vector-db");
69
68
  const process_1 = require("../utils/process");
@@ -766,7 +765,7 @@ class Daemon {
766
765
  });
767
766
  const proj = (0, project_registry_1.getProject)(root);
768
767
  if (proj) {
769
- (0, project_registry_1.registerProject)(Object.assign(Object.assign({}, proj), { lastIndexed: new Date().toISOString(), chunkCount: result.indexed, status: "indexed" }));
768
+ (0, project_registry_1.registerProject)(Object.assign(Object.assign({}, proj), { lastIndexed: new Date().toISOString(), chunkCount: result.indexed, status: "indexed", chunkerVersion: config_1.CONFIG.CHUNKER_VERSION }));
770
769
  }
771
770
  yield this.watchProject(root);
772
771
  (0, logger_1.log)("daemon", `indexPendingProject done: ${name} — ${result.total} files, ${result.indexed} chunks, ${Date.now() - start}ms`);
@@ -841,105 +840,19 @@ class Daemon {
841
840
  }
842
841
  search(payload, signal) {
843
842
  return __awaiter(this, void 0, void 0, function* () {
844
- var _a, _b, _c, _d;
845
- if (!this.vectorDb) {
846
- return { ok: false, error: "daemon not ready" };
847
- }
848
- const root = payload.projectRoot;
849
- if (!this.processors.has(root)) {
850
- // A full index (--reset) or the initial index removes/defers the
851
- // processor while (re)building. The partial index is still queryable, so
852
- // answer the search and flag it partial (below) rather than erroring —
853
- // only truly-unwatched, not-indexing projects get "not watched".
854
- const indexingNow = this.indexProgress.has(root) || ((_a = (0, project_registry_1.getProject)(root)) === null || _a === void 0 ? void 0 : _a.status) === "pending";
855
- if (!indexingNow) {
856
- return {
857
- ok: false,
858
- error: "project not watched",
859
- hint: `run: gmax add ${root}`,
860
- };
861
- }
862
- }
863
- let searcher = this.searchers.get(root);
864
- if (!searcher) {
865
- searcher = new searcher_1.Searcher(this.vectorDb);
866
- this.searchers.set(root, searcher);
867
- }
868
- this.lastActivity = Date.now();
869
- let result;
870
- try {
871
- result = yield searcher.search(payload.query, payload.limit, {
872
- rerank: payload.rerank === true,
873
- explain: payload.explain === true,
874
- seeds: payload.seeds,
875
- }, payload.filters, payload.pathPrefix, undefined, signal);
876
- }
877
- catch (err) {
878
- if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError") {
879
- return { ok: false, error: "aborted" };
880
- }
881
- const msg = err instanceof Error ? err.message : String(err);
882
- return { ok: false, error: "search_failed", hint: msg };
883
- }
884
- const response = { ok: true, data: result.data };
885
- if ((_b = result.warnings) === null || _b === void 0 ? void 0 : _b.length)
886
- response.warnings = result.warnings;
887
- // Annotate partial results when the index is still catching up, so an
888
- // agent can caveat or retry. Only attached when actually indexing (the
889
- // formatter suppresses the settled case anyway).
890
- const idx = this.indexState(root);
891
- if (idx.indexing)
892
- response.indexState = idx;
893
- // --skeleton support: fetch per-file skeletons inline so the CLI doesn't
894
- // have to open its own VectorDB. getStoredSkeleton is a single LIMIT-1
895
- // lookup; cheap enough to call for the top N distinct paths.
896
- if (payload.includeSkeletons && result.data.length > 0) {
897
- const limit = payload.skeletonLimit && payload.skeletonLimit > 0 ? payload.skeletonLimit : 5;
898
- const seen = new Set();
899
- const skeletons = {};
900
- for (const chunk of result.data) {
901
- const p = (_c = chunk.path) !== null && _c !== void 0 ? _c : (_d = chunk.metadata) === null || _d === void 0 ? void 0 : _d.path;
902
- if (!p || seen.has(p))
903
- continue;
904
- seen.add(p);
905
- if (seen.size > limit)
906
- break;
907
- try {
908
- const sk = yield (0, retriever_1.getStoredSkeleton)(this.vectorDb, p);
909
- if (sk)
910
- skeletons[p] = sk;
911
- }
912
- catch (_e) {
913
- // best-effort — drop the entry, keep the search result
914
- }
915
- }
916
- if (Object.keys(skeletons).length > 0)
917
- response.skeletons = skeletons;
918
- }
919
- // --symbol support: build a 1-hop graph using the warm vectorDb. ~5
920
- // LanceDB queries; doesn't touch the worker pool.
921
- if (payload.includeGraph) {
922
- try {
923
- const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../graph/graph-builder")));
924
- const builder = new GraphBuilder(this.vectorDb, root);
925
- response.graph = yield builder.buildGraphMultiHop(payload.query, 1);
926
- }
927
- catch (_f) {
928
- // best-effort — drop graph, keep results
929
- }
930
- }
931
- // 2 MB cap on the JSON line. Lance can return huge chunks for unusual
932
- // queries (very long markdown blobs). Above this we fall back to the
933
- // in-process path which writes to stdout instead of a socket.
934
- const serialized = JSON.stringify(response);
935
- if (serialized.length > 2 * 1024 * 1024) {
936
- return {
937
- ok: false,
938
- error: "oversize",
939
- hint: `${serialized.length} bytes — falling back to in-process search`,
940
- };
941
- }
942
- return response;
843
+ // Search handling lives in search-handler.ts (Phase 12 split). The daemon
844
+ // supplies its warm VectorDB + watcher/index bookkeeping; the handler runs
845
+ // the query and assembles the response.
846
+ return (0, search_handler_1.handleDaemonSearch)({
847
+ vectorDb: this.vectorDb,
848
+ processors: this.processors,
849
+ indexProgress: this.indexProgress,
850
+ searchers: this.searchers,
851
+ getIndexState: (root) => this.indexState(root),
852
+ touchActivity: () => {
853
+ this.lastActivity = Date.now();
854
+ },
855
+ }, payload, signal);
943
856
  });
944
857
  }
945
858
  listProjects() {
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.handleDaemonSearch = handleDaemonSearch;
46
+ const searcher_1 = require("../search/searcher");
47
+ const retriever_1 = require("../skeleton/retriever");
48
+ const project_registry_1 = require("../utils/project-registry");
49
+ /**
50
+ * Daemon-side search: runs the hybrid+rerank against the already-warm VectorDB
51
+ * and optionally attaches inline skeletons / a 1-hop graph / a partial-index
52
+ * footer. Extracted from Daemon.search() (Phase 12) — behavior-preserving.
53
+ */
54
+ function handleDaemonSearch(deps, payload, signal) {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ var _a, _b, _c, _d;
57
+ const { vectorDb } = deps;
58
+ if (!vectorDb) {
59
+ return { ok: false, error: "daemon not ready" };
60
+ }
61
+ const root = payload.projectRoot;
62
+ if (!deps.processors.has(root)) {
63
+ // A full index (--reset) or the initial index removes/defers the
64
+ // processor while (re)building. The partial index is still queryable, so
65
+ // answer the search and flag it partial (below) rather than erroring —
66
+ // only truly-unwatched, not-indexing projects get "not watched".
67
+ const indexingNow = deps.indexProgress.has(root) || ((_a = (0, project_registry_1.getProject)(root)) === null || _a === void 0 ? void 0 : _a.status) === "pending";
68
+ if (!indexingNow) {
69
+ return {
70
+ ok: false,
71
+ error: "project not watched",
72
+ hint: `run: gmax add ${root}`,
73
+ };
74
+ }
75
+ }
76
+ let searcher = deps.searchers.get(root);
77
+ if (!searcher) {
78
+ searcher = new searcher_1.Searcher(vectorDb);
79
+ deps.searchers.set(root, searcher);
80
+ }
81
+ deps.touchActivity();
82
+ let result;
83
+ try {
84
+ result = yield searcher.search(payload.query, payload.limit, {
85
+ rerank: payload.rerank === true,
86
+ explain: payload.explain === true,
87
+ seeds: payload.seeds,
88
+ }, payload.filters, payload.pathPrefix, undefined, signal);
89
+ }
90
+ catch (err) {
91
+ if ((err === null || err === void 0 ? void 0 : err.name) === "AbortError") {
92
+ return { ok: false, error: "aborted" };
93
+ }
94
+ const msg = err instanceof Error ? err.message : String(err);
95
+ return { ok: false, error: "search_failed", hint: msg };
96
+ }
97
+ const response = { ok: true, data: result.data };
98
+ if ((_b = result.warnings) === null || _b === void 0 ? void 0 : _b.length)
99
+ response.warnings = result.warnings;
100
+ // Annotate partial results when the index is still catching up, so an
101
+ // agent can caveat or retry. Only attached when actually indexing (the
102
+ // formatter suppresses the settled case anyway).
103
+ const idx = deps.getIndexState(root);
104
+ if (idx.indexing)
105
+ response.indexState = idx;
106
+ // --skeleton support: fetch per-file skeletons inline so the CLI doesn't
107
+ // have to open its own VectorDB. getStoredSkeleton is a single LIMIT-1
108
+ // lookup; cheap enough to call for the top N distinct paths.
109
+ if (payload.includeSkeletons && result.data.length > 0) {
110
+ const limit = payload.skeletonLimit && payload.skeletonLimit > 0
111
+ ? payload.skeletonLimit
112
+ : 5;
113
+ const seen = new Set();
114
+ const skeletons = {};
115
+ for (const chunk of result.data) {
116
+ const p = (_c = chunk.path) !== null && _c !== void 0 ? _c : (_d = chunk.metadata) === null || _d === void 0 ? void 0 : _d.path;
117
+ if (!p || seen.has(p))
118
+ continue;
119
+ seen.add(p);
120
+ if (seen.size > limit)
121
+ break;
122
+ try {
123
+ const sk = yield (0, retriever_1.getStoredSkeleton)(vectorDb, p);
124
+ if (sk)
125
+ skeletons[p] = sk;
126
+ }
127
+ catch (_e) {
128
+ // best-effort — drop the entry, keep the search result
129
+ }
130
+ }
131
+ if (Object.keys(skeletons).length > 0)
132
+ response.skeletons = skeletons;
133
+ }
134
+ // --symbol support: build a 1-hop graph using the warm vectorDb. ~5
135
+ // LanceDB queries; doesn't touch the worker pool.
136
+ if (payload.includeGraph) {
137
+ try {
138
+ const { GraphBuilder } = yield Promise.resolve().then(() => __importStar(require("../graph/graph-builder")));
139
+ const builder = new GraphBuilder(vectorDb, root);
140
+ response.graph = yield builder.buildGraphMultiHop(payload.query, 1);
141
+ }
142
+ catch (_f) {
143
+ // best-effort — drop graph, keep results
144
+ }
145
+ }
146
+ // 2 MB cap on the JSON line. Lance can return huge chunks for unusual
147
+ // queries (very long markdown blobs). Above this we fall back to the
148
+ // in-process path which writes to stdout instead of a socket.
149
+ const serialized = JSON.stringify(response);
150
+ if (serialized.length > 2 * 1024 * 1024) {
151
+ return {
152
+ ok: false,
153
+ error: "oversize",
154
+ hint: `${serialized.length} bytes — falling back to in-process search`,
155
+ };
156
+ }
157
+ return response;
158
+ });
159
+ }
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.findCallSiteSnippet = findCallSiteSnippet;
37
+ exports.resolveCallSites = resolveCallSites;
38
+ exports.isBuiltinCallee = isBuiltinCallee;
39
+ /**
40
+ * Call-site resolution for chunk-granularity caller edges.
41
+ *
42
+ * `GraphBuilder.getCallers()` returns one row per *chunk* whose
43
+ * referenced_symbols contains the target — for a large class split into many
44
+ * chunks that means many rows pointing at chunk boundaries, not call sites.
45
+ * These helpers re-anchor each caller row to the actual line that mentions the
46
+ * target symbol and collapse duplicates, so consumers (trace, peek, impact)
47
+ * report real call sites with true counts.
48
+ */
49
+ const fs = __importStar(require("node:fs"));
50
+ function findCallSiteSnippet(fileCache, callerFile, callerLine, targetSymbol) {
51
+ if (!callerFile)
52
+ return null;
53
+ let lines = fileCache.get(callerFile);
54
+ if (!lines) {
55
+ try {
56
+ lines = fs.readFileSync(callerFile, "utf-8").split("\n");
57
+ }
58
+ catch (_a) {
59
+ return null;
60
+ }
61
+ fileCache.set(callerFile, lines);
62
+ }
63
+ // Search a bounded window starting at the caller's definition line.
64
+ const start = Math.max(0, callerLine);
65
+ const end = Math.min(lines.length, callerLine + 200);
66
+ const wordRe = new RegExp(`\\b${targetSymbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
67
+ for (let i = start; i < end; i++) {
68
+ if (wordRe.test(lines[i])) {
69
+ return { snippet: lines[i].trim(), snippetLine: i };
70
+ }
71
+ }
72
+ // Window didn't contain the symbol — chunker rolled the reference up to a
73
+ // parent scope. Skip the snippet rather than showing a misleading default;
74
+ // the caller's file:line is still emitted.
75
+ return null;
76
+ }
77
+ /**
78
+ * Resolve chunk-level caller rows to deduplicated call sites. Rows from
79
+ * different chunks of the same file that resolve to the same source line
80
+ * collapse into one entry; rows whose window doesn't contain the symbol
81
+ * dedupe by file+symbol so split-chunk spam can't multiply them.
82
+ */
83
+ function resolveCallSites(callers, targetSymbol, fileCache = new Map()) {
84
+ var _a, _b;
85
+ const out = [];
86
+ const seen = new Set();
87
+ for (const c of callers) {
88
+ const snippet = findCallSiteSnippet(fileCache, c.file, c.line, targetSymbol);
89
+ const key = snippet
90
+ ? `${c.file}:${snippet.snippetLine}`
91
+ : `${c.file}:${c.symbol}`;
92
+ if (seen.has(key))
93
+ continue;
94
+ seen.add(key);
95
+ out.push({
96
+ symbol: c.symbol,
97
+ file: c.file,
98
+ line: c.line,
99
+ snippet: (_a = snippet === null || snippet === void 0 ? void 0 : snippet.snippet) !== null && _a !== void 0 ? _a : null,
100
+ snippetLine: (_b = snippet === null || snippet === void 0 ? void 0 : snippet.snippetLine) !== null && _b !== void 0 ? _b : null,
101
+ });
102
+ }
103
+ return out;
104
+ }
105
+ /**
106
+ * Identifiers that read as "callees" in referenced_symbols but are language
107
+ * or runtime builtins — listing them as "(not indexed)" callees is noise.
108
+ * Only consulted for UNRESOLVED names; a project symbol that shadows one of
109
+ * these still shows up because it resolves to an indexed definition.
110
+ */
111
+ const BUILTIN_CALLEES = new Set([
112
+ // Object/primitive methods
113
+ "toString", "valueOf", "hasOwnProperty",
114
+ // Array/String methods
115
+ "push", "pop", "shift", "unshift", "slice", "splice", "concat", "join",
116
+ "map", "filter", "reduce", "forEach", "find", "findIndex", "some", "every",
117
+ "includes", "indexOf", "lastIndexOf", "sort", "reverse", "flat", "flatMap",
118
+ "keys", "values", "entries", "fill",
119
+ "split", "trim", "trimStart", "trimEnd", "replace", "replaceAll", "match",
120
+ "matchAll", "test", "exec", "padStart", "padEnd", "repeat", "charAt",
121
+ "charCodeAt", "codePointAt", "startsWith", "endsWith", "toLowerCase",
122
+ "toUpperCase", "substring", "substr", "localeCompare", "normalize",
123
+ // Map/Set methods
124
+ "get", "set", "has", "add", "delete", "clear",
125
+ // Math/Date/Number members
126
+ "trunc", "floor", "ceil", "round", "abs", "min", "max", "random", "pow",
127
+ "sqrt", "log2", "log10", "sign", "now", "parse", "parseInt", "parseFloat",
128
+ "isInteger", "isFinite", "isNaN", "toFixed", "toISOString",
129
+ // Promise/Function members
130
+ "then", "catch", "finally", "resolve", "reject", "all", "allSettled",
131
+ "race", "any", "call", "apply", "bind",
132
+ // JSON / global constructors & functions
133
+ "JSON", "stringify", "Math", "Date", "Promise", "Array", "Object", "String",
134
+ "Number", "Boolean", "Symbol", "RegExp", "Error", "TypeError", "RangeError",
135
+ "Map", "Set", "WeakMap", "WeakSet", "Buffer", "URL", "URLSearchParams",
136
+ "isArray", "from", "of", "assign", "freeze", "create", "getOwnPropertyNames",
137
+ "fromEntries", "setTimeout", "setInterval", "clearTimeout", "clearInterval",
138
+ "queueMicrotask", "structuredClone", "fetch", "console", "process",
139
+ "require", "encodeURIComponent", "decodeURIComponent", "encodeURI",
140
+ "decodeURI", "atob", "btoa",
141
+ // console members
142
+ "warn", "info", "debug", "trace", "table", "dir", "group", "groupEnd",
143
+ "assert", "time", "timeEnd",
144
+ ]);
145
+ /** True when an UNRESOLVED callee name is a known JS/TS builtin. */
146
+ function isBuiltinCallee(name) {
147
+ return BUILTIN_CALLEES.has(name);
148
+ }
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.GraphBuilder = void 0;
13
13
  const filter_builder_1 = require("../utils/filter-builder");
14
+ const query_timeout_1 = require("../utils/query-timeout");
14
15
  const graph_traversal_1 = require("./graph-traversal");
15
16
  class GraphBuilder {
16
17
  constructor(db, pathPrefix, excludePrefixes) {
@@ -132,15 +133,18 @@ class GraphBuilder {
132
133
  return __awaiter(this, void 0, void 0, function* () {
133
134
  const table = yield this.db.ensureTable();
134
135
  const escaped = (0, filter_builder_1.escapeSqlString)(symbol);
135
- const rows = yield table
136
+ // No .limit() here: LIKE + limit deadlocks in @lancedb 0.27.x when more
137
+ // rows match than the limit (verified). Unlimited scan is fast; cap in JS.
138
+ const rows = yield (0, query_timeout_1.withQueryTimeout)(table
136
139
  .query()
137
140
  .select(["path"])
138
141
  .where(this.scopeWhere(`content LIKE '%import%${escaped}%'`))
139
- .limit(100)
140
- .toArray();
142
+ .toArray(), `content LIKE %import%${symbol}% (importers)`);
141
143
  const files = new Set();
142
144
  for (const row of rows) {
143
145
  files.add(String(row.path || ""));
146
+ if (files.size >= 100)
147
+ break;
144
148
  }
145
149
  return Array.from(files);
146
150
  });