@wrongstack/tools 0.289.0 → 0.291.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/dist/auto-proceed-loop-guard.d.ts +128 -0
- package/dist/auto-proceed-loop-guard.d.ts.map +1 -0
- package/dist/auto-proceed-loop-guard.js +46 -0
- package/dist/auto-proceed-loop-guard.js.map +7 -0
- package/dist/bash-kill-guard.d.ts +10 -1
- package/dist/bash-kill-guard.d.ts.map +1 -1
- package/dist/bash.js +140 -14
- package/dist/bash.js.map +2 -2
- package/dist/builtin.js +662 -203
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.d.ts +1 -1
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/index.js +181 -104
- package/dist/codebase-index/index.js.map +3 -3
- package/dist/codebase-index/indexer.d.ts.map +1 -1
- package/dist/codebase-index/refs-extractor.d.ts +2 -17
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/codebase-index/ts-parser.d.ts.map +1 -1
- package/dist/codebase-index/worker.js +164 -96
- package/dist/codebase-index/worker.js.map +3 -3
- package/dist/codebase-index/writer.d.ts +33 -0
- package/dist/codebase-index/writer.d.ts.map +1 -1
- package/dist/exec-kill-guard.d.ts +29 -0
- package/dist/exec-kill-guard.d.ts.map +1 -0
- package/dist/exec.d.ts.map +1 -1
- package/dist/exec.js +670 -10
- package/dist/exec.js.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +765 -237
- package/dist/index.js.map +4 -4
- package/dist/kanban.d.ts +10 -0
- package/dist/kanban.d.ts.map +1 -1
- package/dist/kanban.js +34 -17
- package/dist/kanban.js.map +2 -2
- package/dist/pack.js +662 -203
- package/dist/pack.js.map +4 -4
- package/dist/plan.js.map +2 -2
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js.map +2 -2
- package/dist/session-kanban.d.ts.map +1 -1
- package/dist/session-kanban.js +60 -3
- package/dist/session-kanban.js.map +2 -2
- package/dist/task.js.map +2 -2
- package/dist/todo.js.map +2 -2
- package/package.json +9 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/codebase-index/indexer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../../src/codebase-index/indexer.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,KAAK,EAAY,WAAW,EAA0C,MAAM,aAAa,CAAC;AAuEjG,UAAU,cAAc;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACrE;AA+HD,6DAA6D;AAC7D,wBAAsB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAa1F"}
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified cross-reference extraction across all supported languages.
|
|
3
|
-
*
|
|
4
|
-
* `extractRefs(file, content, lang)` → returns `Ref[]`
|
|
5
|
-
*
|
|
6
|
-
* Each language has its own approach:
|
|
7
|
-
* - TS/JS: uses the TypeScript Compiler API (imported from ts-parser.ts)
|
|
8
|
-
* - Go: inline `go run <script>` script using go/ast to walk CallExpr, SelectorExpr, Ident, ImportSpec
|
|
9
|
-
* - Python: inline `python -c` script using ast to walk Call, Name, Attribute, Subscript, Import, ImportFrom
|
|
10
|
-
*/
|
|
1
|
+
/** Cross-reference extraction for languages not handled by ts-parser.ts. */
|
|
11
2
|
import type { Ref, SymbolLang } from './schema.js';
|
|
12
3
|
export interface ExtractRefsOptions {
|
|
13
4
|
file: string;
|
|
14
5
|
content: string;
|
|
15
6
|
lang: SymbolLang;
|
|
16
7
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Extract cross-references (calls, type refs, imports) from a source file.
|
|
19
|
-
* Returns an array of `Ref` objects with `fromId: 0` (caller fills this in).
|
|
20
|
-
*
|
|
21
|
-
* Falls back to an empty array if the language is not supported or the
|
|
22
|
-
* child process fails.
|
|
23
|
-
*/
|
|
8
|
+
/** Return refs with `fromId: 0`; the indexer replaces it with the owning symbol id. */
|
|
24
9
|
export declare function extractRefs(opts: ExtractRefsOptions): Promise<Ref[]>;
|
|
25
10
|
//# sourceMappingURL=refs-extractor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refs-extractor.d.ts","sourceRoot":"","sources":["../../src/codebase-index/refs-extractor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"refs-extractor.d.ts","sourceRoot":"","sources":["../../src/codebase-index/refs-extractor.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAK5E,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsFnD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;CAClB;AAuGD,uFAAuF;AACvF,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAU1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ts-parser.d.ts","sourceRoot":"","sources":["../../src/codebase-index/ts-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,WAAW,EAA0C,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ts-parser.d.ts","sourceRoot":"","sources":["../../src/codebase-index/ts-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,WAAW,EAA0C,UAAU,EAAE,MAAM,aAAa,CAAC;AA6GnG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CA0G5D;AA+BD,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAI1D"}
|
|
@@ -5,6 +5,7 @@ import { parentPort } from "node:worker_threads";
|
|
|
5
5
|
import { expectDefined as expectDefined5 } from "@wrongstack/core";
|
|
6
6
|
import * as fs7 from "node:fs/promises";
|
|
7
7
|
import * as path8 from "node:path";
|
|
8
|
+
import { availableParallelism } from "node:os";
|
|
8
9
|
import { compileGlob as compileGlob2 } from "@wrongstack/core";
|
|
9
10
|
|
|
10
11
|
// src/codebase-index/writer.ts
|
|
@@ -244,6 +245,53 @@ function assignRefsToSymbols(refs, symbols) {
|
|
|
244
245
|
function resolveIndexDir(projectRoot, override) {
|
|
245
246
|
return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
|
|
246
247
|
}
|
|
248
|
+
var StorePool = class {
|
|
249
|
+
stores = /* @__PURE__ */ new Map();
|
|
250
|
+
key(projectRoot, indexDir) {
|
|
251
|
+
return `${projectRoot}\0${indexDir ?? ""}`;
|
|
252
|
+
}
|
|
253
|
+
/** Borrow a store. Creates it on first access for this key. */
|
|
254
|
+
acquire(projectRoot, opts) {
|
|
255
|
+
const k = this.key(projectRoot, opts?.indexDir);
|
|
256
|
+
let store = this.stores.get(k);
|
|
257
|
+
if (!store) {
|
|
258
|
+
store = new IndexStore(projectRoot, { indexDir: opts?.indexDir });
|
|
259
|
+
this.stores.set(k, store);
|
|
260
|
+
}
|
|
261
|
+
return store;
|
|
262
|
+
}
|
|
263
|
+
/** Return the store to the pool. The connection stays warm for subsequent
|
|
264
|
+
* operations on the same (projectRoot, indexDir). */
|
|
265
|
+
release(_store) {
|
|
266
|
+
}
|
|
267
|
+
/** Close every pooled connection and drain the pool. Call on shutdown. */
|
|
268
|
+
closeAll() {
|
|
269
|
+
for (const store of this.stores.values()) {
|
|
270
|
+
try {
|
|
271
|
+
store.close();
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
this.stores.clear();
|
|
276
|
+
}
|
|
277
|
+
/** Remove one store from the pool. Used by tests that need isolation. */
|
|
278
|
+
evict(projectRoot, indexDir) {
|
|
279
|
+
const k = this.key(projectRoot, indexDir);
|
|
280
|
+
const store = this.stores.get(k);
|
|
281
|
+
if (store) {
|
|
282
|
+
try {
|
|
283
|
+
store.close();
|
|
284
|
+
} catch {
|
|
285
|
+
}
|
|
286
|
+
this.stores.delete(k);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** True when the pool holds a connection for the given key. */
|
|
290
|
+
has(projectRoot, indexDir) {
|
|
291
|
+
return this.stores.has(this.key(projectRoot, indexDir));
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
var indexStorePool = new StorePool();
|
|
247
295
|
var warningSilenced = false;
|
|
248
296
|
function silenceSqliteExperimentalWarning() {
|
|
249
297
|
if (warningSilenced) return;
|
|
@@ -461,7 +509,7 @@ var IndexStore = class _IndexStore {
|
|
|
461
509
|
return this.runWithRetry(() => {
|
|
462
510
|
this.db.exec("BEGIN IMMEDIATE");
|
|
463
511
|
try {
|
|
464
|
-
const maxRows = this.
|
|
512
|
+
const maxRows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
465
513
|
let nextId = (maxRows[0]?.m ?? 0) + 1;
|
|
466
514
|
const stmt = this.db.prepare(
|
|
467
515
|
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
@@ -733,7 +781,7 @@ var IndexStore = class _IndexStore {
|
|
|
733
781
|
return { results, total: candidates.length };
|
|
734
782
|
}
|
|
735
783
|
getAllIndexable() {
|
|
736
|
-
return this.
|
|
784
|
+
return this.stmt("SELECT id, text FROM symbols").all().map(({ id, text }) => ({ id, text }));
|
|
737
785
|
}
|
|
738
786
|
/**
|
|
739
787
|
* Largest symbol id currently in the table (0 when empty). New ids must be
|
|
@@ -743,7 +791,7 @@ var IndexStore = class _IndexStore {
|
|
|
743
791
|
* `symbols.id`). Ids may have gaps — that is fine.
|
|
744
792
|
*/
|
|
745
793
|
getMaxSymbolId() {
|
|
746
|
-
const rows = this.
|
|
794
|
+
const rows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
747
795
|
return rows[0]?.m ?? 0;
|
|
748
796
|
}
|
|
749
797
|
// ─── Stats ───────────────────────────────────────────────────────────────────
|
|
@@ -751,14 +799,14 @@ var IndexStore = class _IndexStore {
|
|
|
751
799
|
const sizeBytes = this.sizeBytes();
|
|
752
800
|
const lastRows = this.db.prepare("SELECT value FROM metadata WHERE key = 'last_indexed'").all();
|
|
753
801
|
const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;
|
|
754
|
-
const totalRows = this.
|
|
802
|
+
const totalRows = this.stmt("SELECT COUNT(*) FROM symbols").all();
|
|
755
803
|
const totalSymbols = totalRows[0] ? Number(totalRows[0]["COUNT(*)"]) : 0;
|
|
756
|
-
const fileRows = this.
|
|
804
|
+
const fileRows = this.stmt("SELECT COUNT(*) FROM files").all();
|
|
757
805
|
const totalFiles = fileRows[0] ? Number(fileRows[0]["COUNT(*)"]) : 0;
|
|
758
|
-
const langRows = this.
|
|
806
|
+
const langRows = this.stmt("SELECT lang, COUNT(*) FROM symbols GROUP BY lang").all();
|
|
759
807
|
const byLang = {};
|
|
760
808
|
for (const row of langRows) byLang[row.lang] = Number(row["COUNT(*)"]);
|
|
761
|
-
const kindRows = this.
|
|
809
|
+
const kindRows = this.stmt("SELECT kind, COUNT(*) FROM symbols GROUP BY kind").all();
|
|
762
810
|
const byKind = {};
|
|
763
811
|
for (const row of kindRows) byKind[row.kind] = Number(row["COUNT(*)"]);
|
|
764
812
|
return {
|
|
@@ -790,11 +838,14 @@ var IndexStore = class _IndexStore {
|
|
|
790
838
|
this.runWithRetry(() => {
|
|
791
839
|
this.db.exec("BEGIN IMMEDIATE");
|
|
792
840
|
try {
|
|
793
|
-
this.db.exec("
|
|
794
|
-
this.db.exec("
|
|
795
|
-
this.db.exec("
|
|
796
|
-
|
|
841
|
+
this.db.exec("DROP TABLE IF EXISTS refs");
|
|
842
|
+
this.db.exec("DROP TABLE IF EXISTS symbols");
|
|
843
|
+
this.db.exec("DROP TABLE IF EXISTS files");
|
|
844
|
+
this.db.exec("DROP TABLE IF EXISTS metadata");
|
|
845
|
+
if (this.ftsAvailable) this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
797
846
|
this.db.exec("COMMIT");
|
|
847
|
+
this.stmtCache.clear();
|
|
848
|
+
this.initSchema();
|
|
798
849
|
} catch (err) {
|
|
799
850
|
this.db.exec("ROLLBACK");
|
|
800
851
|
throw err;
|
|
@@ -879,7 +930,7 @@ var IndexStore = class _IndexStore {
|
|
|
879
930
|
).run(...options.deleteForFiles);
|
|
880
931
|
this.db.prepare(`DELETE FROM symbols WHERE file IN (${placeholders})`).run(...options.deleteForFiles);
|
|
881
932
|
}
|
|
882
|
-
const maxRows = this.
|
|
933
|
+
const maxRows = this.stmt("SELECT MAX(id) AS m FROM symbols").all();
|
|
883
934
|
let nextId = (maxRows[0]?.m ?? 0) + 1;
|
|
884
935
|
const symStmt = this.db.prepare(
|
|
885
936
|
`INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope, text, file_fk)
|
|
@@ -1063,7 +1114,7 @@ var IndexStore = class _IndexStore {
|
|
|
1063
1114
|
* symbol resolved in package B). Node metadata includes symbol/file counts.
|
|
1064
1115
|
*/
|
|
1065
1116
|
getPackageGraph() {
|
|
1066
|
-
const symbols = this.db.prepare("SELECT file, id
|
|
1117
|
+
const symbols = this.db.prepare("SELECT file, id FROM symbols ORDER BY id").all();
|
|
1067
1118
|
const pkgNodes = /* @__PURE__ */ new Map();
|
|
1068
1119
|
const fileToPkg = /* @__PURE__ */ new Map();
|
|
1069
1120
|
const symbolToPkg = /* @__PURE__ */ new Map();
|
|
@@ -1154,15 +1205,18 @@ var IndexStore = class _IndexStore {
|
|
|
1154
1205
|
* derived from cross-file symbol references within the package.
|
|
1155
1206
|
*/
|
|
1156
1207
|
getFileGraph(packageFilter) {
|
|
1157
|
-
const
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
);
|
|
1161
|
-
|
|
1208
|
+
const allFiles = this.db.prepare("SELECT DISTINCT file FROM symbols").all();
|
|
1209
|
+
const pkgFilePaths = allFiles.filter((f) => (_IndexStore.derivePackage(f.file) ?? "(root)") === packageFilter).map((f) => f.file);
|
|
1210
|
+
const localFiles = new Set(pkgFilePaths);
|
|
1211
|
+
if (localFiles.size === 0) return { nodes: [], edges: [] };
|
|
1212
|
+
const filePlaceholders = [...localFiles].map(() => "?").join(",");
|
|
1213
|
+
const pkgSyms = this.db.prepare(
|
|
1214
|
+
`SELECT file, id, name, kind, lang, line FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY id`
|
|
1215
|
+
).all(...pkgFilePaths);
|
|
1162
1216
|
const fileNodes = /* @__PURE__ */ new Map();
|
|
1163
1217
|
const symToFile = /* @__PURE__ */ new Map();
|
|
1164
1218
|
const fileStats = /* @__PURE__ */ new Map();
|
|
1165
|
-
for (const s of
|
|
1219
|
+
for (const s of pkgSyms) {
|
|
1166
1220
|
symToFile.set(s.id, s.file);
|
|
1167
1221
|
const current = fileStats.get(s.file);
|
|
1168
1222
|
fileStats.set(s.file, {
|
|
@@ -1170,8 +1224,6 @@ var IndexStore = class _IndexStore {
|
|
|
1170
1224
|
lang: current?.lang ?? s.lang
|
|
1171
1225
|
});
|
|
1172
1226
|
}
|
|
1173
|
-
const localFiles = new Set(pkgSyms.map((s) => s.file));
|
|
1174
|
-
const indexedFiles = new Set(allSymbols.map((s) => s.file));
|
|
1175
1227
|
const ensureFileNode = (file) => {
|
|
1176
1228
|
if (fileNodes.has(file)) return;
|
|
1177
1229
|
const stats = fileStats.get(file);
|
|
@@ -1189,11 +1241,32 @@ var IndexStore = class _IndexStore {
|
|
|
1189
1241
|
for (const file of localFiles) {
|
|
1190
1242
|
ensureFileNode(file);
|
|
1191
1243
|
}
|
|
1244
|
+
const indexedFiles = new Set(allFiles.map((f) => f.file));
|
|
1192
1245
|
const refRows = this.db.prepare(
|
|
1193
1246
|
`SELECT r.from_id, r.to_id, r.call_type
|
|
1194
1247
|
FROM refs r
|
|
1195
|
-
WHERE r.
|
|
1196
|
-
|
|
1248
|
+
WHERE (r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))
|
|
1249
|
+
OR r.to_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders})))
|
|
1250
|
+
AND r.to_id IS NOT NULL`
|
|
1251
|
+
).all(...pkgFilePaths, ...pkgFilePaths);
|
|
1252
|
+
const knownSymIds = new Set(pkgSyms.map((s) => s.id));
|
|
1253
|
+
const crossRefIds = /* @__PURE__ */ new Set();
|
|
1254
|
+
for (const r of refRows) {
|
|
1255
|
+
if (!knownSymIds.has(r.from_id)) crossRefIds.add(r.from_id);
|
|
1256
|
+
if (!knownSymIds.has(r.to_id)) crossRefIds.add(r.to_id);
|
|
1257
|
+
}
|
|
1258
|
+
if (crossRefIds.size > 0) {
|
|
1259
|
+
const crossPlaceholders = [...crossRefIds].map(() => "?").join(",");
|
|
1260
|
+
const extras = this.db.prepare(
|
|
1261
|
+
`SELECT id, file FROM symbols WHERE id IN (${crossPlaceholders})`
|
|
1262
|
+
).all(...crossRefIds);
|
|
1263
|
+
for (const x of extras) {
|
|
1264
|
+
symToFile.set(x.id, x.file);
|
|
1265
|
+
if (!fileStats.has(x.file)) {
|
|
1266
|
+
fileStats.set(x.file, { count: 0, lang: "ts" });
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1197
1270
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
1198
1271
|
for (const r of refRows) {
|
|
1199
1272
|
if (r.call_type === "import") continue;
|
|
@@ -1215,8 +1288,9 @@ var IndexStore = class _IndexStore {
|
|
|
1215
1288
|
const importRows = this.db.prepare(
|
|
1216
1289
|
`SELECT r.from_id, r.to_name
|
|
1217
1290
|
FROM refs r
|
|
1218
|
-
WHERE r.call_type = 'import'
|
|
1219
|
-
|
|
1291
|
+
WHERE r.call_type = 'import'
|
|
1292
|
+
AND r.from_id IN (SELECT id FROM symbols WHERE file IN (${filePlaceholders}))`
|
|
1293
|
+
).all(...pkgFilePaths);
|
|
1220
1294
|
for (const r of importRows) {
|
|
1221
1295
|
const fromFile = symToFile.get(r.from_id);
|
|
1222
1296
|
if (!fromFile || !localFiles.has(fromFile)) continue;
|
|
@@ -1258,12 +1332,11 @@ var IndexStore = class _IndexStore {
|
|
|
1258
1332
|
* derived from intra-file and cross-file symbol references (who calls whom).
|
|
1259
1333
|
*/
|
|
1260
1334
|
getSymbolGraph(fileFilter) {
|
|
1261
|
-
const
|
|
1262
|
-
"SELECT id, name, kind, lang, file, line, signature, scope FROM symbols ORDER BY
|
|
1263
|
-
).all();
|
|
1264
|
-
const syms = allSymbols.filter((symbol) => symbol.file === fileFilter);
|
|
1335
|
+
const syms = this.db.prepare(
|
|
1336
|
+
"SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id"
|
|
1337
|
+
).all(fileFilter);
|
|
1265
1338
|
if (syms.length === 0) return { nodes: [], edges: [] };
|
|
1266
|
-
const symById = new Map(
|
|
1339
|
+
const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
|
|
1267
1340
|
const relatedIds = new Set(syms.map((symbol) => symbol.id));
|
|
1268
1341
|
const toGraphNode = (s) => ({
|
|
1269
1342
|
id: `sym:${s.id}`,
|
|
@@ -1319,6 +1392,15 @@ var IndexStore = class _IndexStore {
|
|
|
1319
1392
|
refType: bestType
|
|
1320
1393
|
});
|
|
1321
1394
|
}
|
|
1395
|
+
const loadedIds = new Set(syms.map((s) => s.id));
|
|
1396
|
+
const missingIds = [...relatedIds].filter((id) => !loadedIds.has(id));
|
|
1397
|
+
if (missingIds.length > 0) {
|
|
1398
|
+
const placeholders = missingIds.map(() => "?").join(",");
|
|
1399
|
+
const extras = this.db.prepare(
|
|
1400
|
+
`SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE id IN (${placeholders})`
|
|
1401
|
+
).all(...missingIds);
|
|
1402
|
+
for (const s of extras) symById.set(s.id, s);
|
|
1403
|
+
}
|
|
1322
1404
|
const nodes = [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
|
|
1323
1405
|
const aExternal = a.file === fileFilter ? 0 : 1;
|
|
1324
1406
|
const bExternal = b.file === fileFilter ? 0 : 1;
|
|
@@ -1389,8 +1471,7 @@ function extToLang(ext) {
|
|
|
1389
1471
|
return null;
|
|
1390
1472
|
}
|
|
1391
1473
|
}
|
|
1392
|
-
function getSignature(node, sourceFile) {
|
|
1393
|
-
const printer = ts.createPrinter({});
|
|
1474
|
+
function getSignature(printer, node, sourceFile) {
|
|
1394
1475
|
const raw = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
|
1395
1476
|
return raw.replace(/\s+/g, " ").slice(0, 500);
|
|
1396
1477
|
}
|
|
@@ -1409,28 +1490,14 @@ function getJsDoc(node, sourceFile) {
|
|
|
1409
1490
|
}
|
|
1410
1491
|
return "";
|
|
1411
1492
|
}
|
|
1412
|
-
function
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
return false;
|
|
1419
|
-
}
|
|
1420
|
-
function buildScope(node) {
|
|
1421
|
-
const parts = [];
|
|
1422
|
-
let current = node.parent;
|
|
1423
|
-
while (current) {
|
|
1424
|
-
if (ts.isClassDeclaration(current) || ts.isInterfaceDeclaration(current) || ts.isEnumDeclaration(current) || ts.isTypeAliasDeclaration(current)) {
|
|
1425
|
-
parts.unshift(current.name?.text ?? "Anon");
|
|
1426
|
-
} else if (ts.isMethodDeclaration(current) || ts.isGetAccessor(current) || ts.isSetAccessor(current) || ts.isPropertyDeclaration(current) || ts.isFunctionDeclaration(current)) {
|
|
1427
|
-
if (current.name && ts.isIdentifier(current.name)) {
|
|
1428
|
-
parts.unshift(current.name.text);
|
|
1429
|
-
}
|
|
1493
|
+
function pushScopeName(node, parts) {
|
|
1494
|
+
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
1495
|
+
parts.push(node.name?.text ?? "Anon");
|
|
1496
|
+
} else if (ts.isMethodDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node) || ts.isPropertyDeclaration(node) || ts.isFunctionDeclaration(node)) {
|
|
1497
|
+
if (node.name && ts.isIdentifier(node.name)) {
|
|
1498
|
+
parts.push(node.name.text);
|
|
1430
1499
|
}
|
|
1431
|
-
current = current.parent;
|
|
1432
1500
|
}
|
|
1433
|
-
return parts.join(".");
|
|
1434
1501
|
}
|
|
1435
1502
|
function parseSymbols(opts) {
|
|
1436
1503
|
const { file, content, lang } = opts;
|
|
@@ -1441,45 +1508,39 @@ function parseSymbols(opts) {
|
|
|
1441
1508
|
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
1442
1509
|
}
|
|
1443
1510
|
const symbols = [];
|
|
1444
|
-
|
|
1511
|
+
const refs = [];
|
|
1512
|
+
const printer = ts.createPrinter({});
|
|
1513
|
+
function visit(node, funcDepth, scopeParts) {
|
|
1445
1514
|
const kind = kindOf(node);
|
|
1446
1515
|
if (kind) {
|
|
1447
|
-
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") &&
|
|
1448
|
-
|
|
1449
|
-
|
|
1516
|
+
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") && funcDepth > 0) {
|
|
1517
|
+
} else {
|
|
1518
|
+
const nameNode = node.name;
|
|
1519
|
+
if (!nameNode || !ts.isIdentifier(nameNode)) {
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
const name = nameNode.text;
|
|
1523
|
+
const pos2 = nameNode.getStart(sourceFile);
|
|
1524
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(pos2);
|
|
1525
|
+
const scope = scopeParts.join(".");
|
|
1526
|
+
const signature = getSignature(printer, node, sourceFile);
|
|
1527
|
+
const docComment = getJsDoc(node, sourceFile);
|
|
1528
|
+
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
1529
|
+
symbols.push({
|
|
1530
|
+
id: 0,
|
|
1531
|
+
lang,
|
|
1532
|
+
kind,
|
|
1533
|
+
name,
|
|
1534
|
+
file,
|
|
1535
|
+
line: line2 + 1,
|
|
1536
|
+
col: character,
|
|
1537
|
+
signature,
|
|
1538
|
+
docComment,
|
|
1539
|
+
scope,
|
|
1540
|
+
text
|
|
1541
|
+
});
|
|
1450
1542
|
}
|
|
1451
|
-
const nameNode = node.name;
|
|
1452
|
-
if (!nameNode || !ts.isIdentifier(nameNode)) return;
|
|
1453
|
-
const name = nameNode.text;
|
|
1454
|
-
const pos = nameNode.getStart(sourceFile);
|
|
1455
|
-
const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
1456
|
-
const scope = buildScope(node);
|
|
1457
|
-
const signature = getSignature(node, sourceFile);
|
|
1458
|
-
const docComment = getJsDoc(node, sourceFile);
|
|
1459
|
-
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
1460
|
-
symbols.push({
|
|
1461
|
-
id: 0,
|
|
1462
|
-
lang,
|
|
1463
|
-
kind,
|
|
1464
|
-
name,
|
|
1465
|
-
file,
|
|
1466
|
-
line: line + 1,
|
|
1467
|
-
col: character,
|
|
1468
|
-
signature,
|
|
1469
|
-
docComment,
|
|
1470
|
-
scope,
|
|
1471
|
-
text
|
|
1472
|
-
});
|
|
1473
1543
|
}
|
|
1474
|
-
ts.forEachChild(node, visit);
|
|
1475
|
-
}
|
|
1476
|
-
visit(sourceFile);
|
|
1477
|
-
const refs = extractRefs(sourceFile);
|
|
1478
|
-
return { file, lang, symbols, refs, mtimeMs: Date.now() };
|
|
1479
|
-
}
|
|
1480
|
-
function extractRefs(sourceFile) {
|
|
1481
|
-
const refs = [];
|
|
1482
|
-
function visit(node) {
|
|
1483
1544
|
const pos = node.getStart(sourceFile);
|
|
1484
1545
|
const { line } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
1485
1546
|
const lineNum = line + 1;
|
|
@@ -1504,10 +1565,14 @@ function extractRefs(sourceFile) {
|
|
|
1504
1565
|
const moduleName = getModuleName(node);
|
|
1505
1566
|
if (moduleName) refs.push({ fromId: 0, toName: moduleName, callType: "import", line: lineNum });
|
|
1506
1567
|
}
|
|
1507
|
-
|
|
1568
|
+
const scopeIdx = scopeParts.length;
|
|
1569
|
+
pushScopeName(node, scopeParts);
|
|
1570
|
+
const childFuncDepth = ts.isFunctionLike(node) ? funcDepth + 1 : funcDepth;
|
|
1571
|
+
ts.forEachChild(node, (child) => visit(child, childFuncDepth, scopeParts));
|
|
1572
|
+
scopeParts.length = scopeIdx;
|
|
1508
1573
|
}
|
|
1509
|
-
visit(sourceFile);
|
|
1510
|
-
return deduplicateRefs(refs);
|
|
1574
|
+
visit(sourceFile, 0, []);
|
|
1575
|
+
return { file, lang, symbols, refs: deduplicateRefs(refs), mtimeMs: Date.now() };
|
|
1511
1576
|
}
|
|
1512
1577
|
function getTypeName(name) {
|
|
1513
1578
|
if (ts.isIdentifier(name)) return name.text;
|
|
@@ -2812,7 +2877,7 @@ async function loadGitignoreMatcher(projectRoot) {
|
|
|
2812
2877
|
|
|
2813
2878
|
// src/codebase-index/indexer.ts
|
|
2814
2879
|
var YIELD_EVERY_N = 50;
|
|
2815
|
-
var PARALLEL_BATCH =
|
|
2880
|
+
var PARALLEL_BATCH = Math.min(availableParallelism() * 4, 40);
|
|
2816
2881
|
function yieldEventLoop() {
|
|
2817
2882
|
return new Promise((resolve2) => setImmediate(resolve2));
|
|
2818
2883
|
}
|
|
@@ -2990,11 +3055,14 @@ async function runIndexerWithStore(store, opts) {
|
|
|
2990
3055
|
if (!force) {
|
|
2991
3056
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
2992
3057
|
}
|
|
3058
|
+
let filesSinceLastYield = 0;
|
|
2993
3059
|
for (let batchStart = 0; batchStart < files.length; batchStart += PARALLEL_BATCH) {
|
|
2994
3060
|
const batchEnd = Math.min(batchStart + PARALLEL_BATCH, files.length);
|
|
2995
3061
|
const batchFiles = files.slice(batchStart, batchEnd);
|
|
2996
3062
|
opts.onProgress?.(batchEnd, files.length);
|
|
2997
|
-
|
|
3063
|
+
filesSinceLastYield += batchFiles.length;
|
|
3064
|
+
if (filesSinceLastYield >= YIELD_EVERY_N) {
|
|
3065
|
+
filesSinceLastYield = 0;
|
|
2998
3066
|
await yieldEventLoop();
|
|
2999
3067
|
throwIfAborted(signal);
|
|
3000
3068
|
}
|
|
@@ -3205,7 +3273,7 @@ async function indexService(args, hooks = {}) {
|
|
|
3205
3273
|
});
|
|
3206
3274
|
}
|
|
3207
3275
|
function searchService(args) {
|
|
3208
|
-
const store =
|
|
3276
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
3209
3277
|
try {
|
|
3210
3278
|
return store.searchRanked(
|
|
3211
3279
|
args.query,
|
|
@@ -3218,15 +3286,15 @@ function searchService(args) {
|
|
|
3218
3286
|
args.limit
|
|
3219
3287
|
);
|
|
3220
3288
|
} finally {
|
|
3221
|
-
|
|
3289
|
+
indexStorePool.release(store);
|
|
3222
3290
|
}
|
|
3223
3291
|
}
|
|
3224
3292
|
function statsService(args) {
|
|
3225
|
-
const store =
|
|
3293
|
+
const store = indexStorePool.acquire(args.projectRoot, { indexDir: args.indexDir });
|
|
3226
3294
|
try {
|
|
3227
3295
|
return store.getStats();
|
|
3228
3296
|
} finally {
|
|
3229
|
-
|
|
3297
|
+
indexStorePool.release(store);
|
|
3230
3298
|
}
|
|
3231
3299
|
}
|
|
3232
3300
|
|