@goshenkata/dryscan-core 1.4.9 → 1.4.11

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.
@@ -0,0 +1,65 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // src/services/ParallelSimilarity.ts
13
+ import os from "os";
14
+ import { Worker } from "worker_threads";
15
+ import { existsSync } from "fs";
16
+ import { fileURLToPath } from "url";
17
+ import { cosineSimilarity } from "@langchain/core/utils/math";
18
+ var MIN_PARALLEL_ROWS = 50;
19
+ async function parallelCosineSimilarity(A, B) {
20
+ if (A.length === 0 || B.length === 0) return [];
21
+ if (A.length < MIN_PARALLEL_ROWS) return cosineSimilarity(A, B);
22
+ const dims = A[0].length;
23
+ const chunkSize = Math.ceil(A.length / os.cpus().length);
24
+ const sharedB = new SharedArrayBuffer(B.length * dims * 8);
25
+ const bView = new Float64Array(sharedB);
26
+ B.forEach((row, i) => bView.set(row, i * dims));
27
+ const runningTsSource = new URL(import.meta.url).pathname.endsWith(".ts");
28
+ const workerCandidates = runningTsSource ? ["./cosineSimilarityWorker.ts", "./services/cosineSimilarityWorker.ts"] : ["./cosineSimilarityWorker.js", "./services/cosineSimilarityWorker.js"];
29
+ const workerUrl = workerCandidates.map((candidate) => new URL(candidate, import.meta.url)).find((url) => existsSync(fileURLToPath(url)));
30
+ if (!workerUrl) {
31
+ throw new Error(
32
+ `cosineSimilarityWorker not found next to ${fileURLToPath(new URL(import.meta.url))}. Tried: ${workerCandidates.join(", ")}`
33
+ );
34
+ }
35
+ const execArgv = runningTsSource ? ["--import", "tsx/esm"] : [];
36
+ const chunks = Array.from(
37
+ { length: Math.ceil(A.length / chunkSize) },
38
+ (_, i) => A.slice(i * chunkSize, (i + 1) * chunkSize)
39
+ );
40
+ const results = await Promise.all(chunks.map((chunk) => runWorker(chunk, sharedB, B.length, dims, workerUrl, execArgv)));
41
+ return results.flat();
42
+ }
43
+ function runWorker(chunk, sharedB, bCount, dims, workerUrl, execArgv) {
44
+ return new Promise((resolve, reject) => {
45
+ const rowsFlat = new Float64Array(chunk.length * dims);
46
+ chunk.forEach((row, i) => rowsFlat.set(row, i * dims));
47
+ const worker = new Worker(workerUrl, {
48
+ workerData: { rowsBuffer: rowsFlat.buffer, rowCount: chunk.length, allBuffer: sharedB, allCount: bCount, dims },
49
+ transferList: [rowsFlat.buffer],
50
+ execArgv
51
+ });
52
+ worker.once("message", ({ result }) => resolve(result));
53
+ worker.once("error", reject);
54
+ });
55
+ }
56
+ var similarityApi = {
57
+ parallelCosineSimilarity
58
+ };
59
+
60
+ export {
61
+ __decorateClass,
62
+ parallelCosineSimilarity,
63
+ similarityApi
64
+ };
65
+ //# sourceMappingURL=chunk-MBLD5OWH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/ParallelSimilarity.ts"],"sourcesContent":["import os from \"node:os\";\nimport { Worker } from \"node:worker_threads\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { cosineSimilarity } from \"@langchain/core/utils/math\";\n\n/** Minimum row count below which synchronous is faster than worker overhead. */\nconst MIN_PARALLEL_ROWS = 50;\n\n/**\n * Computes cosineSimilarity(A, B) using worker threads for large inputs,\n * falling back to the synchronous library call for small ones.\n * B is packed into a SharedArrayBuffer shared across all workers — no copies.\n */\nexport async function parallelCosineSimilarity(A: number[][], B: number[][]): Promise<number[][]> {\n if (A.length === 0 || B.length === 0) return [];\n if (A.length < MIN_PARALLEL_ROWS) return cosineSimilarity(A, B);\n\n const dims = A[0].length;\n const chunkSize = Math.ceil(A.length / os.cpus().length);\n\n const sharedB = new SharedArrayBuffer(B.length * dims * 8);\n const bView = new Float64Array(sharedB);\n B.forEach((row, i) => bView.set(row, i * dims));\n\n const runningTsSource = new URL(import.meta.url).pathname.endsWith('.ts');\n\n const workerCandidates = runningTsSource\n ? ['./cosineSimilarityWorker.ts', './services/cosineSimilarityWorker.ts']\n : ['./cosineSimilarityWorker.js', './services/cosineSimilarityWorker.js'];\n\n const workerUrl = workerCandidates\n .map((candidate) => new URL(candidate, import.meta.url))\n .find((url) => existsSync(fileURLToPath(url)));\n\n if (!workerUrl) {\n throw new Error(\n `cosineSimilarityWorker not found next to ${fileURLToPath(new URL(import.meta.url))}. Tried: ${workerCandidates.join(\", \")}`\n );\n }\n\n const execArgv = runningTsSource ? ['--import', 'tsx/esm'] : [];\n\n const chunks = Array.from(\n { length: Math.ceil(A.length / chunkSize) },\n (_, i) => A.slice(i * chunkSize, (i + 1) * chunkSize),\n );\n\n const results = await Promise.all(chunks.map(chunk => runWorker(chunk, sharedB, B.length, dims, workerUrl, execArgv)));\n return results.flat();\n}\n\nfunction runWorker(\n chunk: number[][],\n sharedB: SharedArrayBuffer,\n bCount: number,\n dims: number,\n workerUrl: URL,\n execArgv: string[],\n): Promise<number[][]> {\n return new Promise((resolve, reject) => {\n const rowsFlat = new Float64Array(chunk.length * dims);\n chunk.forEach((row, i) => rowsFlat.set(row, i * dims));\n\n const worker = new Worker(workerUrl, {\n workerData: { rowsBuffer: rowsFlat.buffer, rowCount: chunk.length, allBuffer: sharedB, allCount: bCount, dims },\n transferList: [rowsFlat.buffer],\n execArgv,\n });\n\n worker.once(\"message\", ({ result }) => resolve(result));\n worker.once(\"error\", reject);\n });\n}\n\n// Mutable export for test stubbing and for callers that want an indirection layer.\nexport const similarityApi = {\n parallelCosineSimilarity,\n};\n"],"mappings":";;;;;;;;;;;;AAAA,OAAO,QAAQ;AACf,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AAGjC,IAAM,oBAAoB;AAO1B,eAAsB,yBAAyB,GAAe,GAAoC;AAChG,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO,CAAC;AAC9C,MAAI,EAAE,SAAS,kBAAmB,QAAO,iBAAiB,GAAG,CAAC;AAE9D,QAAM,OAAO,EAAE,CAAC,EAAE;AAClB,QAAM,YAAY,KAAK,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,MAAM;AAEvD,QAAM,UAAU,IAAI,kBAAkB,EAAE,SAAS,OAAO,CAAC;AACzD,QAAM,QAAQ,IAAI,aAAa,OAAO;AACtC,IAAE,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI,KAAK,IAAI,IAAI,CAAC;AAE9C,QAAM,kBAAkB,IAAI,IAAI,YAAY,GAAG,EAAE,SAAS,SAAS,KAAK;AAExE,QAAM,mBAAmB,kBACrB,CAAC,+BAA+B,sCAAsC,IACtE,CAAC,+BAA+B,sCAAsC;AAE1E,QAAM,YAAY,iBACf,IAAI,CAAC,cAAc,IAAI,IAAI,WAAW,YAAY,GAAG,CAAC,EACtD,KAAK,CAAC,QAAQ,WAAW,cAAc,GAAG,CAAC,CAAC;AAE/C,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,4CAA4C,cAAc,IAAI,IAAI,YAAY,GAAG,CAAC,CAAC,YAAY,iBAAiB,KAAK,IAAI,CAAC;AAAA,IAC5H;AAAA,EACF;AAEA,QAAM,WAAW,kBAAkB,CAAC,YAAY,SAAS,IAAI,CAAC;AAE9D,QAAM,SAAS,MAAM;AAAA,IACnB,EAAE,QAAQ,KAAK,KAAK,EAAE,SAAS,SAAS,EAAE;AAAA,IAC1C,CAAC,GAAG,MAAM,EAAE,MAAM,IAAI,YAAY,IAAI,KAAK,SAAS;AAAA,EACtD;AAEA,QAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,IAAI,WAAS,UAAU,OAAO,SAAS,EAAE,QAAQ,MAAM,WAAW,QAAQ,CAAC,CAAC;AACrH,SAAO,QAAQ,KAAK;AACtB;AAEA,SAAS,UACP,OACA,SACA,QACA,MACA,WACA,UACqB;AACrB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,WAAW,IAAI,aAAa,MAAM,SAAS,IAAI;AACrD,UAAM,QAAQ,CAAC,KAAK,MAAM,SAAS,IAAI,KAAK,IAAI,IAAI,CAAC;AAErD,UAAM,SAAS,IAAI,OAAO,WAAW;AAAA,MACnC,YAAY,EAAE,YAAY,SAAS,QAAQ,UAAU,MAAM,QAAQ,WAAW,SAAS,UAAU,QAAQ,KAAK;AAAA,MAC9G,cAAc,CAAC,SAAS,MAAM;AAAA,MAC9B;AAAA,IACF,CAAC;AAED,WAAO,KAAK,WAAW,CAAC,EAAE,OAAO,MAAM,QAAQ,MAAM,CAAC;AACtD,WAAO,KAAK,SAAS,MAAM;AAAA,EAC7B,CAAC;AACH;AAGO,IAAM,gBAAgB;AAAA,EAC3B;AACF;","names":[]}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
- __decorateClass
3
- } from "./chunk-EUXUH3YW.js";
2
+ __decorateClass,
3
+ similarityApi
4
+ } from "./chunk-MBLD5OWH.js";
4
5
 
5
6
  // src/DryScan.ts
6
7
  import upath6 from "upath";
@@ -213,7 +214,7 @@ var JavaExtractor = class {
213
214
  const endLine = node.endPosition.row;
214
215
  const classLength = endLine - startLine;
215
216
  const skipClass = this.shouldSkip("class" /* CLASS */, className, classLength);
216
- const classId = this.buildId("class" /* CLASS */, className, startLine, endLine);
217
+ const classId = this.buildId("class" /* CLASS */, fileRelPath, className, startLine, endLine);
217
218
  const code = this.stripComments(this.stripClassBody(node, source));
218
219
  const classUnit = {
219
220
  id: classId,
@@ -369,7 +370,7 @@ var JavaExtractor = class {
369
370
  const name = this.getFunctionName(node, source, parentClass) || "<anonymous>";
370
371
  const startLine = node.startPosition.row;
371
372
  const endLine = node.endPosition.row;
372
- const id = this.buildId("function" /* FUNCTION */, name, startLine, endLine);
373
+ const id = this.buildId("function" /* FUNCTION */, file, name, startLine, endLine);
373
374
  const unit = {
374
375
  id,
375
376
  name,
@@ -399,7 +400,7 @@ var JavaExtractor = class {
399
400
  return;
400
401
  }
401
402
  if (lineCount >= indexConfig.blockMinLines) {
402
- const id = this.buildId("block" /* BLOCK */, parentFunction.name, startLine, endLine);
403
+ const id = this.buildId("block" /* BLOCK */, file, parentFunction.name, startLine, endLine);
403
404
  const blockUnit = {
404
405
  id,
405
406
  name: parentFunction.name,
@@ -440,8 +441,27 @@ var JavaExtractor = class {
440
441
  }
441
442
  return code;
442
443
  }
443
- buildId(type, name, startLine, endLine) {
444
- return `${type}:${name}:${startLine}-${endLine}`;
444
+ buildId(type, filePath, name, startLine, endLine) {
445
+ const pathKey = this.pathKeyForId(filePath);
446
+ const scopedName = this.scopeNameWithPath(type, pathKey, name);
447
+ return `${type}:${scopedName}:${startLine}-${endLine}`;
448
+ }
449
+ pathKeyForId(filePath) {
450
+ const normalized = filePath.replace(/\\/g, "/");
451
+ return normalized.replace(/\.[^./]+$/, "");
452
+ }
453
+ scopeNameWithPath(type, pathKey, name) {
454
+ if (type === "class" /* CLASS */) {
455
+ return pathKey;
456
+ }
457
+ const classLeaf = pathKey.split("/").pop() ?? pathKey;
458
+ if (name === classLeaf) {
459
+ return pathKey;
460
+ }
461
+ if (name.startsWith(`${classLeaf}.`)) {
462
+ return `${pathKey}${name.slice(classLeaf.length)}`;
463
+ }
464
+ return `${pathKey}.${name}`;
445
465
  }
446
466
  extractArity(code) {
447
467
  const match = code.match(/^[^{]*?\(([^)]*)\)/s);
@@ -1365,46 +1385,217 @@ var PairingService = class {
1365
1385
  import { existsSync } from "fs";
1366
1386
 
1367
1387
  // src/services/DuplicateService.ts
1368
- import debug6 from "debug";
1388
+ import debug7 from "debug";
1369
1389
  import shortUuid from "short-uuid";
1370
- import { cosineSimilarity } from "@langchain/core/utils/math";
1371
- var log6 = debug6("DryScan:DuplicateService");
1390
+
1391
+ // src/services/DuplicationCache.ts
1392
+ import debug6 from "debug";
1393
+ var log6 = debug6("DryScan:DuplicationCache");
1394
+ var DuplicationCache = class _DuplicationCache {
1395
+ static instance = null;
1396
+ comparisons = /* @__PURE__ */ new Map();
1397
+ fileIndex = /* @__PURE__ */ new Map();
1398
+ initialized = false;
1399
+ /** Per-run similarity matrix from a single batched library call (reset each run). */
1400
+ embSimMatrix = [];
1401
+ /** Maps unit ID to its row/column index in embSimMatrix. */
1402
+ embSimIndex = /* @__PURE__ */ new Map();
1403
+ /** Per-run memoization of parent unit similarity scores (reset each run). */
1404
+ parentSimCache = /* @__PURE__ */ new Map();
1405
+ static getInstance() {
1406
+ if (!_DuplicationCache.instance) {
1407
+ _DuplicationCache.instance = new _DuplicationCache();
1408
+ }
1409
+ return _DuplicationCache.instance;
1410
+ }
1411
+ /**
1412
+ * Updates the cache with fresh duplicate groups. Not awaited by callers to avoid blocking.
1413
+ */
1414
+ async update(groups) {
1415
+ if (!groups) return;
1416
+ for (const group of groups) {
1417
+ const key = this.makeKey(group.left.id, group.right.id);
1418
+ this.comparisons.set(key, group.similarity);
1419
+ this.addKeyForFile(group.left.filePath, key);
1420
+ this.addKeyForFile(group.right.filePath, key);
1421
+ }
1422
+ this.initialized = this.initialized || groups.length > 0;
1423
+ }
1424
+ /**
1425
+ * Retrieves a cached similarity if present and valid for both file paths.
1426
+ * Returns null when the cache has not been initialized or when the pair is missing.
1427
+ */
1428
+ get(leftId, rightId, leftFilePath, rightFilePath) {
1429
+ if (!this.initialized) return null;
1430
+ const key = this.makeKey(leftId, rightId);
1431
+ if (!this.fileHasKey(leftFilePath, key) || !this.fileHasKey(rightFilePath, key)) {
1432
+ return null;
1433
+ }
1434
+ const value = this.comparisons.get(key);
1435
+ return typeof value === "number" ? value : null;
1436
+ }
1437
+ /**
1438
+ * Invalidates all cached comparisons involving the provided file paths.
1439
+ */
1440
+ async invalidate(paths) {
1441
+ if (!this.initialized || !paths || paths.length === 0) return;
1442
+ const unique = new Set(paths);
1443
+ for (const filePath of unique) {
1444
+ const keys = this.fileIndex.get(filePath);
1445
+ if (!keys) continue;
1446
+ for (const key of keys) {
1447
+ this.comparisons.delete(key);
1448
+ for (const [otherPath, otherKeys] of this.fileIndex.entries()) {
1449
+ if (otherKeys.delete(key) && otherKeys.size === 0) {
1450
+ this.fileIndex.delete(otherPath);
1451
+ }
1452
+ }
1453
+ }
1454
+ this.fileIndex.delete(filePath);
1455
+ }
1456
+ if (this.comparisons.size === 0) {
1457
+ this.initialized = false;
1458
+ }
1459
+ }
1460
+ /**
1461
+ * Clears all cached data. Intended for test setup.
1462
+ */
1463
+ clear() {
1464
+ this.comparisons.clear();
1465
+ this.fileIndex.clear();
1466
+ this.initialized = false;
1467
+ this.embSimMatrix = [];
1468
+ this.embSimIndex.clear();
1469
+ this.clearRunCaches();
1470
+ }
1471
+ /**
1472
+ * Resets per-run memoization (parent similarities).
1473
+ * The embedding matrix is intentionally preserved so incremental runs can
1474
+ * reuse clean×clean values across calls.
1475
+ */
1476
+ clearRunCaches() {
1477
+ this.parentSimCache.clear();
1478
+ }
1479
+ /**
1480
+ * Builds or incrementally updates the embedding similarity matrix.
1481
+ *
1482
+ * Full rebuild (default): replaces the entire matrix — O(n²).
1483
+ * Incremental (dirtyPaths provided + prior matrix exists): copies clean×clean
1484
+ * cells from the old matrix and recomputes only dirty rows via one batched
1485
+ * cosineSimilarity call — O(d·n) where d = number of dirty units.
1486
+ */
1487
+ async buildEmbSimCache(units, dirtyPaths) {
1488
+ const embedded = units.filter((u) => Array.isArray(u.embedding) && u.embedding.length > 0);
1489
+ if (embedded.length < 2) {
1490
+ this.embSimMatrix = [];
1491
+ this.embSimIndex.clear();
1492
+ return;
1493
+ }
1494
+ const embeddings = embedded.map((u) => u.embedding);
1495
+ const newIndex = new Map(embedded.map((u, i) => [u.id, i]));
1496
+ const dirtySet = dirtyPaths ? new Set(dirtyPaths) : null;
1497
+ const hasPriorMatrix = this.embSimMatrix.length > 0;
1498
+ if (!dirtySet || !hasPriorMatrix) {
1499
+ this.embSimIndex = newIndex;
1500
+ this.embSimMatrix = await similarityApi.parallelCosineSimilarity(embeddings, embeddings);
1501
+ log6("Built full embedding similarity matrix: %d units", embedded.length);
1502
+ return;
1503
+ }
1504
+ const dirtyIds = new Set(embedded.filter((u) => dirtySet.has(u.filePath)).map((u) => u.id));
1505
+ if (dirtyIds.size === 0) {
1506
+ log6("Matrix reused: no dirty units detected");
1507
+ return;
1508
+ }
1509
+ const n = embedded.length;
1510
+ const newMatrix = Array.from({ length: n }, () => new Array(n).fill(0));
1511
+ for (let i = 0; i < n; i++) {
1512
+ for (let j = 0; j < n; j++) {
1513
+ if (dirtyIds.has(embedded[i].id) || dirtyIds.has(embedded[j].id)) continue;
1514
+ const oi = this.embSimIndex.get(embedded[i].id);
1515
+ const oj = this.embSimIndex.get(embedded[j].id);
1516
+ if (oi !== void 0 && oj !== void 0) newMatrix[i][j] = this.embSimMatrix[oi][oj];
1517
+ }
1518
+ }
1519
+ const dirtyIndices = embedded.reduce((acc, u, i) => dirtyIds.has(u.id) ? [...acc, i] : acc, []);
1520
+ const dirtyRows = await similarityApi.parallelCosineSimilarity(dirtyIndices.map((i) => embeddings[i]), embeddings);
1521
+ dirtyIndices.forEach((rowIdx, di) => {
1522
+ for (let j = 0; j < n; j++) {
1523
+ newMatrix[rowIdx][j] = dirtyRows[di][j];
1524
+ newMatrix[j][rowIdx] = dirtyRows[di][j];
1525
+ }
1526
+ });
1527
+ this.embSimIndex = newIndex;
1528
+ this.embSimMatrix = newMatrix;
1529
+ log6("Incremental matrix update: %d dirty unit(s) out of %d total", dirtyIds.size, n);
1530
+ }
1531
+ /** Returns the pre-computed cosine similarity for a pair of unit IDs, if available. */
1532
+ getEmbSim(id1, id2) {
1533
+ const i = this.embSimIndex.get(id1);
1534
+ const j = this.embSimIndex.get(id2);
1535
+ if (i === void 0 || j === void 0) return void 0;
1536
+ return this.embSimMatrix[i][j];
1537
+ }
1538
+ /** Returns the memoized parent similarity for the given stable key, if available. */
1539
+ getParentSim(key) {
1540
+ return this.parentSimCache.get(key);
1541
+ }
1542
+ /** Stores a memoized parent similarity for the given stable key. */
1543
+ setParentSim(key, sim) {
1544
+ this.parentSimCache.set(key, sim);
1545
+ }
1546
+ addKeyForFile(filePath, key) {
1547
+ const current = this.fileIndex.get(filePath) ?? /* @__PURE__ */ new Set();
1548
+ current.add(key);
1549
+ this.fileIndex.set(filePath, current);
1550
+ }
1551
+ fileHasKey(filePath, key) {
1552
+ const keys = this.fileIndex.get(filePath);
1553
+ return keys ? keys.has(key) : false;
1554
+ }
1555
+ makeKey(leftId, rightId) {
1556
+ return [leftId, rightId].sort().join("::");
1557
+ }
1558
+ };
1559
+
1560
+ // src/services/DuplicateService.ts
1561
+ var log7 = debug7("DryScan:DuplicateService");
1372
1562
  var DuplicateService = class {
1373
1563
  constructor(deps) {
1374
1564
  this.deps = deps;
1375
1565
  }
1376
1566
  config;
1377
- similarityCache = /* @__PURE__ */ new Map();
1378
- parentSimCache = /* @__PURE__ */ new Map();
1567
+ cache = DuplicationCache.getInstance();
1568
+ simMemo = /* @__PURE__ */ new Map();
1379
1569
  async findDuplicates(config, dirtyPaths = [], previousReport) {
1380
1570
  this.config = config;
1381
- this.similarityCache = /* @__PURE__ */ new Map();
1382
- this.parentSimCache = /* @__PURE__ */ new Map();
1571
+ this.simMemo = /* @__PURE__ */ new Map();
1383
1572
  const t0 = performance.now();
1384
1573
  const allUnits = await this.deps.db.getAllUnits();
1385
- log6("Starting duplicate analysis on %d units", allUnits.length);
1574
+ log7("Starting duplicate analysis on %d units", allUnits.length);
1386
1575
  if (allUnits.length < 2) {
1387
1576
  return { duplicates: [], score: this.computeDuplicationScore([], allUnits) };
1388
1577
  }
1578
+ if (dirtyPaths.length > 0) {
1579
+ await this.cache.invalidate(dirtyPaths);
1580
+ }
1581
+ this.cache.clearRunCaches();
1582
+ await this.cache.buildEmbSimCache(allUnits, dirtyPaths);
1389
1583
  const thresholds = this.resolveThresholds(config.threshold);
1390
1584
  const dirtySet = new Set(dirtyPaths);
1391
1585
  const canReuseFromReport = Boolean(previousReport && previousReport.threshold === config.threshold);
1392
1586
  const reusableClean = canReuseFromReport ? this.reuseCleanPairsFromPreviousReport(previousReport, allUnits, dirtySet) : [];
1393
- const recomputed = this.computeDuplicates(
1394
- allUnits,
1395
- thresholds,
1396
- canReuseFromReport ? dirtySet : null
1397
- );
1587
+ const recomputed = this.computeDuplicates(allUnits, thresholds, canReuseFromReport ? dirtySet : null);
1398
1588
  const merged = this.mergeDuplicates(reusableClean, recomputed);
1399
1589
  const filtered = merged.filter((g) => !this.isGroupExcluded(g));
1400
- log6(
1590
+ log7(
1401
1591
  "Found %d duplicate groups (%d excluded, %d reused)",
1402
1592
  filtered.length,
1403
1593
  merged.length - filtered.length,
1404
1594
  reusableClean.length
1405
1595
  );
1596
+ this.cache.update(filtered).catch((err) => log7("Cache update failed: %O", err));
1406
1597
  const score = this.computeDuplicationScore(filtered, allUnits);
1407
- log6("findDuplicates completed in %dms", (performance.now() - t0).toFixed(2));
1598
+ log7("findDuplicates completed in %dms", (performance.now() - t0).toFixed(2));
1408
1599
  return { duplicates: filtered, score };
1409
1600
  }
1410
1601
  resolveThresholds(functionThreshold) {
@@ -1419,14 +1610,14 @@ var DuplicateService = class {
1419
1610
  }
1420
1611
  computeDuplicates(units, thresholds, dirtySet) {
1421
1612
  if (dirtySet && dirtySet.size === 0) {
1422
- log6("Skipping recomputation: no dirty files and previous report threshold matches");
1613
+ log7("Skipping recomputation: no dirty files and previous report threshold matches");
1423
1614
  return [];
1424
1615
  }
1425
1616
  const duplicates = [];
1426
1617
  const t0 = performance.now();
1427
1618
  for (const [type, typedUnits] of this.groupByType(units)) {
1428
1619
  const threshold = this.getThreshold(type, thresholds);
1429
- log6("Comparing %d %s units (threshold=%.3f)", typedUnits.length, type, threshold);
1620
+ log7("Comparing %d %s units (threshold=%.3f)", typedUnits.length, type, threshold);
1430
1621
  for (let i = 0; i < typedUnits.length; i++) {
1431
1622
  for (let j = i + 1; j < typedUnits.length; j++) {
1432
1623
  const left = typedUnits[i];
@@ -1435,8 +1626,8 @@ var DuplicateService = class {
1435
1626
  if (dirtySet && !dirtySet.has(left.filePath) && !dirtySet.has(right.filePath)) {
1436
1627
  continue;
1437
1628
  }
1438
- const hasEmbeddings = left.embedding?.length && right.embedding?.length;
1439
- const similarity = hasEmbeddings ? this.computeWeightedSimilarity(left, right, threshold) : 0;
1629
+ const cached = this.cache.get(left.id, right.id, left.filePath, right.filePath);
1630
+ const similarity = cached ?? this.computeWeightedSimilarity(left, right, threshold);
1440
1631
  if (similarity < threshold) continue;
1441
1632
  const exclusionString = this.deps.pairing.pairKeyForUnits(left, right);
1442
1633
  if (!exclusionString) continue;
@@ -1451,7 +1642,7 @@ var DuplicateService = class {
1451
1642
  }
1452
1643
  }
1453
1644
  }
1454
- log6("computeDuplicates: %d duplicates in %dms", duplicates.length, (performance.now() - t0).toFixed(2));
1645
+ log7("computeDuplicates: %d duplicates in %dms", duplicates.length, (performance.now() - t0).toFixed(2));
1455
1646
  return duplicates.sort((a, b) => b.similarity - a.similarity);
1456
1647
  }
1457
1648
  reuseCleanPairsFromPreviousReport(report, units, dirtySet) {
@@ -1462,7 +1653,7 @@ var DuplicateService = class {
1462
1653
  if (leftDirty || rightDirty) return false;
1463
1654
  return unitIds.has(group.left.id) && unitIds.has(group.right.id);
1464
1655
  });
1465
- log6("Reused %d clean-clean duplicate groups from previous report", reusable.length);
1656
+ log7("Reused %d clean-clean duplicate groups from previous report", reusable.length);
1466
1657
  return reusable;
1467
1658
  }
1468
1659
  mergeDuplicates(reused, recomputed) {
@@ -1542,23 +1733,19 @@ var DuplicateService = class {
1542
1733
  const rp = this.findParent(right, type);
1543
1734
  if (!lp || !rp) return 0;
1544
1735
  const key = lp.id < rp.id ? `${lp.id}::${rp.id}` : `${rp.id}::${lp.id}`;
1545
- const cached = this.parentSimCache.get(key);
1736
+ const cached = this.cache.getParentSim(key);
1546
1737
  if (cached !== void 0) return cached;
1547
1738
  const sim = this.similarity(lp, rp);
1548
- this.parentSimCache.set(key, sim);
1739
+ this.cache.setParentSim(key, sim);
1549
1740
  return sim;
1550
1741
  }
1551
1742
  similarity(left, right) {
1552
1743
  const key = left.id < right.id ? `${left.id}::${right.id}` : `${right.id}::${left.id}`;
1553
- const cached = this.similarityCache.get(key);
1554
- if (cached !== void 0) return cached;
1555
- let value = 0;
1556
- if (left.embedding?.length && right.embedding?.length) {
1557
- value = cosineSimilarity([left.embedding], [right.embedding])[0][0] ?? 0;
1558
- } else {
1559
- value = this.childSimilarity(left, right);
1560
- }
1561
- this.similarityCache.set(key, value);
1744
+ const memo = this.simMemo.get(key);
1745
+ if (memo !== void 0) return memo;
1746
+ const embSim = this.cache.getEmbSim(left.id, right.id);
1747
+ const value = embSim ?? this.childSimilarity(left, right);
1748
+ this.simMemo.set(key, value);
1562
1749
  return value;
1563
1750
  }
1564
1751
  childSimilarity(left, right) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/DryScan.ts","../src/const.ts","../src/IndexUnitExtractor.ts","../src/extractors/java.ts","../src/config/indexConfig.ts","../src/config/configStore.ts","../src/config/dryconfig.ts","../src/Gitignore.ts","../src/db/DryScanDatabase.ts","../src/db/entities/FileEntity.ts","../src/db/entities/IndexUnitEntity.ts","../src/services/RepositoryInitializer.ts","../src/services/EmbeddingService.ts","../src/services/UpdateService.ts","../src/DryScanUpdater.ts","../src/services/ExclusionService.ts","../src/services/PairingService.ts","../src/services/DuplicateService.ts"],"sourcesContent":["import upath from \"upath\";\nimport fs from \"fs/promises\";\nimport { DuplicateAnalysisResult, DuplicateReport } from \"./types\";\nimport { DRYSCAN_DIR, INDEX_DB, REPORTS_DIR } from \"./const\";\nimport { defaultExtractors, IndexUnitExtractor } from \"./IndexUnitExtractor\";\nimport { DryScanDatabase } from \"./db/DryScanDatabase\";\nimport { RepositoryInitializer, InitOptions as InitServiceOptions } from \"./services/RepositoryInitializer\";\nimport { UpdateService } from \"./services/UpdateService\";\nimport { ExclusionService } from \"./services/ExclusionService\";\nimport { DryScanServiceDeps } from \"./services/types\";\nimport { configStore } from \"./config/configStore\";\nimport { DryConfig } from \"./types\";\nimport { PairingService } from \"./services/PairingService\";\nimport { existsSync } from \"fs\";\nimport { DuplicateService } from \"./services/DuplicateService\";\n\nexport type InitOptions = InitServiceOptions;\n\n\nexport class DryScan {\n repoPath: string;\n private readonly extractor: IndexUnitExtractor;\n private db: DryScanDatabase;\n private readonly services: {\n initializer: RepositoryInitializer;\n updater: UpdateService;\n duplicate: DuplicateService;\n exclusion: ExclusionService;\n };\n private readonly serviceDeps: DryScanServiceDeps;\n\n constructor(\n repoPath: string,\n extractor?: IndexUnitExtractor,\n db?: DryScanDatabase\n ) {\n this.repoPath = repoPath;\n this.extractor = extractor ?? new IndexUnitExtractor(repoPath, defaultExtractors(repoPath));\n this.db = db ?? new DryScanDatabase();\n\n this.serviceDeps = {\n repoPath: this.repoPath,\n db: this.db,\n extractor: this.extractor,\n pairing: new PairingService(this.extractor),\n };\n\n const exclusion = new ExclusionService(this.serviceDeps);\n this.services = {\n initializer: new RepositoryInitializer(this.serviceDeps, exclusion),\n updater: new UpdateService(this.serviceDeps, exclusion),\n duplicate: new DuplicateService(this.serviceDeps),\n exclusion,\n };\n }\n\n /**\n * Initializes the DryScan repository with a 3-phase analysis:\n * Phase 1: Extract and save all functions\n * Phase 2: Resolve and save internal dependencies\n * Phase 3: Compute and save semantic embeddings\n */\n async init(options?: InitOptions): Promise<void> {\n console.log(`[DryScan] Initializing repository at ${this.repoPath}`);\n\n const dryDir = upath.join(this.repoPath, DRYSCAN_DIR);\n if (existsSync(dryDir)) {\n console.warn(`[DryScan] Warning: a '.dry' folder already exists at ${dryDir}.`);\n }\n console.log(\"[DryScan] Preparing database and cache...\");\n await configStore.init(this.repoPath);\n await this.ensureDatabase();\n console.log(\"[DryScan] Starting initial scan (may take a moment)...\");\n await this.services.initializer.init(options);\n console.log(\"[DryScan] Initial scan complete.\");\n }\n\n /**\n * Updates the index by detecting changed, new, and deleted files.\n * Only reprocesses units in changed files for efficiency.\n * Delegates to DryScanUpdater module for implementation.\n * \n * Update process:\n * 1. List all current source files in repository\n * 2. For each file, check if it's new, changed, or unchanged (via mtime + checksum)\n * 3. Remove old units from changed/deleted files\n * 4. Extract and save units from new/changed files\n * 5. Recompute internal dependencies for affected units\n * 6. Recompute embeddings for affected units\n * 7. Update file tracking metadata\n */\n async updateIndex(): Promise<string[]> {\n console.log(`[DryScan] Updating index at ${this.repoPath}...`);\n console.log(\"[DryScan] Checking for file changes...\");\n const start = Date.now();\n await this.ensureDatabase();\n const dirtyPaths = await this.services.updater.updateIndex();\n const duration = Date.now() - start;\n console.log(`[DryScan] Index update complete. Took ${duration}ms.`);\n return dirtyPaths;\n }\n\n\n /**\n * Runs duplicate detection and returns a normalized report payload ready for persistence or display.\n */\n async buildDuplicateReport(): Promise<DuplicateReport> {\n const config = await this.loadConfig();\n const analysis = await this.findDuplicates(config);\n const report = {\n version: 1,\n generatedAt: new Date().toISOString(),\n threshold: config.threshold,\n grade: analysis.score.grade,\n score: analysis.score,\n duplicates: analysis.duplicates,\n };\n await this.saveReport(report);\n return report;\n }\n\n /**\n * Finds duplicate code blocks using cosine similarity on embeddings.\n * Automatically updates the index before searching to ensure results are current.\n * Compares all function pairs and returns groups with similarity above the configured threshold.\n *\n * @returns Analysis result with duplicate groups and duplication score\n */\n private async findDuplicates(config: DryConfig): Promise<DuplicateAnalysisResult> {\n console.log(`[DryScan] Finding duplicates (threshold: ${config.threshold})...`);\n await this.ensureDatabase();\n\n console.log(\"[DryScan] Updating index...\");\n const updateStart = Date.now();\n const dirtyPaths = await this.updateIndex();\n const updateDuration = Date.now() - updateStart;\n console.log(`[DryScan] Index update took ${updateDuration}ms.`);\n\n const previousReport = await this.loadLatestReport();\n if (previousReport?.threshold === config.threshold) {\n console.log(\"[DryScan] Reusing clean-clean duplicates from latest report (threshold unchanged).\");\n }\n\n console.log(\"[DryScan] Detecting duplicates...\");\n const dupStart = Date.now();\n const result = await this.services.duplicate.findDuplicates(config, dirtyPaths, previousReport);\n const dupDuration = Date.now() - dupStart;\n console.log(`[DryScan] Duplicate detection took ${dupDuration}ms.`);\n\n return result;\n }\n\n /**\n * Cleans excludedPairs entries that no longer match any indexed units.\n * Runs an update first to ensure the index reflects current code.\n */\n async cleanExclusions(): Promise<{ removed: number; kept: number }> {\n await this.updateIndex();\n return this.services.exclusion.cleanExclusions();\n }\n\n private async ensureDatabase(): Promise<void> {\n if (this.db.isInitialized()) return;\n const dbPath = upath.join(this.repoPath, DRYSCAN_DIR, INDEX_DB);\n await fs.mkdir(upath.dirname(dbPath), { recursive: true });\n await this.db.init(dbPath);\n }\n\n private async loadConfig(): Promise<DryConfig> {\n return configStore.get(this.repoPath);\n }\n\n private async saveReport(report: DuplicateReport): Promise<void> {\n const reportDir = upath.join(this.repoPath, DRYSCAN_DIR, REPORTS_DIR);\n await fs.mkdir(reportDir, { recursive: true });\n const safeTimestamp = report.generatedAt.replace(/[:.]/g, \"-\");\n const reportPath = upath.join(reportDir, `dupes-${safeTimestamp}.json`);\n await fs.writeFile(reportPath, JSON.stringify(report, null, 2), \"utf8\");\n }\n\n private async loadLatestReport(): Promise<DuplicateReport | null> {\n const reportDir = upath.join(this.repoPath, DRYSCAN_DIR, REPORTS_DIR);\n let entries: string[];\n try {\n entries = await fs.readdir(reportDir);\n } catch (err: any) {\n if (err?.code === \"ENOENT\") return null;\n throw err;\n }\n\n const jsonReports = entries.filter((name) => name.endsWith(\".json\"));\n if (jsonReports.length === 0) return null;\n\n const withStats = await Promise.all(\n jsonReports.map(async (name) => {\n const fullPath = upath.join(reportDir, name);\n const stat = await fs.stat(fullPath);\n return { fullPath, mtimeMs: stat.mtimeMs };\n })\n );\n\n withStats.sort((a, b) => b.mtimeMs - a.mtimeMs);\n const latest = withStats[0];\n const raw = await fs.readFile(latest.fullPath, \"utf8\");\n const parsed = JSON.parse(raw) as DuplicateReport;\n if (!parsed || !Array.isArray(parsed.duplicates) || typeof parsed.threshold !== \"number\") {\n return null;\n }\n return parsed;\n }\n\n}\n","export const DRYSCAN_DIR = \".dry\";\nexport const INDEX_DB = \"index.db\";\nexport const REPORTS_DIR = \"reports\";\nexport const FILE_CHECKSUM_ALGO = \"md5\";\nexport const BLOCK_HASH_ALGO = \"sha1\";","import path from \"path\";\nimport type { Stats } from \"fs\";\nimport fs from \"fs/promises\";\nimport upath from \"upath\";\nimport crypto from \"node:crypto\";\nimport debug from \"debug\";\nimport { glob } from \"glob-gitignore\";\nimport { IndexUnit } from \"./types\";\nimport { LanguageExtractor } from \"./extractors/LanguageExtractor\";\nimport { JavaExtractor } from \"./extractors/java\";\nimport { FILE_CHECKSUM_ALGO } from \"./const\";\nimport { configStore } from \"./config/configStore\";\nimport { DryConfig } from \"./types\";\nimport { Gitignore } from \"./Gitignore\"\nimport { Ignore } from \"ignore\";\n\nconst log = debug(\"DryScan:Extractor\");\n\nexport type { LanguageExtractor } from \"./extractors/LanguageExtractor\";\n/**\n * Returns the default set of language extractors supported by DryScan.\n * Extend/override by passing custom extractors into the IndexUnitExtractor constructor.\n */\nexport function defaultExtractors(repoPath: string): LanguageExtractor[] {\n return [new JavaExtractor(repoPath)];\n}\n\n/**\n * Extracts and indexes code units (classes, functions, blocks) for a repository.\n * Owns shared file-system helpers and delegates language-specific parsing to LanguageExtractors.\n */\nexport class IndexUnitExtractor {\n private readonly root: string;\n readonly extractors: LanguageExtractor[];\n private readonly gitignore: Gitignore;\n\n constructor(\n rootPath: string,\n extractors?: LanguageExtractor[]\n ) {\n this.root = rootPath;\n this.extractors = extractors ?? defaultExtractors(rootPath);\n this.gitignore = new Gitignore(this.root);\n log(\"Initialized extractor for %s\", this.root);\n }\n\n /**\n * Lists all supported source files from a path. Honors exclusion globs from config.\n */\n async listSourceFiles(dirPath: string): Promise<string[]> {\n const target = await this.resolveTarget(dirPath);\n const config = await this.loadConfig();\n const ignoreMatcher = await this.gitignore.buildMatcher(config);\n\n if (target.stat.isFile()) {\n return this.filterSingleFile(target.baseRel, ignoreMatcher);\n }\n\n const matches = await this.globSourceFiles(target.baseRel);\n return this.filterSupportedFiles(matches, ignoreMatcher);\n }\n\n /**\n * Computes MD5 checksum of file content to track changes.\n */\n async computeChecksum(filePath: string): Promise<string> {\n const fullPath = path.isAbsolute(filePath)\n ? filePath\n : path.join(this.root, filePath);\n\n const content = await fs.readFile(fullPath, \"utf8\");\n return crypto.createHash(FILE_CHECKSUM_ALGO).update(content).digest(\"hex\");\n }\n\n /**\n * Scans a file or directory and extracts indexable units using the matching LanguageExtractor.\n * The returned units have repo-relative file paths and no embedding attached.\n */\n async scan(targetPath: string): Promise<IndexUnit[]> {\n const fullPath = path.isAbsolute(targetPath)\n ? targetPath\n : path.join(this.root, targetPath);\n\n const stat = await fs.stat(fullPath).catch(() => null);\n if (!stat) {\n throw new Error(`Path not found: ${fullPath}`);\n }\n\n if (stat.isDirectory()) {\n log(\"Scanning directory %s\", fullPath);\n return this.scanDirectory(fullPath);\n }\n\n return this.scanFile(fullPath);\n }\n\n\n /**\n * Scans a directory recursively, extracting units from supported files while honoring exclusions.\n */\n private async scanDirectory(dir: string): Promise<IndexUnit[]> {\n const out: IndexUnit[] = [];\n const relDir = this.relPath(dir);\n const files = await this.listSourceFiles(relDir);\n for (const relFile of files) {\n const absFile = path.join(this.root, relFile);\n const extracted = await this.tryScanSupportedFile(absFile);\n out.push(...extracted);\n }\n return out;\n }\n\n /**\n * Scans a single file and extracts supported units.\n */\n private async scanFile(filePath: string): Promise<IndexUnit[]> {\n return this.tryScanSupportedFile(filePath, true);\n }\n\n /**\n * Extracts units from a supported file.\n * Optionally throws when the file type is unsupported (used when scanning an explicit file).\n */\n private async tryScanSupportedFile(filePath: string, throwOnUnsupported = false): Promise<IndexUnit[]> {\n const extractor = this.extractors.find(ex => ex.supports(filePath));\n if (!extractor) {\n if (throwOnUnsupported) {\n throw new Error(`Unsupported file type: ${filePath}`);\n }\n return [];\n }\n const rel = this.relPath(filePath);\n if (await this.shouldExclude(rel)) {\n log(\"Skipping excluded file %s\", rel);\n return [];\n }\n const source = await fs.readFile(filePath, \"utf8\");\n const units = await extractor.extractFromText(rel, source);\n log(\"Extracted %d units from %s\", units.length, rel);\n return units.map(unit => ({\n ...unit,\n filePath: rel,\n embedding: undefined,\n }));\n }\n\n /**\n * Converts an absolute path to a repo-relative, normalized (POSIX-style) path.\n * This keeps paths stable across platforms and consistent in the index/DB.\n */\n private relPath(absPath: string): string {\n return this.normalizeRelPath(upath.relative(this.root, absPath));\n }\n\n /**\n * Returns true if a repo-relative path matches any configured exclusion glob.\n */\n private async shouldExclude(relPath: string): Promise<boolean> {\n const config = await this.loadConfig();\n const ignoreMatcher = await this.gitignore.buildMatcher(config);\n return ignoreMatcher.ignores(this.normalizeRelPath(relPath));\n }\n\n private async loadConfig(): Promise<DryConfig> {\n return await configStore.get(this.root);\n }\n\n /**\n * Normalizes repo-relative paths and strips leading \"./\" to keep matcher inputs consistent.\n */\n private normalizeRelPath(relPath: string): string {\n const normalized = upath.normalizeTrim(relPath);\n return normalized.startsWith(\"./\") ? normalized.slice(2) : normalized;\n }\n\n private async resolveTarget(dirPath: string): Promise<{ fullPath: string; baseRel: string; stat: Stats; }> {\n const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(this.root, dirPath);\n const stat = await fs.stat(fullPath).catch(() => null);\n if (!stat) {\n throw new Error(`Path not found: ${fullPath}`);\n }\n const baseRel = this.relPath(fullPath);\n log(\"Listing source files under %s\", fullPath);\n return { fullPath, baseRel, stat };\n }\n\n private async filterSingleFile(baseRel: string, ignoreMatcher: Ignore): Promise<string[]> {\n const relFile = this.normalizeRelPath(baseRel);\n if (ignoreMatcher.ignores(relFile)) return [];\n return this.extractors.some((ex) => ex.supports(relFile)) ? [relFile] : [];\n }\n\n private async globSourceFiles(baseRel: string): Promise<string[]> {\n const pattern = baseRel ? `${baseRel.replace(/\\\\/g, \"/\")}/**/*` : \"**/*\";\n const matches = await glob(pattern, {\n cwd: this.root,\n dot: false,\n nodir: true,\n });\n return matches.map((p: string) => this.normalizeRelPath(p));\n }\n\n private filterSupportedFiles(relPaths: string[], ignoreMatcher: Ignore): string[] {\n return relPaths\n .filter((relPath: string) => !ignoreMatcher.ignores(relPath))\n .filter((relPath: string) => this.extractors.some((ex) => ex.supports(relPath)));\n }\n}\n","import crypto from \"node:crypto\";\nimport Parser from \"tree-sitter\";\nimport Java from \"tree-sitter-java\";\nimport { LanguageExtractor } from \"./LanguageExtractor\";\nimport { IndexUnit, IndexUnitType } from \"../types\";\nimport { indexConfig } from \"../config/indexConfig\";\nimport { DryConfig } from \"../types\";\nimport { configStore } from \"../config/configStore\";\nimport { BLOCK_HASH_ALGO } from \"../const\";\n\nexport class JavaExtractor implements LanguageExtractor {\n readonly id = \"java\";\n readonly exts = [\".java\"];\n\n private parser: Parser;\n private readonly repoPath: string;\n private config?: DryConfig;\n\n constructor(repoPath: string) {\n this.repoPath = repoPath;\n this.parser = new Parser();\n this.parser.setLanguage(Java);\n }\n\n supports(filePath: string): boolean {\n const lower = filePath.toLowerCase();\n return this.exts.some((ext) => lower.endsWith(ext));\n }\n\n async extractFromText(fileRelPath: string, source: string): Promise<IndexUnit[]> {\n if (!source.trim()) return [];\n\n this.config = await configStore.get(this.repoPath);\n\n const tree = this.parser.parse(source);\n const units: IndexUnit[] = [];\n\n const visit = (node: Parser.SyntaxNode, currentClass?: IndexUnit) => {\n if (this.isClassNode(node)) {\n const className = this.getClassName(node, source) || \"<anonymous>\";\n if (this.isDtoClass(node, source, className)) {\n return;\n }\n const startLine = node.startPosition.row;\n const endLine = node.endPosition.row;\n const classLength = endLine - startLine;\n const skipClass = this.shouldSkip(IndexUnitType.CLASS, className, classLength);\n const classId = this.buildId(IndexUnitType.CLASS, className, startLine, endLine);\n const code = this.stripComments(this.stripClassBody(node, source));\n const classUnit: IndexUnit = {\n id: classId,\n name: className,\n filePath: fileRelPath,\n startLine,\n endLine,\n code,\n unitType: IndexUnitType.CLASS,\n children: [],\n };\n if (!skipClass) {\n units.push(classUnit);\n }\n\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (child) visit(child, skipClass ? undefined : classUnit);\n }\n return;\n }\n\n if (this.isFunctionNode(node)) {\n const fnUnit = this.buildFunctionUnit(node, source, fileRelPath, currentClass);\n const fnLength = fnUnit.endLine - fnUnit.startLine;\n const bodyNode = this.getFunctionBody(node);\n const fnArity = this.getNodeArity(node);\n const skipFunction = this.shouldSkip(IndexUnitType.FUNCTION, fnUnit.name, fnLength, fnArity);\n\n if (skipFunction) {\n return;\n }\n\n units.push(fnUnit);\n\n if (bodyNode) {\n const blocks = this.extractBlocks(bodyNode, source, fileRelPath, fnUnit);\n units.push(...blocks);\n }\n }\n\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (child) visit(child, currentClass);\n }\n };\n\n visit(tree.rootNode);\n\n //remove duplicates if any\n return this.removeDuplicates(units);\n }\n\n unitLabel(unit: IndexUnit): string | null {\n if (unit.unitType === IndexUnitType.CLASS) return unit.filePath;\n if (unit.unitType === IndexUnitType.FUNCTION) return this.canonicalFunctionSignature(unit);\n if (unit.unitType === IndexUnitType.BLOCK) return this.normalizedBlockHash(unit);\n return unit.name;\n }\n\n private isClassNode(node: Parser.SyntaxNode): boolean {\n return node.type === \"class_declaration\";\n }\n\n private getClassName(node: Parser.SyntaxNode, source: string): string | null {\n const nameNode = node.childForFieldName?.(\"name\");\n return nameNode ? source.slice(nameNode.startIndex, nameNode.endIndex) : null;\n }\n\n private isFunctionNode(node: Parser.SyntaxNode): boolean {\n return node.type === \"method_declaration\" || node.type === \"constructor_declaration\";\n }\n\n private getFunctionName(node: Parser.SyntaxNode, source: string, parentClass?: IndexUnit): string | null {\n const nameNode = node.childForFieldName?.(\"name\");\n const nameText = nameNode ? source.slice(nameNode.startIndex, nameNode.endIndex) : \"<anonymous>\";\n return parentClass ? `${parentClass.name}.${nameText}` : nameText;\n }\n\n private getFunctionBody(node: Parser.SyntaxNode): Parser.SyntaxNode | null {\n return node.childForFieldName?.(\"body\") ?? null;\n }\n\n private isBlockNode(node: Parser.SyntaxNode): boolean {\n return node.type === \"block\";\n }\n\n private getMethodBodiesForClass(node: Parser.SyntaxNode): Parser.SyntaxNode[] {\n const bodies: Parser.SyntaxNode[] = [];\n const classBody = node.children.find(child => child.type === \"class_body\");\n if (!classBody) return bodies;\n \n for (let i = 0; i < classBody.namedChildCount; i++) {\n const child = classBody.namedChild(i);\n if (!child) continue;\n if (child.type === \"method_declaration\" || child.type === \"constructor_declaration\") {\n const body = child.childForFieldName?.(\"body\");\n if (body) bodies.push(body);\n }\n }\n return bodies;\n }\n\n private canonicalFunctionSignature(unit: IndexUnit): string {\n const arity = this.extractArity(unit.code);\n return `${unit.name}(arity:${arity})`;\n }\n\n private normalizedBlockHash(unit: IndexUnit): string {\n const normalized = this.normalizeCode(unit.code);\n return crypto.createHash(BLOCK_HASH_ALGO).update(normalized).digest(\"hex\");\n }\n\n private shouldSkip(unitType: IndexUnitType, name: string, lineCount: number, arity?: number): boolean {\n if (!this.config) {\n throw new Error(\"Config not loaded before skip evaluation\");\n }\n const config = this.config;\n const minLines = unitType === IndexUnitType.BLOCK\n ? Math.max(indexConfig.blockMinLines, config.minBlockLines ?? 0)\n : config.minLines;\n const belowMin = minLines > 0 && lineCount < minLines;\n const trivial = unitType === IndexUnitType.FUNCTION && this.isTrivialFunction(name, arity ?? 0);\n return belowMin || trivial;\n }\n\n /**\n * A function is trivial if it follows a simple accessor pattern:\n * - getters/isers: name matches get[A-Z] or is[A-Z] with exactly 0 parameters\n * - setters: name matches set[A-Z] with at most 1 parameter\n * Methods like getUserById(Long id) have arity > 0 and are NOT trivial.\n */\n private isTrivialFunction(fullName: string, arity: number): boolean {\n const simpleName = fullName.split(\".\").pop() || fullName;\n const isGetter = /^(get|is)[A-Z]/.test(simpleName) && arity === 0;\n const isSetter = /^set[A-Z]/.test(simpleName) && arity <= 1;\n return isGetter || isSetter;\n }\n\n /** Counts the formal parameters of a method or constructor node. */\n private getNodeArity(node: Parser.SyntaxNode): number {\n const params = node.childForFieldName?.(\"parameters\");\n if (!params) return 0;\n return params.namedChildren.filter(c => c.type === \"formal_parameter\" || c.type === \"spread_parameter\").length;\n }\n\n private isDtoClass(node: Parser.SyntaxNode, source: string, className: string): boolean {\n const classBody = node.children.find((child) => child.type === \"class_body\");\n if (!classBody) return false;\n\n let hasField = false;\n\n for (let i = 0; i < classBody.namedChildCount; i++) {\n const child = classBody.namedChild(i);\n if (!child) continue;\n\n if (child.type === \"field_declaration\") {\n hasField = true;\n continue;\n }\n\n if (child.type.includes(\"annotation\")) {\n continue;\n }\n\n if (child.type === \"method_declaration\" || child.type === \"constructor_declaration\") {\n const simpleName = this.getSimpleFunctionName(child, source);\n const fullName = `${className}.${simpleName}`;\n const arity = this.getNodeArity(child);\n if (!this.isTrivialFunction(fullName, arity)) {\n return false;\n }\n continue;\n }\n\n return false;\n }\n\n return hasField;\n }\n\n private getSimpleFunctionName(node: Parser.SyntaxNode, source: string): string {\n const nameNode = node.childForFieldName?.(\"name\");\n return nameNode ? source.slice(nameNode.startIndex, nameNode.endIndex) : \"<anonymous>\";\n }\n\n private buildFunctionUnit(\n node: Parser.SyntaxNode,\n source: string,\n file: string,\n parentClass?: IndexUnit\n ): IndexUnit {\n const name = this.getFunctionName(node, source, parentClass) || \"<anonymous>\";\n const startLine = node.startPosition.row;\n const endLine = node.endPosition.row;\n const id = this.buildId(IndexUnitType.FUNCTION, name, startLine, endLine);\n const unit: IndexUnit = {\n id,\n name,\n filePath: file,\n startLine,\n endLine,\n children: [],\n code: this.stripComments(source.slice(node.startIndex, node.endIndex)),\n unitType: IndexUnitType.FUNCTION,\n parentId: parentClass?.id,\n parent: parentClass,\n };\n if (parentClass) {\n parentClass.children = parentClass.children || [];\n parentClass.children.push(unit);\n }\n return unit;\n }\n\n private extractBlocks(\n bodyNode: Parser.SyntaxNode,\n source: string,\n file: string,\n parentFunction: IndexUnit\n ): IndexUnit[] {\n const blocks: IndexUnit[] = [];\n\n const visit = (n: Parser.SyntaxNode) => {\n if (this.isBlockNode(n)) {\n const startLine = n.startPosition.row;\n const endLine = n.endPosition.row;\n const lineCount = endLine - startLine;\n if (this.shouldSkip(IndexUnitType.BLOCK, parentFunction.name, lineCount)) {\n return;\n }\n if (lineCount >= indexConfig.blockMinLines) {\n const id = this.buildId(IndexUnitType.BLOCK, parentFunction.name, startLine, endLine);\n const blockUnit: IndexUnit = {\n id,\n name: parentFunction.name,\n filePath: file,\n startLine,\n endLine,\n code: this.stripComments(source.slice(n.startIndex, n.endIndex)),\n unitType: IndexUnitType.BLOCK,\n parentId: parentFunction.id,\n parent: parentFunction,\n };\n const contextLength = this.config?.contextLength ?? 2048;\n const splitBlocks = this.textSplitBlockIfOverContextLimit(blockUnit, contextLength);\n parentFunction.children = parentFunction.children || [];\n parentFunction.children.push(...splitBlocks);\n blocks.push(...splitBlocks);\n }\n }\n\n for (let i = 0; i < n.namedChildCount; i++) {\n const child = n.namedChild(i);\n if (child) visit(child);\n }\n };\n\n visit(bodyNode);\n return blocks;\n }\n\n private stripClassBody(node: Parser.SyntaxNode, source: string): string {\n const classStart = node.startIndex;\n let code = source.slice(classStart, node.endIndex);\n\n const methodBodies: Array<{ start: number; end: number }> = [];\n const candidates = this.getMethodBodiesForClass(node);\n\n for (const body of candidates) {\n methodBodies.push({ start: body.startIndex - classStart, end: body.endIndex - classStart });\n }\n\n methodBodies.sort((a, b) => b.start - a.start);\n for (const body of methodBodies) {\n code = code.slice(0, body.start) + \" { }\" + code.slice(body.end);\n }\n\n return code;\n }\n\n private buildId(type: IndexUnitType, name: string, startLine: number, endLine: number): string {\n return `${type}:${name}:${startLine}-${endLine}`;\n }\n\n private extractArity(code: string): number {\n const match = code.match(/^[^{]*?\\(([^)]*)\\)/s);\n if (!match) return 0;\n const params = match[1]\n .split(\",\")\n .map((p) => p.trim())\n .filter(Boolean);\n return params.length;\n }\n\n private normalizeCode(code: string): string {\n const withoutBlockComments = code.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n const withoutLineComments = withoutBlockComments.replace(/\\/\\/[^\\n\\r]*/g, \"\");\n return withoutLineComments.replace(/\\s+/g, \"\");\n }\n\n private stripComments(code: string): string {\n const withoutBlockComments = code.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (match) => match.replace(/[^\\n\\r]/g, \"\"));\n return withoutBlockComments.replace(/\\/\\/[^\\n\\r]*/g, \"\");\n }\n\n private removeDuplicates(units: IndexUnit[]): IndexUnit[] | PromiseLike<IndexUnit[]> {\n return Array.from(new Map(units.map(u => [u.id, u])).values());\n }\n\n /** Splits a block unit's code into chunks if it exceeds the context length limit. */\n private textSplitBlockIfOverContextLimit(unit: IndexUnit, contextLength: number): IndexUnit[] {\n if (unit.code.length <= contextLength) return [unit];\n\n const chunks: IndexUnit[] = [];\n let chunkIndex = 0;\n for (let i = 0; i < unit.code.length; i += contextLength) {\n chunks.push({\n ...unit,\n id: `${unit.id}:chunk${chunkIndex}`,\n code: unit.code.slice(i, i + contextLength),\n });\n chunkIndex++;\n }\n return chunks;\n }\n}\n\n","export const indexConfig = {\n blockMinLines: 5,\n thresholds: {\n class: 0.88,\n function: 0.88,\n block: 0.88,\n },\n weights: {\n class: { self: 1 },\n function: { self: 0.8, parentClass: 0.2 },\n block: { self: 0.7, parentFunction: 0.2, parentClass: 0.1 },\n },\n};\n","import upath from \"upath\";\nimport { DryConfig } from \"../types\";\nimport { ensureDefaultConfig, resolveDryConfig, saveDryConfig } from \"./dryconfig\";\n\nclass ConfigStore {\n private readonly cache = new Map<string, DryConfig>();\n private readonly loading = new Map<string, Promise<DryConfig>>();\n\n async init(repoPath: string): Promise<DryConfig> {\n const key = this.normalize(repoPath);\n return this.load(key, repoPath);\n }\n\n async get(repoPath: string): Promise<DryConfig> {\n const key = this.normalize(repoPath);\n const cached = this.cache.get(key);\n if (cached) return cached;\n return this.load(key, repoPath);\n }\n\n async refresh(repoPath: string): Promise<DryConfig> {\n const key = this.normalize(repoPath);\n this.cache.delete(key);\n return this.load(key, repoPath);\n }\n\n async save(repoPath: string, config: DryConfig): Promise<void> {\n const key = this.normalize(repoPath);\n await saveDryConfig(repoPath, config);\n this.cache.set(key, config);\n }\n\n private async load(key: string, repoPath: string): Promise<DryConfig> {\n const existing = this.loading.get(key);\n if (existing) return existing;\n\n const promise = ensureDefaultConfig(repoPath).then(() => resolveDryConfig(repoPath)).then((config) => {\n this.cache.set(key, config);\n this.loading.delete(key);\n return config;\n }).catch((err) => {\n this.loading.delete(key);\n throw err;\n });\n\n this.loading.set(key, promise);\n return promise;\n }\n\n private normalize(repoPath: string): string {\n return upath.normalizeTrim(upath.resolve(repoPath));\n }\n}\n\nexport const configStore = new ConfigStore();\n","import fs from \"fs/promises\";\nimport upath from \"upath\";\nimport { Validator, Schema } from \"jsonschema\";\nimport { DryConfig } from \"../types\";\n\n// Baseline config used when no file is present; exported so tests and constructors can seed defaults.\nexport const DEFAULT_CONFIG: DryConfig = {\n excludedPaths: [\n \"**/test/**\",\n ],\n excludedPairs: [],\n minLines: 3,\n minBlockLines: 5,\n threshold: 0.8,\n embeddingSource: \"http://localhost:11434\",\n contextLength: 2048,\n};\n\nconst validator = new Validator();\n\nconst partialConfigSchema: Schema = {\n type: \"object\",\n properties: {\n excludedPaths: { type: \"array\", items: { type: \"string\" } },\n excludedPairs: { type: \"array\", items: { type: \"string\" } },\n minLines: { type: \"number\" },\n minBlockLines: { type: \"number\" },\n threshold: { type: \"number\" },\n embeddingSource: { type: \"string\" },\n contextLength: { type: \"number\" },\n },\n};\n\nconst fullConfigSchema: Schema = {\n ...partialConfigSchema,\n required: [\n \"excludedPaths\",\n \"excludedPairs\",\n \"minLines\",\n \"minBlockLines\",\n \"threshold\",\n \"embeddingSource\",\n \"contextLength\",\n ],\n};\n\nfunction validateConfig(raw: unknown, schema: Schema, source: string): any {\n const result = validator.validate(raw, schema);\n if (!result.valid) {\n const details = result.errors.map((e) => e.stack).join(\"; \");\n throw new Error(`${source} config is invalid: ${details}`);\n }\n return raw;\n}\n\nasync function readConfigFile(repoPath: string): Promise<Partial<DryConfig>> {\n const configPath = upath.join(repoPath, \"dryconfig.json\");\n try {\n const content = await fs.readFile(configPath, \"utf8\");\n let parsed: Partial<DryConfig> = {};\n try {\n parsed = JSON.parse(content) as Partial<DryConfig>;\n } catch (parseErr) {\n throw new Error(`Invalid JSON in ${configPath}: ${(parseErr as Error).message}`);\n }\n return parsed;\n } catch (err: any) {\n if (err?.code === \"ENOENT\") {\n return {};\n }\n throw err;\n }\n}\n\n/**\n * Resolves the effective config for a repo using defaults merged with any file config.\n */\nexport async function resolveDryConfig(repoPath: string): Promise<DryConfig> {\n const fileConfigRaw = await readConfigFile(repoPath);\n validateConfig(fileConfigRaw, partialConfigSchema, \"Config file\");\n\n const merged = { ...DEFAULT_CONFIG, ...fileConfigRaw };\n validateConfig(merged, fullConfigSchema, \"Merged\");\n return merged as DryConfig;\n}\n\n// Backwards-compatible helper used by existing callers (file + defaults).\nexport async function loadDryConfig(repoPath: string): Promise<DryConfig> {\n return resolveDryConfig(repoPath);\n}\n\nexport async function saveDryConfig(repoPath: string, config: DryConfig): Promise<void> {\n const configPath = upath.join(repoPath, \"dryconfig.json\");\n validateConfig(config, fullConfigSchema, \"Config to save\");\n await fs.writeFile(configPath, JSON.stringify(config, null, 2), \"utf8\");\n}\n\nexport async function ensureDefaultConfig(repoPath: string): Promise<void> {\n const configPath = upath.join(repoPath, \"dryconfig.json\");\n const repoExists = await fs.stat(repoPath).then((s) => s.isDirectory()).catch((err: any) => {\n if (err?.code === \"ENOENT\") return false;\n throw err;\n });\n\n if (!repoExists) return;\n\n const exists = await fs.stat(configPath).then(() => true).catch((err: any) => {\n if (err?.code === \"ENOENT\") return false;\n throw err;\n });\n\n if (!exists) {\n await saveDryConfig(repoPath, DEFAULT_CONFIG);\n }\n}\n","import path from \"path\";\nimport fs from \"fs/promises\";\nimport upath from \"upath\";\nimport { glob } from \"glob-gitignore\";\nimport ignore, { Ignore } from \"ignore\";\nimport { DryConfig } from \"./types\";\n\n/**\n * Gitignore helper that builds ignore matchers by combining default rules,\n * repo .gitignore files, and config-driven exclusions.\n */\nexport class Gitignore {\n private readonly defaultIgnores = [\".git/**\", \".dry/**\"];\n\n constructor(private readonly root: string) {}\n\n async buildMatcher(config: DryConfig): Promise<Ignore> {\n const rules = await this.resolveRules(config);\n return ignore({ allowRelativePaths: true }).add(rules);\n }\n\n private async resolveRules(config: DryConfig): Promise<string[]> {\n const gitignoreRules = await this.loadGitignoreRules();\n const configRules = config.excludedPaths || [];\n return [...this.defaultIgnores, ...gitignoreRules, ...configRules];\n }\n\n private async loadGitignoreRules(): Promise<string[]> {\n const gitignoreFiles = await glob(\"**/.gitignore\", {\n cwd: this.root,\n dot: true,\n nodir: true,\n ignore: this.defaultIgnores,\n });\n\n const rules: string[] = [];\n\n for (const file of gitignoreFiles) {\n const absPath = path.join(this.root, file);\n const dir = upath.normalizeTrim(upath.dirname(file));\n const content = await fs.readFile(absPath, \"utf8\").catch(() => \"\");\n const lines = content.split(/\\r?\\n/);\n\n for (const raw of lines) {\n const trimmed = raw.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const negated = trimmed.startsWith(\"!\");\n const body = negated ? trimmed.slice(1) : trimmed;\n\n const scoped = this.scopeRule(body, dir);\n if (!scoped) continue;\n\n rules.push(negated ? `!${scoped}` : scoped);\n }\n }\n\n return rules;\n }\n\n private scopeRule(rule: string, gitignoreDir: string): string | null {\n const cleaned = rule.replace(/^\\//, \"\");\n if (!cleaned) return null;\n\n if (!gitignoreDir || gitignoreDir === \".\") {\n return cleaned;\n }\n\n return upath.normalizeTrim(upath.join(gitignoreDir, cleaned));\n }\n}\n","import \"reflect-metadata\";\nimport fs from \"fs/promises\";\nimport upath from \"upath\";\nimport { DataSource, Repository, In } from \"typeorm\";\nimport { FileEntity } from \"./entities/FileEntity\";\nimport { IndexUnit } from \"../types\";\nimport { IndexUnitEntity } from \"./entities/IndexUnitEntity\";\n\nexport class DryScanDatabase {\n private dataSource?: DataSource;\n private unitRepository?: Repository<IndexUnitEntity>;\n private fileRepository?: Repository<FileEntity>;\n\n isInitialized(): boolean {\n return !!this.dataSource?.isInitialized;\n }\n\n async init(dbPath: string): Promise<void> {\n await fs.mkdir(upath.dirname(dbPath), { recursive: true });\n\n this.dataSource = new DataSource({\n type: \"sqlite\",\n database: dbPath,\n entities: [IndexUnitEntity, FileEntity],\n synchronize: true,\n logging: false,\n });\n\n await this.dataSource.initialize();\n this.unitRepository = this.dataSource.getRepository(IndexUnitEntity);\n this.fileRepository = this.dataSource.getRepository(FileEntity);\n }\n\n async saveUnit(unit: IndexUnit): Promise<void> {\n await this.saveUnits(unit);\n }\n\n async saveUnits(units: IndexUnit | IndexUnit[]): Promise<void> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n const payload = Array.isArray(units) ? units : [units];\n const BATCH_SIZE = 500;\n for (let i = 0; i < payload.length; i += BATCH_SIZE) {\n console.debug(`[DryScanDatabase] Saving units ${i + 1}-${Math.min(i + BATCH_SIZE, payload.length)} of ${payload.length}...`);\n const batch = payload.slice(i, i + BATCH_SIZE);\n await this.unitRepository.save(batch);\n }\n }\n\n async getUnit(id: string): Promise<IndexUnit | null> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n return this.unitRepository.findOne({\n where: { id },\n relations: [\"children\", \"parent\"]\n });\n }\n\n async getAllUnits(): Promise<IndexUnit[]> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n return this.unitRepository.find({ relations: [\"children\", \"parent\"] });\n }\n\n async updateUnit(unit: IndexUnit): Promise<void> {\n await this.saveUnits(unit);\n }\n\n async updateUnits(units: IndexUnit | IndexUnit[]): Promise<void> {\n await this.saveUnits(units);\n }\n\n /**\n * Returns total count of indexed units.\n */\n async countUnits(): Promise<number> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n return this.unitRepository.count();\n }\n\n /**\n * Removes index units by their file paths.\n * Used during incremental updates when files change.\n */\n async removeUnitsByFilePaths(filePaths: string[]): Promise<void> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n await this.unitRepository.delete({ filePath: In(filePaths) });\n }\n\n /**\n * Saves file metadata (path, checksum, mtime) to track changes.\n */\n async saveFile(file: FileEntity): Promise<void> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n await this.fileRepository.save(file);\n }\n\n /**\n * Saves multiple file metadata entries.\n */\n async saveFiles(files: FileEntity[]): Promise<void> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n await this.fileRepository.save(files);\n }\n\n /**\n * Gets file metadata by file path.\n */\n async getFile(filePath: string): Promise<FileEntity | null> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n return this.fileRepository.findOne({ where: { filePath } });\n }\n\n /**\n * Gets all tracked files.\n */\n async getAllFiles(): Promise<FileEntity[]> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n return this.fileRepository.find();\n }\n\n /**\n * Removes file metadata entries by file paths.\n * Used when files are deleted from repository.\n */\n async removeFilesByFilePaths(filePaths: string[]): Promise<void> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n await this.fileRepository.delete({ filePath: In(filePaths) });\n }\n\n async close(): Promise<void> {\n if (this.dataSource?.isInitialized) {\n await this.dataSource.destroy();\n }\n }\n}\n","import { Entity, PrimaryColumn, Column } from \"typeorm\";\n\n/**\n * Represents a tracked source file in the repository.\n * Used to detect changes via checksum and mtime for incremental updates.\n */\n@Entity(\"files\")\nexport class FileEntity {\n /**\n * Relative path to the file from repository root.\n * Used as primary key for uniqueness.\n */\n @PrimaryColumn(\"text\")\n filePath!: string;\n\n /**\n * MD5 checksum of file content.\n * Used to detect content changes.\n */\n @Column(\"text\")\n checksum!: string;\n\n /**\n * Last modification time in milliseconds since epoch.\n * Used as fast sanity check before computing checksum.\n */\n @Column(\"integer\")\n mtime!: number;\n}\n","import {\n Column,\n Entity,\n JoinColumn,\n ManyToOne,\n OneToMany,\n PrimaryColumn,\n RelationId,\n} from \"typeorm\";\nimport { IndexUnit, IndexUnitType } from \"../../types\";\n\n@Entity(\"index_units\")\nexport class IndexUnitEntity implements IndexUnit {\n @PrimaryColumn(\"text\")\n id!: string;\n\n @Column(\"text\")\n name!: string;\n\n @Column(\"text\")\n filePath!: string;\n\n @Column(\"integer\")\n startLine!: number;\n\n @Column(\"integer\")\n endLine!: number;\n\n @Column(\"text\")\n code!: string;\n\n @Column(\"text\")\n unitType!: IndexUnitType;\n\n @ManyToOne(() => IndexUnitEntity, (unit) => unit.children, {\n nullable: true,\n onDelete: \"CASCADE\",\n })\n @JoinColumn({ name: \"parent_id\" })\n parent?: IndexUnitEntity | null;\n\n @RelationId((unit: IndexUnitEntity) => unit.parent)\n parentId?: string | null;\n\n @OneToMany(() => IndexUnitEntity, (unit) => unit.parent, { nullable: true })\n children?: IndexUnitEntity[];\n\n @Column(\"simple-array\", { nullable: true })\n embedding?: number[] | null;\n}\n","import path from \"path\";\nimport fs from \"fs/promises\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { ExclusionService } from \"./ExclusionService\";\nimport { IndexUnit } from \"../types\";\nimport { EmbeddingService } from \"./EmbeddingService\";\nimport { FileEntity } from \"../db/entities/FileEntity\";\nimport { IndexUnitExtractor } from \"../IndexUnitExtractor\";\n\nexport interface InitOptions {\n skipEmbeddings?: boolean;\n}\n\nexport class RepositoryInitializer {\n constructor(\n private readonly deps: DryScanServiceDeps,\n private readonly exclusionService: ExclusionService\n ) {}\n\n async init(options?: InitOptions): Promise<void> {\n const extractor = this.deps.extractor;\n\n console.log(\"[DryScan] Phase 1/3: Extracting code units...\");\n await this.initUnits(extractor);\n console.log(\"[DryScan] Phase 2/3: Computing embeddings (may be slow)...\");\n await this.computeEmbeddings(options?.skipEmbeddings === true);\n console.log(\"[DryScan] Phase 3/3: Tracking files...\");\n await this.trackFiles(extractor);\n await this.exclusionService.cleanupExcludedFiles();\n console.log(\"[DryScan] Initialization phases complete.\");\n }\n\n private async initUnits(extractor: IndexUnitExtractor): Promise<void> {\n const units = await extractor.scan(this.deps.repoPath);\n console.log(`[DryScan] Extracted ${units.length} index units.`);\n await this.deps.db.saveUnits(units);\n }\n\n private async computeEmbeddings(skipEmbeddings: boolean): Promise<void> {\n if (skipEmbeddings) {\n console.log(\"[DryScan] Skipping embedding computation by request.\");\n return;\n }\n const allUnits: IndexUnit[] = await this.deps.db.getAllUnits();\n const total = allUnits.length;\n console.log(`[DryScan] Computing embeddings for ${total} units...`);\n\n const updated: IndexUnit[] = [];\n const progressInterval = Math.max(1, Math.ceil(total / 10));\n const embeddingService = new EmbeddingService(this.deps.repoPath);\n\n for (let i = 0; i < total; i++) {\n const unit = allUnits[i];\n try {\n const enriched = await embeddingService.addEmbedding(unit);\n updated.push(enriched);\n } catch (err: any) {\n console.error(\n `[DryScan] Embedding failed for ${unit.filePath} (${unit.name}): ${err?.message || err}`\n );\n throw err;\n }\n\n const completed = i + 1;\n if (completed === total || completed % progressInterval === 0) {\n const pct = Math.floor((completed / total) * 100);\n console.log(`[DryScan] Embeddings ${completed}/${total} (${pct}%)`);\n }\n }\n\n await this.deps.db.updateUnits(updated);\n }\n\n private async trackFiles(extractor: IndexUnitExtractor): Promise<void> {\n const allFunctions = await extractor.listSourceFiles(this.deps.repoPath);\n const fileEntities: FileEntity[] = [];\n\n for (const relPath of allFunctions) {\n const fullPath = path.join(this.deps.repoPath, relPath);\n const stat = await fs.stat(fullPath);\n const checksum = await extractor.computeChecksum(fullPath);\n\n const fileEntity = new FileEntity();\n fileEntity.filePath = relPath;\n fileEntity.checksum = checksum;\n fileEntity.mtime = stat.mtimeMs;\n fileEntities.push(fileEntity);\n }\n\n await this.deps.db.saveFiles(fileEntities);\n console.log(`[DryScan] Tracked ${fileEntities.length} files.`);\n }\n}","import debug from \"debug\";\nimport { OllamaEmbeddings } from \"@langchain/ollama\";\nimport { HuggingFaceInferenceEmbeddings } from \"@langchain/community/embeddings/hf\";\nimport { IndexUnit } from \"../types\";\nimport { configStore } from \"../config/configStore\";\n\nconst log = debug(\"DryScan:EmbeddingService\");\n\n// Model names for each provider\nconst OLLAMA_MODEL = \"qwen3-embedding:0.6b\";\nconst HUGGINGFACE_MODEL = \"Qwen/Qwen3-Embedding-0.6B\";\n\nexport class EmbeddingService {\n constructor(private readonly repoPath: string) { }\n\n /**\n * Generates an embedding for the given index unit using the configured provider.\n * Skips embedding if code exceeds the configured context length.\n */\n async addEmbedding(fn: IndexUnit): Promise<IndexUnit> {\n const config = await configStore.get(this.repoPath);\n const maxContext = config?.contextLength ?? 2048;\n if (fn.code.length > maxContext) {\n log(\n \"Skipping embedding for %s (code length %d exceeds context %d)\",\n fn.id,\n fn.code.length,\n maxContext\n );\n return { ...fn, embedding: null };\n }\n\n const source = config.embeddingSource;\n if (!source) {\n const message = `Embedding source is not configured for repository at ${this.repoPath}`;\n log(message);\n throw new Error(message);\n }\n\n const embeddings = this.buildProvider(source);\n const embedding = await embeddings.embedQuery(fn.code);\n return { ...fn, embedding };\n }\n\n /**\n * Builds the embedding provider based on the source configuration.\n * - URL (http/https): Uses Ollama with \"embeddinggemma\" model\n * - \"huggingface\": Uses HuggingFace Inference API with \"embeddinggemma-300m\" model\n */\n private buildProvider(source: string) {\n // HuggingFace Inference API\n if (source.toLowerCase() === \"huggingface\") {\n log(\"Using HuggingFace Inference with model: %s\", HUGGINGFACE_MODEL);\n return new HuggingFaceInferenceEmbeddings({\n model: HUGGINGFACE_MODEL,\n provider: \"hf-inference\",\n });\n }\n\n // Ollama keyword or direct URL\n const ollamaBaseUrl = this.resolveOllamaBaseUrl(source);\n if (ollamaBaseUrl !== null) {\n log(\"Using Ollama%s with model: %s\", ollamaBaseUrl ? ` at ${ollamaBaseUrl}` : \"\", OLLAMA_MODEL);\n return new OllamaEmbeddings({ model: OLLAMA_MODEL, ...(ollamaBaseUrl && { baseUrl: ollamaBaseUrl }) });\n }\n\n const message = `Unsupported embedding source: ${source || \"(empty)\"}. Use \"huggingface\" or an Ollama URL.`;\n log(message);\n throw new Error(message);\n }\n\n /**\n * Returns the Ollama base URL if source is an HTTP URL, undefined if source is \"ollama\" (use default),\n * or null if source is not an Ollama provider at all.\n */\n private resolveOllamaBaseUrl(source: string): string | undefined | null {\n if (/^https?:\\/\\//i.test(source)) return source;\n if (source.toLowerCase() === \"ollama\") return undefined;\n return null;\n }\n}","import debug from \"debug\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { ExclusionService } from \"./ExclusionService\";\nimport { performIncrementalUpdate } from \"../DryScanUpdater\";\n\nconst log = debug(\"DryScan:UpdateService\");\n\nexport class UpdateService {\n constructor(\n private readonly deps: DryScanServiceDeps,\n private readonly exclusionService: ExclusionService\n ) {}\n\n /** Returns the list of file paths that were modified or deleted (dirty). */\n async updateIndex(): Promise<string[]> {\n const extractor = this.deps.extractor;\n\n try {\n const changeSet = await performIncrementalUpdate(this.deps.repoPath, extractor, this.deps.db);\n await this.exclusionService.cleanupExcludedFiles();\n const dirtyPaths = [...changeSet.changed, ...changeSet.deleted, ...changeSet.added];\n return dirtyPaths;\n } catch (err) {\n log(\"Error during index update:\", err);\n throw err;\n }\n }\n}","import path from \"path\";\nimport fs from \"fs/promises\";\nimport debug from \"debug\";\nimport { IndexUnit } from \"./types\";\nimport { IndexUnitExtractor } from \"./IndexUnitExtractor\";\nimport { DryScanDatabase } from \"./db/DryScanDatabase\";\nimport { FileEntity } from \"./db/entities/FileEntity\";\nimport { EmbeddingService } from \"./services/EmbeddingService\";\n\nconst log = debug(\"DryScan:Updater\");\n\n/**\n * DryScan Updater Module\n * \n * This module contains all incremental update logic for DryScan.\n * Separated from DryScan.ts to keep that file focused on core operations.\n * \n * Represents the result of change detection.\n * Categorizes files into added, changed, deleted, and unchanged.\n */\nexport interface FileChangeSet {\n added: string[];\n changed: string[];\n deleted: string[];\n unchanged: string[];\n}\n\n/**\n * Detects which files have been added, changed, or deleted since last scan.\n * Uses mtime as fast check, then checksum for verification.\n * \n * @param repoPath - Root path of the repository\n * @param extractor - Index unit extractor instance for file operations\n * @param db - Database instance for retrieving tracked files\n * @returns Change set with categorized file paths\n */\nexport async function detectFileChanges(\n repoPath: string,\n extractor: IndexUnitExtractor,\n db: DryScanDatabase\n): Promise<FileChangeSet> {\n // Get current files in repository\n const currentFiles = await extractor.listSourceFiles(repoPath);\n const currentFileSet = new Set(currentFiles);\n\n // Get tracked files from database\n const trackedFiles = await db.getAllFiles();\n const trackedFileMap = new Map(trackedFiles.map(f => [f.filePath, f]));\n\n const added: string[] = [];\n const changed: string[] = [];\n const unchanged: string[] = [];\n\n // Check each current file\n for (const filePath of currentFiles) {\n const tracked = trackedFileMap.get(filePath);\n \n if (!tracked) {\n // New file\n added.push(filePath);\n continue;\n }\n\n // Check if file changed using mtime first (fast check)\n const fullPath = path.join(repoPath, filePath);\n const stat = await fs.stat(fullPath);\n \n if (stat.mtimeMs !== tracked.mtime) {\n // Mtime changed, verify with checksum\n const currentChecksum = await extractor.computeChecksum(fullPath);\n if (currentChecksum !== tracked.checksum) {\n changed.push(filePath);\n } else {\n // Mtime changed but content same\n unchanged.push(filePath);\n }\n } else {\n unchanged.push(filePath);\n }\n }\n\n // Find deleted files\n const deleted = trackedFiles\n .map(f => f.filePath)\n .filter(fp => !currentFileSet.has(fp));\n\n return { added, changed, deleted, unchanged };\n}\n\n/**\n * Extracts index units from a list of files.\n * Used during incremental updates.\n * \n * @param filePaths - Array of relative file paths to extract from\n * @param extractor - Index unit extractor instance\n * @returns Array of extracted units\n */\nexport async function extractUnitsFromFiles(\n filePaths: string[],\n extractor: IndexUnitExtractor\n): Promise<IndexUnit[]> {\n const allUnits: IndexUnit[] = [];\n \n for (const relPath of filePaths) {\n const functions = await extractor.scan(relPath);\n allUnits.push(...functions);\n }\n \n return allUnits;\n}\n\n/**\n * Updates file tracking metadata after processing changes.\n * Removes deleted files, updates changed files, adds new files.\n * \n * @param changeSet - Set of file changes to apply\n * @param repoPath - Root path of the repository\n * @param extractor - Index unit extractor for checksum computation\n * @param db - Database instance for file tracking\n */\nexport async function updateFileTracking(\n changeSet: FileChangeSet,\n repoPath: string,\n extractor: IndexUnitExtractor,\n db: DryScanDatabase\n): Promise<void> {\n // Remove deleted files\n if (changeSet.deleted.length > 0) {\n if (typeof (db as any).removeFilesByFilePaths === \"function\") {\n await (db as any).removeFilesByFilePaths(changeSet.deleted);\n } else if (typeof (db as any).removeFiles === \"function\") {\n await (db as any).removeFiles(changeSet.deleted);\n }\n }\n\n // Create file entities for new and changed files\n const filesToTrack = [...changeSet.added, ...changeSet.changed];\n if (filesToTrack.length > 0) {\n const fileEntities: FileEntity[] = [];\n \n for (const relPath of filesToTrack) {\n const fullPath = path.join(repoPath, relPath);\n const stat = await fs.stat(fullPath);\n const checksum = await extractor.computeChecksum(fullPath);\n \n const fileEntity = new FileEntity();\n fileEntity.filePath = relPath;\n fileEntity.checksum = checksum;\n fileEntity.mtime = stat.mtimeMs;\n \n fileEntities.push(fileEntity);\n }\n \n await db.saveFiles(fileEntities);\n }\n}\n\n/**\n * Performs incremental update of the DryScan index.\n * Detects file changes and reprocesses only affected files.\n * \n * @param repoPath - Root path of the repository\n * @param extractor - Index unit extractor instance\n * @param db - Database instance (must be initialized)\n */\nexport async function performIncrementalUpdate(\n repoPath: string,\n extractor: IndexUnitExtractor,\n db: DryScanDatabase,\n): Promise<FileChangeSet> {\n log(\"Starting incremental update\");\n const embeddingService = new EmbeddingService(repoPath);\n \n // Step 1: Detect changes\n const changeSet = await detectFileChanges(repoPath, extractor, db);\n \n if (changeSet.changed.length === 0 && \n changeSet.added.length === 0 && \n changeSet.deleted.length === 0) {\n log(\"No changes detected. Index is up to date.\");\n return changeSet;\n }\n\n log(`Changes detected: ${changeSet.added.length} added, ${changeSet.changed.length} changed, ${changeSet.deleted.length} deleted`);\n\n // Step 2: Remove old data for changed/deleted files\n const filesToRemove = [...changeSet.changed, ...changeSet.deleted];\n if (filesToRemove.length > 0) {\n await db.removeUnitsByFilePaths(filesToRemove);\n log(`Removed units from ${filesToRemove.length} files`);\n }\n\n // Step 3: Extract functions from new/changed files\n const filesToProcess = [...changeSet.added, ...changeSet.changed];\n if (filesToProcess.length > 0) {\n const newUnits = await extractUnitsFromFiles(filesToProcess, extractor);\n await db.saveUnits(newUnits);\n log(`Extracted and saved ${newUnits.length} units from ${filesToProcess.length} files`);\n\n // Step 4: Recompute embeddings for affected units only\n const total = newUnits.length;\n if (total > 0) {\n log(`Recomputing embeddings for ${total} units`);\n const progressInterval = Math.max(1, Math.ceil(total / 10));\n const updatedWithEmbeddings = [] as IndexUnit[];\n\n for (let i = 0; i < total; i++) {\n const unit = newUnits[i];\n try {\n const enriched = await embeddingService.addEmbedding(unit);\n updatedWithEmbeddings.push(enriched);\n } catch (err: any) {\n console.error(\n `[DryScan] embedding failed for ${unit.filePath} (${unit.name}): ${err?.message || err}`\n );\n throw err;\n }\n\n const completed = i + 1;\n if (completed === total || completed % progressInterval === 0) {\n const pct = Math.floor((completed / total) * 100);\n console.log(`[DryScan] Incremental embeddings ${completed}/${total} (${pct}%)`);\n }\n }\n\n await db.updateUnits(updatedWithEmbeddings);\n log(`Recomputed embeddings for ${updatedWithEmbeddings.length} units`);\n }\n }\n\n // Step 5: Update file tracking\n await updateFileTracking(changeSet, repoPath, extractor, db);\n log(\"Incremental update complete\");\n\n return changeSet;\n}\n","import { DryConfig } from \"../types\";\nimport { configStore } from \"../config/configStore\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { IndexUnitType } from \"../types\";\nimport { minimatch } from \"minimatch\";\nimport { ParsedPairKey } from \"./PairingService\";\n\nexport class ExclusionService {\n private config?: DryConfig;\n\n constructor(private readonly deps: DryScanServiceDeps) {}\n\n async cleanupExcludedFiles(): Promise<void> {\n const config = await this.loadConfig();\n if (!config.excludedPaths || config.excludedPaths.length === 0) return;\n\n const units = await this.deps.db.getAllUnits();\n const files = await this.deps.db.getAllFiles();\n\n const unitPathsToRemove = new Set<string>();\n for (const unit of units) {\n if (this.pathExcluded(unit.filePath)) {\n unitPathsToRemove.add(unit.filePath);\n }\n }\n\n const filePathsToRemove = new Set<string>();\n for (const file of files) {\n if (this.pathExcluded(file.filePath)) {\n filePathsToRemove.add(file.filePath);\n }\n }\n\n const paths = [...new Set([...unitPathsToRemove, ...filePathsToRemove])];\n if (paths.length > 0) {\n await this.deps.db.removeUnitsByFilePaths(paths);\n await this.deps.db.removeFilesByFilePaths(paths);\n }\n }\n\n async cleanExclusions(): Promise<{ removed: number; kept: number }> {\n const config = await this.loadConfig();\n const units = await this.deps.db.getAllUnits();\n\n const actualPairsByType = {\n [IndexUnitType.CLASS]: this.buildPairKeys(units, IndexUnitType.CLASS),\n [IndexUnitType.FUNCTION]: this.buildPairKeys(units, IndexUnitType.FUNCTION),\n [IndexUnitType.BLOCK]: this.buildPairKeys(units, IndexUnitType.BLOCK),\n };\n\n const kept: string[] = [];\n const removed: string[] = [];\n\n for (const entry of config.excludedPairs || []) {\n const parsed = this.deps.pairing.parsePairKey(entry);\n if (!parsed) {\n removed.push(entry);\n continue;\n }\n\n const candidates = actualPairsByType[parsed.type];\n const matched = candidates.some((actual) => this.deps.pairing.pairKeyMatches(actual, parsed));\n if (matched) {\n kept.push(entry);\n } else {\n removed.push(entry);\n }\n }\n\n const nextConfig: DryConfig = { ...config, excludedPairs: kept };\n await configStore.save(this.deps.repoPath, nextConfig);\n this.config = nextConfig;\n\n return { removed: removed.length, kept: kept.length };\n }\n\n private pathExcluded(filePath: string): boolean {\n const config = this.config;\n if (!config || !config.excludedPaths || config.excludedPaths.length === 0) return false;\n return config.excludedPaths.some((pattern) => minimatch(filePath, pattern, { dot: true }));\n }\n\n private buildPairKeys(units: any[], type: IndexUnitType): ParsedPairKey[] {\n const typed = units.filter((u) => u.unitType === type);\n const pairs: ParsedPairKey[] = [];\n for (let i = 0; i < typed.length; i++) {\n for (let j = i + 1; j < typed.length; j++) {\n const key = this.deps.pairing.pairKeyForUnits(typed[i], typed[j]);\n const parsed = key ? this.deps.pairing.parsePairKey(key) : null;\n if (parsed) {\n pairs.push(parsed);\n }\n }\n }\n return pairs;\n }\n\n private async loadConfig(): Promise<DryConfig> {\n this.config = await configStore.get(this.deps.repoPath);\n return this.config;\n }\n}","import crypto from \"node:crypto\";\nimport debug from \"debug\";\nimport { minimatch } from \"minimatch\";\nimport { LanguageExtractor } from \"../extractors/LanguageExtractor\";\nimport { IndexUnitExtractor } from \"../IndexUnitExtractor\";\nimport { IndexUnit, IndexUnitType } from \"../types\";\nimport { BLOCK_HASH_ALGO } from \"../const\";\n\nconst log = debug(\"DryScan:pairs\");\n\ntype UnitLike = Pick<IndexUnit, \"unitType\" | \"filePath\" | \"name\" | \"code\">;\n\nexport interface ParsedPairKey {\n type: IndexUnitType;\n left: string;\n right: string;\n key: string;\n}\n\n/**\n * Service for building and parsing pair keys with extractor-aware labeling.\n */\nexport class PairingService {\n constructor(private readonly indexUnitExtractor: IndexUnitExtractor) {}\n\n /**\n * Creates a stable, order-independent key for two units of the same type.\n * Returns null when units differ in type so callers can skip invalid pairs.\n */\n pairKeyForUnits(left: UnitLike, right: UnitLike): string | null {\n if (left.unitType !== right.unitType) {\n log(\"Skipping pair with mismatched types: %s vs %s\", left.unitType, right.unitType);\n return null;\n }\n const type = left.unitType;\n const leftLabel = this.unitLabel(left);\n const rightLabel = this.unitLabel(right);\n const [a, b] = [leftLabel, rightLabel].sort();\n return `${type}|${a}|${b}`;\n }\n\n /**\n * Parses a raw pair key into its components, returning null for malformed values.\n * Sorting is applied so callers can compare pairs without worrying about order.\n */\n parsePairKey(value: string): ParsedPairKey | null {\n const parts = value.split(\"|\");\n if (parts.length !== 3) {\n log(\"Invalid pair key format: %s\", value);\n return null;\n }\n const [typeRaw, leftRaw, rightRaw] = parts;\n const type = this.stringToUnitType(typeRaw);\n if (!type) {\n log(\"Unknown unit type in pair key: %s\", typeRaw);\n return null;\n }\n const [left, right] = [leftRaw, rightRaw].sort();\n return { type, left, right, key: `${type}|${left}|${right}` };\n }\n\n /**\n * Checks whether an actual pair key satisfies a pattern, with glob matching for class paths.\n */\n pairKeyMatches(actual: ParsedPairKey, pattern: ParsedPairKey): boolean {\n if (actual.type !== pattern.type) return false;\n if (actual.type === IndexUnitType.CLASS) {\n // Allow glob matching for class file paths.\n const forward =\n minimatch(actual.left, pattern.left, { dot: true }) &&\n minimatch(actual.right, pattern.right, { dot: true });\n const swapped =\n minimatch(actual.left, pattern.right, { dot: true }) &&\n minimatch(actual.right, pattern.left, { dot: true });\n return forward || swapped;\n }\n\n // Functions and blocks use exact matching on canonical strings.\n return (\n (actual.left === pattern.left && actual.right === pattern.right) ||\n (actual.left === pattern.right && actual.right === pattern.left)\n );\n }\n\n /**\n * Derives a reversible, extractor-aware label for a unit.\n * Extractors may override; fallback uses a fixed format per unit type.\n */\n unitLabel(unit: UnitLike): string {\n const extractor = this.findExtractor(unit.filePath);\n const customLabel = extractor?.unitLabel?.(unit as IndexUnit);\n if (customLabel) return customLabel;\n\n switch (unit.unitType) {\n case IndexUnitType.CLASS:\n return unit.filePath;\n case IndexUnitType.FUNCTION:\n return this.canonicalFunctionSignature(unit);\n case IndexUnitType.BLOCK:\n return this.normalizedBlockHash(unit);\n default:\n return unit.name;\n }\n }\n\n private findExtractor(filePath: string): LanguageExtractor | undefined {\n return this.indexUnitExtractor.extractors.find((ex) => ex.supports(filePath));\n }\n\n private canonicalFunctionSignature(unit: UnitLike): string {\n const arity = this.extractArity(unit.code);\n return `${unit.name}(arity:${arity})`;\n }\n\n /**\n * Normalizes block code (strips comments/whitespace) and hashes it for pair matching.\n */\n private normalizedBlockHash(unit: UnitLike): string {\n const normalized = this.normalizeCode(unit.code);\n return crypto.createHash(BLOCK_HASH_ALGO).update(normalized).digest(\"hex\");\n }\n\n private stringToUnitType(value: string): IndexUnitType | null {\n if (value === IndexUnitType.CLASS) return IndexUnitType.CLASS;\n if (value === IndexUnitType.FUNCTION) return IndexUnitType.FUNCTION;\n if (value === IndexUnitType.BLOCK) return IndexUnitType.BLOCK;\n return null;\n }\n\n private extractArity(code: string): number {\n const match = code.match(/^[^{]*?\\(([^)]*)\\)/s);\n if (!match) return 0;\n const params = match[1]\n .split(\",\")\n .map((p) => p.trim())\n .filter(Boolean);\n return params.length;\n }\n\n private normalizeCode(code: string): string {\n const withoutBlockComments = code.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n const withoutLineComments = withoutBlockComments.replace(/\\/\\/[^\\n\\r]*/g, \"\");\n return withoutLineComments.replace(/\\s+/g, \"\");\n }\n}\n","import debug from \"debug\";\nimport shortUuid from \"short-uuid\";\nimport { cosineSimilarity } from \"@langchain/core/utils/math\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { DuplicateAnalysisResult, DuplicateGroup, DuplicateReport, DuplicationScore, IndexUnit, IndexUnitType } from \"../types\";\nimport { indexConfig } from \"../config/indexConfig\";\nimport { DryConfig } from \"../types\";\n\nconst log = debug(\"DryScan:DuplicateService\");\n\nexport class DuplicateService {\n private config?: DryConfig;\n private similarityCache = new Map<string, number>();\n private parentSimCache = new Map<string, number>();\n\n constructor(private readonly deps: DryScanServiceDeps) {}\n\n async findDuplicates(\n config: DryConfig,\n dirtyPaths: string[] = [],\n previousReport?: DuplicateReport | null\n ): Promise<DuplicateAnalysisResult> {\n this.config = config;\n this.similarityCache = new Map<string, number>();\n this.parentSimCache = new Map<string, number>();\n\n const t0 = performance.now();\n const allUnits = await this.deps.db.getAllUnits();\n log(\"Starting duplicate analysis on %d units\", allUnits.length);\n\n if (allUnits.length < 2) {\n return { duplicates: [], score: this.computeDuplicationScore([], allUnits) };\n }\n\n const thresholds = this.resolveThresholds(config.threshold);\n const dirtySet = new Set(dirtyPaths);\n const canReuseFromReport = Boolean(previousReport && previousReport.threshold === config.threshold);\n\n const reusableClean = canReuseFromReport\n ? this.reuseCleanPairsFromPreviousReport(previousReport as DuplicateReport, allUnits, dirtySet)\n : [];\n\n const recomputed = this.computeDuplicates(\n allUnits,\n thresholds,\n canReuseFromReport ? dirtySet : null\n );\n\n const merged = this.mergeDuplicates(reusableClean, recomputed);\n const filtered = merged.filter((g) => !this.isGroupExcluded(g));\n\n log(\n \"Found %d duplicate groups (%d excluded, %d reused)\",\n filtered.length,\n merged.length - filtered.length,\n reusableClean.length\n );\n\n const score = this.computeDuplicationScore(filtered, allUnits);\n log(\"findDuplicates completed in %dms\", (performance.now() - t0).toFixed(2));\n return { duplicates: filtered, score };\n }\n\n private resolveThresholds(functionThreshold?: number): { function: number; block: number; class: number } {\n const d = indexConfig.thresholds;\n const clamp = (v: number) => Math.min(1, Math.max(0, v));\n const fn = clamp(functionThreshold ?? d.function);\n return {\n function: fn,\n block: clamp(fn + d.block - d.function),\n class: clamp(fn + d.class - d.function),\n };\n }\n\n private computeDuplicates(\n units: IndexUnit[],\n thresholds: { function: number; block: number; class: number },\n dirtySet: Set<string> | null\n ): DuplicateGroup[] {\n if (dirtySet && dirtySet.size === 0) {\n log(\"Skipping recomputation: no dirty files and previous report threshold matches\");\n return [];\n }\n\n const duplicates: DuplicateGroup[] = [];\n const t0 = performance.now();\n\n for (const [type, typedUnits] of this.groupByType(units)) {\n const threshold = this.getThreshold(type, thresholds);\n log(\"Comparing %d %s units (threshold=%.3f)\", typedUnits.length, type, threshold);\n\n for (let i = 0; i < typedUnits.length; i++) {\n for (let j = i + 1; j < typedUnits.length; j++) {\n const left = typedUnits[i];\n const right = typedUnits[j];\n if (this.shouldSkipComparison(left, right)) continue;\n\n if (dirtySet && !dirtySet.has(left.filePath) && !dirtySet.has(right.filePath)) {\n continue;\n }\n\n const hasEmbeddings = left.embedding?.length && right.embedding?.length;\n const similarity = hasEmbeddings ? this.computeWeightedSimilarity(left, right, threshold) : 0;\n if (similarity < threshold) continue;\n\n const exclusionString = this.deps.pairing.pairKeyForUnits(left, right);\n if (!exclusionString) continue;\n\n duplicates.push({\n id: `${left.id}::${right.id}`,\n similarity,\n shortId: shortUuid.generate(),\n exclusionString,\n left: this.toMember(left),\n right: this.toMember(right),\n });\n }\n }\n }\n\n log(\"computeDuplicates: %d duplicates in %dms\", duplicates.length, (performance.now() - t0).toFixed(2));\n return duplicates.sort((a, b) => b.similarity - a.similarity);\n }\n\n private reuseCleanPairsFromPreviousReport(\n report: DuplicateReport,\n units: IndexUnit[],\n dirtySet: Set<string>\n ): DuplicateGroup[] {\n const unitIds = new Set(units.map((u) => u.id));\n const reusable = report.duplicates.filter((group) => {\n const leftDirty = dirtySet.has(group.left.filePath);\n const rightDirty = dirtySet.has(group.right.filePath);\n if (leftDirty || rightDirty) return false;\n return unitIds.has(group.left.id) && unitIds.has(group.right.id);\n });\n\n log(\"Reused %d clean-clean duplicate groups from previous report\", reusable.length);\n return reusable;\n }\n\n private mergeDuplicates(reused: DuplicateGroup[], recomputed: DuplicateGroup[]): DuplicateGroup[] {\n const merged = new Map<string, DuplicateGroup>();\n\n for (const group of reused) {\n merged.set(this.groupKey(group), group);\n }\n\n for (const group of recomputed) {\n merged.set(this.groupKey(group), group);\n }\n\n return Array.from(merged.values()).sort((a, b) => b.similarity - a.similarity);\n }\n\n private groupKey(group: DuplicateGroup): string {\n return [group.left.id, group.right.id].sort().join(\"::\");\n }\n\n private isGroupExcluded(group: DuplicateGroup): boolean {\n const config = this.config;\n if (!config?.excludedPairs?.length) return false;\n const key = this.deps.pairing.pairKeyForUnits(group.left, group.right);\n if (!key) return false;\n const actual = this.deps.pairing.parsePairKey(key);\n if (!actual) return false;\n return config.excludedPairs.some((entry) => {\n const parsed = this.deps.pairing.parsePairKey(entry);\n return parsed ? this.deps.pairing.pairKeyMatches(actual, parsed) : false;\n });\n }\n\n private getThreshold(type: IndexUnitType, thresholds: { function: number; block: number; class: number }): number {\n if (type === IndexUnitType.CLASS) return thresholds.class;\n if (type === IndexUnitType.BLOCK) return thresholds.block;\n return thresholds.function;\n }\n\n private computeWeightedSimilarity(left: IndexUnit, right: IndexUnit, threshold: number): number {\n const selfSim = this.similarity(left, right);\n\n if (left.unitType === IndexUnitType.CLASS) {\n return selfSim * indexConfig.weights.class.self;\n }\n\n if (left.unitType === IndexUnitType.FUNCTION) {\n const w = indexConfig.weights.function;\n const hasPC = this.bothHaveParent(left, right, IndexUnitType.CLASS);\n const total = w.self + (hasPC ? w.parentClass : 0);\n if ((w.self * selfSim + (hasPC ? w.parentClass : 0)) / total < threshold) return 0;\n return (w.self * selfSim + (hasPC ? w.parentClass * this.parentSimilarity(left, right, IndexUnitType.CLASS) : 0)) / total;\n }\n\n const w = indexConfig.weights.block;\n const hasPF = this.bothHaveParent(left, right, IndexUnitType.FUNCTION);\n const hasPC = this.bothHaveParent(left, right, IndexUnitType.CLASS);\n const total = w.self + (hasPF ? w.parentFunction : 0) + (hasPC ? w.parentClass : 0);\n if ((w.self * selfSim + (hasPF ? w.parentFunction : 0) + (hasPC ? w.parentClass : 0)) / total < threshold) return 0;\n return (\n w.self * selfSim +\n (hasPF ? w.parentFunction * this.parentSimilarity(left, right, IndexUnitType.FUNCTION) : 0) +\n (hasPC ? w.parentClass * this.parentSimilarity(left, right, IndexUnitType.CLASS) : 0)\n ) / total;\n }\n\n private groupByType(units: IndexUnit[]): Map<IndexUnitType, IndexUnit[]> {\n const byType = new Map<IndexUnitType, IndexUnit[]>();\n for (const unit of units) {\n const list = byType.get(unit.unitType) ?? [];\n list.push(unit);\n byType.set(unit.unitType, list);\n }\n return byType;\n }\n\n private toMember(unit: IndexUnit): DuplicateGroup[\"left\"] {\n return {\n id: unit.id,\n name: unit.name,\n filePath: unit.filePath,\n startLine: unit.startLine,\n endLine: unit.endLine,\n code: unit.code,\n unitType: unit.unitType,\n };\n }\n\n private bothHaveParent(left: IndexUnit, right: IndexUnit, type: IndexUnitType): boolean {\n return !!this.findParent(left, type) && !!this.findParent(right, type);\n }\n\n private parentSimilarity(left: IndexUnit, right: IndexUnit, type: IndexUnitType): number {\n const lp = this.findParent(left, type);\n const rp = this.findParent(right, type);\n if (!lp || !rp) return 0;\n\n const key = lp.id < rp.id ? `${lp.id}::${rp.id}` : `${rp.id}::${lp.id}`;\n const cached = this.parentSimCache.get(key);\n if (cached !== undefined) return cached;\n\n const sim = this.similarity(lp, rp);\n this.parentSimCache.set(key, sim);\n return sim;\n }\n\n private similarity(left: IndexUnit, right: IndexUnit): number {\n const key = left.id < right.id ? `${left.id}::${right.id}` : `${right.id}::${left.id}`;\n const cached = this.similarityCache.get(key);\n if (cached !== undefined) return cached;\n\n let value = 0;\n if (left.embedding?.length && right.embedding?.length) {\n value = cosineSimilarity([left.embedding], [right.embedding])[0][0] ?? 0;\n } else {\n value = this.childSimilarity(left, right);\n }\n\n this.similarityCache.set(key, value);\n return value;\n }\n\n private childSimilarity(left: IndexUnit, right: IndexUnit): number {\n const lc = left.children ?? [];\n const rc = right.children ?? [];\n if (!lc.length || !rc.length) return 0;\n\n let best = 0;\n for (const l of lc) {\n for (const r of rc) {\n if (l.unitType !== r.unitType) continue;\n const sim = this.similarity(l, r);\n if (sim > best) best = sim;\n }\n }\n return best;\n }\n\n private shouldSkipComparison(left: IndexUnit, right: IndexUnit): boolean {\n if (left.unitType !== IndexUnitType.BLOCK || right.unitType !== IndexUnitType.BLOCK) return false;\n if (left.filePath !== right.filePath) return false;\n return (left.startLine <= right.startLine && left.endLine >= right.endLine)\n || (right.startLine <= left.startLine && right.endLine >= left.endLine);\n }\n\n private findParent(unit: IndexUnit, type: IndexUnitType): IndexUnit | null {\n let p = unit.parent;\n while (p) {\n if (p.unitType === type) return p;\n p = p.parent;\n }\n return null;\n }\n\n private computeDuplicationScore(duplicates: DuplicateGroup[], allUnits: IndexUnit[]): DuplicationScore {\n const totalLines = allUnits.reduce((sum, u) => sum + u.endLine - u.startLine + 1, 0);\n\n if (!totalLines || !duplicates.length) {\n return { score: 0, grade: \"Excellent\", totalLines, duplicateLines: 0, duplicateGroups: 0 };\n }\n\n const duplicateLines = duplicates.reduce((sum, g) => {\n const avg = ((g.left.endLine - g.left.startLine + 1) + (g.right.endLine - g.right.startLine + 1)) / 2;\n return sum + g.similarity * avg;\n }, 0);\n\n const score = (duplicateLines / totalLines) * 100;\n return {\n score,\n grade: this.getScoreGrade(score),\n totalLines,\n duplicateLines: Math.round(duplicateLines),\n duplicateGroups: duplicates.length,\n };\n }\n\n private getScoreGrade(score: number): DuplicationScore[\"grade\"] {\n if (score < 5) return \"Excellent\";\n if (score < 15) return \"Good\";\n if (score < 30) return \"Fair\";\n if (score < 50) return \"Poor\";\n return \"Critical\";\n }\n}\n"],"mappings":"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","names":["upath","fs","path","fs","upath","crypto","glob","upath","upath","fs","upath","path","fs","crypto","upath","glob","fs","upath","Column","Entity","PrimaryColumn","PrimaryColumn","Column","Entity","fs","upath","path","fs","debug","log","debug","path","fs","debug","path","fs","debug","log","debug","path","fs","log","debug","crypto","debug","minimatch","log","debug","minimatch","crypto","debug","log","debug","w","hasPC","total","upath","fs"]}
1
+ {"version":3,"sources":["../src/DryScan.ts","../src/const.ts","../src/IndexUnitExtractor.ts","../src/extractors/java.ts","../src/config/indexConfig.ts","../src/config/configStore.ts","../src/config/dryconfig.ts","../src/Gitignore.ts","../src/db/DryScanDatabase.ts","../src/db/entities/FileEntity.ts","../src/db/entities/IndexUnitEntity.ts","../src/services/RepositoryInitializer.ts","../src/services/EmbeddingService.ts","../src/services/UpdateService.ts","../src/DryScanUpdater.ts","../src/services/ExclusionService.ts","../src/services/PairingService.ts","../src/services/DuplicateService.ts","../src/services/DuplicationCache.ts"],"sourcesContent":["import upath from \"upath\";\nimport fs from \"fs/promises\";\nimport { DuplicateAnalysisResult, DuplicateReport } from \"./types\";\nimport { DRYSCAN_DIR, INDEX_DB, REPORTS_DIR } from \"./const\";\nimport { defaultExtractors, IndexUnitExtractor } from \"./IndexUnitExtractor\";\nimport { DryScanDatabase } from \"./db/DryScanDatabase\";\nimport { RepositoryInitializer, InitOptions as InitServiceOptions } from \"./services/RepositoryInitializer\";\nimport { UpdateService } from \"./services/UpdateService\";\nimport { ExclusionService } from \"./services/ExclusionService\";\nimport { DryScanServiceDeps } from \"./services/types\";\nimport { configStore } from \"./config/configStore\";\nimport { DryConfig } from \"./types\";\nimport { PairingService } from \"./services/PairingService\";\nimport { existsSync } from \"fs\";\nimport { DuplicateService } from \"./services/DuplicateService\";\n\nexport type InitOptions = InitServiceOptions;\n\n\nexport class DryScan {\n repoPath: string;\n private readonly extractor: IndexUnitExtractor;\n private db: DryScanDatabase;\n private readonly services: {\n initializer: RepositoryInitializer;\n updater: UpdateService;\n duplicate: DuplicateService;\n exclusion: ExclusionService;\n };\n private readonly serviceDeps: DryScanServiceDeps;\n\n constructor(\n repoPath: string,\n extractor?: IndexUnitExtractor,\n db?: DryScanDatabase\n ) {\n this.repoPath = repoPath;\n this.extractor = extractor ?? new IndexUnitExtractor(repoPath, defaultExtractors(repoPath));\n this.db = db ?? new DryScanDatabase();\n\n this.serviceDeps = {\n repoPath: this.repoPath,\n db: this.db,\n extractor: this.extractor,\n pairing: new PairingService(this.extractor),\n };\n\n const exclusion = new ExclusionService(this.serviceDeps);\n this.services = {\n initializer: new RepositoryInitializer(this.serviceDeps, exclusion),\n updater: new UpdateService(this.serviceDeps, exclusion),\n duplicate: new DuplicateService(this.serviceDeps),\n exclusion,\n };\n }\n\n /**\n * Initializes the DryScan repository with a 3-phase analysis:\n * Phase 1: Extract and save all functions\n * Phase 2: Resolve and save internal dependencies\n * Phase 3: Compute and save semantic embeddings\n */\n async init(options?: InitOptions): Promise<void> {\n console.log(`[DryScan] Initializing repository at ${this.repoPath}`);\n\n const dryDir = upath.join(this.repoPath, DRYSCAN_DIR);\n if (existsSync(dryDir)) {\n console.warn(`[DryScan] Warning: a '.dry' folder already exists at ${dryDir}.`);\n }\n console.log(\"[DryScan] Preparing database and cache...\");\n await configStore.init(this.repoPath);\n await this.ensureDatabase();\n console.log(\"[DryScan] Starting initial scan (may take a moment)...\");\n await this.services.initializer.init(options);\n console.log(\"[DryScan] Initial scan complete.\");\n }\n\n /**\n * Updates the index by detecting changed, new, and deleted files.\n * Only reprocesses units in changed files for efficiency.\n * Delegates to DryScanUpdater module for implementation.\n * \n * Update process:\n * 1. List all current source files in repository\n * 2. For each file, check if it's new, changed, or unchanged (via mtime + checksum)\n * 3. Remove old units from changed/deleted files\n * 4. Extract and save units from new/changed files\n * 5. Recompute internal dependencies for affected units\n * 6. Recompute embeddings for affected units\n * 7. Update file tracking metadata\n */\n async updateIndex(): Promise<string[]> {\n console.log(`[DryScan] Updating index at ${this.repoPath}...`);\n console.log(\"[DryScan] Checking for file changes...\");\n const start = Date.now();\n await this.ensureDatabase();\n const dirtyPaths = await this.services.updater.updateIndex();\n const duration = Date.now() - start;\n console.log(`[DryScan] Index update complete. Took ${duration}ms.`);\n return dirtyPaths;\n }\n\n\n /**\n * Runs duplicate detection and returns a normalized report payload ready for persistence or display.\n */\n async buildDuplicateReport(): Promise<DuplicateReport> {\n const config = await this.loadConfig();\n const analysis = await this.findDuplicates(config);\n const report = {\n version: 1,\n generatedAt: new Date().toISOString(),\n threshold: config.threshold,\n grade: analysis.score.grade,\n score: analysis.score,\n duplicates: analysis.duplicates,\n };\n await this.saveReport(report);\n return report;\n }\n\n /**\n * Finds duplicate code blocks using cosine similarity on embeddings.\n * Automatically updates the index before searching to ensure results are current.\n * Compares all function pairs and returns groups with similarity above the configured threshold.\n *\n * @returns Analysis result with duplicate groups and duplication score\n */\n private async findDuplicates(config: DryConfig): Promise<DuplicateAnalysisResult> {\n console.log(`[DryScan] Finding duplicates (threshold: ${config.threshold})...`);\n await this.ensureDatabase();\n\n console.log(\"[DryScan] Updating index...\");\n const updateStart = Date.now();\n const dirtyPaths = await this.updateIndex();\n const updateDuration = Date.now() - updateStart;\n console.log(`[DryScan] Index update took ${updateDuration}ms.`);\n\n const previousReport = await this.loadLatestReport();\n if (previousReport?.threshold === config.threshold) {\n console.log(\"[DryScan] Reusing clean-clean duplicates from latest report (threshold unchanged).\");\n }\n\n console.log(\"[DryScan] Detecting duplicates...\");\n const dupStart = Date.now();\n const result = await this.services.duplicate.findDuplicates(config, dirtyPaths, previousReport);\n const dupDuration = Date.now() - dupStart;\n console.log(`[DryScan] Duplicate detection took ${dupDuration}ms.`);\n\n return result;\n }\n\n /**\n * Cleans excludedPairs entries that no longer match any indexed units.\n * Runs an update first to ensure the index reflects current code.\n */\n async cleanExclusions(): Promise<{ removed: number; kept: number }> {\n await this.updateIndex();\n return this.services.exclusion.cleanExclusions();\n }\n\n private async ensureDatabase(): Promise<void> {\n if (this.db.isInitialized()) return;\n const dbPath = upath.join(this.repoPath, DRYSCAN_DIR, INDEX_DB);\n await fs.mkdir(upath.dirname(dbPath), { recursive: true });\n await this.db.init(dbPath);\n }\n\n private async loadConfig(): Promise<DryConfig> {\n return configStore.get(this.repoPath);\n }\n\n private async saveReport(report: DuplicateReport): Promise<void> {\n const reportDir = upath.join(this.repoPath, DRYSCAN_DIR, REPORTS_DIR);\n await fs.mkdir(reportDir, { recursive: true });\n const safeTimestamp = report.generatedAt.replace(/[:.]/g, \"-\");\n const reportPath = upath.join(reportDir, `dupes-${safeTimestamp}.json`);\n await fs.writeFile(reportPath, JSON.stringify(report, null, 2), \"utf8\");\n }\n\n private async loadLatestReport(): Promise<DuplicateReport | null> {\n const reportDir = upath.join(this.repoPath, DRYSCAN_DIR, REPORTS_DIR);\n let entries: string[];\n try {\n entries = await fs.readdir(reportDir);\n } catch (err: any) {\n if (err?.code === \"ENOENT\") return null;\n throw err;\n }\n\n const jsonReports = entries.filter((name) => name.endsWith(\".json\"));\n if (jsonReports.length === 0) return null;\n\n const withStats = await Promise.all(\n jsonReports.map(async (name) => {\n const fullPath = upath.join(reportDir, name);\n const stat = await fs.stat(fullPath);\n return { fullPath, mtimeMs: stat.mtimeMs };\n })\n );\n\n withStats.sort((a, b) => b.mtimeMs - a.mtimeMs);\n const latest = withStats[0];\n const raw = await fs.readFile(latest.fullPath, \"utf8\");\n const parsed = JSON.parse(raw) as DuplicateReport;\n if (!parsed || !Array.isArray(parsed.duplicates) || typeof parsed.threshold !== \"number\") {\n return null;\n }\n return parsed;\n }\n\n}\n","export const DRYSCAN_DIR = \".dry\";\nexport const INDEX_DB = \"index.db\";\nexport const REPORTS_DIR = \"reports\";\nexport const FILE_CHECKSUM_ALGO = \"md5\";\nexport const BLOCK_HASH_ALGO = \"sha1\";","import path from \"path\";\nimport type { Stats } from \"fs\";\nimport fs from \"fs/promises\";\nimport upath from \"upath\";\nimport crypto from \"node:crypto\";\nimport debug from \"debug\";\nimport { glob } from \"glob-gitignore\";\nimport { IndexUnit } from \"./types\";\nimport { LanguageExtractor } from \"./extractors/LanguageExtractor\";\nimport { JavaExtractor } from \"./extractors/java\";\nimport { FILE_CHECKSUM_ALGO } from \"./const\";\nimport { configStore } from \"./config/configStore\";\nimport { DryConfig } from \"./types\";\nimport { Gitignore } from \"./Gitignore\"\nimport { Ignore } from \"ignore\";\n\nconst log = debug(\"DryScan:Extractor\");\n\nexport type { LanguageExtractor } from \"./extractors/LanguageExtractor\";\n/**\n * Returns the default set of language extractors supported by DryScan.\n * Extend/override by passing custom extractors into the IndexUnitExtractor constructor.\n */\nexport function defaultExtractors(repoPath: string): LanguageExtractor[] {\n return [new JavaExtractor(repoPath)];\n}\n\n/**\n * Extracts and indexes code units (classes, functions, blocks) for a repository.\n * Owns shared file-system helpers and delegates language-specific parsing to LanguageExtractors.\n */\nexport class IndexUnitExtractor {\n private readonly root: string;\n readonly extractors: LanguageExtractor[];\n private readonly gitignore: Gitignore;\n\n constructor(\n rootPath: string,\n extractors?: LanguageExtractor[]\n ) {\n this.root = rootPath;\n this.extractors = extractors ?? defaultExtractors(rootPath);\n this.gitignore = new Gitignore(this.root);\n log(\"Initialized extractor for %s\", this.root);\n }\n\n /**\n * Lists all supported source files from a path. Honors exclusion globs from config.\n */\n async listSourceFiles(dirPath: string): Promise<string[]> {\n const target = await this.resolveTarget(dirPath);\n const config = await this.loadConfig();\n const ignoreMatcher = await this.gitignore.buildMatcher(config);\n\n if (target.stat.isFile()) {\n return this.filterSingleFile(target.baseRel, ignoreMatcher);\n }\n\n const matches = await this.globSourceFiles(target.baseRel);\n return this.filterSupportedFiles(matches, ignoreMatcher);\n }\n\n /**\n * Computes MD5 checksum of file content to track changes.\n */\n async computeChecksum(filePath: string): Promise<string> {\n const fullPath = path.isAbsolute(filePath)\n ? filePath\n : path.join(this.root, filePath);\n\n const content = await fs.readFile(fullPath, \"utf8\");\n return crypto.createHash(FILE_CHECKSUM_ALGO).update(content).digest(\"hex\");\n }\n\n /**\n * Scans a file or directory and extracts indexable units using the matching LanguageExtractor.\n * The returned units have repo-relative file paths and no embedding attached.\n */\n async scan(targetPath: string): Promise<IndexUnit[]> {\n const fullPath = path.isAbsolute(targetPath)\n ? targetPath\n : path.join(this.root, targetPath);\n\n const stat = await fs.stat(fullPath).catch(() => null);\n if (!stat) {\n throw new Error(`Path not found: ${fullPath}`);\n }\n\n if (stat.isDirectory()) {\n log(\"Scanning directory %s\", fullPath);\n return this.scanDirectory(fullPath);\n }\n\n return this.scanFile(fullPath);\n }\n\n\n /**\n * Scans a directory recursively, extracting units from supported files while honoring exclusions.\n */\n private async scanDirectory(dir: string): Promise<IndexUnit[]> {\n const out: IndexUnit[] = [];\n const relDir = this.relPath(dir);\n const files = await this.listSourceFiles(relDir);\n for (const relFile of files) {\n const absFile = path.join(this.root, relFile);\n const extracted = await this.tryScanSupportedFile(absFile);\n out.push(...extracted);\n }\n return out;\n }\n\n /**\n * Scans a single file and extracts supported units.\n */\n private async scanFile(filePath: string): Promise<IndexUnit[]> {\n return this.tryScanSupportedFile(filePath, true);\n }\n\n /**\n * Extracts units from a supported file.\n * Optionally throws when the file type is unsupported (used when scanning an explicit file).\n */\n private async tryScanSupportedFile(filePath: string, throwOnUnsupported = false): Promise<IndexUnit[]> {\n const extractor = this.extractors.find(ex => ex.supports(filePath));\n if (!extractor) {\n if (throwOnUnsupported) {\n throw new Error(`Unsupported file type: ${filePath}`);\n }\n return [];\n }\n const rel = this.relPath(filePath);\n if (await this.shouldExclude(rel)) {\n log(\"Skipping excluded file %s\", rel);\n return [];\n }\n const source = await fs.readFile(filePath, \"utf8\");\n const units = await extractor.extractFromText(rel, source);\n log(\"Extracted %d units from %s\", units.length, rel);\n return units.map(unit => ({\n ...unit,\n filePath: rel,\n embedding: undefined,\n }));\n }\n\n /**\n * Converts an absolute path to a repo-relative, normalized (POSIX-style) path.\n * This keeps paths stable across platforms and consistent in the index/DB.\n */\n private relPath(absPath: string): string {\n return this.normalizeRelPath(upath.relative(this.root, absPath));\n }\n\n /**\n * Returns true if a repo-relative path matches any configured exclusion glob.\n */\n private async shouldExclude(relPath: string): Promise<boolean> {\n const config = await this.loadConfig();\n const ignoreMatcher = await this.gitignore.buildMatcher(config);\n return ignoreMatcher.ignores(this.normalizeRelPath(relPath));\n }\n\n private async loadConfig(): Promise<DryConfig> {\n return await configStore.get(this.root);\n }\n\n /**\n * Normalizes repo-relative paths and strips leading \"./\" to keep matcher inputs consistent.\n */\n private normalizeRelPath(relPath: string): string {\n const normalized = upath.normalizeTrim(relPath);\n return normalized.startsWith(\"./\") ? normalized.slice(2) : normalized;\n }\n\n private async resolveTarget(dirPath: string): Promise<{ fullPath: string; baseRel: string; stat: Stats; }> {\n const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(this.root, dirPath);\n const stat = await fs.stat(fullPath).catch(() => null);\n if (!stat) {\n throw new Error(`Path not found: ${fullPath}`);\n }\n const baseRel = this.relPath(fullPath);\n log(\"Listing source files under %s\", fullPath);\n return { fullPath, baseRel, stat };\n }\n\n private async filterSingleFile(baseRel: string, ignoreMatcher: Ignore): Promise<string[]> {\n const relFile = this.normalizeRelPath(baseRel);\n if (ignoreMatcher.ignores(relFile)) return [];\n return this.extractors.some((ex) => ex.supports(relFile)) ? [relFile] : [];\n }\n\n private async globSourceFiles(baseRel: string): Promise<string[]> {\n const pattern = baseRel ? `${baseRel.replace(/\\\\/g, \"/\")}/**/*` : \"**/*\";\n const matches = await glob(pattern, {\n cwd: this.root,\n dot: false,\n nodir: true,\n });\n return matches.map((p: string) => this.normalizeRelPath(p));\n }\n\n private filterSupportedFiles(relPaths: string[], ignoreMatcher: Ignore): string[] {\n return relPaths\n .filter((relPath: string) => !ignoreMatcher.ignores(relPath))\n .filter((relPath: string) => this.extractors.some((ex) => ex.supports(relPath)));\n }\n}\n","import crypto from \"node:crypto\";\nimport Parser from \"tree-sitter\";\nimport Java from \"tree-sitter-java\";\nimport { LanguageExtractor } from \"./LanguageExtractor\";\nimport { IndexUnit, IndexUnitType } from \"../types\";\nimport { indexConfig } from \"../config/indexConfig\";\nimport { DryConfig } from \"../types\";\nimport { configStore } from \"../config/configStore\";\nimport { BLOCK_HASH_ALGO } from \"../const\";\n\nexport class JavaExtractor implements LanguageExtractor {\n readonly id = \"java\";\n readonly exts = [\".java\"];\n\n private parser: Parser;\n private readonly repoPath: string;\n private config?: DryConfig;\n\n constructor(repoPath: string) {\n this.repoPath = repoPath;\n this.parser = new Parser();\n this.parser.setLanguage(Java);\n }\n\n supports(filePath: string): boolean {\n const lower = filePath.toLowerCase();\n return this.exts.some((ext) => lower.endsWith(ext));\n }\n\n async extractFromText(fileRelPath: string, source: string): Promise<IndexUnit[]> {\n if (!source.trim()) return [];\n\n this.config = await configStore.get(this.repoPath);\n\n const tree = this.parser.parse(source);\n const units: IndexUnit[] = [];\n\n const visit = (node: Parser.SyntaxNode, currentClass?: IndexUnit) => {\n if (this.isClassNode(node)) {\n const className = this.getClassName(node, source) || \"<anonymous>\";\n if (this.isDtoClass(node, source, className)) {\n return;\n }\n const startLine = node.startPosition.row;\n const endLine = node.endPosition.row;\n const classLength = endLine - startLine;\n const skipClass = this.shouldSkip(IndexUnitType.CLASS, className, classLength);\n const classId = this.buildId(IndexUnitType.CLASS, fileRelPath, className, startLine, endLine);\n const code = this.stripComments(this.stripClassBody(node, source));\n const classUnit: IndexUnit = {\n id: classId,\n name: className,\n filePath: fileRelPath,\n startLine,\n endLine,\n code,\n unitType: IndexUnitType.CLASS,\n children: [],\n };\n if (!skipClass) {\n units.push(classUnit);\n }\n\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (child) visit(child, skipClass ? undefined : classUnit);\n }\n return;\n }\n\n if (this.isFunctionNode(node)) {\n const fnUnit = this.buildFunctionUnit(node, source, fileRelPath, currentClass);\n const fnLength = fnUnit.endLine - fnUnit.startLine;\n const bodyNode = this.getFunctionBody(node);\n const fnArity = this.getNodeArity(node);\n const skipFunction = this.shouldSkip(IndexUnitType.FUNCTION, fnUnit.name, fnLength, fnArity);\n\n if (skipFunction) {\n return;\n }\n\n units.push(fnUnit);\n\n if (bodyNode) {\n const blocks = this.extractBlocks(bodyNode, source, fileRelPath, fnUnit);\n units.push(...blocks);\n }\n }\n\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i);\n if (child) visit(child, currentClass);\n }\n };\n\n visit(tree.rootNode);\n\n //remove duplicates if any\n return this.removeDuplicates(units);\n }\n\n unitLabel(unit: IndexUnit): string | null {\n if (unit.unitType === IndexUnitType.CLASS) return unit.filePath;\n if (unit.unitType === IndexUnitType.FUNCTION) return this.canonicalFunctionSignature(unit);\n if (unit.unitType === IndexUnitType.BLOCK) return this.normalizedBlockHash(unit);\n return unit.name;\n }\n\n private isClassNode(node: Parser.SyntaxNode): boolean {\n return node.type === \"class_declaration\";\n }\n\n private getClassName(node: Parser.SyntaxNode, source: string): string | null {\n const nameNode = node.childForFieldName?.(\"name\");\n return nameNode ? source.slice(nameNode.startIndex, nameNode.endIndex) : null;\n }\n\n private isFunctionNode(node: Parser.SyntaxNode): boolean {\n return node.type === \"method_declaration\" || node.type === \"constructor_declaration\";\n }\n\n private getFunctionName(node: Parser.SyntaxNode, source: string, parentClass?: IndexUnit): string | null {\n const nameNode = node.childForFieldName?.(\"name\");\n const nameText = nameNode ? source.slice(nameNode.startIndex, nameNode.endIndex) : \"<anonymous>\";\n return parentClass ? `${parentClass.name}.${nameText}` : nameText;\n }\n\n private getFunctionBody(node: Parser.SyntaxNode): Parser.SyntaxNode | null {\n return node.childForFieldName?.(\"body\") ?? null;\n }\n\n private isBlockNode(node: Parser.SyntaxNode): boolean {\n return node.type === \"block\";\n }\n\n private getMethodBodiesForClass(node: Parser.SyntaxNode): Parser.SyntaxNode[] {\n const bodies: Parser.SyntaxNode[] = [];\n const classBody = node.children.find(child => child.type === \"class_body\");\n if (!classBody) return bodies;\n \n for (let i = 0; i < classBody.namedChildCount; i++) {\n const child = classBody.namedChild(i);\n if (!child) continue;\n if (child.type === \"method_declaration\" || child.type === \"constructor_declaration\") {\n const body = child.childForFieldName?.(\"body\");\n if (body) bodies.push(body);\n }\n }\n return bodies;\n }\n\n private canonicalFunctionSignature(unit: IndexUnit): string {\n const arity = this.extractArity(unit.code);\n return `${unit.name}(arity:${arity})`;\n }\n\n private normalizedBlockHash(unit: IndexUnit): string {\n const normalized = this.normalizeCode(unit.code);\n return crypto.createHash(BLOCK_HASH_ALGO).update(normalized).digest(\"hex\");\n }\n\n private shouldSkip(unitType: IndexUnitType, name: string, lineCount: number, arity?: number): boolean {\n if (!this.config) {\n throw new Error(\"Config not loaded before skip evaluation\");\n }\n const config = this.config;\n const minLines = unitType === IndexUnitType.BLOCK\n ? Math.max(indexConfig.blockMinLines, config.minBlockLines ?? 0)\n : config.minLines;\n const belowMin = minLines > 0 && lineCount < minLines;\n const trivial = unitType === IndexUnitType.FUNCTION && this.isTrivialFunction(name, arity ?? 0);\n return belowMin || trivial;\n }\n\n /**\n * A function is trivial if it follows a simple accessor pattern:\n * - getters/isers: name matches get[A-Z] or is[A-Z] with exactly 0 parameters\n * - setters: name matches set[A-Z] with at most 1 parameter\n * Methods like getUserById(Long id) have arity > 0 and are NOT trivial.\n */\n private isTrivialFunction(fullName: string, arity: number): boolean {\n const simpleName = fullName.split(\".\").pop() || fullName;\n const isGetter = /^(get|is)[A-Z]/.test(simpleName) && arity === 0;\n const isSetter = /^set[A-Z]/.test(simpleName) && arity <= 1;\n return isGetter || isSetter;\n }\n\n /** Counts the formal parameters of a method or constructor node. */\n private getNodeArity(node: Parser.SyntaxNode): number {\n const params = node.childForFieldName?.(\"parameters\");\n if (!params) return 0;\n return params.namedChildren.filter(c => c.type === \"formal_parameter\" || c.type === \"spread_parameter\").length;\n }\n\n private isDtoClass(node: Parser.SyntaxNode, source: string, className: string): boolean {\n const classBody = node.children.find((child) => child.type === \"class_body\");\n if (!classBody) return false;\n\n let hasField = false;\n\n for (let i = 0; i < classBody.namedChildCount; i++) {\n const child = classBody.namedChild(i);\n if (!child) continue;\n\n if (child.type === \"field_declaration\") {\n hasField = true;\n continue;\n }\n\n if (child.type.includes(\"annotation\")) {\n continue;\n }\n\n if (child.type === \"method_declaration\" || child.type === \"constructor_declaration\") {\n const simpleName = this.getSimpleFunctionName(child, source);\n const fullName = `${className}.${simpleName}`;\n const arity = this.getNodeArity(child);\n if (!this.isTrivialFunction(fullName, arity)) {\n return false;\n }\n continue;\n }\n\n return false;\n }\n\n return hasField;\n }\n\n private getSimpleFunctionName(node: Parser.SyntaxNode, source: string): string {\n const nameNode = node.childForFieldName?.(\"name\");\n return nameNode ? source.slice(nameNode.startIndex, nameNode.endIndex) : \"<anonymous>\";\n }\n\n private buildFunctionUnit(\n node: Parser.SyntaxNode,\n source: string,\n file: string,\n parentClass?: IndexUnit\n ): IndexUnit {\n const name = this.getFunctionName(node, source, parentClass) || \"<anonymous>\";\n const startLine = node.startPosition.row;\n const endLine = node.endPosition.row;\n const id = this.buildId(IndexUnitType.FUNCTION, file, name, startLine, endLine);\n const unit: IndexUnit = {\n id,\n name,\n filePath: file,\n startLine,\n endLine,\n children: [],\n code: this.stripComments(source.slice(node.startIndex, node.endIndex)),\n unitType: IndexUnitType.FUNCTION,\n parentId: parentClass?.id,\n parent: parentClass,\n };\n if (parentClass) {\n parentClass.children = parentClass.children || [];\n parentClass.children.push(unit);\n }\n return unit;\n }\n\n private extractBlocks(\n bodyNode: Parser.SyntaxNode,\n source: string,\n file: string,\n parentFunction: IndexUnit\n ): IndexUnit[] {\n const blocks: IndexUnit[] = [];\n\n const visit = (n: Parser.SyntaxNode) => {\n if (this.isBlockNode(n)) {\n const startLine = n.startPosition.row;\n const endLine = n.endPosition.row;\n const lineCount = endLine - startLine;\n if (this.shouldSkip(IndexUnitType.BLOCK, parentFunction.name, lineCount)) {\n return;\n }\n if (lineCount >= indexConfig.blockMinLines) {\n const id = this.buildId(IndexUnitType.BLOCK, file, parentFunction.name, startLine, endLine);\n const blockUnit: IndexUnit = {\n id,\n name: parentFunction.name,\n filePath: file,\n startLine,\n endLine,\n code: this.stripComments(source.slice(n.startIndex, n.endIndex)),\n unitType: IndexUnitType.BLOCK,\n parentId: parentFunction.id,\n parent: parentFunction,\n };\n const contextLength = this.config?.contextLength ?? 2048;\n const splitBlocks = this.textSplitBlockIfOverContextLimit(blockUnit, contextLength);\n parentFunction.children = parentFunction.children || [];\n parentFunction.children.push(...splitBlocks);\n blocks.push(...splitBlocks);\n }\n }\n\n for (let i = 0; i < n.namedChildCount; i++) {\n const child = n.namedChild(i);\n if (child) visit(child);\n }\n };\n\n visit(bodyNode);\n return blocks;\n }\n\n private stripClassBody(node: Parser.SyntaxNode, source: string): string {\n const classStart = node.startIndex;\n let code = source.slice(classStart, node.endIndex);\n\n const methodBodies: Array<{ start: number; end: number }> = [];\n const candidates = this.getMethodBodiesForClass(node);\n\n for (const body of candidates) {\n methodBodies.push({ start: body.startIndex - classStart, end: body.endIndex - classStart });\n }\n\n methodBodies.sort((a, b) => b.start - a.start);\n for (const body of methodBodies) {\n code = code.slice(0, body.start) + \" { }\" + code.slice(body.end);\n }\n\n return code;\n }\n\n private buildId(type: IndexUnitType, filePath: string, name: string, startLine: number, endLine: number): string {\n const pathKey = this.pathKeyForId(filePath);\n const scopedName = this.scopeNameWithPath(type, pathKey, name);\n return `${type}:${scopedName}:${startLine}-${endLine}`;\n }\n\n private pathKeyForId(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, \"/\");\n return normalized.replace(/\\.[^./]+$/, \"\");\n }\n\n private scopeNameWithPath(type: IndexUnitType, pathKey: string, name: string): string {\n if (type === IndexUnitType.CLASS) {\n return pathKey;\n }\n\n const classLeaf = pathKey.split(\"/\").pop() ?? pathKey;\n if (name === classLeaf) {\n return pathKey;\n }\n if (name.startsWith(`${classLeaf}.`)) {\n return `${pathKey}${name.slice(classLeaf.length)}`;\n }\n\n return `${pathKey}.${name}`;\n }\n\n private extractArity(code: string): number {\n const match = code.match(/^[^{]*?\\(([^)]*)\\)/s);\n if (!match) return 0;\n const params = match[1]\n .split(\",\")\n .map((p) => p.trim())\n .filter(Boolean);\n return params.length;\n }\n\n private normalizeCode(code: string): string {\n const withoutBlockComments = code.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n const withoutLineComments = withoutBlockComments.replace(/\\/\\/[^\\n\\r]*/g, \"\");\n return withoutLineComments.replace(/\\s+/g, \"\");\n }\n\n private stripComments(code: string): string {\n const withoutBlockComments = code.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (match) => match.replace(/[^\\n\\r]/g, \"\"));\n return withoutBlockComments.replace(/\\/\\/[^\\n\\r]*/g, \"\");\n }\n\n private removeDuplicates(units: IndexUnit[]): IndexUnit[] | PromiseLike<IndexUnit[]> {\n return Array.from(new Map(units.map(u => [u.id, u])).values());\n }\n\n /** Splits a block unit's code into chunks if it exceeds the context length limit. */\n private textSplitBlockIfOverContextLimit(unit: IndexUnit, contextLength: number): IndexUnit[] {\n if (unit.code.length <= contextLength) return [unit];\n\n const chunks: IndexUnit[] = [];\n let chunkIndex = 0;\n for (let i = 0; i < unit.code.length; i += contextLength) {\n chunks.push({\n ...unit,\n id: `${unit.id}:chunk${chunkIndex}`,\n code: unit.code.slice(i, i + contextLength),\n });\n chunkIndex++;\n }\n return chunks;\n }\n}\n\n","export const indexConfig = {\n blockMinLines: 5,\n thresholds: {\n class: 0.88,\n function: 0.88,\n block: 0.88,\n },\n weights: {\n class: { self: 1 },\n function: { self: 0.8, parentClass: 0.2 },\n block: { self: 0.7, parentFunction: 0.2, parentClass: 0.1 },\n },\n};\n","import upath from \"upath\";\nimport { DryConfig } from \"../types\";\nimport { ensureDefaultConfig, resolveDryConfig, saveDryConfig } from \"./dryconfig\";\n\nclass ConfigStore {\n private readonly cache = new Map<string, DryConfig>();\n private readonly loading = new Map<string, Promise<DryConfig>>();\n\n async init(repoPath: string): Promise<DryConfig> {\n const key = this.normalize(repoPath);\n return this.load(key, repoPath);\n }\n\n async get(repoPath: string): Promise<DryConfig> {\n const key = this.normalize(repoPath);\n const cached = this.cache.get(key);\n if (cached) return cached;\n return this.load(key, repoPath);\n }\n\n async refresh(repoPath: string): Promise<DryConfig> {\n const key = this.normalize(repoPath);\n this.cache.delete(key);\n return this.load(key, repoPath);\n }\n\n async save(repoPath: string, config: DryConfig): Promise<void> {\n const key = this.normalize(repoPath);\n await saveDryConfig(repoPath, config);\n this.cache.set(key, config);\n }\n\n private async load(key: string, repoPath: string): Promise<DryConfig> {\n const existing = this.loading.get(key);\n if (existing) return existing;\n\n const promise = ensureDefaultConfig(repoPath).then(() => resolveDryConfig(repoPath)).then((config) => {\n this.cache.set(key, config);\n this.loading.delete(key);\n return config;\n }).catch((err) => {\n this.loading.delete(key);\n throw err;\n });\n\n this.loading.set(key, promise);\n return promise;\n }\n\n private normalize(repoPath: string): string {\n return upath.normalizeTrim(upath.resolve(repoPath));\n }\n}\n\nexport const configStore = new ConfigStore();\n","import fs from \"fs/promises\";\nimport upath from \"upath\";\nimport { Validator, Schema } from \"jsonschema\";\nimport { DryConfig } from \"../types\";\n\n// Baseline config used when no file is present; exported so tests and constructors can seed defaults.\nexport const DEFAULT_CONFIG: DryConfig = {\n excludedPaths: [\n \"**/test/**\",\n ],\n excludedPairs: [],\n minLines: 3,\n minBlockLines: 5,\n threshold: 0.8,\n embeddingSource: \"http://localhost:11434\",\n contextLength: 2048,\n};\n\nconst validator = new Validator();\n\nconst partialConfigSchema: Schema = {\n type: \"object\",\n properties: {\n excludedPaths: { type: \"array\", items: { type: \"string\" } },\n excludedPairs: { type: \"array\", items: { type: \"string\" } },\n minLines: { type: \"number\" },\n minBlockLines: { type: \"number\" },\n threshold: { type: \"number\" },\n embeddingSource: { type: \"string\" },\n contextLength: { type: \"number\" },\n },\n};\n\nconst fullConfigSchema: Schema = {\n ...partialConfigSchema,\n required: [\n \"excludedPaths\",\n \"excludedPairs\",\n \"minLines\",\n \"minBlockLines\",\n \"threshold\",\n \"embeddingSource\",\n \"contextLength\",\n ],\n};\n\nfunction validateConfig(raw: unknown, schema: Schema, source: string): any {\n const result = validator.validate(raw, schema);\n if (!result.valid) {\n const details = result.errors.map((e) => e.stack).join(\"; \");\n throw new Error(`${source} config is invalid: ${details}`);\n }\n return raw;\n}\n\nasync function readConfigFile(repoPath: string): Promise<Partial<DryConfig>> {\n const configPath = upath.join(repoPath, \"dryconfig.json\");\n try {\n const content = await fs.readFile(configPath, \"utf8\");\n let parsed: Partial<DryConfig> = {};\n try {\n parsed = JSON.parse(content) as Partial<DryConfig>;\n } catch (parseErr) {\n throw new Error(`Invalid JSON in ${configPath}: ${(parseErr as Error).message}`);\n }\n return parsed;\n } catch (err: any) {\n if (err?.code === \"ENOENT\") {\n return {};\n }\n throw err;\n }\n}\n\n/**\n * Resolves the effective config for a repo using defaults merged with any file config.\n */\nexport async function resolveDryConfig(repoPath: string): Promise<DryConfig> {\n const fileConfigRaw = await readConfigFile(repoPath);\n validateConfig(fileConfigRaw, partialConfigSchema, \"Config file\");\n\n const merged = { ...DEFAULT_CONFIG, ...fileConfigRaw };\n validateConfig(merged, fullConfigSchema, \"Merged\");\n return merged as DryConfig;\n}\n\n// Backwards-compatible helper used by existing callers (file + defaults).\nexport async function loadDryConfig(repoPath: string): Promise<DryConfig> {\n return resolveDryConfig(repoPath);\n}\n\nexport async function saveDryConfig(repoPath: string, config: DryConfig): Promise<void> {\n const configPath = upath.join(repoPath, \"dryconfig.json\");\n validateConfig(config, fullConfigSchema, \"Config to save\");\n await fs.writeFile(configPath, JSON.stringify(config, null, 2), \"utf8\");\n}\n\nexport async function ensureDefaultConfig(repoPath: string): Promise<void> {\n const configPath = upath.join(repoPath, \"dryconfig.json\");\n const repoExists = await fs.stat(repoPath).then((s) => s.isDirectory()).catch((err: any) => {\n if (err?.code === \"ENOENT\") return false;\n throw err;\n });\n\n if (!repoExists) return;\n\n const exists = await fs.stat(configPath).then(() => true).catch((err: any) => {\n if (err?.code === \"ENOENT\") return false;\n throw err;\n });\n\n if (!exists) {\n await saveDryConfig(repoPath, DEFAULT_CONFIG);\n }\n}\n","import path from \"path\";\nimport fs from \"fs/promises\";\nimport upath from \"upath\";\nimport { glob } from \"glob-gitignore\";\nimport ignore, { Ignore } from \"ignore\";\nimport { DryConfig } from \"./types\";\n\n/**\n * Gitignore helper that builds ignore matchers by combining default rules,\n * repo .gitignore files, and config-driven exclusions.\n */\nexport class Gitignore {\n private readonly defaultIgnores = [\".git/**\", \".dry/**\"];\n\n constructor(private readonly root: string) {}\n\n async buildMatcher(config: DryConfig): Promise<Ignore> {\n const rules = await this.resolveRules(config);\n return ignore({ allowRelativePaths: true }).add(rules);\n }\n\n private async resolveRules(config: DryConfig): Promise<string[]> {\n const gitignoreRules = await this.loadGitignoreRules();\n const configRules = config.excludedPaths || [];\n return [...this.defaultIgnores, ...gitignoreRules, ...configRules];\n }\n\n private async loadGitignoreRules(): Promise<string[]> {\n const gitignoreFiles = await glob(\"**/.gitignore\", {\n cwd: this.root,\n dot: true,\n nodir: true,\n ignore: this.defaultIgnores,\n });\n\n const rules: string[] = [];\n\n for (const file of gitignoreFiles) {\n const absPath = path.join(this.root, file);\n const dir = upath.normalizeTrim(upath.dirname(file));\n const content = await fs.readFile(absPath, \"utf8\").catch(() => \"\");\n const lines = content.split(/\\r?\\n/);\n\n for (const raw of lines) {\n const trimmed = raw.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const negated = trimmed.startsWith(\"!\");\n const body = negated ? trimmed.slice(1) : trimmed;\n\n const scoped = this.scopeRule(body, dir);\n if (!scoped) continue;\n\n rules.push(negated ? `!${scoped}` : scoped);\n }\n }\n\n return rules;\n }\n\n private scopeRule(rule: string, gitignoreDir: string): string | null {\n const cleaned = rule.replace(/^\\//, \"\");\n if (!cleaned) return null;\n\n if (!gitignoreDir || gitignoreDir === \".\") {\n return cleaned;\n }\n\n return upath.normalizeTrim(upath.join(gitignoreDir, cleaned));\n }\n}\n","import \"reflect-metadata\";\nimport fs from \"fs/promises\";\nimport upath from \"upath\";\nimport { DataSource, Repository, In } from \"typeorm\";\nimport { FileEntity } from \"./entities/FileEntity\";\nimport { IndexUnit } from \"../types\";\nimport { IndexUnitEntity } from \"./entities/IndexUnitEntity\";\n\nexport class DryScanDatabase {\n private dataSource?: DataSource;\n private unitRepository?: Repository<IndexUnitEntity>;\n private fileRepository?: Repository<FileEntity>;\n\n isInitialized(): boolean {\n return !!this.dataSource?.isInitialized;\n }\n\n async init(dbPath: string): Promise<void> {\n await fs.mkdir(upath.dirname(dbPath), { recursive: true });\n\n this.dataSource = new DataSource({\n type: \"sqlite\",\n database: dbPath,\n entities: [IndexUnitEntity, FileEntity],\n synchronize: true,\n logging: false,\n });\n\n await this.dataSource.initialize();\n this.unitRepository = this.dataSource.getRepository(IndexUnitEntity);\n this.fileRepository = this.dataSource.getRepository(FileEntity);\n }\n\n async saveUnit(unit: IndexUnit): Promise<void> {\n await this.saveUnits(unit);\n }\n\n async saveUnits(units: IndexUnit | IndexUnit[]): Promise<void> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n const payload = Array.isArray(units) ? units : [units];\n const BATCH_SIZE = 500;\n for (let i = 0; i < payload.length; i += BATCH_SIZE) {\n console.debug(`[DryScanDatabase] Saving units ${i + 1}-${Math.min(i + BATCH_SIZE, payload.length)} of ${payload.length}...`);\n const batch = payload.slice(i, i + BATCH_SIZE);\n await this.unitRepository.save(batch);\n }\n }\n\n async getUnit(id: string): Promise<IndexUnit | null> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n return this.unitRepository.findOne({\n where: { id },\n relations: [\"children\", \"parent\"]\n });\n }\n\n async getAllUnits(): Promise<IndexUnit[]> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n return this.unitRepository.find({ relations: [\"children\", \"parent\"] });\n }\n\n async updateUnit(unit: IndexUnit): Promise<void> {\n await this.saveUnits(unit);\n }\n\n async updateUnits(units: IndexUnit | IndexUnit[]): Promise<void> {\n await this.saveUnits(units);\n }\n\n /**\n * Returns total count of indexed units.\n */\n async countUnits(): Promise<number> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n return this.unitRepository.count();\n }\n\n /**\n * Removes index units by their file paths.\n * Used during incremental updates when files change.\n */\n async removeUnitsByFilePaths(filePaths: string[]): Promise<void> {\n if (!this.unitRepository) throw new Error(\"Database not initialized\");\n await this.unitRepository.delete({ filePath: In(filePaths) });\n }\n\n /**\n * Saves file metadata (path, checksum, mtime) to track changes.\n */\n async saveFile(file: FileEntity): Promise<void> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n await this.fileRepository.save(file);\n }\n\n /**\n * Saves multiple file metadata entries.\n */\n async saveFiles(files: FileEntity[]): Promise<void> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n await this.fileRepository.save(files);\n }\n\n /**\n * Gets file metadata by file path.\n */\n async getFile(filePath: string): Promise<FileEntity | null> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n return this.fileRepository.findOne({ where: { filePath } });\n }\n\n /**\n * Gets all tracked files.\n */\n async getAllFiles(): Promise<FileEntity[]> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n return this.fileRepository.find();\n }\n\n /**\n * Removes file metadata entries by file paths.\n * Used when files are deleted from repository.\n */\n async removeFilesByFilePaths(filePaths: string[]): Promise<void> {\n if (!this.fileRepository) throw new Error(\"Database not initialized\");\n await this.fileRepository.delete({ filePath: In(filePaths) });\n }\n\n async close(): Promise<void> {\n if (this.dataSource?.isInitialized) {\n await this.dataSource.destroy();\n }\n }\n}\n","import { Entity, PrimaryColumn, Column } from \"typeorm\";\n\n/**\n * Represents a tracked source file in the repository.\n * Used to detect changes via checksum and mtime for incremental updates.\n */\n@Entity(\"files\")\nexport class FileEntity {\n /**\n * Relative path to the file from repository root.\n * Used as primary key for uniqueness.\n */\n @PrimaryColumn(\"text\")\n filePath!: string;\n\n /**\n * MD5 checksum of file content.\n * Used to detect content changes.\n */\n @Column(\"text\")\n checksum!: string;\n\n /**\n * Last modification time in milliseconds since epoch.\n * Used as fast sanity check before computing checksum.\n */\n @Column(\"integer\")\n mtime!: number;\n}\n","import {\n Column,\n Entity,\n JoinColumn,\n ManyToOne,\n OneToMany,\n PrimaryColumn,\n RelationId,\n} from \"typeorm\";\nimport { IndexUnit, IndexUnitType } from \"../../types\";\n\n@Entity(\"index_units\")\nexport class IndexUnitEntity implements IndexUnit {\n @PrimaryColumn(\"text\")\n id!: string;\n\n @Column(\"text\")\n name!: string;\n\n @Column(\"text\")\n filePath!: string;\n\n @Column(\"integer\")\n startLine!: number;\n\n @Column(\"integer\")\n endLine!: number;\n\n @Column(\"text\")\n code!: string;\n\n @Column(\"text\")\n unitType!: IndexUnitType;\n\n @ManyToOne(() => IndexUnitEntity, (unit) => unit.children, {\n nullable: true,\n onDelete: \"CASCADE\",\n })\n @JoinColumn({ name: \"parent_id\" })\n parent?: IndexUnitEntity | null;\n\n @RelationId((unit: IndexUnitEntity) => unit.parent)\n parentId?: string | null;\n\n @OneToMany(() => IndexUnitEntity, (unit) => unit.parent, { nullable: true })\n children?: IndexUnitEntity[];\n\n @Column(\"simple-array\", { nullable: true })\n embedding?: number[] | null;\n}\n","import path from \"path\";\nimport fs from \"fs/promises\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { ExclusionService } from \"./ExclusionService\";\nimport { IndexUnit } from \"../types\";\nimport { EmbeddingService } from \"./EmbeddingService\";\nimport { FileEntity } from \"../db/entities/FileEntity\";\nimport { IndexUnitExtractor } from \"../IndexUnitExtractor\";\n\nexport interface InitOptions {\n skipEmbeddings?: boolean;\n}\n\nexport class RepositoryInitializer {\n constructor(\n private readonly deps: DryScanServiceDeps,\n private readonly exclusionService: ExclusionService\n ) {}\n\n async init(options?: InitOptions): Promise<void> {\n const extractor = this.deps.extractor;\n\n console.log(\"[DryScan] Phase 1/3: Extracting code units...\");\n await this.initUnits(extractor);\n console.log(\"[DryScan] Phase 2/3: Computing embeddings (may be slow)...\");\n await this.computeEmbeddings(options?.skipEmbeddings === true);\n console.log(\"[DryScan] Phase 3/3: Tracking files...\");\n await this.trackFiles(extractor);\n await this.exclusionService.cleanupExcludedFiles();\n console.log(\"[DryScan] Initialization phases complete.\");\n }\n\n private async initUnits(extractor: IndexUnitExtractor): Promise<void> {\n const units = await extractor.scan(this.deps.repoPath);\n console.log(`[DryScan] Extracted ${units.length} index units.`);\n await this.deps.db.saveUnits(units);\n }\n\n private async computeEmbeddings(skipEmbeddings: boolean): Promise<void> {\n if (skipEmbeddings) {\n console.log(\"[DryScan] Skipping embedding computation by request.\");\n return;\n }\n const allUnits: IndexUnit[] = await this.deps.db.getAllUnits();\n const total = allUnits.length;\n console.log(`[DryScan] Computing embeddings for ${total} units...`);\n\n const updated: IndexUnit[] = [];\n const progressInterval = Math.max(1, Math.ceil(total / 10));\n const embeddingService = new EmbeddingService(this.deps.repoPath);\n\n for (let i = 0; i < total; i++) {\n const unit = allUnits[i];\n try {\n const enriched = await embeddingService.addEmbedding(unit);\n updated.push(enriched);\n } catch (err: any) {\n console.error(\n `[DryScan] Embedding failed for ${unit.filePath} (${unit.name}): ${err?.message || err}`\n );\n throw err;\n }\n\n const completed = i + 1;\n if (completed === total || completed % progressInterval === 0) {\n const pct = Math.floor((completed / total) * 100);\n console.log(`[DryScan] Embeddings ${completed}/${total} (${pct}%)`);\n }\n }\n\n await this.deps.db.updateUnits(updated);\n }\n\n private async trackFiles(extractor: IndexUnitExtractor): Promise<void> {\n const allFunctions = await extractor.listSourceFiles(this.deps.repoPath);\n const fileEntities: FileEntity[] = [];\n\n for (const relPath of allFunctions) {\n const fullPath = path.join(this.deps.repoPath, relPath);\n const stat = await fs.stat(fullPath);\n const checksum = await extractor.computeChecksum(fullPath);\n\n const fileEntity = new FileEntity();\n fileEntity.filePath = relPath;\n fileEntity.checksum = checksum;\n fileEntity.mtime = stat.mtimeMs;\n fileEntities.push(fileEntity);\n }\n\n await this.deps.db.saveFiles(fileEntities);\n console.log(`[DryScan] Tracked ${fileEntities.length} files.`);\n }\n}","import debug from \"debug\";\nimport { OllamaEmbeddings } from \"@langchain/ollama\";\nimport { HuggingFaceInferenceEmbeddings } from \"@langchain/community/embeddings/hf\";\nimport { IndexUnit } from \"../types\";\nimport { configStore } from \"../config/configStore\";\n\nconst log = debug(\"DryScan:EmbeddingService\");\n\n// Model names for each provider\nconst OLLAMA_MODEL = \"qwen3-embedding:0.6b\";\nconst HUGGINGFACE_MODEL = \"Qwen/Qwen3-Embedding-0.6B\";\n\nexport class EmbeddingService {\n constructor(private readonly repoPath: string) { }\n\n /**\n * Generates an embedding for the given index unit using the configured provider.\n * Skips embedding if code exceeds the configured context length.\n */\n async addEmbedding(fn: IndexUnit): Promise<IndexUnit> {\n const config = await configStore.get(this.repoPath);\n const maxContext = config?.contextLength ?? 2048;\n if (fn.code.length > maxContext) {\n log(\n \"Skipping embedding for %s (code length %d exceeds context %d)\",\n fn.id,\n fn.code.length,\n maxContext\n );\n return { ...fn, embedding: null };\n }\n\n const source = config.embeddingSource;\n if (!source) {\n const message = `Embedding source is not configured for repository at ${this.repoPath}`;\n log(message);\n throw new Error(message);\n }\n\n const embeddings = this.buildProvider(source);\n const embedding = await embeddings.embedQuery(fn.code);\n return { ...fn, embedding };\n }\n\n /**\n * Builds the embedding provider based on the source configuration.\n * - URL (http/https): Uses Ollama with \"embeddinggemma\" model\n * - \"huggingface\": Uses HuggingFace Inference API with \"embeddinggemma-300m\" model\n */\n private buildProvider(source: string) {\n // HuggingFace Inference API\n if (source.toLowerCase() === \"huggingface\") {\n log(\"Using HuggingFace Inference with model: %s\", HUGGINGFACE_MODEL);\n return new HuggingFaceInferenceEmbeddings({\n model: HUGGINGFACE_MODEL,\n provider: \"hf-inference\",\n });\n }\n\n // Ollama keyword or direct URL\n const ollamaBaseUrl = this.resolveOllamaBaseUrl(source);\n if (ollamaBaseUrl !== null) {\n log(\"Using Ollama%s with model: %s\", ollamaBaseUrl ? ` at ${ollamaBaseUrl}` : \"\", OLLAMA_MODEL);\n return new OllamaEmbeddings({ model: OLLAMA_MODEL, ...(ollamaBaseUrl && { baseUrl: ollamaBaseUrl }) });\n }\n\n const message = `Unsupported embedding source: ${source || \"(empty)\"}. Use \"huggingface\" or an Ollama URL.`;\n log(message);\n throw new Error(message);\n }\n\n /**\n * Returns the Ollama base URL if source is an HTTP URL, undefined if source is \"ollama\" (use default),\n * or null if source is not an Ollama provider at all.\n */\n private resolveOllamaBaseUrl(source: string): string | undefined | null {\n if (/^https?:\\/\\//i.test(source)) return source;\n if (source.toLowerCase() === \"ollama\") return undefined;\n return null;\n }\n}","import debug from \"debug\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { ExclusionService } from \"./ExclusionService\";\nimport { performIncrementalUpdate } from \"../DryScanUpdater\";\n\nconst log = debug(\"DryScan:UpdateService\");\n\nexport class UpdateService {\n constructor(\n private readonly deps: DryScanServiceDeps,\n private readonly exclusionService: ExclusionService\n ) {}\n\n /** Returns the list of file paths that were modified or deleted (dirty). */\n async updateIndex(): Promise<string[]> {\n const extractor = this.deps.extractor;\n\n try {\n const changeSet = await performIncrementalUpdate(this.deps.repoPath, extractor, this.deps.db);\n await this.exclusionService.cleanupExcludedFiles();\n const dirtyPaths = [...changeSet.changed, ...changeSet.deleted, ...changeSet.added];\n return dirtyPaths;\n } catch (err) {\n log(\"Error during index update:\", err);\n throw err;\n }\n }\n}","import path from \"path\";\nimport fs from \"fs/promises\";\nimport debug from \"debug\";\nimport { IndexUnit } from \"./types\";\nimport { IndexUnitExtractor } from \"./IndexUnitExtractor\";\nimport { DryScanDatabase } from \"./db/DryScanDatabase\";\nimport { FileEntity } from \"./db/entities/FileEntity\";\nimport { EmbeddingService } from \"./services/EmbeddingService\";\n\nconst log = debug(\"DryScan:Updater\");\n\n/**\n * DryScan Updater Module\n * \n * This module contains all incremental update logic for DryScan.\n * Separated from DryScan.ts to keep that file focused on core operations.\n * \n * Represents the result of change detection.\n * Categorizes files into added, changed, deleted, and unchanged.\n */\nexport interface FileChangeSet {\n added: string[];\n changed: string[];\n deleted: string[];\n unchanged: string[];\n}\n\n/**\n * Detects which files have been added, changed, or deleted since last scan.\n * Uses mtime as fast check, then checksum for verification.\n * \n * @param repoPath - Root path of the repository\n * @param extractor - Index unit extractor instance for file operations\n * @param db - Database instance for retrieving tracked files\n * @returns Change set with categorized file paths\n */\nexport async function detectFileChanges(\n repoPath: string,\n extractor: IndexUnitExtractor,\n db: DryScanDatabase\n): Promise<FileChangeSet> {\n // Get current files in repository\n const currentFiles = await extractor.listSourceFiles(repoPath);\n const currentFileSet = new Set(currentFiles);\n\n // Get tracked files from database\n const trackedFiles = await db.getAllFiles();\n const trackedFileMap = new Map(trackedFiles.map(f => [f.filePath, f]));\n\n const added: string[] = [];\n const changed: string[] = [];\n const unchanged: string[] = [];\n\n // Check each current file\n for (const filePath of currentFiles) {\n const tracked = trackedFileMap.get(filePath);\n \n if (!tracked) {\n // New file\n added.push(filePath);\n continue;\n }\n\n // Check if file changed using mtime first (fast check)\n const fullPath = path.join(repoPath, filePath);\n const stat = await fs.stat(fullPath);\n \n if (stat.mtimeMs !== tracked.mtime) {\n // Mtime changed, verify with checksum\n const currentChecksum = await extractor.computeChecksum(fullPath);\n if (currentChecksum !== tracked.checksum) {\n changed.push(filePath);\n } else {\n // Mtime changed but content same\n unchanged.push(filePath);\n }\n } else {\n unchanged.push(filePath);\n }\n }\n\n // Find deleted files\n const deleted = trackedFiles\n .map(f => f.filePath)\n .filter(fp => !currentFileSet.has(fp));\n\n return { added, changed, deleted, unchanged };\n}\n\n/**\n * Extracts index units from a list of files.\n * Used during incremental updates.\n * \n * @param filePaths - Array of relative file paths to extract from\n * @param extractor - Index unit extractor instance\n * @returns Array of extracted units\n */\nexport async function extractUnitsFromFiles(\n filePaths: string[],\n extractor: IndexUnitExtractor\n): Promise<IndexUnit[]> {\n const allUnits: IndexUnit[] = [];\n \n for (const relPath of filePaths) {\n const functions = await extractor.scan(relPath);\n allUnits.push(...functions);\n }\n \n return allUnits;\n}\n\n/**\n * Updates file tracking metadata after processing changes.\n * Removes deleted files, updates changed files, adds new files.\n * \n * @param changeSet - Set of file changes to apply\n * @param repoPath - Root path of the repository\n * @param extractor - Index unit extractor for checksum computation\n * @param db - Database instance for file tracking\n */\nexport async function updateFileTracking(\n changeSet: FileChangeSet,\n repoPath: string,\n extractor: IndexUnitExtractor,\n db: DryScanDatabase\n): Promise<void> {\n // Remove deleted files\n if (changeSet.deleted.length > 0) {\n if (typeof (db as any).removeFilesByFilePaths === \"function\") {\n await (db as any).removeFilesByFilePaths(changeSet.deleted);\n } else if (typeof (db as any).removeFiles === \"function\") {\n await (db as any).removeFiles(changeSet.deleted);\n }\n }\n\n // Create file entities for new and changed files\n const filesToTrack = [...changeSet.added, ...changeSet.changed];\n if (filesToTrack.length > 0) {\n const fileEntities: FileEntity[] = [];\n \n for (const relPath of filesToTrack) {\n const fullPath = path.join(repoPath, relPath);\n const stat = await fs.stat(fullPath);\n const checksum = await extractor.computeChecksum(fullPath);\n \n const fileEntity = new FileEntity();\n fileEntity.filePath = relPath;\n fileEntity.checksum = checksum;\n fileEntity.mtime = stat.mtimeMs;\n \n fileEntities.push(fileEntity);\n }\n \n await db.saveFiles(fileEntities);\n }\n}\n\n/**\n * Performs incremental update of the DryScan index.\n * Detects file changes and reprocesses only affected files.\n * \n * @param repoPath - Root path of the repository\n * @param extractor - Index unit extractor instance\n * @param db - Database instance (must be initialized)\n */\nexport async function performIncrementalUpdate(\n repoPath: string,\n extractor: IndexUnitExtractor,\n db: DryScanDatabase,\n): Promise<FileChangeSet> {\n log(\"Starting incremental update\");\n const embeddingService = new EmbeddingService(repoPath);\n \n // Step 1: Detect changes\n const changeSet = await detectFileChanges(repoPath, extractor, db);\n \n if (changeSet.changed.length === 0 && \n changeSet.added.length === 0 && \n changeSet.deleted.length === 0) {\n log(\"No changes detected. Index is up to date.\");\n return changeSet;\n }\n\n log(`Changes detected: ${changeSet.added.length} added, ${changeSet.changed.length} changed, ${changeSet.deleted.length} deleted`);\n\n // Step 2: Remove old data for changed/deleted files\n const filesToRemove = [...changeSet.changed, ...changeSet.deleted];\n if (filesToRemove.length > 0) {\n await db.removeUnitsByFilePaths(filesToRemove);\n log(`Removed units from ${filesToRemove.length} files`);\n }\n\n // Step 3: Extract functions from new/changed files\n const filesToProcess = [...changeSet.added, ...changeSet.changed];\n if (filesToProcess.length > 0) {\n const newUnits = await extractUnitsFromFiles(filesToProcess, extractor);\n await db.saveUnits(newUnits);\n log(`Extracted and saved ${newUnits.length} units from ${filesToProcess.length} files`);\n\n // Step 4: Recompute embeddings for affected units only\n const total = newUnits.length;\n if (total > 0) {\n log(`Recomputing embeddings for ${total} units`);\n const progressInterval = Math.max(1, Math.ceil(total / 10));\n const updatedWithEmbeddings = [] as IndexUnit[];\n\n for (let i = 0; i < total; i++) {\n const unit = newUnits[i];\n try {\n const enriched = await embeddingService.addEmbedding(unit);\n updatedWithEmbeddings.push(enriched);\n } catch (err: any) {\n console.error(\n `[DryScan] embedding failed for ${unit.filePath} (${unit.name}): ${err?.message || err}`\n );\n throw err;\n }\n\n const completed = i + 1;\n if (completed === total || completed % progressInterval === 0) {\n const pct = Math.floor((completed / total) * 100);\n console.log(`[DryScan] Incremental embeddings ${completed}/${total} (${pct}%)`);\n }\n }\n\n await db.updateUnits(updatedWithEmbeddings);\n log(`Recomputed embeddings for ${updatedWithEmbeddings.length} units`);\n }\n }\n\n // Step 5: Update file tracking\n await updateFileTracking(changeSet, repoPath, extractor, db);\n log(\"Incremental update complete\");\n\n return changeSet;\n}\n","import { DryConfig } from \"../types\";\nimport { configStore } from \"../config/configStore\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { IndexUnitType } from \"../types\";\nimport { minimatch } from \"minimatch\";\nimport { ParsedPairKey } from \"./PairingService\";\n\nexport class ExclusionService {\n private config?: DryConfig;\n\n constructor(private readonly deps: DryScanServiceDeps) {}\n\n async cleanupExcludedFiles(): Promise<void> {\n const config = await this.loadConfig();\n if (!config.excludedPaths || config.excludedPaths.length === 0) return;\n\n const units = await this.deps.db.getAllUnits();\n const files = await this.deps.db.getAllFiles();\n\n const unitPathsToRemove = new Set<string>();\n for (const unit of units) {\n if (this.pathExcluded(unit.filePath)) {\n unitPathsToRemove.add(unit.filePath);\n }\n }\n\n const filePathsToRemove = new Set<string>();\n for (const file of files) {\n if (this.pathExcluded(file.filePath)) {\n filePathsToRemove.add(file.filePath);\n }\n }\n\n const paths = [...new Set([...unitPathsToRemove, ...filePathsToRemove])];\n if (paths.length > 0) {\n await this.deps.db.removeUnitsByFilePaths(paths);\n await this.deps.db.removeFilesByFilePaths(paths);\n }\n }\n\n async cleanExclusions(): Promise<{ removed: number; kept: number }> {\n const config = await this.loadConfig();\n const units = await this.deps.db.getAllUnits();\n\n const actualPairsByType = {\n [IndexUnitType.CLASS]: this.buildPairKeys(units, IndexUnitType.CLASS),\n [IndexUnitType.FUNCTION]: this.buildPairKeys(units, IndexUnitType.FUNCTION),\n [IndexUnitType.BLOCK]: this.buildPairKeys(units, IndexUnitType.BLOCK),\n };\n\n const kept: string[] = [];\n const removed: string[] = [];\n\n for (const entry of config.excludedPairs || []) {\n const parsed = this.deps.pairing.parsePairKey(entry);\n if (!parsed) {\n removed.push(entry);\n continue;\n }\n\n const candidates = actualPairsByType[parsed.type];\n const matched = candidates.some((actual) => this.deps.pairing.pairKeyMatches(actual, parsed));\n if (matched) {\n kept.push(entry);\n } else {\n removed.push(entry);\n }\n }\n\n const nextConfig: DryConfig = { ...config, excludedPairs: kept };\n await configStore.save(this.deps.repoPath, nextConfig);\n this.config = nextConfig;\n\n return { removed: removed.length, kept: kept.length };\n }\n\n private pathExcluded(filePath: string): boolean {\n const config = this.config;\n if (!config || !config.excludedPaths || config.excludedPaths.length === 0) return false;\n return config.excludedPaths.some((pattern) => minimatch(filePath, pattern, { dot: true }));\n }\n\n private buildPairKeys(units: any[], type: IndexUnitType): ParsedPairKey[] {\n const typed = units.filter((u) => u.unitType === type);\n const pairs: ParsedPairKey[] = [];\n for (let i = 0; i < typed.length; i++) {\n for (let j = i + 1; j < typed.length; j++) {\n const key = this.deps.pairing.pairKeyForUnits(typed[i], typed[j]);\n const parsed = key ? this.deps.pairing.parsePairKey(key) : null;\n if (parsed) {\n pairs.push(parsed);\n }\n }\n }\n return pairs;\n }\n\n private async loadConfig(): Promise<DryConfig> {\n this.config = await configStore.get(this.deps.repoPath);\n return this.config;\n }\n}","import crypto from \"node:crypto\";\nimport debug from \"debug\";\nimport { minimatch } from \"minimatch\";\nimport { LanguageExtractor } from \"../extractors/LanguageExtractor\";\nimport { IndexUnitExtractor } from \"../IndexUnitExtractor\";\nimport { IndexUnit, IndexUnitType } from \"../types\";\nimport { BLOCK_HASH_ALGO } from \"../const\";\n\nconst log = debug(\"DryScan:pairs\");\n\ntype UnitLike = Pick<IndexUnit, \"unitType\" | \"filePath\" | \"name\" | \"code\">;\n\nexport interface ParsedPairKey {\n type: IndexUnitType;\n left: string;\n right: string;\n key: string;\n}\n\n/**\n * Service for building and parsing pair keys with extractor-aware labeling.\n */\nexport class PairingService {\n constructor(private readonly indexUnitExtractor: IndexUnitExtractor) {}\n\n /**\n * Creates a stable, order-independent key for two units of the same type.\n * Returns null when units differ in type so callers can skip invalid pairs.\n */\n pairKeyForUnits(left: UnitLike, right: UnitLike): string | null {\n if (left.unitType !== right.unitType) {\n log(\"Skipping pair with mismatched types: %s vs %s\", left.unitType, right.unitType);\n return null;\n }\n const type = left.unitType;\n const leftLabel = this.unitLabel(left);\n const rightLabel = this.unitLabel(right);\n const [a, b] = [leftLabel, rightLabel].sort();\n return `${type}|${a}|${b}`;\n }\n\n /**\n * Parses a raw pair key into its components, returning null for malformed values.\n * Sorting is applied so callers can compare pairs without worrying about order.\n */\n parsePairKey(value: string): ParsedPairKey | null {\n const parts = value.split(\"|\");\n if (parts.length !== 3) {\n log(\"Invalid pair key format: %s\", value);\n return null;\n }\n const [typeRaw, leftRaw, rightRaw] = parts;\n const type = this.stringToUnitType(typeRaw);\n if (!type) {\n log(\"Unknown unit type in pair key: %s\", typeRaw);\n return null;\n }\n const [left, right] = [leftRaw, rightRaw].sort();\n return { type, left, right, key: `${type}|${left}|${right}` };\n }\n\n /**\n * Checks whether an actual pair key satisfies a pattern, with glob matching for class paths.\n */\n pairKeyMatches(actual: ParsedPairKey, pattern: ParsedPairKey): boolean {\n if (actual.type !== pattern.type) return false;\n if (actual.type === IndexUnitType.CLASS) {\n // Allow glob matching for class file paths.\n const forward =\n minimatch(actual.left, pattern.left, { dot: true }) &&\n minimatch(actual.right, pattern.right, { dot: true });\n const swapped =\n minimatch(actual.left, pattern.right, { dot: true }) &&\n minimatch(actual.right, pattern.left, { dot: true });\n return forward || swapped;\n }\n\n // Functions and blocks use exact matching on canonical strings.\n return (\n (actual.left === pattern.left && actual.right === pattern.right) ||\n (actual.left === pattern.right && actual.right === pattern.left)\n );\n }\n\n /**\n * Derives a reversible, extractor-aware label for a unit.\n * Extractors may override; fallback uses a fixed format per unit type.\n */\n unitLabel(unit: UnitLike): string {\n const extractor = this.findExtractor(unit.filePath);\n const customLabel = extractor?.unitLabel?.(unit as IndexUnit);\n if (customLabel) return customLabel;\n\n switch (unit.unitType) {\n case IndexUnitType.CLASS:\n return unit.filePath;\n case IndexUnitType.FUNCTION:\n return this.canonicalFunctionSignature(unit);\n case IndexUnitType.BLOCK:\n return this.normalizedBlockHash(unit);\n default:\n return unit.name;\n }\n }\n\n private findExtractor(filePath: string): LanguageExtractor | undefined {\n return this.indexUnitExtractor.extractors.find((ex) => ex.supports(filePath));\n }\n\n private canonicalFunctionSignature(unit: UnitLike): string {\n const arity = this.extractArity(unit.code);\n return `${unit.name}(arity:${arity})`;\n }\n\n /**\n * Normalizes block code (strips comments/whitespace) and hashes it for pair matching.\n */\n private normalizedBlockHash(unit: UnitLike): string {\n const normalized = this.normalizeCode(unit.code);\n return crypto.createHash(BLOCK_HASH_ALGO).update(normalized).digest(\"hex\");\n }\n\n private stringToUnitType(value: string): IndexUnitType | null {\n if (value === IndexUnitType.CLASS) return IndexUnitType.CLASS;\n if (value === IndexUnitType.FUNCTION) return IndexUnitType.FUNCTION;\n if (value === IndexUnitType.BLOCK) return IndexUnitType.BLOCK;\n return null;\n }\n\n private extractArity(code: string): number {\n const match = code.match(/^[^{]*?\\(([^)]*)\\)/s);\n if (!match) return 0;\n const params = match[1]\n .split(\",\")\n .map((p) => p.trim())\n .filter(Boolean);\n return params.length;\n }\n\n private normalizeCode(code: string): string {\n const withoutBlockComments = code.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n const withoutLineComments = withoutBlockComments.replace(/\\/\\/[^\\n\\r]*/g, \"\");\n return withoutLineComments.replace(/\\s+/g, \"\");\n }\n}\n","import debug from \"debug\";\nimport shortUuid from \"short-uuid\";\nimport { DryScanServiceDeps } from \"./types\";\nimport { DuplicateAnalysisResult, DuplicateGroup, DuplicateReport, DuplicationScore, IndexUnit, IndexUnitType } from \"../types\";\nimport { indexConfig } from \"../config/indexConfig\";\nimport { DryConfig } from \"../types\";\nimport { DuplicationCache } from \"./DuplicationCache\";\n\nconst log = debug(\"DryScan:DuplicateService\");\n\nexport class DuplicateService {\n private config?: DryConfig;\n private readonly cache = DuplicationCache.getInstance();\n private simMemo = new Map<string, number>();\n\n constructor(private readonly deps: DryScanServiceDeps) {}\n\n async findDuplicates(\n config: DryConfig,\n dirtyPaths: string[] = [],\n previousReport?: DuplicateReport | null\n ): Promise<DuplicateAnalysisResult> {\n this.config = config;\n this.simMemo = new Map<string, number>();\n\n const t0 = performance.now();\n const allUnits = await this.deps.db.getAllUnits();\n log(\"Starting duplicate analysis on %d units\", allUnits.length);\n\n if (allUnits.length < 2) {\n return { duplicates: [], score: this.computeDuplicationScore([], allUnits) };\n }\n\n if (dirtyPaths.length > 0) {\n await this.cache.invalidate(dirtyPaths);\n }\n\n this.cache.clearRunCaches();\n await this.cache.buildEmbSimCache(allUnits, dirtyPaths);\n\n const thresholds = this.resolveThresholds(config.threshold);\n const dirtySet = new Set(dirtyPaths);\n const canReuseFromReport = Boolean(previousReport && previousReport.threshold === config.threshold);\n\n const reusableClean = canReuseFromReport\n ? this.reuseCleanPairsFromPreviousReport(previousReport as DuplicateReport, allUnits, dirtySet)\n : [];\n\n const recomputed = this.computeDuplicates(allUnits, thresholds, canReuseFromReport ? dirtySet : null);\n\n const merged = this.mergeDuplicates(reusableClean, recomputed);\n const filtered = merged.filter((g) => !this.isGroupExcluded(g));\n\n log(\n \"Found %d duplicate groups (%d excluded, %d reused)\",\n filtered.length,\n merged.length - filtered.length,\n reusableClean.length\n );\n\n this.cache.update(filtered).catch((err) => log(\"Cache update failed: %O\", err));\n\n const score = this.computeDuplicationScore(filtered, allUnits);\n log(\"findDuplicates completed in %dms\", (performance.now() - t0).toFixed(2));\n return { duplicates: filtered, score };\n }\n\n private resolveThresholds(functionThreshold?: number): { function: number; block: number; class: number } {\n const d = indexConfig.thresholds;\n const clamp = (v: number) => Math.min(1, Math.max(0, v));\n const fn = clamp(functionThreshold ?? d.function);\n return {\n function: fn,\n block: clamp(fn + d.block - d.function),\n class: clamp(fn + d.class - d.function),\n };\n }\n\n private computeDuplicates(\n units: IndexUnit[],\n thresholds: { function: number; block: number; class: number },\n dirtySet: Set<string> | null\n ): DuplicateGroup[] {\n if (dirtySet && dirtySet.size === 0) {\n log(\"Skipping recomputation: no dirty files and previous report threshold matches\");\n return [];\n }\n\n const duplicates: DuplicateGroup[] = [];\n const t0 = performance.now();\n\n for (const [type, typedUnits] of this.groupByType(units)) {\n const threshold = this.getThreshold(type, thresholds);\n log(\"Comparing %d %s units (threshold=%.3f)\", typedUnits.length, type, threshold);\n\n for (let i = 0; i < typedUnits.length; i++) {\n for (let j = i + 1; j < typedUnits.length; j++) {\n const left = typedUnits[i];\n const right = typedUnits[j];\n if (this.shouldSkipComparison(left, right)) continue;\n\n if (dirtySet && !dirtySet.has(left.filePath) && !dirtySet.has(right.filePath)) {\n continue;\n }\n\n const cached = this.cache.get(left.id, right.id, left.filePath, right.filePath);\n const similarity = cached ?? this.computeWeightedSimilarity(left, right, threshold);\n if (similarity < threshold) continue;\n\n const exclusionString = this.deps.pairing.pairKeyForUnits(left, right);\n if (!exclusionString) continue;\n\n duplicates.push({\n id: `${left.id}::${right.id}`,\n similarity,\n shortId: shortUuid.generate(),\n exclusionString,\n left: this.toMember(left),\n right: this.toMember(right),\n });\n }\n }\n }\n\n log(\"computeDuplicates: %d duplicates in %dms\", duplicates.length, (performance.now() - t0).toFixed(2));\n return duplicates.sort((a, b) => b.similarity - a.similarity);\n }\n\n private reuseCleanPairsFromPreviousReport(\n report: DuplicateReport,\n units: IndexUnit[],\n dirtySet: Set<string>\n ): DuplicateGroup[] {\n const unitIds = new Set(units.map((u) => u.id));\n const reusable = report.duplicates.filter((group) => {\n const leftDirty = dirtySet.has(group.left.filePath);\n const rightDirty = dirtySet.has(group.right.filePath);\n if (leftDirty || rightDirty) return false;\n return unitIds.has(group.left.id) && unitIds.has(group.right.id);\n });\n\n log(\"Reused %d clean-clean duplicate groups from previous report\", reusable.length);\n return reusable;\n }\n\n private mergeDuplicates(reused: DuplicateGroup[], recomputed: DuplicateGroup[]): DuplicateGroup[] {\n const merged = new Map<string, DuplicateGroup>();\n\n for (const group of reused) {\n merged.set(this.groupKey(group), group);\n }\n\n for (const group of recomputed) {\n merged.set(this.groupKey(group), group);\n }\n\n return Array.from(merged.values()).sort((a, b) => b.similarity - a.similarity);\n }\n\n private groupKey(group: DuplicateGroup): string {\n return [group.left.id, group.right.id].sort().join(\"::\");\n }\n\n private isGroupExcluded(group: DuplicateGroup): boolean {\n const config = this.config;\n if (!config?.excludedPairs?.length) return false;\n const key = this.deps.pairing.pairKeyForUnits(group.left, group.right);\n if (!key) return false;\n const actual = this.deps.pairing.parsePairKey(key);\n if (!actual) return false;\n return config.excludedPairs.some((entry) => {\n const parsed = this.deps.pairing.parsePairKey(entry);\n return parsed ? this.deps.pairing.pairKeyMatches(actual, parsed) : false;\n });\n }\n\n private getThreshold(type: IndexUnitType, thresholds: { function: number; block: number; class: number }): number {\n if (type === IndexUnitType.CLASS) return thresholds.class;\n if (type === IndexUnitType.BLOCK) return thresholds.block;\n return thresholds.function;\n }\n\n private computeWeightedSimilarity(left: IndexUnit, right: IndexUnit, threshold: number): number {\n const selfSim = this.similarity(left, right);\n\n if (left.unitType === IndexUnitType.CLASS) {\n return selfSim * indexConfig.weights.class.self;\n }\n\n if (left.unitType === IndexUnitType.FUNCTION) {\n const w = indexConfig.weights.function;\n const hasPC = this.bothHaveParent(left, right, IndexUnitType.CLASS);\n const total = w.self + (hasPC ? w.parentClass : 0);\n if ((w.self * selfSim + (hasPC ? w.parentClass : 0)) / total < threshold) return 0;\n return (w.self * selfSim + (hasPC ? w.parentClass * this.parentSimilarity(left, right, IndexUnitType.CLASS) : 0)) / total;\n }\n\n const w = indexConfig.weights.block;\n const hasPF = this.bothHaveParent(left, right, IndexUnitType.FUNCTION);\n const hasPC = this.bothHaveParent(left, right, IndexUnitType.CLASS);\n const total = w.self + (hasPF ? w.parentFunction : 0) + (hasPC ? w.parentClass : 0);\n if ((w.self * selfSim + (hasPF ? w.parentFunction : 0) + (hasPC ? w.parentClass : 0)) / total < threshold) return 0;\n return (\n w.self * selfSim +\n (hasPF ? w.parentFunction * this.parentSimilarity(left, right, IndexUnitType.FUNCTION) : 0) +\n (hasPC ? w.parentClass * this.parentSimilarity(left, right, IndexUnitType.CLASS) : 0)\n ) / total;\n }\n\n private groupByType(units: IndexUnit[]): Map<IndexUnitType, IndexUnit[]> {\n const byType = new Map<IndexUnitType, IndexUnit[]>();\n for (const unit of units) {\n const list = byType.get(unit.unitType) ?? [];\n list.push(unit);\n byType.set(unit.unitType, list);\n }\n return byType;\n }\n\n private toMember(unit: IndexUnit): DuplicateGroup[\"left\"] {\n return {\n id: unit.id,\n name: unit.name,\n filePath: unit.filePath,\n startLine: unit.startLine,\n endLine: unit.endLine,\n code: unit.code,\n unitType: unit.unitType,\n };\n }\n\n private bothHaveParent(left: IndexUnit, right: IndexUnit, type: IndexUnitType): boolean {\n return !!this.findParent(left, type) && !!this.findParent(right, type);\n }\n\n private parentSimilarity(left: IndexUnit, right: IndexUnit, type: IndexUnitType): number {\n const lp = this.findParent(left, type);\n const rp = this.findParent(right, type);\n if (!lp || !rp) return 0;\n\n const key = lp.id < rp.id ? `${lp.id}::${rp.id}` : `${rp.id}::${lp.id}`;\n const cached = this.cache.getParentSim(key);\n if (cached !== undefined) return cached;\n\n const sim = this.similarity(lp, rp);\n this.cache.setParentSim(key, sim);\n return sim;\n }\n\n private similarity(left: IndexUnit, right: IndexUnit): number {\n const key = left.id < right.id ? `${left.id}::${right.id}` : `${right.id}::${left.id}`;\n const memo = this.simMemo.get(key);\n if (memo !== undefined) return memo;\n\n const embSim = this.cache.getEmbSim(left.id, right.id);\n const value = embSim ?? this.childSimilarity(left, right);\n\n this.simMemo.set(key, value);\n return value;\n }\n\n private childSimilarity(left: IndexUnit, right: IndexUnit): number {\n const lc = left.children ?? [];\n const rc = right.children ?? [];\n if (!lc.length || !rc.length) return 0;\n\n let best = 0;\n for (const l of lc) {\n for (const r of rc) {\n if (l.unitType !== r.unitType) continue;\n const sim = this.similarity(l, r);\n if (sim > best) best = sim;\n }\n }\n return best;\n }\n\n private shouldSkipComparison(left: IndexUnit, right: IndexUnit): boolean {\n if (left.unitType !== IndexUnitType.BLOCK || right.unitType !== IndexUnitType.BLOCK) return false;\n if (left.filePath !== right.filePath) return false;\n return (left.startLine <= right.startLine && left.endLine >= right.endLine)\n || (right.startLine <= left.startLine && right.endLine >= left.endLine);\n }\n\n private findParent(unit: IndexUnit, type: IndexUnitType): IndexUnit | null {\n let p = unit.parent;\n while (p) {\n if (p.unitType === type) return p;\n p = p.parent;\n }\n return null;\n }\n\n private computeDuplicationScore(duplicates: DuplicateGroup[], allUnits: IndexUnit[]): DuplicationScore {\n const totalLines = allUnits.reduce((sum, u) => sum + u.endLine - u.startLine + 1, 0);\n\n if (!totalLines || !duplicates.length) {\n return { score: 0, grade: \"Excellent\", totalLines, duplicateLines: 0, duplicateGroups: 0 };\n }\n\n const duplicateLines = duplicates.reduce((sum, g) => {\n const avg = ((g.left.endLine - g.left.startLine + 1) + (g.right.endLine - g.right.startLine + 1)) / 2;\n return sum + g.similarity * avg;\n }, 0);\n\n const score = (duplicateLines / totalLines) * 100;\n return {\n score,\n grade: this.getScoreGrade(score),\n totalLines,\n duplicateLines: Math.round(duplicateLines),\n duplicateGroups: duplicates.length,\n };\n }\n\n private getScoreGrade(score: number): DuplicationScore[\"grade\"] {\n if (score < 5) return \"Excellent\";\n if (score < 15) return \"Good\";\n if (score < 30) return \"Fair\";\n if (score < 50) return \"Poor\";\n return \"Critical\";\n }\n}\n","import debug from \"debug\";\nimport { DuplicateGroup, IndexUnit } from \"../types\";\nimport { similarityApi } from \"./ParallelSimilarity\";\n\nconst log = debug(\"DryScan:DuplicationCache\");\n\n/**\n * In-memory cache for duplicate comparison scores.\n * Stores a global map of comparison keys and a per-file index for fast invalidation.\n */\nexport class DuplicationCache {\n private static instance: DuplicationCache | null = null;\n\n private readonly comparisons = new Map<string, number>();\n private readonly fileIndex = new Map<string, Set<string>>();\n private initialized = false;\n\n /** Per-run similarity matrix from a single batched library call (reset each run). */\n private embSimMatrix: number[][] = [];\n /** Maps unit ID to its row/column index in embSimMatrix. */\n private embSimIndex = new Map<string, number>();\n /** Per-run memoization of parent unit similarity scores (reset each run). */\n private parentSimCache = new Map<string, number>();\n\n static getInstance(): DuplicationCache {\n if (!DuplicationCache.instance) {\n DuplicationCache.instance = new DuplicationCache();\n }\n return DuplicationCache.instance;\n }\n\n /**\n * Updates the cache with fresh duplicate groups. Not awaited by callers to avoid blocking.\n */\n async update(groups: DuplicateGroup[]): Promise<void> {\n if (!groups) return;\n\n for (const group of groups) {\n const key = this.makeKey(group.left.id, group.right.id);\n this.comparisons.set(key, group.similarity);\n this.addKeyForFile(group.left.filePath, key);\n this.addKeyForFile(group.right.filePath, key);\n }\n\n this.initialized = this.initialized || groups.length > 0;\n }\n\n /**\n * Retrieves a cached similarity if present and valid for both file paths.\n * Returns null when the cache has not been initialized or when the pair is missing.\n */\n get(leftId: string, rightId: string, leftFilePath: string, rightFilePath: string): number | null {\n if (!this.initialized) return null;\n\n const key = this.makeKey(leftId, rightId);\n if (!this.fileHasKey(leftFilePath, key) || !this.fileHasKey(rightFilePath, key)) {\n return null;\n }\n\n const value = this.comparisons.get(key);\n return typeof value === \"number\" ? value : null;\n }\n\n /**\n * Invalidates all cached comparisons involving the provided file paths.\n */\n async invalidate(paths: string[]): Promise<void> {\n if (!this.initialized || !paths || paths.length === 0) return;\n\n const unique = new Set(paths);\n for (const filePath of unique) {\n const keys = this.fileIndex.get(filePath);\n if (!keys) continue;\n\n for (const key of keys) {\n this.comparisons.delete(key);\n for (const [otherPath, otherKeys] of this.fileIndex.entries()) {\n if (otherKeys.delete(key) && otherKeys.size === 0) {\n this.fileIndex.delete(otherPath);\n }\n }\n }\n\n this.fileIndex.delete(filePath);\n }\n\n if (this.comparisons.size === 0) {\n this.initialized = false;\n }\n }\n\n /**\n * Clears all cached data. Intended for test setup.\n */\n clear(): void {\n this.comparisons.clear();\n this.fileIndex.clear();\n this.initialized = false;\n this.embSimMatrix = [];\n this.embSimIndex.clear();\n this.clearRunCaches();\n }\n\n /**\n * Resets per-run memoization (parent similarities).\n * The embedding matrix is intentionally preserved so incremental runs can\n * reuse clean×clean values across calls.\n */\n clearRunCaches(): void {\n this.parentSimCache.clear();\n }\n\n /**\n * Builds or incrementally updates the embedding similarity matrix.\n *\n * Full rebuild (default): replaces the entire matrix — O(n²).\n * Incremental (dirtyPaths provided + prior matrix exists): copies clean×clean\n * cells from the old matrix and recomputes only dirty rows via one batched\n * cosineSimilarity call — O(d·n) where d = number of dirty units.\n */\n async buildEmbSimCache(units: IndexUnit[], dirtyPaths?: string[]): Promise<void> {\n const embedded = units.filter(u => Array.isArray(u.embedding) && u.embedding.length > 0);\n if (embedded.length < 2) {\n this.embSimMatrix = [];\n this.embSimIndex.clear();\n return;\n }\n\n const embeddings = embedded.map(u => u.embedding as number[]);\n const newIndex = new Map(embedded.map((u, i) => [u.id, i] as [string, number]));\n const dirtySet = dirtyPaths ? new Set(dirtyPaths) : null;\n const hasPriorMatrix = this.embSimMatrix.length > 0;\n\n if (!dirtySet || !hasPriorMatrix) {\n // Full rebuild\n this.embSimIndex = newIndex;\n this.embSimMatrix = await similarityApi.parallelCosineSimilarity(embeddings, embeddings);\n log(\"Built full embedding similarity matrix: %d units\", embedded.length);\n return;\n }\n\n // Incremental: identify dirty unit IDs\n const dirtyIds = new Set(embedded.filter(u => dirtySet.has(u.filePath)).map(u => u.id));\n\n if (dirtyIds.size === 0) {\n log(\"Matrix reused: no dirty units detected\");\n return;\n }\n\n const n = embedded.length;\n\n // Start with zeroes; copy clean×clean values from prior matrix\n const newMatrix: number[][] = Array.from({ length: n }, () => new Array(n).fill(0));\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n; j++) {\n if (dirtyIds.has(embedded[i].id) || dirtyIds.has(embedded[j].id)) continue;\n const oi = this.embSimIndex.get(embedded[i].id);\n const oj = this.embSimIndex.get(embedded[j].id);\n if (oi !== undefined && oj !== undefined) newMatrix[i][j] = this.embSimMatrix[oi][oj];\n }\n }\n\n // Recompute dirty rows in one batched call\n const dirtyIndices = embedded.reduce<number[]>((acc, u, i) => (dirtyIds.has(u.id) ? [...acc, i] : acc), []);\n const dirtyRows = await similarityApi.parallelCosineSimilarity(dirtyIndices.map(i => embeddings[i]), embeddings);\n dirtyIndices.forEach((rowIdx, di) => {\n for (let j = 0; j < n; j++) {\n newMatrix[rowIdx][j] = dirtyRows[di][j];\n newMatrix[j][rowIdx] = dirtyRows[di][j];\n }\n });\n\n this.embSimIndex = newIndex;\n this.embSimMatrix = newMatrix;\n log(\"Incremental matrix update: %d dirty unit(s) out of %d total\", dirtyIds.size, n);\n }\n\n /** Returns the pre-computed cosine similarity for a pair of unit IDs, if available. */\n getEmbSim(id1: string, id2: string): number | undefined {\n const i = this.embSimIndex.get(id1);\n const j = this.embSimIndex.get(id2);\n if (i === undefined || j === undefined) return undefined;\n return this.embSimMatrix[i][j];\n }\n\n /** Returns the memoized parent similarity for the given stable key, if available. */\n getParentSim(key: string): number | undefined {\n return this.parentSimCache.get(key);\n }\n\n /** Stores a memoized parent similarity for the given stable key. */\n setParentSim(key: string, sim: number): void {\n this.parentSimCache.set(key, sim);\n }\n\n private addKeyForFile(filePath: string, key: string): void {\n const current = this.fileIndex.get(filePath) ?? new Set<string>();\n current.add(key);\n this.fileIndex.set(filePath, current);\n }\n\n private fileHasKey(filePath: string, key: string): boolean {\n const keys = this.fileIndex.get(filePath);\n return keys ? keys.has(key) : false;\n }\n\n private makeKey(leftId: string, rightId: string): string {\n return [leftId, rightId].sort().join(\"::\");\n }\n}\n"],"mappings":"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","names":["upath","fs","path","fs","upath","crypto","glob","upath","upath","fs","upath","path","fs","crypto","upath","glob","fs","upath","Column","Entity","PrimaryColumn","PrimaryColumn","Column","Entity","fs","upath","path","fs","debug","log","debug","path","fs","debug","path","fs","debug","log","debug","path","fs","log","debug","crypto","debug","minimatch","log","debug","minimatch","crypto","debug","debug","log","debug","log","debug","w","hasPC","total","upath","fs"]}
@@ -4,5 +4,8 @@
4
4
  * B is packed into a SharedArrayBuffer shared across all workers — no copies.
5
5
  */
6
6
  declare function parallelCosineSimilarity(A: number[][], B: number[][]): Promise<number[][]>;
7
+ declare const similarityApi: {
8
+ parallelCosineSimilarity: typeof parallelCosineSimilarity;
9
+ };
7
10
 
8
- export { parallelCosineSimilarity };
11
+ export { parallelCosineSimilarity, similarityApi };
@@ -1,43 +1,9 @@
1
- import "../chunk-EUXUH3YW.js";
2
-
3
- // src/services/ParallelSimilarity.ts
4
- import os from "os";
5
- import { Worker } from "worker_threads";
6
- import { cosineSimilarity } from "@langchain/core/utils/math";
7
- var MIN_PARALLEL_ROWS = 50;
8
- async function parallelCosineSimilarity(A, B) {
9
- if (A.length === 0 || B.length === 0) return [];
10
- if (A.length < MIN_PARALLEL_ROWS) return cosineSimilarity(A, B);
11
- const dims = A[0].length;
12
- const chunkSize = Math.ceil(A.length / os.cpus().length);
13
- const sharedB = new SharedArrayBuffer(B.length * dims * 8);
14
- const bView = new Float64Array(sharedB);
15
- B.forEach((row, i) => bView.set(row, i * dims));
16
- const runningTsSource = new URL(import.meta.url).pathname.endsWith(".ts");
17
- const workerFile = runningTsSource ? "./cosineSimilarityWorker.ts" : "./cosineSimilarityWorker.js";
18
- const workerUrl = new URL(workerFile, import.meta.url);
19
- const execArgv = runningTsSource ? ["--import", "tsx/esm"] : [];
20
- const chunks = Array.from(
21
- { length: Math.ceil(A.length / chunkSize) },
22
- (_, i) => A.slice(i * chunkSize, (i + 1) * chunkSize)
23
- );
24
- const results = await Promise.all(chunks.map((chunk) => runWorker(chunk, sharedB, B.length, dims, workerUrl, execArgv)));
25
- return results.flat();
26
- }
27
- function runWorker(chunk, sharedB, bCount, dims, workerUrl, execArgv) {
28
- return new Promise((resolve, reject) => {
29
- const rowsFlat = new Float64Array(chunk.length * dims);
30
- chunk.forEach((row, i) => rowsFlat.set(row, i * dims));
31
- const worker = new Worker(workerUrl, {
32
- workerData: { rowsBuffer: rowsFlat.buffer, rowCount: chunk.length, allBuffer: sharedB, allCount: bCount, dims },
33
- transferList: [rowsFlat.buffer],
34
- execArgv
35
- });
36
- worker.once("message", ({ result }) => resolve(result));
37
- worker.once("error", reject);
38
- });
39
- }
1
+ import {
2
+ parallelCosineSimilarity,
3
+ similarityApi
4
+ } from "../chunk-MBLD5OWH.js";
40
5
  export {
41
- parallelCosineSimilarity
6
+ parallelCosineSimilarity,
7
+ similarityApi
42
8
  };
43
9
  //# sourceMappingURL=ParallelSimilarity.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/ParallelSimilarity.ts"],"sourcesContent":["import os from \"node:os\";\nimport { Worker } from \"node:worker_threads\";\nimport { cosineSimilarity } from \"@langchain/core/utils/math\";\n\n/** Minimum row count below which synchronous is faster than worker overhead. */\nconst MIN_PARALLEL_ROWS = 50;\n\n/**\n * Computes cosineSimilarity(A, B) using worker threads for large inputs,\n * falling back to the synchronous library call for small ones.\n * B is packed into a SharedArrayBuffer shared across all workers — no copies.\n */\nexport async function parallelCosineSimilarity(A: number[][], B: number[][]): Promise<number[][]> {\n if (A.length === 0 || B.length === 0) return [];\n if (A.length < MIN_PARALLEL_ROWS) return cosineSimilarity(A, B);\n\n const dims = A[0].length;\n const chunkSize = Math.ceil(A.length / os.cpus().length);\n\n const sharedB = new SharedArrayBuffer(B.length * dims * 8);\n const bView = new Float64Array(sharedB);\n B.forEach((row, i) => bView.set(row, i * dims));\n\n const runningTsSource = new URL(import.meta.url).pathname.endsWith('.ts');\n const workerFile = runningTsSource ? './cosineSimilarityWorker.ts' : './cosineSimilarityWorker.js';\n const workerUrl = new URL(workerFile, import.meta.url);\n const execArgv = runningTsSource ? ['--import', 'tsx/esm'] : [];\n\n const chunks = Array.from(\n { length: Math.ceil(A.length / chunkSize) },\n (_, i) => A.slice(i * chunkSize, (i + 1) * chunkSize),\n );\n\n const results = await Promise.all(chunks.map(chunk => runWorker(chunk, sharedB, B.length, dims, workerUrl, execArgv)));\n return results.flat();\n}\n\nfunction runWorker(\n chunk: number[][],\n sharedB: SharedArrayBuffer,\n bCount: number,\n dims: number,\n workerUrl: URL,\n execArgv: string[],\n): Promise<number[][]> {\n return new Promise((resolve, reject) => {\n const rowsFlat = new Float64Array(chunk.length * dims);\n chunk.forEach((row, i) => rowsFlat.set(row, i * dims));\n\n const worker = new Worker(workerUrl, {\n workerData: { rowsBuffer: rowsFlat.buffer, rowCount: chunk.length, allBuffer: sharedB, allCount: bCount, dims },\n transferList: [rowsFlat.buffer],\n execArgv,\n });\n\n worker.once(\"message\", ({ result }) => resolve(result));\n worker.once(\"error\", reject);\n });\n}\n"],"mappings":";;;AAAA,OAAO,QAAQ;AACf,SAAS,cAAc;AACvB,SAAS,wBAAwB;AAGjC,IAAM,oBAAoB;AAO1B,eAAsB,yBAAyB,GAAe,GAAoC;AAChG,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO,CAAC;AAC9C,MAAI,EAAE,SAAS,kBAAmB,QAAO,iBAAiB,GAAG,CAAC;AAE9D,QAAM,OAAO,EAAE,CAAC,EAAE;AAClB,QAAM,YAAY,KAAK,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,MAAM;AAEvD,QAAM,UAAU,IAAI,kBAAkB,EAAE,SAAS,OAAO,CAAC;AACzD,QAAM,QAAQ,IAAI,aAAa,OAAO;AACtC,IAAE,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI,KAAK,IAAI,IAAI,CAAC;AAE9C,QAAM,kBAAkB,IAAI,IAAI,YAAY,GAAG,EAAE,SAAS,SAAS,KAAK;AACxE,QAAM,aAAa,kBAAkB,gCAAgC;AACrE,QAAM,YAAY,IAAI,IAAI,YAAY,YAAY,GAAG;AACrD,QAAM,WAAW,kBAAkB,CAAC,YAAY,SAAS,IAAI,CAAC;AAE9D,QAAM,SAAS,MAAM;AAAA,IACnB,EAAE,QAAQ,KAAK,KAAK,EAAE,SAAS,SAAS,EAAE;AAAA,IAC1C,CAAC,GAAG,MAAM,EAAE,MAAM,IAAI,YAAY,IAAI,KAAK,SAAS;AAAA,EACtD;AAEA,QAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,IAAI,WAAS,UAAU,OAAO,SAAS,EAAE,QAAQ,MAAM,WAAW,QAAQ,CAAC,CAAC;AACrH,SAAO,QAAQ,KAAK;AACtB;AAEA,SAAS,UACP,OACA,SACA,QACA,MACA,WACA,UACqB;AACrB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,WAAW,IAAI,aAAa,MAAM,SAAS,IAAI;AACrD,UAAM,QAAQ,CAAC,KAAK,MAAM,SAAS,IAAI,KAAK,IAAI,IAAI,CAAC;AAErD,UAAM,SAAS,IAAI,OAAO,WAAW;AAAA,MACnC,YAAY,EAAE,YAAY,SAAS,QAAQ,UAAU,MAAM,QAAQ,WAAW,SAAS,UAAU,QAAQ,KAAK;AAAA,MAC9G,cAAc,CAAC,SAAS,MAAM;AAAA,MAC9B;AAAA,IACF,CAAC;AAED,WAAO,KAAK,WAAW,CAAC,EAAE,OAAO,MAAM,QAAQ,MAAM,CAAC;AACtD,WAAO,KAAK,SAAS,MAAM;AAAA,EAC7B,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goshenkata/dryscan-core",
3
- "version": "1.4.9",
3
+ "version": "1.4.11",
4
4
  "description": "Core library for DryScan - semantic code duplication analyzer",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,15 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __decorateClass = (decorators, target, key, kind) => {
4
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
- for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
- if (decorator = decorators[i])
7
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
- if (kind && result) __defProp(target, key, result);
9
- return result;
10
- };
11
-
12
- export {
13
- __decorateClass
14
- };
15
- //# sourceMappingURL=chunk-EUXUH3YW.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}