@wrongstack/tools 0.267.0 → 0.269.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -296,7 +296,7 @@ var IndexStore = class {
296
296
  throw new LockError(`SQLite lock conflict after ${MAX_LOCK_RETRIES} retries: ${msg}`);
297
297
  }
298
298
  const delay = Math.min(
299
- LOCK_RETRY_BASE_DELAY_MS * Math.pow(2, attempt),
299
+ LOCK_RETRY_BASE_DELAY_MS * 2 ** attempt,
300
300
  LOCK_RETRY_MAX_DELAY_MS
301
301
  );
302
302
  sleepSync(delay);
@@ -589,14 +589,15 @@ var IndexStore = class {
589
589
  if (!query.trim()) {
590
590
  return { results: candidates.slice(0, limit), total: candidates.length };
591
591
  }
592
+ const candidateById = new Map(candidates.map((c) => [c.id, c]));
592
593
  const bm25 = buildBm25Index(
593
594
  candidates.map((c) => ({ id: c.id, text: buildIndexableText(c.name, c.signature, c.docComment) }))
594
595
  );
595
- const scored = bm25.score(query, (id) => candidates.some((c) => c.id === id));
596
+ const scored = bm25.score(query, (id) => candidateById.has(id));
596
597
  scored.sort((a, b) => b.score - a.score);
597
598
  const qTokens = tokenise(query);
598
599
  const results = scored.slice(0, limit).map(({ id, score }) => {
599
- const c = expectDefined(candidates.find((cand) => cand.id === id));
600
+ const c = expectDefined(candidateById.get(id));
600
601
  return { ...c, score, snippet: bm25.extractSnippet(id, qTokens) };
601
602
  });
602
603
  return { results, total: candidates.length };
@@ -2013,6 +2014,7 @@ async function loadGitignoreMatcher(projectRoot) {
2013
2014
 
2014
2015
  // src/codebase-index/indexer.ts
2015
2016
  var YIELD_EVERY_N = 50;
2017
+ var PARALLEL_BATCH = 20;
2016
2018
  function yieldEventLoop() {
2017
2019
  return new Promise((resolve2) => setImmediate(resolve2));
2018
2020
  }
@@ -2146,97 +2148,111 @@ async function runIndexerWithStore(store, opts) {
2146
2148
  if (!force) {
2147
2149
  for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
2148
2150
  }
2149
- for (let fi = 0; fi < files.length; fi++) {
2150
- const file = expectDefined(files[fi]);
2151
- opts.onProgress?.(fi + 1, files.length);
2152
- if (fi > 0 && fi % YIELD_EVERY_N === 0) {
2151
+ for (let batchStart = 0; batchStart < files.length; batchStart += PARALLEL_BATCH) {
2152
+ const batchEnd = Math.min(batchStart + PARALLEL_BATCH, files.length);
2153
+ const batchFiles = files.slice(batchStart, batchEnd);
2154
+ opts.onProgress?.(batchEnd, files.length);
2155
+ if (batchStart > 0 && batchStart % YIELD_EVERY_N === 0) {
2153
2156
  await yieldEventLoop();
2154
2157
  throwIfAborted(signal);
2155
2158
  }
2156
- let stat2;
2157
- try {
2158
- const statOpts = signal ? { signal } : {};
2159
- stat2 = await fs3.stat(file, statOpts);
2160
- } catch (e) {
2161
- if (isAbortError(e)) throw e;
2162
- store.deleteFile(file);
2163
- continue;
2164
- }
2165
- if (!stat2.isFile()) continue;
2166
- const lang = detectLang(file);
2167
- if (!lang) continue;
2168
- const meta = existingMeta.get(file);
2169
- if (!force && meta && meta.mtimeMs === Math.floor(stat2.mtimeMs)) {
2170
- langStats[lang] = (langStats[lang] ?? 0) + meta.symbolCount;
2171
- symbolsIndexed += meta.symbolCount;
2172
- filesIndexed++;
2173
- continue;
2174
- }
2175
- store.deleteRefsForFile(file);
2176
- store.deleteSymbolsForFile(file);
2177
- let content;
2178
- try {
2179
- content = await fs3.readFile(file, { encoding: "utf8", signal });
2180
- } catch (e) {
2181
- if (isAbortError(e)) throw e;
2182
- errors.push(`read error: ${file}: ${e instanceof Error ? e.message : String(e)}`);
2183
- continue;
2184
- }
2185
- let parsed;
2186
- try {
2187
- parsed = await parseFile(file, content, lang);
2188
- } catch (e) {
2189
- errors.push(`parse error: ${file}: ${e instanceof Error ? e.message : String(e)}`);
2190
- continue;
2191
- }
2192
- if (parsed.symbols.length === 0) {
2193
- store.upsertFile({
2194
- file,
2195
- lang,
2196
- mtimeMs: Math.floor(stat2.mtimeMs),
2197
- symbolCount: 0,
2198
- lastIndexed: Date.now()
2199
- });
2200
- filesIndexed++;
2201
- continue;
2202
- }
2203
- const nextId = store.getMaxSymbolId() + 1;
2204
- const symbolsWithIds = parsed.symbols.map((s, i) => ({ ...s, id: nextId + i }));
2205
- store.insertSymbols(symbolsWithIds, nextId);
2206
- const count = symbolsWithIds.length;
2207
- symbolsIndexed += count;
2208
- langStats[lang] = (langStats[lang] ?? 0) + count;
2209
- if (parsed.refs && parsed.refs.length > 0) {
2210
- const refsByLine = /* @__PURE__ */ new Map();
2211
- for (const r of parsed.refs) {
2212
- let arr = refsByLine.get(r.line);
2213
- if (!arr) {
2214
- arr = [];
2215
- refsByLine.set(r.line, arr);
2159
+ const statOpts = signal ? { signal } : {};
2160
+ const statReadParse = await Promise.allSettled(
2161
+ batchFiles.map(async (file) => {
2162
+ let stat2;
2163
+ try {
2164
+ stat2 = await fs3.stat(file, statOpts);
2165
+ } catch (e) {
2166
+ if (isAbortError(e)) throw e;
2167
+ return { file, stat: null, lang: "", parsed: null, error: `stat error: ${e instanceof Error ? e.message : String(e)}` };
2168
+ }
2169
+ if (!stat2.isFile()) return { file, stat: stat2, lang: "", parsed: null };
2170
+ const lang = detectLang(file);
2171
+ if (!lang) return { file, stat: stat2, lang: "", parsed: null };
2172
+ const meta = existingMeta.get(file);
2173
+ if (!force && meta && meta.mtimeMs === Math.floor(stat2.mtimeMs)) {
2174
+ return { file, stat: stat2, lang, parsed: null, skippedMeta: meta };
2175
+ }
2176
+ let content;
2177
+ try {
2178
+ content = await fs3.readFile(file, { encoding: "utf8", signal });
2179
+ } catch (e) {
2180
+ if (isAbortError(e)) throw e;
2181
+ return { file, stat: stat2, lang, parsed: null, error: `read error: ${e instanceof Error ? e.message : String(e)}` };
2216
2182
  }
2217
- arr.push(r);
2183
+ let parsed;
2184
+ try {
2185
+ parsed = await parseFile(file, content, lang);
2186
+ } catch (e) {
2187
+ return { file, stat: stat2, lang, parsed: null, error: `parse error: ${e instanceof Error ? e.message : String(e)}` };
2188
+ }
2189
+ return { file, stat: stat2, lang, parsed, content };
2190
+ })
2191
+ );
2192
+ for (let fi = 0; fi < statReadParse.length; fi++) {
2193
+ const settled = statReadParse[fi];
2194
+ const file = expectDefined(batchFiles[fi]);
2195
+ if (settled.status === "rejected") {
2196
+ const err = settled.reason;
2197
+ if (err instanceof Error && isAbortError(err)) throw err;
2198
+ errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
2199
+ continue;
2218
2200
  }
2219
- const batch = [];
2220
- for (const sym of symbolsWithIds) {
2221
- const symRefs = refsByLine.get(sym.line);
2222
- if (symRefs) {
2223
- for (const r of symRefs) {
2224
- batch.push({ ...r, fromId: sym.id });
2225
- }
2201
+ const result = settled.value;
2202
+ if (result.error) {
2203
+ if (result.stat) store.deleteFile(file);
2204
+ if (result.error.includes("error:")) errors.push(result.error);
2205
+ continue;
2206
+ }
2207
+ const { stat: stat2, lang, parsed } = result;
2208
+ if (result.skippedMeta) {
2209
+ langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
2210
+ symbolsIndexed += result.skippedMeta.symbolCount;
2211
+ filesIndexed++;
2212
+ continue;
2213
+ }
2214
+ if (!lang || !parsed) {
2215
+ if (lang) {
2216
+ store.upsertFile({ file, lang, mtimeMs: Math.floor(stat2.mtimeMs), symbolCount: 0, lastIndexed: Date.now() });
2217
+ filesIndexed++;
2226
2218
  }
2219
+ continue;
2220
+ }
2221
+ store.deleteRefsForFile(file);
2222
+ store.deleteSymbolsForFile(file);
2223
+ if (parsed.symbols.length === 0) {
2224
+ store.upsertFile({ file, lang, mtimeMs: Math.floor(stat2.mtimeMs), symbolCount: 0, lastIndexed: Date.now() });
2225
+ filesIndexed++;
2226
+ continue;
2227
2227
  }
2228
- if (batch.length > 0) {
2229
- store.insertRefsBatch(batch);
2228
+ const nextId = store.getMaxSymbolId() + 1;
2229
+ const symbolsWithIds = parsed.symbols.map((s, i) => ({ ...s, id: nextId + i }));
2230
+ store.insertSymbols(symbolsWithIds, nextId);
2231
+ const count = symbolsWithIds.length;
2232
+ symbolsIndexed += count;
2233
+ langStats[lang] = (langStats[lang] ?? 0) + count;
2234
+ if (parsed.refs && parsed.refs.length > 0) {
2235
+ const refsByLine = /* @__PURE__ */ new Map();
2236
+ for (const r of parsed.refs) {
2237
+ let arr = refsByLine.get(r.line);
2238
+ if (!arr) {
2239
+ arr = [];
2240
+ refsByLine.set(r.line, arr);
2241
+ }
2242
+ arr.push(r);
2243
+ }
2244
+ const batch = [];
2245
+ for (const sym of symbolsWithIds) {
2246
+ const symRefs = refsByLine.get(sym.line);
2247
+ if (symRefs) {
2248
+ for (const r of symRefs) batch.push({ ...r, fromId: sym.id });
2249
+ }
2250
+ }
2251
+ if (batch.length > 0) store.insertRefsBatch(batch);
2230
2252
  }
2253
+ store.upsertFile({ file, lang, mtimeMs: Math.floor(stat2.mtimeMs), symbolCount: count, lastIndexed: Date.now() });
2254
+ filesIndexed++;
2231
2255
  }
2232
- store.upsertFile({
2233
- file,
2234
- lang,
2235
- mtimeMs: Math.floor(stat2.mtimeMs),
2236
- symbolCount: count,
2237
- lastIndexed: Date.now()
2238
- });
2239
- filesIndexed++;
2240
2256
  }
2241
2257
  for (const [file_] of existingMeta) {
2242
2258
  try {