scip-query 0.4.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/dist/chunk-2DSS2NGF.js +10 -0
  2. package/dist/chunk-2RLP74AO.js +2 -0
  3. package/dist/chunk-4QJ7LVW5.js +2 -0
  4. package/dist/chunk-4TYGGOLO.js +5 -0
  5. package/dist/chunk-5IADAU5B.js +7 -0
  6. package/dist/chunk-7754WFFV.js +18 -0
  7. package/dist/chunk-7VOF4ZG6.js +2 -0
  8. package/dist/chunk-7Z4COVMC.js +2 -0
  9. package/dist/chunk-AJ5PWKD4.js +2 -0
  10. package/dist/chunk-BDBRZPX3.js +7 -0
  11. package/dist/chunk-BE6EQIWY.js +2 -0
  12. package/dist/chunk-BQ3INTYT.js +8 -0
  13. package/dist/chunk-D7KLLMPB.js +2 -0
  14. package/dist/chunk-D7YBWSON.js +29 -0
  15. package/dist/chunk-DE5ZBHMK.js +39 -0
  16. package/dist/chunk-DHYIJHXZ.js +33 -0
  17. package/dist/chunk-EEF3YEHW.js +2 -0
  18. package/dist/chunk-F2LLHRRZ.js +2 -0
  19. package/dist/chunk-FCC3XJTI.js +2 -0
  20. package/dist/chunk-GNXRLK5G.js +2 -0
  21. package/dist/chunk-GXVB36TG.js +62 -0
  22. package/dist/chunk-HMKJTAZD.js +2 -0
  23. package/dist/chunk-IBGBI3VU.js +2 -0
  24. package/dist/chunk-IYFZS4PV.js +84 -0
  25. package/dist/chunk-JH3A7HTU.js +2 -0
  26. package/dist/chunk-JS2RNIC7.js +2 -0
  27. package/dist/chunk-K5FQFCSN.js +41 -0
  28. package/dist/chunk-K6GBKEQE.js +6 -0
  29. package/dist/chunk-KO7YJRWP.js +12 -0
  30. package/dist/chunk-KYT47WU2.js +4 -0
  31. package/dist/chunk-LORWXBOO.js +2 -0
  32. package/dist/chunk-LX4H4LLG.js +89 -0
  33. package/dist/chunk-N3Z2SJCR.js +2 -0
  34. package/dist/chunk-NTDA4A2D.js +25 -0
  35. package/dist/chunk-NXMYYHDO.js +24 -0
  36. package/dist/chunk-PZ6ESKRH.js +7 -0
  37. package/dist/chunk-QXE6EDY2.js +6 -0
  38. package/dist/chunk-RJ7SPBJ5.js +5 -0
  39. package/dist/chunk-RWE6FHG3.js +3 -0
  40. package/dist/chunk-SDX6MDBL.js +2 -0
  41. package/dist/chunk-SG35Y7J2.js +2 -0
  42. package/dist/chunk-STOGKRJH.js +4 -0
  43. package/dist/chunk-TINPMWJK.js +2 -0
  44. package/dist/chunk-UJB62HV3.js +2 -0
  45. package/dist/chunk-VEUMRDHW.js +2 -0
  46. package/dist/chunk-WCDXJGYT.js +65 -0
  47. package/dist/chunk-WTSTDJZ7.js +6 -0
  48. package/dist/chunk-XAZTIDST.js +2 -0
  49. package/dist/chunk-XVDASCN7.js +35 -0
  50. package/dist/chunk-Y7H6D2EV.js +2 -0
  51. package/dist/chunk-Y7LOQSWY.js +2 -0
  52. package/dist/chunk-YIPCV7M7.js +70 -0
  53. package/dist/chunk-ZSRXMNMK.js +5 -0
  54. package/dist/chunk-ZXKURFVB.js +56 -0
  55. package/dist/cli.js +509 -8938
  56. package/dist/{db-6F9R9e_t.d.ts → db-BSTtBG_H.d.ts} +146 -1
  57. package/dist/index.d.ts +11 -2
  58. package/dist/index.js +13 -1616
  59. package/dist/postinstall.js +4 -100
  60. package/dist/queries/affected.d.ts +1 -1
  61. package/dist/queries/affected.js +1 -8
  62. package/dist/queries/bottlenecks.d.ts +1 -1
  63. package/dist/queries/bottlenecks.js +1 -8
  64. package/dist/queries/by-kind.d.ts +1 -4
  65. package/dist/queries/by-kind.js +1 -10
  66. package/dist/queries/call-graph.d.ts +1 -1
  67. package/dist/queries/call-graph.js +1 -8
  68. package/dist/queries/change-surface.d.ts +4 -1
  69. package/dist/queries/change-surface.js +1 -8
  70. package/dist/queries/code.d.ts +1 -1
  71. package/dist/queries/code.js +1 -8
  72. package/dist/queries/complexity-hotspots.d.ts +5 -3
  73. package/dist/queries/complexity-hotspots.js +1 -8
  74. package/dist/queries/complexity.d.ts +1 -1
  75. package/dist/queries/complexity.js +1 -8
  76. package/dist/queries/convergence.d.ts +1 -1
  77. package/dist/queries/convergence.js +1 -8
  78. package/dist/queries/coupling.d.ts +1 -1
  79. package/dist/queries/coupling.js +1 -10
  80. package/dist/queries/cycles.d.ts +1 -1
  81. package/dist/queries/cycles.js +1 -8
  82. package/dist/queries/dataflow.d.ts +1 -1
  83. package/dist/queries/dataflow.js +1 -8
  84. package/dist/queries/dead.d.ts +1 -1
  85. package/dist/queries/dead.js +1 -9
  86. package/dist/queries/deep-chains.d.ts +9 -1
  87. package/dist/queries/deep-chains.js +1 -8
  88. package/dist/queries/deps.d.ts +1 -1
  89. package/dist/queries/deps.js +1 -10
  90. package/dist/queries/diff-impact.d.ts +1 -1
  91. package/dist/queries/diff-impact.js +1 -7
  92. package/dist/queries/drift.d.ts +1 -1
  93. package/dist/queries/drift.js +1 -8
  94. package/dist/queries/extract-candidates.d.ts +1 -1
  95. package/dist/queries/extract-candidates.js +1 -8
  96. package/dist/queries/fan.d.ts +1 -1
  97. package/dist/queries/fan.js +1 -14
  98. package/dist/queries/files.d.ts +1 -1
  99. package/dist/queries/files.js +1 -6
  100. package/dist/queries/health.d.ts +1 -1
  101. package/dist/queries/health.js +1 -20
  102. package/dist/queries/hierarchy.d.ts +1 -1
  103. package/dist/queries/hierarchy.js +1 -8
  104. package/dist/queries/hotspots.d.ts +1 -1
  105. package/dist/queries/hotspots.js +1 -8
  106. package/dist/queries/imports.d.ts +1 -1
  107. package/dist/queries/imports.js +1 -12
  108. package/dist/queries/index.d.ts +1 -1
  109. package/dist/queries/index.js +1 -197
  110. package/dist/queries/isolated.d.ts +1 -1
  111. package/dist/queries/isolated.js +1 -9
  112. package/dist/queries/members.d.ts +4 -1
  113. package/dist/queries/members.js +1 -8
  114. package/dist/queries/methods.d.ts +1 -1
  115. package/dist/queries/methods.js +1 -8
  116. package/dist/queries/outline.d.ts +4 -1
  117. package/dist/queries/outline.js +1 -8
  118. package/dist/queries/passthrough-candidates.d.ts +1 -1
  119. package/dist/queries/passthrough-candidates.js +1 -8
  120. package/dist/queries/redundant-reexports.d.ts +1 -1
  121. package/dist/queries/redundant-reexports.js +1 -9
  122. package/dist/queries/refs.d.ts +1 -1
  123. package/dist/queries/refs.js +1 -8
  124. package/dist/queries/similar-chains.d.ts +1 -1
  125. package/dist/queries/similar-chains.js +1 -8
  126. package/dist/queries/similar-files.d.ts +1 -1
  127. package/dist/queries/similar-files.js +1 -8
  128. package/dist/queries/similar-signatures.d.ts +1 -1
  129. package/dist/queries/similar-signatures.js +1 -8
  130. package/dist/queries/similar.d.ts +1 -1
  131. package/dist/queries/similar.js +1 -10
  132. package/dist/queries/slice.d.ts +1 -1
  133. package/dist/queries/slice.js +1 -8
  134. package/dist/queries/stale-abstractions.d.ts +15 -5
  135. package/dist/queries/stale-abstractions.js +1 -8
  136. package/dist/queries/stats.d.ts +1 -1
  137. package/dist/queries/stats.js +1 -6
  138. package/dist/queries/surface.d.ts +1 -1
  139. package/dist/queries/surface.js +1 -8
  140. package/dist/queries/symbols.d.ts +1 -1
  141. package/dist/queries/symbols.js +1 -9
  142. package/dist/queries/system.d.ts +6 -2
  143. package/dist/queries/system.js +1 -9
  144. package/dist/queries/trace.d.ts +1 -1
  145. package/dist/queries/trace.js +1 -9
  146. package/dist/queries/wrapper-candidates.d.ts +1 -1
  147. package/dist/queries/wrapper-candidates.js +1 -8
  148. package/dist/reindex-worker.js +7 -672
  149. package/package.json +20 -2
  150. package/dist/chunk-334PCFO3.js +0 -221
  151. package/dist/chunk-46ZTW4AI.js +0 -61
  152. package/dist/chunk-4TYLS5XX.js +0 -10
  153. package/dist/chunk-4YN3PE57.js +0 -124
  154. package/dist/chunk-5YB6UXQ3.js +0 -101
  155. package/dist/chunk-6PVHJ332.js +0 -44
  156. package/dist/chunk-74RFWB5T.js +0 -24
  157. package/dist/chunk-7KGTWDAX.js +0 -87
  158. package/dist/chunk-7OGXSMLY.js +0 -105
  159. package/dist/chunk-AEBM56CO.js +0 -69
  160. package/dist/chunk-B747RITP.js +0 -38
  161. package/dist/chunk-B7LDMCUS.js +0 -110
  162. package/dist/chunk-C2VSV54P.js +0 -110
  163. package/dist/chunk-DIYEUFVP.js +0 -81
  164. package/dist/chunk-E74RY6AQ.js +0 -93
  165. package/dist/chunk-FIMTTUGE.js +0 -165
  166. package/dist/chunk-FMAYH7GS.js +0 -2542
  167. package/dist/chunk-FO2CBB7U.js +0 -23
  168. package/dist/chunk-HESWGDIV.js +0 -162
  169. package/dist/chunk-HL2LXSBW.js +0 -34
  170. package/dist/chunk-HW76DVE4.js +0 -216
  171. package/dist/chunk-IC5NTO47.js +0 -53
  172. package/dist/chunk-J34HAAEQ.js +0 -59
  173. package/dist/chunk-JSXGC2EH.js +0 -151
  174. package/dist/chunk-LQXBFCP2.js +0 -87
  175. package/dist/chunk-NML6M5AS.js +0 -37
  176. package/dist/chunk-NNFP4ZRF.js +0 -72
  177. package/dist/chunk-NWXTQGUE.js +0 -49
  178. package/dist/chunk-NYZ6INK3.js +0 -95
  179. package/dist/chunk-OMVF3BHY.js +0 -84
  180. package/dist/chunk-P3VCDYMJ.js +0 -269
  181. package/dist/chunk-PGQXIAJF.js +0 -79
  182. package/dist/chunk-PKDFXASW.js +0 -76
  183. package/dist/chunk-PSK5BPFE.js +0 -82
  184. package/dist/chunk-QZ4FRB65.js +0 -341
  185. package/dist/chunk-R2QBMQQN.js +0 -185
  186. package/dist/chunk-T3ALCNCP.js +0 -113
  187. package/dist/chunk-T6UVM534.js +0 -213
  188. package/dist/chunk-U74VYTLX.js +0 -46
  189. package/dist/chunk-UIRCHPOU.js +0 -34
  190. package/dist/chunk-UNS6ZQVX.js +0 -71
  191. package/dist/chunk-VJMTX3OR.js +0 -115
  192. package/dist/chunk-XJSPWHNT.js +0 -64
  193. package/dist/chunk-XMZAC2VU.js +0 -84
  194. package/dist/chunk-Y7FKURZG.js +0 -130
  195. package/dist/chunk-YMSJCSRG.js +0 -213
  196. package/dist/chunk-YQIWS5V6.js +0 -172
  197. package/dist/chunk-ZPEI7DRJ.js +0 -66
  198. package/dist/queries/clean-signature.d.ts +0 -9
  199. package/dist/queries/clean-signature.js +0 -7
@@ -1,79 +0,0 @@
1
- import {
2
- getAllDefinitions,
3
- getCalleeRowsForSymbol,
4
- getCallerRowsForSymbol
5
- } from "./chunk-FMAYH7GS.js";
6
- import {
7
- shortenSymbol
8
- } from "./chunk-YMSJCSRG.js";
9
-
10
- // src/queries/bottlenecks.ts
11
- function bottlenecks(db, opts = {}) {
12
- const { limit = 20, scope, minFanIn = 2, minFanOut = 2 } = opts;
13
- const scopeFilter = scope ? `AND def_d.relative_path LIKE '%${scope}%'` : "";
14
- const rows = db.all(
15
- `SELECT * FROM (
16
- SELECT
17
- gs.symbol,
18
- def_d.relative_path AS defined_in,
19
- (SELECT COUNT(DISTINCT ref_c.document_id)
20
- FROM mentions ref_m
21
- JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
22
- WHERE ref_m.symbol_id = gs.id AND ref_m.role != 1
23
- ) AS fan_in,
24
- (SELECT COUNT(DISTINCT ref_gs.id)
25
- FROM mentions ref_m
26
- JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
27
- JOIN global_symbols ref_gs ON ref_m.symbol_id = ref_gs.id
28
- JOIN defn_enclosing_ranges ref_der ON ref_gs.id = ref_der.symbol_id
29
- WHERE ref_c.document_id = def_d.id
30
- AND ref_m.role != 1
31
- AND ref_der.document_id != def_d.id
32
- ) AS fan_out
33
- FROM global_symbols gs
34
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
35
- JOIN documents def_d ON der.document_id = def_d.id
36
- WHERE 1 = 1
37
- ${db.pathExclusionsFor("def_d")}
38
- ${db.symbolNoiseFor("gs")}
39
- ${scopeFilter}
40
- ) WHERE fan_in >= ? AND fan_out >= ?
41
- ORDER BY (fan_in * fan_out) DESC
42
- LIMIT ?`,
43
- minFanIn,
44
- minFanOut,
45
- limit
46
- );
47
- const indexedResults = rows.filter((r) => !db.isIgnored(r.defined_in)).map((r) => ({
48
- symbol: r.symbol,
49
- shortName: shortenSymbol(r.symbol),
50
- fanIn: r.fan_in,
51
- fanOut: r.fan_out,
52
- score: r.fan_in * r.fan_out,
53
- definedIn: r.defined_in
54
- }));
55
- if (indexedResults.length > 0) {
56
- return indexedResults;
57
- }
58
- return getAllDefinitions(db, { scope }).filter((definition) => !db.isIgnored(definition.relativePath)).map((definition) => {
59
- const fanIn = new Set(
60
- getCallerRowsForSymbol(db, definition, { limit: 500 }).map((row) => row.file)
61
- ).size;
62
- const fanOut = new Set(
63
- getCalleeRowsForSymbol(db, definition, { limit: 500 }).filter((row) => row.file !== definition.relativePath).map((row) => `${row.symbol}|${row.file}`)
64
- ).size;
65
- return {
66
- symbol: definition.symbol,
67
- shortName: shortenSymbol(definition.symbol),
68
- fanIn,
69
- fanOut,
70
- score: fanIn * fanOut,
71
- definedIn: definition.relativePath
72
- };
73
- }).filter((row) => row.fanIn >= minFanIn && row.fanOut >= minFanOut).sort((left, right) => right.score - left.score || right.fanIn - left.fanIn).slice(0, limit);
74
- }
75
-
76
- export {
77
- bottlenecks
78
- };
79
- //# sourceMappingURL=chunk-PGQXIAJF.js.map
@@ -1,76 +0,0 @@
1
- import {
2
- findFirstSymbolMatch,
3
- getCalleeRowsForSymbol
4
- } from "./chunk-FMAYH7GS.js";
5
- import {
6
- shortenSymbol
7
- } from "./chunk-YMSJCSRG.js";
8
-
9
- // src/queries/convergence.ts
10
- function convergence(db, symbolPatternA, symbolPatternB) {
11
- const matchA = findFirstSymbolMatch(db, symbolPatternA);
12
- const matchB = findFirstSymbolMatch(db, symbolPatternB);
13
- if (!matchA || !matchB) return null;
14
- const calleesA = new Set(
15
- getCalleeRowsForSymbol(db, matchA).map((r) => r.symbol)
16
- );
17
- const calleesB = new Set(
18
- getCalleeRowsForSymbol(db, matchB).map((r) => r.symbol)
19
- );
20
- const shared = [];
21
- for (const c of calleesA) {
22
- if (calleesB.has(c)) shared.push(c);
23
- }
24
- const uniqueA = [];
25
- for (const c of calleesA) {
26
- if (!calleesB.has(c)) uniqueA.push(c);
27
- }
28
- const uniqueB = [];
29
- for (const c of calleesB) {
30
- if (!calleesA.has(c)) uniqueB.push(c);
31
- }
32
- const union = /* @__PURE__ */ new Set([...calleesA, ...calleesB]);
33
- const similarity = union.size > 0 ? shared.length / union.size : 0;
34
- let strategy;
35
- if (union.size === 0) {
36
- strategy = "Neither function calls other tracked symbols. There is no callee-pattern evidence for consolidation; inspect the source bodies directly.";
37
- } else if (shared.length === 0) {
38
- strategy = "These functions do not share any callees. They are not a callee-based consolidation candidate.";
39
- } else if (uniqueA.length === 0 && uniqueB.length === 0) {
40
- strategy = "These functions have identical tracked callee sets. They are a strong structural match, but identical callees do not prove interchangeable semantics; inspect signatures, control flow, and return values before consolidating.";
41
- } else if (uniqueA.length === 0) {
42
- strategy = `A's tracked callees are a subset of B's. B may subsume part of A's structure, but verify signatures, guards, and non-call logic before replacing A with B.`;
43
- } else if (uniqueB.length === 0) {
44
- strategy = `B's tracked callees are a subset of A's. A may subsume part of B's structure, but verify signatures, guards, and non-call logic before replacing B with A.`;
45
- } else if (uniqueA.length <= 2 && uniqueB.length <= 2) {
46
- strategy = `Create a shared function with the ${shared.length} common callees. Pass the ${uniqueA.length + uniqueB.length} divergent callees as parameters or strategy callbacks.`;
47
- } else {
48
- strategy = `Extract the ${shared.length} shared callees into a common helper. Each function calls the helper plus its own unique logic (${uniqueA.length} callees in A, ${uniqueB.length} in B).`;
49
- }
50
- const locA = matchA.endLine - matchA.startLine + 1;
51
- const locB = matchB.endLine - matchB.startLine + 1;
52
- return {
53
- symbolA: {
54
- symbol: matchA.symbol,
55
- shortName: shortenSymbol(matchA.symbol),
56
- file: matchA.relativePath,
57
- loc: locA
58
- },
59
- symbolB: {
60
- symbol: matchB.symbol,
61
- shortName: shortenSymbol(matchB.symbol),
62
- file: matchB.relativePath,
63
- loc: locB
64
- },
65
- similarity,
66
- sharedCallees: shared.map(shortenSymbol),
67
- uniqueToA: uniqueA.map(shortenSymbol),
68
- uniqueToB: uniqueB.map(shortenSymbol),
69
- consolidationStrategy: strategy
70
- };
71
- }
72
-
73
- export {
74
- convergence
75
- };
76
- //# sourceMappingURL=chunk-PKDFXASW.js.map
@@ -1,82 +0,0 @@
1
- import {
2
- cleanSignature
3
- } from "./chunk-4TYLS5XX.js";
4
- import {
5
- resolveIndexedPaths
6
- } from "./chunk-FMAYH7GS.js";
7
- import {
8
- shortenSymbol
9
- } from "./chunk-YMSJCSRG.js";
10
-
11
- // src/queries/system.ts
12
- function system(db, modulePattern) {
13
- const matchedPaths = resolveIndexedPaths(db, modulePattern);
14
- if (matchedPaths.length === 0) {
15
- return { files: [], symbols: [], dependsOn: [], dependedOnBy: [] };
16
- }
17
- const placeholders = matchedPaths.map(() => "?").join(", ");
18
- const fileRows = db.all(
19
- `SELECT relative_path FROM documents
20
- WHERE relative_path IN (${placeholders})
21
- ORDER BY relative_path`,
22
- ...matchedPaths
23
- );
24
- const files = fileRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
25
- const symbolRows = db.all(
26
- `SELECT der.start_line, der.end_line, gs.symbol,
27
- REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
28
- FROM defn_enclosing_ranges der
29
- JOIN global_symbols gs ON der.symbol_id = gs.id
30
- JOIN documents d ON der.document_id = d.id
31
- WHERE d.relative_path IN (${placeholders})
32
- AND ${db.localSymbolPredicate}
33
- ${db.symbolNoise}
34
- AND gs.documentation IS NOT NULL
35
- ORDER BY d.relative_path, der.start_line`,
36
- ...matchedPaths
37
- );
38
- const symbols = symbolRows.map((r) => ({
39
- startLine: r.start_line,
40
- endLine: r.end_line,
41
- symbol: r.symbol,
42
- shortName: shortenSymbol(r.symbol),
43
- signature: cleanSignature(r.sig)
44
- }));
45
- const depRows = db.all(
46
- `SELECT DISTINCT d2.relative_path
47
- FROM mentions m
48
- JOIN chunks c ON m.chunk_id = c.id
49
- JOIN documents d1 ON c.document_id = d1.id
50
- JOIN global_symbols gs ON m.symbol_id = gs.id
51
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
52
- JOIN documents d2 ON der.document_id = d2.id
53
- WHERE d1.relative_path IN (${placeholders})
54
- AND d2.relative_path NOT IN (${placeholders})
55
- AND ${db.localSymbolPredicate}
56
- ORDER BY d2.relative_path`,
57
- ...matchedPaths,
58
- ...matchedPaths
59
- );
60
- const dependsOn = depRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
61
- const rdepRows = db.all(
62
- `SELECT DISTINCT d1.relative_path
63
- FROM mentions m
64
- JOIN chunks c ON m.chunk_id = c.id
65
- JOIN documents d1 ON c.document_id = d1.id
66
- JOIN global_symbols gs ON m.symbol_id = gs.id
67
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
68
- JOIN documents d2 ON der.document_id = d2.id
69
- WHERE d2.relative_path IN (${placeholders})
70
- AND d1.relative_path NOT IN (${placeholders})
71
- ORDER BY d1.relative_path`,
72
- ...matchedPaths,
73
- ...matchedPaths
74
- );
75
- const dependedOnBy = rdepRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
76
- return { files, symbols, dependsOn, dependedOnBy };
77
- }
78
-
79
- export {
80
- system
81
- };
82
- //# sourceMappingURL=chunk-PSK5BPFE.js.map
@@ -1,341 +0,0 @@
1
- import {
2
- findFirstSymbolMatch,
3
- getAllDefinitions,
4
- getCalleeRowsForSymbol,
5
- getSourceText
6
- } from "./chunk-FMAYH7GS.js";
7
- import {
8
- isFunctionLikeSymbol,
9
- leafName,
10
- shortenSymbol
11
- } from "./chunk-YMSJCSRG.js";
12
-
13
- // src/queries/similar.ts
14
- function similar(db, symbolPattern, opts = {}) {
15
- const { minSimilarity = 0.4, limit = 20 } = opts;
16
- const target = findCallees(db, symbolPattern);
17
- if (!target) return [];
18
- if (!isFunctionLikeSymbol(target.symbol)) return [];
19
- const candidates = getAllCalleeFingerprints(db, {
20
- minCallees: 3,
21
- excludeSymbol: target.symbol
22
- });
23
- const allFingerprints = [target, ...candidates];
24
- const idfWeights = computeIdf(allFingerprints);
25
- const results = [];
26
- for (const candidate of candidates) {
27
- if (candidate.callees.size < 3) continue;
28
- const { similarity, significantShared } = weightedSimilarity(
29
- target.callees,
30
- candidate.callees,
31
- idfWeights
32
- );
33
- if (similarity < minSimilarity) continue;
34
- if (significantShared.length < 1) continue;
35
- results.push({
36
- symbolA: target.symbol,
37
- shortNameA: shortenSymbol(target.symbol),
38
- fileA: target.file,
39
- symbolB: candidate.symbol,
40
- shortNameB: shortenSymbol(candidate.symbol),
41
- fileB: candidate.file,
42
- similarity,
43
- sharedCallees: significantShared.map(shortenSymbol),
44
- uniqueToA: [...difference(target.callees, candidate.callees)].map(shortenSymbol),
45
- uniqueToB: [...difference(candidate.callees, target.callees)].map(shortenSymbol)
46
- });
47
- }
48
- results.sort((a, b) => b.similarity - a.similarity);
49
- if (results.length > 0) {
50
- return results.slice(0, limit);
51
- }
52
- return similarBySourceShape(db, symbolPattern, { minSimilarity, limit });
53
- }
54
- function similarAll(db, opts = {}) {
55
- const { minSimilarity = 0.5, limit = 20, scope, minCallees = 4, crossFileOnly = false } = opts;
56
- const all = getAllCalleeFingerprints(db, { minCallees, scope });
57
- const idfWeights = computeIdf(all);
58
- const results = [];
59
- for (let i = 0; i < all.length; i++) {
60
- for (let j = i + 1; j < all.length; j++) {
61
- const a = all[i];
62
- const b = all[j];
63
- if (crossFileOnly && a.file === b.file) continue;
64
- const { similarity, significantShared } = weightedSimilarity(
65
- a.callees,
66
- b.callees,
67
- idfWeights
68
- );
69
- if (similarity < minSimilarity) continue;
70
- const sharedCount = intersection(a.callees, b.callees).size;
71
- if (significantShared.length < 2 && sharedCount < 4) continue;
72
- const displayShared = significantShared.length > 0 ? significantShared : [...intersection(a.callees, b.callees)];
73
- results.push({
74
- symbolA: a.symbol,
75
- shortNameA: shortenSymbol(a.symbol),
76
- fileA: a.file,
77
- symbolB: b.symbol,
78
- shortNameB: shortenSymbol(b.symbol),
79
- fileB: b.file,
80
- similarity,
81
- sharedCallees: displayShared.map(shortenSymbol),
82
- uniqueToA: [...difference(a.callees, b.callees)].map(shortenSymbol),
83
- uniqueToB: [...difference(b.callees, a.callees)].map(shortenSymbol)
84
- });
85
- }
86
- if (results.length > limit * 5) break;
87
- }
88
- results.sort((a, b) => b.similarity - a.similarity);
89
- return results.slice(0, limit);
90
- }
91
- function computeIdf(fingerprints) {
92
- const n = fingerprints.length;
93
- if (n === 0) return /* @__PURE__ */ new Map();
94
- const docFreq = /* @__PURE__ */ new Map();
95
- for (const fp of fingerprints) {
96
- for (const callee of fp.callees) {
97
- docFreq.set(callee, (docFreq.get(callee) ?? 0) + 1);
98
- }
99
- }
100
- const idf = /* @__PURE__ */ new Map();
101
- for (const [callee, df] of docFreq) {
102
- idf.set(callee, Math.log(n / df));
103
- }
104
- return idf;
105
- }
106
- function weightedSimilarity(a, b, idf) {
107
- const shared = intersection(a, b);
108
- if (shared.size === 0) return { similarity: 0, significantShared: [], trivialShared: [] };
109
- let dotProduct = 0;
110
- let magA = 0;
111
- let magB = 0;
112
- const allCallees = /* @__PURE__ */ new Set([...a, ...b]);
113
- for (const callee of allCallees) {
114
- const weight = idf.get(callee) ?? 0;
115
- const inA = a.has(callee) ? weight : 0;
116
- const inB = b.has(callee) ? weight : 0;
117
- dotProduct += inA * inB;
118
- magA += inA * inA;
119
- magB += inB * inB;
120
- }
121
- const magnitude = Math.sqrt(magA) * Math.sqrt(magB);
122
- const similarity = magnitude > 0 ? dotProduct / magnitude : 0;
123
- const medianIdf = getMedianIdf(idf);
124
- const significantShared = [];
125
- const trivialShared = [];
126
- for (const callee of shared) {
127
- const weight = idf.get(callee) ?? 0;
128
- if (weight >= medianIdf) {
129
- significantShared.push(callee);
130
- } else {
131
- trivialShared.push(callee);
132
- }
133
- }
134
- significantShared.sort((x, y) => (idf.get(y) ?? 0) - (idf.get(x) ?? 0));
135
- return { similarity, significantShared, trivialShared };
136
- }
137
- function getMedianIdf(idf) {
138
- const values = [...idf.values()].sort((a, b) => a - b);
139
- if (values.length === 0) return 0;
140
- const mid = Math.floor(values.length / 2);
141
- return values.length % 2 === 0 ? (values[mid - 1] + values[mid]) / 2 : values[mid];
142
- }
143
- function findCallees(db, symbolPattern) {
144
- const target = findFirstSymbolMatch(db, symbolPattern);
145
- if (!target) return null;
146
- const calleeRows = getCalleeRowsForSymbol(db, target);
147
- return {
148
- symbol: target.symbol,
149
- file: target.relativePath,
150
- callees: new Set(calleeRows.map((r) => r.symbol))
151
- };
152
- }
153
- function getAllCalleeFingerprints(db, opts) {
154
- const { minCallees, scope, excludeSymbol } = opts;
155
- const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
156
- const excludeFilter = excludeSymbol ? `AND gs.symbol != '${excludeSymbol.replace(/'/g, "''")}'` : "";
157
- const symbols = db.all(
158
- `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
159
- FROM global_symbols gs
160
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
161
- JOIN documents d ON der.document_id = d.id
162
- WHERE 1 = 1
163
- ${db.pathExclusionsFor("d")}
164
- ${db.symbolNoiseFor("gs")}
165
- AND (der.end_line - der.start_line + 1) >= 5
166
- ${scopeFilter}
167
- ${excludeFilter}
168
- ORDER BY d.relative_path`
169
- );
170
- const fingerprints = [];
171
- for (const sym of symbols) {
172
- if (db.isIgnored(sym.relative_path)) continue;
173
- if (!isFunctionLikeSymbol(sym.symbol)) continue;
174
- const calleeRows = getCalleeRowsForSymbol(db, {
175
- documentId: sym.document_id,
176
- startLine: sym.start_line,
177
- endLine: sym.end_line,
178
- symbolId: sym.id
179
- });
180
- const callees = new Set(calleeRows.map((r) => r.symbol));
181
- if (callees.size >= minCallees) {
182
- fingerprints.push({ symbol: sym.symbol, file: sym.relative_path, callees });
183
- }
184
- }
185
- return fingerprints;
186
- }
187
- function intersection(a, b) {
188
- const result = /* @__PURE__ */ new Set();
189
- for (const item of a) {
190
- if (b.has(item)) result.add(item);
191
- }
192
- return result;
193
- }
194
- function difference(a, b) {
195
- const result = /* @__PURE__ */ new Set();
196
- for (const item of a) {
197
- if (!b.has(item)) result.add(item);
198
- }
199
- return result;
200
- }
201
- function similarBySourceShape(db, symbolPattern, opts) {
202
- const target = findSourceFingerprint(db, symbolPattern);
203
- if (!target || target.tokens.size < 3) {
204
- return [];
205
- }
206
- const minSimilarity = opts.minSimilarity >= 0.5 ? opts.minSimilarity : 0.3;
207
- const results = [];
208
- for (const candidate of getAllSourceFingerprints(db)) {
209
- if (candidate.symbol === target.symbol || candidate.tokens.size < 3) continue;
210
- const shared = intersection(target.tokens, candidate.tokens);
211
- if (shared.size < 2) continue;
212
- const union = /* @__PURE__ */ new Set([...target.tokens, ...candidate.tokens]);
213
- const similarity = union.size > 0 ? shared.size / union.size : 0;
214
- if (similarity < minSimilarity) continue;
215
- results.push({
216
- symbolA: target.symbol,
217
- shortNameA: shortenSymbol(target.symbol),
218
- fileA: target.file,
219
- symbolB: candidate.symbol,
220
- shortNameB: shortenSymbol(candidate.symbol),
221
- fileB: candidate.file,
222
- similarity,
223
- sharedCallees: [...shared].sort(),
224
- uniqueToA: [...difference(target.tokens, candidate.tokens)].sort(),
225
- uniqueToB: [...difference(candidate.tokens, target.tokens)].sort()
226
- });
227
- }
228
- results.sort((a, b) => b.similarity - a.similarity || a.shortNameB.localeCompare(b.shortNameB));
229
- return results.slice(0, opts.limit);
230
- }
231
- function findSourceFingerprint(db, symbolPattern) {
232
- const match = findFirstSymbolMatch(db, symbolPattern);
233
- if (!match || !isFunctionLikeSymbol(match.symbol)) {
234
- return null;
235
- }
236
- const snippet = definitionSnippet(db, match.relativePath, match.startLine, match.endLine, leafName(match.symbol));
237
- const tokens = sourceTokens(snippet, leafName(match.symbol));
238
- if (tokens.size === 0) {
239
- return null;
240
- }
241
- return {
242
- symbol: match.symbol,
243
- file: match.relativePath,
244
- tokens
245
- };
246
- }
247
- function getAllSourceFingerprints(db) {
248
- return getAllDefinitions(db).filter((definition) => definition.isFunctionLike).map((definition) => ({
249
- symbol: definition.symbol,
250
- file: definition.relativePath,
251
- tokens: sourceTokens(
252
- definitionSnippet(db, definition.relativePath, definition.startLine, definition.endLine, definition.leaf),
253
- definition.leaf
254
- )
255
- })).filter((fingerprint) => fingerprint.tokens.size > 0);
256
- }
257
- function definitionSnippet(db, relativePath, startLine, endLine, leaf) {
258
- const source = getSourceText(db, relativePath);
259
- if (!source) {
260
- return "";
261
- }
262
- const lines = source.split("\n");
263
- if (endLine >= startLine && endLine - startLine <= 12) {
264
- return lines.slice(startLine, endLine + 1).join("\n");
265
- }
266
- const markerPatterns = [
267
- new RegExp(`\\bdef\\s+${escapeRegex(leaf)}\\b`),
268
- new RegExp(`\\bfun\\s+${escapeRegex(leaf)}\\b`),
269
- new RegExp(`\\bfn\\s+${escapeRegex(leaf)}\\b`),
270
- new RegExp(`\\bfunction\\s+${escapeRegex(leaf)}\\b`),
271
- new RegExp(`\\b${escapeRegex(leaf)}\\s*\\(`)
272
- ];
273
- const definitionStart = lines.findIndex((line) => markerPatterns.some((pattern) => pattern.test(line)));
274
- if (definitionStart >= 0) {
275
- let definitionEnd = definitionStart;
276
- for (let index = definitionStart + 1; index < lines.length && index <= definitionStart + 8; index++) {
277
- const line = lines[index] ?? "";
278
- if (index > definitionStart && looksLikeDefinitionBoundary(line)) {
279
- break;
280
- }
281
- definitionEnd = index;
282
- if (line.trim() === "" && index > definitionStart + 1) {
283
- break;
284
- }
285
- }
286
- return lines.slice(definitionStart, definitionEnd + 1).join("\n");
287
- }
288
- return lines.slice(startLine, Math.min(lines.length, startLine + 8)).join("\n");
289
- }
290
- function sourceTokens(snippet, leaf) {
291
- if (!snippet) {
292
- return /* @__PURE__ */ new Set();
293
- }
294
- const stopWords = /* @__PURE__ */ new Set([
295
- "public",
296
- "private",
297
- "protected",
298
- "final",
299
- "static",
300
- "class",
301
- "def",
302
- "fun",
303
- "fn",
304
- "function",
305
- "return",
306
- "string",
307
- "bool",
308
- "boolean",
309
- "void",
310
- "unit",
311
- "self",
312
- "this",
313
- "new",
314
- "const",
315
- "let",
316
- "var",
317
- "end",
318
- "pub"
319
- ]);
320
- const normalizedLeafParts = splitIdentifier(leaf);
321
- const normalized = snippet.replace(/["'`]/g, " ").replace(/\b\d+\b/g, " NUM ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^A-Za-z0-9_]+/g, " ").replace(/_/g, " ").toLowerCase();
322
- const tokens = normalized.split(/\s+/).map((token) => token.trim()).filter((token) => token.length > 1).filter((token) => !stopWords.has(token)).filter((token) => !normalizedLeafParts.has(token));
323
- return new Set(tokens);
324
- }
325
- function splitIdentifier(value) {
326
- return new Set(
327
- value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9_]+|_/).map((part) => part.toLowerCase()).filter((part) => part.length > 1)
328
- );
329
- }
330
- function looksLikeDefinitionBoundary(line) {
331
- return /^\s*(?:def|fun|fn|function|class|trait|module|object|enum|interface|public|private|protected)\b/.test(line);
332
- }
333
- function escapeRegex(value) {
334
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
335
- }
336
-
337
- export {
338
- similar,
339
- similarAll
340
- };
341
- //# sourceMappingURL=chunk-QZ4FRB65.js.map