scip-query 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +16 -43
  2. package/dist/chunk-26DOJ63W.js +161 -0
  3. package/dist/chunk-2UELLEBI.js +1 -0
  4. package/dist/{chunk-4PDAL6IL.js → chunk-4JCSOF2O.js} +3 -3
  5. package/dist/{chunk-6SXADWLW.js → chunk-5OMVSV6E.js} +13 -5
  6. package/dist/{chunk-KPPHZCZJ.js → chunk-7HK5ZLOE.js} +28 -46
  7. package/dist/{chunk-7RLE5EWE.js → chunk-7KIMF5PV.js} +34 -13
  8. package/dist/chunk-AXQKUYKF.js +1442 -0
  9. package/dist/chunk-C7H5WBTJ.js +46 -0
  10. package/dist/{chunk-NHBZIL2J.js → chunk-CHDJXYBG.js} +3 -28
  11. package/dist/{chunk-KCBMVQL5.js → chunk-CPVAQJEC.js} +13 -5
  12. package/dist/{chunk-ZQIIPFD7.js → chunk-DH7G3DDV.js} +2 -2
  13. package/dist/{chunk-BOVXCR46.js → chunk-EOROMIFO.js} +14 -6
  14. package/dist/{chunk-2CKGIR6G.js → chunk-EPWLXXBL.js} +3 -3
  15. package/dist/{chunk-5RMYT5WH.js → chunk-F7XU27LU.js} +2 -2
  16. package/dist/{chunk-63G7IQTD.js → chunk-FYYOWQXK.js} +20 -40
  17. package/dist/chunk-GEXE2T6I.js +87 -0
  18. package/dist/{chunk-DGUPQSOR.js → chunk-GJDHTTR2.js} +11 -4
  19. package/dist/chunk-GSH2FPKV.js +87 -0
  20. package/dist/{chunk-NUZ4OMU3.js → chunk-GU2H5QRN.js} +2 -2
  21. package/dist/{chunk-Z6YZJ36C.js → chunk-HJZUSUPU.js} +8 -4
  22. package/dist/{chunk-LAWMH22O.js → chunk-HLKAFWWJ.js} +82 -3
  23. package/dist/{chunk-HPFZLISB.js → chunk-HLUS2HEB.js} +2 -2
  24. package/dist/{chunk-H2MDONBU.js → chunk-J3JSOSUO.js} +9 -6
  25. package/dist/{chunk-7PBOG4YE.js → chunk-KBOQX573.js} +2 -2
  26. package/dist/{chunk-HDSRORNV.js → chunk-KKCHYLVI.js} +17 -11
  27. package/dist/{chunk-HMLMH7VZ.js → chunk-LFJQVJYJ.js} +2 -2
  28. package/dist/{chunk-4EXL2CUA.js → chunk-LQJUPXQY.js} +16 -8
  29. package/dist/{chunk-ITZ3DDOG.js → chunk-MPGIHELS.js} +18 -3
  30. package/dist/chunk-NFS5W3PP.js +37 -0
  31. package/dist/chunk-NG5F43OU.js +200 -0
  32. package/dist/{chunk-DCKMSTJ4.js → chunk-O7Q7FDUJ.js} +22 -14
  33. package/dist/{chunk-UJQN5N3I.js → chunk-OIDHN6GD.js} +6 -3
  34. package/dist/{chunk-Z4GHE2HD.js → chunk-P3E6L7KW.js} +6 -2
  35. package/dist/{chunk-BNN2RKD2.js → chunk-P4WO3BBW.js} +3 -3
  36. package/dist/{chunk-QOV2R2WT.js → chunk-QIXNAB5K.js} +42 -2
  37. package/dist/{chunk-NWCE4CIC.js → chunk-SMDCNPMK.js} +11 -28
  38. package/dist/{chunk-SEFSL2GF.js → chunk-TOIEB3LG.js} +2 -2
  39. package/dist/{chunk-4XHWPRAX.js → chunk-UGQKAVCD.js} +3 -3
  40. package/dist/{chunk-OVPLOMPY.js → chunk-UQEQ6AHX.js} +7 -4
  41. package/dist/{chunk-ZK6GXM3J.js → chunk-VIYSWZCO.js} +3 -3
  42. package/dist/chunk-VJJKSGIX.js +121 -0
  43. package/dist/{chunk-N5KEREIA.js → chunk-VT4JBH6L.js} +19 -7
  44. package/dist/{chunk-7LLPRPR5.js → chunk-WGAD3GNR.js} +2 -2
  45. package/dist/chunk-YDBXNPYU.js +69 -0
  46. package/dist/chunk-YY4QGUQ5.js +84 -0
  47. package/dist/{chunk-FGXRVW7G.js → chunk-YZ6L7GFO.js} +2 -2
  48. package/dist/{chunk-W4ALF422.js → chunk-ZEUCXQBN.js} +3 -3
  49. package/dist/cli.js +2697 -1054
  50. package/dist/{db-BNVVZSfP.d.ts → db-ShvwGDKf.d.ts} +12 -19
  51. package/dist/index.d.ts +15 -15
  52. package/dist/index.js +263 -234
  53. package/dist/postinstall.js +5 -76
  54. package/dist/queries/affected.d.ts +1 -1
  55. package/dist/queries/affected.js +3 -3
  56. package/dist/queries/bottlenecks.d.ts +1 -1
  57. package/dist/queries/bottlenecks.js +2 -2
  58. package/dist/queries/by-kind.d.ts +1 -1
  59. package/dist/queries/by-kind.js +2 -2
  60. package/dist/queries/call-graph.d.ts +1 -1
  61. package/dist/queries/call-graph.js +3 -3
  62. package/dist/queries/change-surface.d.ts +2 -2
  63. package/dist/queries/change-surface.js +3 -3
  64. package/dist/queries/code.d.ts +1 -1
  65. package/dist/queries/code.js +3 -3
  66. package/dist/queries/complexity-hotspots.d.ts +1 -1
  67. package/dist/queries/complexity-hotspots.js +3 -3
  68. package/dist/queries/complexity.d.ts +1 -1
  69. package/dist/queries/complexity.js +3 -3
  70. package/dist/queries/convergence.d.ts +1 -1
  71. package/dist/queries/convergence.js +3 -3
  72. package/dist/queries/coupling.d.ts +1 -1
  73. package/dist/queries/coupling.js +3 -1
  74. package/dist/queries/cycles.d.ts +1 -1
  75. package/dist/queries/cycles.js +3 -2
  76. package/dist/queries/dataflow.d.ts +1 -1
  77. package/dist/queries/dataflow.js +3 -3
  78. package/dist/queries/dead.d.ts +1 -1
  79. package/dist/queries/dead.js +4 -3
  80. package/dist/queries/deep-chains.d.ts +1 -1
  81. package/dist/queries/deep-chains.js +3 -2
  82. package/dist/queries/deps.d.ts +1 -1
  83. package/dist/queries/deps.js +3 -1
  84. package/dist/queries/diff-impact.d.ts +2 -2
  85. package/dist/queries/diff-impact.js +2 -3
  86. package/dist/queries/doc-coverage.d.ts +1 -1
  87. package/dist/queries/doc-coverage.js +2 -2
  88. package/dist/queries/drift.d.ts +1 -1
  89. package/dist/queries/drift.js +3 -2
  90. package/dist/queries/extract-candidates.d.ts +1 -1
  91. package/dist/queries/extract-candidates.js +3 -3
  92. package/dist/queries/fan.d.ts +1 -1
  93. package/dist/queries/fan.js +3 -2
  94. package/dist/queries/files.d.ts +1 -1
  95. package/dist/queries/health.d.ts +1 -1
  96. package/dist/queries/health.js +14 -14
  97. package/dist/queries/hierarchy.d.ts +1 -1
  98. package/dist/queries/hierarchy.js +3 -2
  99. package/dist/queries/hotspots.d.ts +1 -1
  100. package/dist/queries/hotspots.js +2 -2
  101. package/dist/queries/imports.d.ts +1 -1
  102. package/dist/queries/imports.js +3 -2
  103. package/dist/queries/index.d.ts +1 -2
  104. package/dist/queries/index.js +46 -51
  105. package/dist/queries/isolated.d.ts +1 -1
  106. package/dist/queries/isolated.js +4 -3
  107. package/dist/queries/members.d.ts +2 -2
  108. package/dist/queries/members.js +3 -2
  109. package/dist/queries/methods.d.ts +1 -1
  110. package/dist/queries/methods.js +2 -2
  111. package/dist/queries/outline.d.ts +1 -1
  112. package/dist/queries/outline.js +3 -2
  113. package/dist/queries/passthrough-candidates.d.ts +1 -1
  114. package/dist/queries/passthrough-candidates.js +3 -3
  115. package/dist/queries/redundant-reexports.d.ts +1 -1
  116. package/dist/queries/redundant-reexports.js +4 -2
  117. package/dist/queries/refs.d.ts +1 -1
  118. package/dist/queries/refs.js +3 -1
  119. package/dist/queries/similar-chains.d.ts +1 -1
  120. package/dist/queries/similar-chains.js +3 -2
  121. package/dist/queries/similar-files.d.ts +1 -1
  122. package/dist/queries/similar-files.js +3 -2
  123. package/dist/queries/similar-signatures.d.ts +1 -1
  124. package/dist/queries/similar-signatures.js +2 -2
  125. package/dist/queries/similar.d.ts +1 -1
  126. package/dist/queries/similar.js +3 -3
  127. package/dist/queries/slice.d.ts +2 -2
  128. package/dist/queries/slice.js +3 -3
  129. package/dist/queries/stale-abstractions.d.ts +1 -1
  130. package/dist/queries/stale-abstractions.js +3 -3
  131. package/dist/queries/stats.d.ts +1 -1
  132. package/dist/queries/surface.d.ts +1 -1
  133. package/dist/queries/surface.js +3 -2
  134. package/dist/queries/symbols.d.ts +1 -1
  135. package/dist/queries/symbols.js +3 -2
  136. package/dist/queries/system.d.ts +1 -1
  137. package/dist/queries/system.js +3 -2
  138. package/dist/queries/trace.d.ts +1 -1
  139. package/dist/queries/trace.js +3 -1
  140. package/dist/queries/wrapper-candidates.d.ts +1 -1
  141. package/dist/queries/wrapper-candidates.js +3 -3
  142. package/dist/reindex-worker.js +24 -12
  143. package/package.json +6 -1
  144. package/IMPROVEMENTS.md +0 -143
  145. package/PLAN.md +0 -320
  146. package/dist/chunk-2CKGIR6G.js.map +0 -1
  147. package/dist/chunk-3UOUTZQT.js +0 -45
  148. package/dist/chunk-3UOUTZQT.js.map +0 -1
  149. package/dist/chunk-4EXL2CUA.js.map +0 -1
  150. package/dist/chunk-4PDAL6IL.js.map +0 -1
  151. package/dist/chunk-4TYLS5XX.js.map +0 -1
  152. package/dist/chunk-4XHWPRAX.js.map +0 -1
  153. package/dist/chunk-5RMYT5WH.js.map +0 -1
  154. package/dist/chunk-63G7IQTD.js.map +0 -1
  155. package/dist/chunk-6SXADWLW.js.map +0 -1
  156. package/dist/chunk-74RFWB5T.js.map +0 -1
  157. package/dist/chunk-7LLPRPR5.js.map +0 -1
  158. package/dist/chunk-7PBOG4YE.js.map +0 -1
  159. package/dist/chunk-7RLE5EWE.js.map +0 -1
  160. package/dist/chunk-7UCKSQRS.js +0 -55
  161. package/dist/chunk-7UCKSQRS.js.map +0 -1
  162. package/dist/chunk-BNN2RKD2.js.map +0 -1
  163. package/dist/chunk-BOVXCR46.js.map +0 -1
  164. package/dist/chunk-D567NFIF.js +0 -65
  165. package/dist/chunk-D567NFIF.js.map +0 -1
  166. package/dist/chunk-DCKMSTJ4.js.map +0 -1
  167. package/dist/chunk-DEZKCZXD.js +0 -40
  168. package/dist/chunk-DEZKCZXD.js.map +0 -1
  169. package/dist/chunk-DGUPQSOR.js.map +0 -1
  170. package/dist/chunk-DVWGWHFW.js +0 -99
  171. package/dist/chunk-DVWGWHFW.js.map +0 -1
  172. package/dist/chunk-EQYLEQCW.js +0 -46
  173. package/dist/chunk-EQYLEQCW.js.map +0 -1
  174. package/dist/chunk-FGXRVW7G.js.map +0 -1
  175. package/dist/chunk-H2MDONBU.js.map +0 -1
  176. package/dist/chunk-HB7MRLLL.js +0 -76
  177. package/dist/chunk-HB7MRLLL.js.map +0 -1
  178. package/dist/chunk-HDSRORNV.js.map +0 -1
  179. package/dist/chunk-HMLMH7VZ.js.map +0 -1
  180. package/dist/chunk-HPFZLISB.js.map +0 -1
  181. package/dist/chunk-HZBC7PPD.js +0 -88
  182. package/dist/chunk-HZBC7PPD.js.map +0 -1
  183. package/dist/chunk-ITZ3DDOG.js.map +0 -1
  184. package/dist/chunk-JJP7KQND.js +0 -1
  185. package/dist/chunk-JJP7KQND.js.map +0 -1
  186. package/dist/chunk-KCBMVQL5.js.map +0 -1
  187. package/dist/chunk-KPPHZCZJ.js.map +0 -1
  188. package/dist/chunk-LAWMH22O.js.map +0 -1
  189. package/dist/chunk-MCUX5LA7.js +0 -103
  190. package/dist/chunk-MCUX5LA7.js.map +0 -1
  191. package/dist/chunk-MGNMHKX3.js.map +0 -1
  192. package/dist/chunk-N5KEREIA.js.map +0 -1
  193. package/dist/chunk-NHBZIL2J.js.map +0 -1
  194. package/dist/chunk-NUZ4OMU3.js.map +0 -1
  195. package/dist/chunk-NWCE4CIC.js.map +0 -1
  196. package/dist/chunk-OVPLOMPY.js.map +0 -1
  197. package/dist/chunk-QOV2R2WT.js.map +0 -1
  198. package/dist/chunk-SEFSL2GF.js.map +0 -1
  199. package/dist/chunk-UJQN5N3I.js.map +0 -1
  200. package/dist/chunk-W4ALF422.js.map +0 -1
  201. package/dist/chunk-Z4GHE2HD.js.map +0 -1
  202. package/dist/chunk-Z6YZJ36C.js.map +0 -1
  203. package/dist/chunk-ZK6GXM3J.js.map +0 -1
  204. package/dist/chunk-ZOGY2V3N.js +0 -158
  205. package/dist/chunk-ZOGY2V3N.js.map +0 -1
  206. package/dist/chunk-ZQIIPFD7.js.map +0 -1
  207. package/dist/cli.js.map +0 -1
  208. package/dist/index.js.map +0 -1
  209. package/dist/postinstall.js.map +0 -1
  210. package/dist/queries/affected.js.map +0 -1
  211. package/dist/queries/bottlenecks.js.map +0 -1
  212. package/dist/queries/by-kind.js.map +0 -1
  213. package/dist/queries/call-graph.js.map +0 -1
  214. package/dist/queries/change-surface.js.map +0 -1
  215. package/dist/queries/clean-signature.js.map +0 -1
  216. package/dist/queries/code.js.map +0 -1
  217. package/dist/queries/complexity-hotspots.js.map +0 -1
  218. package/dist/queries/complexity.js.map +0 -1
  219. package/dist/queries/convergence.js.map +0 -1
  220. package/dist/queries/coupling.js.map +0 -1
  221. package/dist/queries/cycles.js.map +0 -1
  222. package/dist/queries/dataflow.js.map +0 -1
  223. package/dist/queries/dead.js.map +0 -1
  224. package/dist/queries/deep-chains.js.map +0 -1
  225. package/dist/queries/deps.js.map +0 -1
  226. package/dist/queries/diff-impact.js.map +0 -1
  227. package/dist/queries/doc-coverage.js.map +0 -1
  228. package/dist/queries/drift.js.map +0 -1
  229. package/dist/queries/extract-candidates.js.map +0 -1
  230. package/dist/queries/fan.js.map +0 -1
  231. package/dist/queries/files.js.map +0 -1
  232. package/dist/queries/health.js.map +0 -1
  233. package/dist/queries/hierarchy.js.map +0 -1
  234. package/dist/queries/hotspots.js.map +0 -1
  235. package/dist/queries/imports.js.map +0 -1
  236. package/dist/queries/index.js.map +0 -1
  237. package/dist/queries/isolated.js.map +0 -1
  238. package/dist/queries/members.js.map +0 -1
  239. package/dist/queries/methods.js.map +0 -1
  240. package/dist/queries/outline.js.map +0 -1
  241. package/dist/queries/passthrough-candidates.js.map +0 -1
  242. package/dist/queries/redundant-reexports.js.map +0 -1
  243. package/dist/queries/refs.js.map +0 -1
  244. package/dist/queries/similar-chains.js.map +0 -1
  245. package/dist/queries/similar-files.js.map +0 -1
  246. package/dist/queries/similar-signatures.js.map +0 -1
  247. package/dist/queries/similar.js.map +0 -1
  248. package/dist/queries/slice.js.map +0 -1
  249. package/dist/queries/stale-abstractions.js.map +0 -1
  250. package/dist/queries/stats.js.map +0 -1
  251. package/dist/queries/surface.js.map +0 -1
  252. package/dist/queries/symbols.js.map +0 -1
  253. package/dist/queries/system.js.map +0 -1
  254. package/dist/queries/test-coverage.d.ts +0 -22
  255. package/dist/queries/test-coverage.js +0 -11
  256. package/dist/queries/test-coverage.js.map +0 -1
  257. package/dist/queries/trace.js.map +0 -1
  258. package/dist/queries/wrapper-candidates.js.map +0 -1
  259. package/dist/reindex-worker.js.map +0 -1
  260. package/docs/AGENT_GUIDE.md +0 -359
  261. package/reports/debloat/2026-04-10-scip-query-self-audit.md +0 -161
  262. package/src/cli.ts +0 -1480
  263. package/src/config.ts +0 -117
  264. package/src/db.ts +0 -127
  265. package/src/gitignore-filter.ts +0 -143
  266. package/src/index.ts +0 -11
  267. package/src/postinstall.ts +0 -8
  268. package/src/queries/affected.ts +0 -86
  269. package/src/queries/bottlenecks.ts +0 -67
  270. package/src/queries/by-kind.ts +0 -204
  271. package/src/queries/call-graph.ts +0 -66
  272. package/src/queries/change-surface.ts +0 -110
  273. package/src/queries/clean-signature.ts +0 -22
  274. package/src/queries/code.ts +0 -101
  275. package/src/queries/complexity-hotspots.ts +0 -119
  276. package/src/queries/complexity.ts +0 -152
  277. package/src/queries/convergence.ts +0 -82
  278. package/src/queries/coupling.ts +0 -99
  279. package/src/queries/cycles.ts +0 -78
  280. package/src/queries/dataflow.ts +0 -128
  281. package/src/queries/dead.ts +0 -122
  282. package/src/queries/deep-chains.ts +0 -59
  283. package/src/queries/deps.ts +0 -46
  284. package/src/queries/diff-impact.ts +0 -204
  285. package/src/queries/doc-coverage.ts +0 -86
  286. package/src/queries/drift.ts +0 -224
  287. package/src/queries/extract-candidates.ts +0 -167
  288. package/src/queries/fan.ts +0 -148
  289. package/src/queries/files.ts +0 -16
  290. package/src/queries/health.ts +0 -324
  291. package/src/queries/hierarchy.ts +0 -49
  292. package/src/queries/hotspots.ts +0 -53
  293. package/src/queries/imports.ts +0 -95
  294. package/src/queries/index.ts +0 -45
  295. package/src/queries/isolated.ts +0 -67
  296. package/src/queries/members.ts +0 -54
  297. package/src/queries/methods.ts +0 -27
  298. package/src/queries/outline.ts +0 -52
  299. package/src/queries/passthrough-candidates.ts +0 -94
  300. package/src/queries/redundant-reexports.ts +0 -170
  301. package/src/queries/refs.ts +0 -27
  302. package/src/queries/similar-chains.ts +0 -314
  303. package/src/queries/similar-files.ts +0 -140
  304. package/src/queries/similar-signatures.ts +0 -151
  305. package/src/queries/similar.ts +0 -305
  306. package/src/queries/slice.ts +0 -154
  307. package/src/queries/stale-abstractions.ts +0 -82
  308. package/src/queries/stats.ts +0 -22
  309. package/src/queries/surface.ts +0 -34
  310. package/src/queries/symbols.ts +0 -39
  311. package/src/queries/system.ts +0 -86
  312. package/src/queries/test-coverage.ts +0 -106
  313. package/src/queries/trace.ts +0 -55
  314. package/src/queries/wrapper-candidates.ts +0 -112
  315. package/src/query-support.ts +0 -226
  316. package/src/reindex/detect.ts +0 -58
  317. package/src/reindex/index.ts +0 -153
  318. package/src/reindex/indexers.ts +0 -220
  319. package/src/reindex/install.ts +0 -125
  320. package/src/reindex-worker.ts +0 -35
  321. package/src/setup.ts +0 -202
  322. package/src/symbol-parser.ts +0 -278
  323. package/src/types.ts +0 -654
  324. package/src/watch.ts +0 -274
  325. package/tests/gitignore-filter.test.ts +0 -48
  326. package/tests/queries.test.ts +0 -300
  327. package/tests/symbol-parser.test.ts +0 -157
  328. package/tsconfig.json +0 -20
  329. package/tsup.config.ts +0 -40
  330. package/vitest.config.ts +0 -7
@@ -0,0 +1,1442 @@
1
+ import {
2
+ isFunctionLikeSymbol,
3
+ isModuleLikeSymbol,
4
+ leafName,
5
+ leafSuffix,
6
+ parseSymbol,
7
+ shortenSymbol
8
+ } from "./chunk-QIXNAB5K.js";
9
+
10
+ // src/source-analysis.ts
11
+ import {
12
+ existsSync,
13
+ readFileSync
14
+ } from "fs";
15
+ import {
16
+ dirname,
17
+ extname,
18
+ join,
19
+ relative,
20
+ resolve
21
+ } from "path";
22
+ var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
23
+ var SOURCE_TEXT_CACHE = /* @__PURE__ */ new WeakMap();
24
+ var SOURCE_CALL_CACHE = /* @__PURE__ */ new WeakMap();
25
+ var SOURCE_BINDING_CACHE = /* @__PURE__ */ new WeakMap();
26
+ var INDEXED_PATH_CACHE = /* @__PURE__ */ new WeakMap();
27
+ var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
28
+ var PYTHON_SOURCE_EXTENSIONS = [".py", ".pyi"];
29
+ function getSourceImports(db, relativePath) {
30
+ const cache = getCachedMap(SOURCE_IMPORT_CACHE, db);
31
+ const normalized = normalizePath(relativePath);
32
+ const cached = cache.get(normalized);
33
+ if (cached) {
34
+ return cached;
35
+ }
36
+ const fullPath = join(db.config.projectRoot, normalized);
37
+ if (!existsSync(fullPath)) {
38
+ cache.set(normalized, []);
39
+ return [];
40
+ }
41
+ const source = readFileSync(fullPath, "utf-8");
42
+ const parsed = isPythonSourcePath(normalized) ? parsePythonImports(db, normalized, source) : parseJavaScriptImports(db, normalized, source);
43
+ cache.set(normalized, parsed);
44
+ return parsed;
45
+ }
46
+ function getSourceCalls(db, relativePath, opts = {}) {
47
+ const normalized = normalizePath(relativePath);
48
+ if (!isPythonSourcePath(normalized) && !isJavaScriptSourcePath(normalized)) {
49
+ return [];
50
+ }
51
+ const cache = getCachedMap(SOURCE_CALL_CACHE, db);
52
+ const key = `${normalized}:${opts.startLine ?? 0}:${opts.endLine ?? Number.MAX_SAFE_INTEGER}`;
53
+ const cached = cache.get(key);
54
+ if (cached) {
55
+ return cached;
56
+ }
57
+ const source = getSourceText(db, normalized);
58
+ if (!source) {
59
+ cache.set(key, []);
60
+ return [];
61
+ }
62
+ const lines = source.split("\n");
63
+ const startLine = Math.max(0, opts.startLine ?? 0);
64
+ const endLine = Math.min(lines.length - 1, opts.endLine ?? lines.length - 1);
65
+ const scopedLines = lines.slice(startLine, endLine + 1);
66
+ const calls = isPythonSourcePath(normalized) ? parsePythonCalls(scopedLines, startLine) : parseJavaScriptCalls(scopedLines, startLine);
67
+ cache.set(key, calls);
68
+ return calls;
69
+ }
70
+ function getSourceConstructorBindings(db, relativePath, opts = {}) {
71
+ const normalized = normalizePath(relativePath);
72
+ if (!isPythonSourcePath(normalized) && !isJavaScriptSourcePath(normalized)) {
73
+ return [];
74
+ }
75
+ const cache = getCachedMap(SOURCE_BINDING_CACHE, db);
76
+ const key = `${normalized}:${opts.startLine ?? 0}:${opts.endLine ?? Number.MAX_SAFE_INTEGER}`;
77
+ const cached = cache.get(key);
78
+ if (cached) {
79
+ return cached;
80
+ }
81
+ const source = getSourceText(db, normalized);
82
+ if (!source) {
83
+ cache.set(key, []);
84
+ return [];
85
+ }
86
+ const lines = source.split("\n");
87
+ const startLine = Math.max(0, opts.startLine ?? 0);
88
+ const endLine = Math.min(lines.length - 1, opts.endLine ?? lines.length - 1);
89
+ const scopedLines = lines.slice(startLine, endLine + 1);
90
+ const bindings = isPythonSourcePath(normalized) ? parsePythonConstructorBindings(scopedLines) : parseJavaScriptConstructorBindings(scopedLines);
91
+ cache.set(key, bindings);
92
+ return bindings;
93
+ }
94
+ function findIdentifierLines(db, relativePath, identifier, opts = {}) {
95
+ if (!identifier) {
96
+ return [];
97
+ }
98
+ const source = getSourceText(db, normalizePath(relativePath));
99
+ if (!source) {
100
+ return [];
101
+ }
102
+ const lines = stripCommentsAndStrings(source).split("\n");
103
+ const regex = new RegExp(`\\b${escapeRegex(identifier)}\\b`);
104
+ const results = [];
105
+ for (let line = 0; line < lines.length; line++) {
106
+ if (typeof opts.excludeStartLine === "number" && typeof opts.excludeEndLine === "number" && line >= opts.excludeStartLine && line <= opts.excludeEndLine) {
107
+ continue;
108
+ }
109
+ if (regex.test(lines[line] ?? "")) {
110
+ results.push(line);
111
+ }
112
+ }
113
+ return results;
114
+ }
115
+ function parseJavaScriptImports(db, importerPath, source) {
116
+ return parseJavaScriptImportStatements(source).flatMap((statement) => parseJavaScriptImportStatement(
117
+ db,
118
+ importerPath,
119
+ statement.clause,
120
+ statement.specifier,
121
+ statement.start,
122
+ statement.end,
123
+ source
124
+ ));
125
+ }
126
+ function parseJavaScriptImportStatements(source) {
127
+ const statements = [];
128
+ const importFromRegex = /^[ \t]*import\s+([\s\S]*?)\s+from\s+['"]([^'"]+)['"]\s*;?/gm;
129
+ for (const match of source.matchAll(importFromRegex)) {
130
+ const full = match[0];
131
+ const clause = match[1];
132
+ const specifier = match[2];
133
+ if (!full || !specifier || typeof match.index !== "number") continue;
134
+ statements.push({
135
+ clause,
136
+ specifier,
137
+ start: match.index,
138
+ end: match.index + full.length
139
+ });
140
+ }
141
+ const sideEffectRegex = /^[ \t]*import\s+['"]([^'"]+)['"]\s*;?/gm;
142
+ for (const match of source.matchAll(sideEffectRegex)) {
143
+ const full = match[0];
144
+ const specifier = match[1];
145
+ if (!full || !specifier || typeof match.index !== "number") continue;
146
+ statements.push({
147
+ clause: null,
148
+ specifier,
149
+ start: match.index,
150
+ end: match.index + full.length
151
+ });
152
+ }
153
+ return statements.sort((a, b) => a.start - b.start);
154
+ }
155
+ function parseJavaScriptImportStatement(db, importerPath, clause, specifier, start, end, source) {
156
+ const resolvedSource = resolveImportPath(db, importerPath, specifier);
157
+ const body = buildUsageBody(source, start, end);
158
+ if (!clause) {
159
+ return [{
160
+ importedName: "*",
161
+ localName: null,
162
+ sourcePath: resolvedSource,
163
+ kind: "side-effect",
164
+ used: true,
165
+ usedMembers: []
166
+ }];
167
+ }
168
+ const bindings = parseImportClause(clause).map((binding) => ({
169
+ ...binding,
170
+ sourcePath: resolvedSource
171
+ }));
172
+ return bindings.map((binding) => {
173
+ if (binding.kind === "namespace") {
174
+ const usedMembers = collectNamespaceMembers(body, binding.localName);
175
+ return {
176
+ ...binding,
177
+ used: usedMembers.length > 0 || hasIdentifierUsage(body, binding.localName),
178
+ usedMembers
179
+ };
180
+ }
181
+ if (binding.kind === "side-effect") {
182
+ return { ...binding, used: true, usedMembers: [] };
183
+ }
184
+ return {
185
+ ...binding,
186
+ used: binding.localName ? hasIdentifierUsage(body, binding.localName) : false,
187
+ usedMembers: []
188
+ };
189
+ });
190
+ }
191
+ function parsePythonCalls(lines, baseLine) {
192
+ const calls = [];
193
+ const controlKeywords = /* @__PURE__ */ new Set([
194
+ "if",
195
+ "for",
196
+ "while",
197
+ "with",
198
+ "except",
199
+ "elif",
200
+ "return",
201
+ "yield",
202
+ "assert",
203
+ "raise",
204
+ "lambda",
205
+ "class",
206
+ "def"
207
+ ]);
208
+ for (let index = 0; index < lines.length; index++) {
209
+ const rawLine = lines[index] ?? "";
210
+ const stripped = stripCommentsAndStrings(rawLine);
211
+ if (!stripped.trim()) continue;
212
+ const attributeMatches = [...stripped.matchAll(/\b([A-Za-z_][\w]*)\s*\.\s*([A-Za-z_][\w]*)\s*\(/g)];
213
+ const attributeRanges = attributeMatches.map((match) => ({
214
+ start: match.index ?? -1,
215
+ end: (match.index ?? -1) + match[0].length
216
+ }));
217
+ for (const match of attributeMatches) {
218
+ const receiverName = match[1];
219
+ const calleeName = match[2];
220
+ if (!receiverName || !calleeName) continue;
221
+ calls.push({
222
+ receiverName,
223
+ calleeName,
224
+ line: baseLine + index
225
+ });
226
+ }
227
+ for (const match of stripped.matchAll(/\b([A-Za-z_][\w]*)\s*\(/g)) {
228
+ const calleeName = match[1];
229
+ const start = match.index ?? -1;
230
+ if (!calleeName || start < 0) continue;
231
+ if (controlKeywords.has(calleeName)) continue;
232
+ if (attributeRanges.some((range) => start >= range.start && start < range.end)) continue;
233
+ const prefix = stripped.slice(0, start).trimEnd();
234
+ if (prefix.endsWith("def") || prefix.endsWith("class") || prefix.endsWith("async def")) {
235
+ continue;
236
+ }
237
+ calls.push({
238
+ receiverName: null,
239
+ calleeName,
240
+ line: baseLine + index
241
+ });
242
+ }
243
+ }
244
+ return calls;
245
+ }
246
+ function parseJavaScriptCalls(lines, baseLine) {
247
+ const calls = [];
248
+ const controlKeywords = /* @__PURE__ */ new Set([
249
+ "if",
250
+ "for",
251
+ "while",
252
+ "switch",
253
+ "catch",
254
+ "function",
255
+ "class",
256
+ "return",
257
+ "typeof",
258
+ "import"
259
+ ]);
260
+ for (let index = 0; index < lines.length; index++) {
261
+ const stripped = stripCommentsAndStrings(lines[index] ?? "");
262
+ if (!stripped.trim()) continue;
263
+ const attributeMatches = [
264
+ ...stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)\s*\(/g),
265
+ ...stripped.matchAll(/\bthis\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)\s*\(/g)
266
+ ];
267
+ const attributeRanges = attributeMatches.map((match) => ({
268
+ start: match.index ?? -1,
269
+ end: (match.index ?? -1) + match[0].length
270
+ }));
271
+ for (const match of attributeMatches) {
272
+ const receiverName = match[2] ? match[1] : "this";
273
+ const calleeName = match[2] ?? match[1];
274
+ if (!receiverName || !calleeName) continue;
275
+ calls.push({
276
+ receiverName,
277
+ calleeName,
278
+ line: baseLine + index
279
+ });
280
+ }
281
+ for (const match of stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*\(/g)) {
282
+ const calleeName = match[1];
283
+ const start = match.index ?? -1;
284
+ if (!calleeName || start < 0) continue;
285
+ if (controlKeywords.has(calleeName)) continue;
286
+ if (attributeRanges.some((range) => start >= range.start && start < range.end)) continue;
287
+ const prefix = stripped.slice(0, start).trimEnd();
288
+ if (prefix.endsWith("function") || prefix.endsWith("class") || prefix.endsWith("new")) {
289
+ continue;
290
+ }
291
+ calls.push({
292
+ receiverName: null,
293
+ calleeName,
294
+ line: baseLine + index
295
+ });
296
+ }
297
+ }
298
+ return calls;
299
+ }
300
+ function parsePythonConstructorBindings(lines) {
301
+ const bindings = /* @__PURE__ */ new Map();
302
+ for (const rawLine of lines) {
303
+ const stripped = stripCommentsAndStrings(rawLine);
304
+ const constructorMatch = stripped.match(/\b([A-Za-z_][\w]*)\s*=\s*([A-Z][\w]*)\s*\(/);
305
+ if (constructorMatch?.[1] && constructorMatch[2]) {
306
+ bindings.set(constructorMatch[1], constructorMatch[2]);
307
+ }
308
+ }
309
+ return [...bindings.entries()].map(([localName, typeName]) => ({ localName, typeName }));
310
+ }
311
+ function parseJavaScriptConstructorBindings(lines) {
312
+ const bindings = /* @__PURE__ */ new Map();
313
+ for (const rawLine of lines) {
314
+ const stripped = stripCommentsAndStrings(rawLine);
315
+ for (const match of stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*([A-Z][A-Za-z0-9_$]*)\b/g)) {
316
+ const localName = match[1];
317
+ const typeName = match[2];
318
+ if (localName && typeName) {
319
+ bindings.set(localName, typeName);
320
+ }
321
+ }
322
+ const constructorMatch = stripped.match(/\b(?:const|let|var)?\s*([A-Za-z_$][\w$]*)\s*(?::\s*[A-Z][A-Za-z0-9_$<> ,]*)?\s*=\s*new\s+([A-Z][A-Za-z0-9_$]*)\s*\(/);
323
+ if (constructorMatch?.[1] && constructorMatch[2]) {
324
+ bindings.set(constructorMatch[1], constructorMatch[2]);
325
+ }
326
+ }
327
+ return [...bindings.entries()].map(([localName, typeName]) => ({ localName, typeName }));
328
+ }
329
+ function parsePythonImports(db, importerPath, source) {
330
+ return collectPythonImportStatements(source).flatMap(
331
+ (statement) => parsePythonImportStatement(db, importerPath, statement, source)
332
+ );
333
+ }
334
+ function collectPythonImportStatements(source) {
335
+ const lines = source.split("\n");
336
+ const statements = [];
337
+ let offset = 0;
338
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
339
+ const line = lines[lineIndex];
340
+ const trimmed = line.trimStart();
341
+ const lineStart = offset;
342
+ offset += line.length + 1;
343
+ if (!trimmed.startsWith("import ") && !trimmed.startsWith("from ")) {
344
+ continue;
345
+ }
346
+ let statement = line;
347
+ let statementEnd = lineStart + line.length;
348
+ let balance = pythonParenBalance(line);
349
+ while (lineIndex + 1 < lines.length && (balance > 0 || statement.trimEnd().endsWith("\\"))) {
350
+ lineIndex++;
351
+ const nextLine = lines[lineIndex];
352
+ statement += `
353
+ ${nextLine}`;
354
+ statementEnd += 1 + nextLine.length;
355
+ balance += pythonParenBalance(nextLine);
356
+ offset += nextLine.length + 1;
357
+ }
358
+ const parsed = parsePythonStatementHeader(statement);
359
+ if (parsed) {
360
+ statements.push({
361
+ ...parsed,
362
+ start: lineStart,
363
+ end: statementEnd
364
+ });
365
+ }
366
+ }
367
+ return statements;
368
+ }
369
+ function parsePythonStatementHeader(statement) {
370
+ const normalized = statement.replace(/\\\s*\n/g, " ").trim();
371
+ if (normalized.startsWith("import ")) {
372
+ return {
373
+ kind: "import",
374
+ module: null,
375
+ clause: normalized.slice("import ".length).trim()
376
+ };
377
+ }
378
+ const fromMatch = normalized.match(/^from\s+([.\w]+)\s+import\s+([\s\S]+)$/);
379
+ if (!fromMatch) {
380
+ return null;
381
+ }
382
+ let clause = fromMatch[2].trim();
383
+ if (clause.startsWith("(") && clause.endsWith(")")) {
384
+ clause = clause.slice(1, -1).trim();
385
+ }
386
+ return {
387
+ kind: "from",
388
+ module: fromMatch[1],
389
+ clause
390
+ };
391
+ }
392
+ function parsePythonImportStatement(db, importerPath, statement, source) {
393
+ const body = buildUsageBody(source, statement.start, statement.end);
394
+ const normalizedClause = statement.clause.replace(/\n/g, " ").trim();
395
+ if (statement.kind === "import") {
396
+ return splitTopLevel(normalizedClause).flatMap((entry) => {
397
+ const cleaned = entry.trim().replace(/,$/, "");
398
+ if (!cleaned) return [];
399
+ const [moduleName, alias] = cleaned.split(/\s+as\s+/);
400
+ const importedName = moduleName.trim();
401
+ const localName = (alias ?? importedName.split(".")[0] ?? importedName).trim();
402
+ const sourcePath2 = resolvePythonImportPath(db, importerPath, importedName);
403
+ const usedMembers = collectNamespaceMembers(body, localName);
404
+ return [{
405
+ importedName,
406
+ localName,
407
+ sourcePath: sourcePath2,
408
+ kind: "namespace",
409
+ used: hasIdentifierUsage(body, localName) || usedMembers.length > 0,
410
+ usedMembers
411
+ }];
412
+ });
413
+ }
414
+ const sourcePath = statement.module ? resolvePythonImportPath(db, importerPath, statement.module) : null;
415
+ const results = [];
416
+ for (const entry of splitTopLevel(normalizedClause)) {
417
+ const cleaned = entry.trim().replace(/,$/, "");
418
+ if (!cleaned) continue;
419
+ if (cleaned === "*") {
420
+ results.push({
421
+ importedName: "*",
422
+ localName: null,
423
+ sourcePath,
424
+ kind: "side-effect",
425
+ used: true,
426
+ usedMembers: []
427
+ });
428
+ continue;
429
+ }
430
+ const [importedName, alias] = cleaned.split(/\s+as\s+/);
431
+ const localName = (alias ?? importedName).trim();
432
+ results.push({
433
+ importedName: importedName.trim(),
434
+ localName,
435
+ sourcePath,
436
+ kind: "named",
437
+ used: hasIdentifierUsage(body, localName),
438
+ usedMembers: []
439
+ });
440
+ }
441
+ return results;
442
+ }
443
+ function parseImportClause(clause) {
444
+ const trimmed = clause.trim().replace(/^type\s+/, "");
445
+ const [first, second] = splitImportClause(trimmed);
446
+ const entries = [];
447
+ if (first) {
448
+ entries.push(...parseImportBinding(first));
449
+ }
450
+ if (second) {
451
+ entries.push(...parseImportBinding(second));
452
+ }
453
+ return entries;
454
+ }
455
+ function parseImportBinding(binding) {
456
+ const trimmed = binding.trim();
457
+ if (!trimmed) return [];
458
+ if (trimmed.startsWith("{")) {
459
+ const inner = trimmed.slice(1, -1).trim();
460
+ if (!inner) return [];
461
+ return splitTopLevel(inner).map((entry) => {
462
+ const cleaned = entry.trim().replace(/^type\s+/, "");
463
+ const [importedName, alias] = cleaned.split(/\s+as\s+/);
464
+ return {
465
+ importedName: importedName.trim(),
466
+ localName: (alias ?? importedName).trim(),
467
+ kind: "named"
468
+ };
469
+ });
470
+ }
471
+ if (trimmed.startsWith("* as ")) {
472
+ return [{
473
+ importedName: "*",
474
+ localName: trimmed.slice(5).trim(),
475
+ kind: "namespace"
476
+ }];
477
+ }
478
+ return [{
479
+ importedName: "default",
480
+ localName: trimmed,
481
+ kind: "default"
482
+ }];
483
+ }
484
+ function splitImportClause(clause) {
485
+ let depth = 0;
486
+ for (let i = 0; i < clause.length; i++) {
487
+ const char = clause[i];
488
+ if (char === "{") depth++;
489
+ if (char === "}") depth--;
490
+ if (char === "," && depth === 0) {
491
+ return [clause.slice(0, i).trim(), clause.slice(i + 1).trim()];
492
+ }
493
+ }
494
+ return [clause.trim(), null];
495
+ }
496
+ function splitTopLevel(input) {
497
+ const parts = [];
498
+ let depth = 0;
499
+ let start = 0;
500
+ for (let i = 0; i < input.length; i++) {
501
+ const char = input[i];
502
+ if (char === "{" || char === "[" || char === "(") depth++;
503
+ if (char === "}" || char === "]" || char === ")") depth--;
504
+ if (char === "," && depth === 0) {
505
+ parts.push(input.slice(start, i));
506
+ start = i + 1;
507
+ }
508
+ }
509
+ parts.push(input.slice(start));
510
+ return parts;
511
+ }
512
+ function buildUsageBody(source, start, end) {
513
+ const masked = `${source.slice(0, start)}${" ".repeat(end - start)}${source.slice(end)}`;
514
+ return stripCommentsAndStrings(masked);
515
+ }
516
+ function stripCommentsAndStrings(source) {
517
+ return source.replace(/'''[\s\S]*?'''/g, maskPreservingLines).replace(/"""[\s\S]*?"""/g, maskPreservingLines).replace(/#.*$/gm, maskPreservingLines).replace(/\/\/.*$/gm, maskPreservingLines).replace(/\/\*[\s\S]*?\*\//g, maskPreservingLines).replace(/`(?:\\[\s\S]|[^`])*`/g, maskPreservingLines).replace(/'(?:\\.|[^'\\\r\n])*'/g, maskPreservingLines).replace(/"(?:\\.|[^"\\\r\n])*"/g, maskPreservingLines);
518
+ }
519
+ function maskPreservingLines(segment) {
520
+ return segment.replace(/[^\r\n]/g, " ");
521
+ }
522
+ function hasIdentifierUsage(body, identifier) {
523
+ return new RegExp(`\\b${escapeRegex(identifier)}\\b`, "m").test(body);
524
+ }
525
+ function collectNamespaceMembers(body, namespaceName) {
526
+ const members = /* @__PURE__ */ new Set();
527
+ const regex = new RegExp(`\\b${escapeRegex(namespaceName)}\\s*\\.\\s*([A-Za-z_$][\\w$]*)`, "g");
528
+ for (const match of body.matchAll(regex)) {
529
+ const member = match[1];
530
+ if (member) {
531
+ members.add(member);
532
+ }
533
+ }
534
+ return [...members];
535
+ }
536
+ function resolveImportPath(db, importerPath, specifier) {
537
+ if (isPythonSourcePath(importerPath)) {
538
+ return resolvePythonImportPath(db, importerPath, specifier);
539
+ }
540
+ return resolveJavaScriptImportPath(db, importerPath, specifier);
541
+ }
542
+ function resolveJavaScriptImportPath(db, importerPath, specifier) {
543
+ if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
544
+ return null;
545
+ }
546
+ const importerDir = dirname(join(db.config.projectRoot, importerPath));
547
+ const absolute = resolve(importerDir, specifier);
548
+ const indexedPaths = getIndexedPaths(db);
549
+ for (const candidate of candidateImportPaths(absolute)) {
550
+ const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
551
+ if (indexedPaths.has(relativeCandidate) || existsSync(candidate)) {
552
+ return relativeCandidate;
553
+ }
554
+ }
555
+ return normalizePath(relative(db.config.projectRoot, absolute));
556
+ }
557
+ function resolvePythonImportPath(db, importerPath, specifier) {
558
+ const indexedPaths = getIndexedPaths(db);
559
+ let basePath;
560
+ if (specifier.startsWith(".")) {
561
+ const match = specifier.match(/^(\.+)(.*)$/);
562
+ if (!match) return null;
563
+ const dots = match[1].length;
564
+ const remainder = match[2].replace(/^\./, "");
565
+ let baseDir = dirname(join(db.config.projectRoot, importerPath));
566
+ for (let i = 1; i < dots; i++) {
567
+ baseDir = dirname(baseDir);
568
+ }
569
+ basePath = remainder ? resolve(baseDir, remainder.replace(/\./g, "/")) : baseDir;
570
+ } else {
571
+ basePath = resolve(db.config.projectRoot, specifier.replace(/\./g, "/"));
572
+ }
573
+ for (const candidate of pythonCandidateImportPaths(basePath)) {
574
+ const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
575
+ if (indexedPaths.has(relativeCandidate) || existsSync(candidate)) {
576
+ return relativeCandidate;
577
+ }
578
+ }
579
+ return null;
580
+ }
581
+ function pythonCandidateImportPaths(basePath) {
582
+ const ext = extname(basePath);
583
+ if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
584
+ return [basePath];
585
+ }
586
+ return [
587
+ `${basePath}.py`,
588
+ `${basePath}.pyi`,
589
+ join(basePath, "__init__.py"),
590
+ join(basePath, "__init__.pyi")
591
+ ];
592
+ }
593
+ function candidateImportPaths(absolute) {
594
+ const ext = extname(absolute);
595
+ const candidates = /* @__PURE__ */ new Set();
596
+ if (ext) {
597
+ candidates.add(absolute);
598
+ for (const sourceExt of SOURCE_EXTENSIONS) {
599
+ candidates.add(absolute.slice(0, -ext.length) + sourceExt);
600
+ }
601
+ } else {
602
+ for (const sourceExt of SOURCE_EXTENSIONS) {
603
+ candidates.add(`${absolute}${sourceExt}`);
604
+ candidates.add(join(absolute, `index${sourceExt}`));
605
+ }
606
+ }
607
+ return [...candidates];
608
+ }
609
+ function getIndexedPaths(db) {
610
+ const cached = INDEXED_PATH_CACHE.get(db);
611
+ if (cached) {
612
+ return cached;
613
+ }
614
+ const paths = new Set(
615
+ db.all(
616
+ `SELECT relative_path
617
+ FROM documents
618
+ WHERE 1 = 1
619
+ ${db.pathExclusionsFor("documents")}`
620
+ ).map((row) => normalizePath(row.relative_path)).filter((relativePath) => !db.isIgnored(relativePath))
621
+ );
622
+ INDEXED_PATH_CACHE.set(db, paths);
623
+ return paths;
624
+ }
625
+ function getCachedMap(cache, db) {
626
+ let map = cache.get(db);
627
+ if (!map) {
628
+ map = /* @__PURE__ */ new Map();
629
+ cache.set(db, map);
630
+ }
631
+ return map;
632
+ }
633
+ function normalizePath(path) {
634
+ return path.replace(/\\/g, "/");
635
+ }
636
+ function isJavaScriptSourcePath(relativePath) {
637
+ return SOURCE_EXTENSIONS.includes(extname(relativePath).toLowerCase());
638
+ }
639
+ function isPythonSourcePath(relativePath) {
640
+ return PYTHON_SOURCE_EXTENSIONS.includes(extname(relativePath).toLowerCase());
641
+ }
642
+ function getSourceText(db, relativePath) {
643
+ const cache = getCachedMap(SOURCE_TEXT_CACHE, db);
644
+ const normalized = normalizePath(relativePath);
645
+ const cached = cache.get(normalized);
646
+ if (typeof cached === "string") {
647
+ return cached;
648
+ }
649
+ const fullPath = join(db.config.projectRoot, normalized);
650
+ if (!existsSync(fullPath)) {
651
+ cache.set(normalized, "");
652
+ return "";
653
+ }
654
+ const source = readFileSync(fullPath, "utf-8");
655
+ cache.set(normalized, source);
656
+ return source;
657
+ }
658
+ function pythonParenBalance(value) {
659
+ let balance = 0;
660
+ for (const char of value) {
661
+ if (char === "(") balance++;
662
+ if (char === ")") balance--;
663
+ }
664
+ return balance;
665
+ }
666
+ function escapeRegex(value) {
667
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
668
+ }
669
+
670
+ // src/query-support.ts
671
+ import { basename } from "path";
672
+ var FILE_DEFINITION_CACHE = /* @__PURE__ */ new WeakMap();
673
+ var TEST_FILE_PATTERNS = [
674
+ "%/__tests__/%",
675
+ "%.test.%",
676
+ "%.spec.%",
677
+ "%/test/%",
678
+ "%/tests/%",
679
+ "%_test.%",
680
+ "%_spec.%",
681
+ "%/test_%.%",
682
+ "%/spec_%.%"
683
+ ];
684
+ var TEST_SUPPORT_PATH_PATTERNS = [
685
+ "%/test-utils/%"
686
+ ];
687
+ function testFileExclusionSql(alias, extraPatterns = []) {
688
+ const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);
689
+ return patterns.map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`).join("\n AND ");
690
+ }
691
+ function buildFileDepGraph(db, scope) {
692
+ const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : "";
693
+ const edges = db.all(
694
+ `SELECT DISTINCT
695
+ d1.relative_path AS from_file,
696
+ d2.relative_path AS to_file
697
+ FROM mentions m
698
+ JOIN chunks c ON m.chunk_id = c.id
699
+ JOIN documents d1 ON c.document_id = d1.id
700
+ JOIN global_symbols gs ON m.symbol_id = gs.id
701
+ JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
702
+ JOIN documents d2 ON der.document_id = d2.id
703
+ WHERE d1.id != d2.id
704
+ AND m.role != 1
705
+ ${db.pathExclusionsFor("d1", "d2")}
706
+ ${scopeFilter}`
707
+ );
708
+ const graph = /* @__PURE__ */ new Map();
709
+ for (const edge of edges) {
710
+ if (db.isIgnored(edge.from_file) || db.isIgnored(edge.to_file)) continue;
711
+ if (!graph.has(edge.from_file)) graph.set(edge.from_file, /* @__PURE__ */ new Set());
712
+ graph.get(edge.from_file).add(edge.to_file);
713
+ }
714
+ return graph;
715
+ }
716
+ function findFirstSymbolMatch(db, symbolPattern) {
717
+ const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
718
+ if (fileLineMatch) {
719
+ const [, filePath, startStr, endStr] = fileLineMatch;
720
+ const row = db.get(
721
+ `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
722
+ FROM global_symbols gs
723
+ JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
724
+ JOIN documents d ON der.document_id = d.id
725
+ WHERE d.relative_path LIKE ?
726
+ AND der.start_line <= ? AND der.end_line >= ?
727
+ ${db.pathExclusionsFor("d")}
728
+ ORDER BY (der.end_line - der.start_line) ASC
729
+ LIMIT 1`,
730
+ `%${filePath}%`,
731
+ parseInt(startStr, 10),
732
+ parseInt(endStr, 10)
733
+ );
734
+ if (row && !db.isIgnored(row.relative_path)) {
735
+ return {
736
+ symbolId: row.id,
737
+ symbol: row.symbol,
738
+ documentId: row.document_id,
739
+ startLine: row.start_line,
740
+ endLine: row.end_line,
741
+ relativePath: row.relative_path
742
+ };
743
+ }
744
+ }
745
+ const cleaned = normalizeLookupPattern(symbolPattern);
746
+ const tokens = lookupTokens(symbolPattern);
747
+ const candidates = getSymbolLookupCandidates(db, tokens);
748
+ let best = null;
749
+ for (const row of candidates) {
750
+ if (db.isIgnored(row.relative_path)) continue;
751
+ const score = scoreSymbolCandidate(row, symbolPattern, cleaned, tokens);
752
+ if (score <= 0) continue;
753
+ if (!best || score > best.score) {
754
+ best = { row, score };
755
+ }
756
+ }
757
+ if (best) {
758
+ return {
759
+ symbolId: best.row.id,
760
+ symbol: best.row.symbol,
761
+ documentId: best.row.document_id,
762
+ startLine: best.row.start_line,
763
+ endLine: best.row.end_line,
764
+ relativePath: best.row.relative_path
765
+ };
766
+ }
767
+ return null;
768
+ }
769
+ function findExactSymbolMatch(db, symbol) {
770
+ const row = db.get(
771
+ `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
772
+ FROM global_symbols gs
773
+ JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
774
+ JOIN documents d ON der.document_id = d.id
775
+ WHERE gs.symbol = ?
776
+ ${db.pathExclusionsFor("d")}
777
+ ORDER BY d.relative_path, der.start_line
778
+ LIMIT 1`,
779
+ symbol
780
+ );
781
+ if (!row || db.isIgnored(row.relative_path)) {
782
+ return null;
783
+ }
784
+ return {
785
+ symbolId: row.id,
786
+ symbol: row.symbol,
787
+ documentId: row.document_id,
788
+ startLine: row.start_line,
789
+ endLine: row.end_line,
790
+ relativePath: row.relative_path
791
+ };
792
+ }
793
+ function resolveIndexedFile(db, filePattern) {
794
+ return resolveDocumentCandidates(db, filePattern, { allowMultiple: false })[0]?.relativePath ?? null;
795
+ }
796
+ function resolveIndexedPaths(db, filePattern) {
797
+ return resolveDocumentCandidates(db, filePattern, { allowMultiple: true }).map((candidate) => candidate.relativePath);
798
+ }
799
+ function normalizeLookupPattern(symbolPattern) {
800
+ return symbolPattern.trim().replace(/\(\)$/, "").replace(/\(.*$/, "");
801
+ }
802
+ function lookupTokens(symbolPattern) {
803
+ const cleaned = normalizeLookupPattern(symbolPattern);
804
+ const tokens = cleaned.split(/[^A-Za-z0-9_]+/).map((token) => token.trim()).filter((token) => token.length > 0);
805
+ return tokens.length > 0 ? [...new Set(tokens)] : [cleaned];
806
+ }
807
+ function getSymbolLookupCandidates(db, tokens) {
808
+ const tokenClauses = tokens.map(
809
+ () => `(gs.symbol LIKE ? OR d.relative_path LIKE ? OR COALESCE(gs.display_name, '') LIKE ?)`
810
+ );
811
+ const params = tokens.flatMap((token) => {
812
+ const like = `%${token}%`;
813
+ return [like, like, like];
814
+ });
815
+ return db.all(
816
+ `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path, gs.display_name
817
+ FROM global_symbols gs
818
+ JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
819
+ JOIN documents d ON der.document_id = d.id
820
+ WHERE ${tokenClauses.join("\n AND ")}
821
+ ${db.pathExclusionsFor("d")}
822
+ LIMIT 200`,
823
+ ...params
824
+ );
825
+ }
826
+ function scoreSymbolCandidate(row, originalPattern, cleanedPattern, tokens) {
827
+ const original = originalPattern.toLowerCase();
828
+ const cleaned = cleanedPattern.toLowerCase();
829
+ const noParens = cleaned.replace(/\(\)$/, "");
830
+ const raw = row.symbol.toLowerCase();
831
+ const short = shortenSymbol(row.symbol).toLowerCase();
832
+ const leaf = leafName(row.symbol).toLowerCase();
833
+ const display = (row.display_name ?? "").toLowerCase();
834
+ const path = row.relative_path.toLowerCase();
835
+ const looksPathLike = /[/:.]/.test(cleanedPattern);
836
+ let score = 0;
837
+ if (raw === original || raw === cleaned) score += 1e3;
838
+ if (short === original || short === cleaned) score += 950;
839
+ if (path === original || path === cleaned) score += 925;
840
+ if (path.endsWith(`/${cleaned}`) || path.endsWith(`/${original}`)) score += 875;
841
+ if (display === noParens) score += 850;
842
+ if (leaf === noParens) score += 825;
843
+ if (`${leaf}()` === original || `${leaf}()` === cleaned) score += 820;
844
+ if (short.endsWith(`:${cleaned}`) || short.endsWith(`:${noParens}`) || short.endsWith(`:${noParens}()`)) score += 800;
845
+ if (raw.includes(cleaned)) score += 120;
846
+ if (short.includes(cleaned)) score += 140;
847
+ if (path.includes(cleaned)) score += 140;
848
+ if (display.includes(cleaned)) score += 110;
849
+ if (tokens.every((token) => {
850
+ const lower = token.toLowerCase();
851
+ return raw.includes(lower) || short.includes(lower) || path.includes(lower) || display.includes(lower);
852
+ })) {
853
+ score += 100 + tokens.length * 15;
854
+ }
855
+ if (isFunctionLikeSymbol(row.symbol) && leaf === noParens) {
856
+ score += 60;
857
+ }
858
+ if (!looksPathLike && isModuleLikeSymbol(row.symbol)) {
859
+ score -= 160;
860
+ }
861
+ score -= Math.min(50, Math.max(0, row.end_line - row.start_line));
862
+ return score;
863
+ }
864
+ function getCalleeRowsForSymbol(db, symbol, opts = {}) {
865
+ const rows = db.all(
866
+ `SELECT DISTINCT
867
+ callee_gs.symbol AS symbol,
868
+ callee_d.relative_path AS file,
869
+ c.id AS chunk_id
870
+ FROM mentions m
871
+ JOIN chunks c ON m.chunk_id = c.id
872
+ JOIN global_symbols callee_gs ON m.symbol_id = callee_gs.id
873
+ JOIN defn_enclosing_ranges callee_der ON callee_gs.id = callee_der.symbol_id
874
+ JOIN documents callee_d ON callee_der.document_id = callee_d.id
875
+ WHERE c.document_id = ?
876
+ AND c.start_line >= ?
877
+ AND c.end_line <= ?
878
+ AND m.role != 1
879
+ AND callee_gs.id != ?
880
+ ${db.symbolNoiseFor("callee_gs")}
881
+ ${db.pathExclusionsFor("callee_d")}
882
+ ORDER BY callee_d.relative_path
883
+ ${opts.limit ? "LIMIT ?" : ""}`,
884
+ ...calleeQueryParams(symbol, opts.limit)
885
+ );
886
+ const primary = rows.filter((row) => !db.isIgnored(row.file)).map((row) => ({
887
+ symbol: row.symbol,
888
+ file: row.file,
889
+ chunkId: row.chunk_id
890
+ }));
891
+ const sourceFallback = getSourceBackedCalleeRows(db, symbol, opts.limit);
892
+ if (sourceFallback.length === 0) {
893
+ return primary;
894
+ }
895
+ if (primary.length === 0) {
896
+ return applyLimit(sourceFallback, opts.limit);
897
+ }
898
+ const seen = new Set(primary.map((row) => `${row.symbol}|${row.file}`));
899
+ const merged = [...primary];
900
+ for (const row of sourceFallback) {
901
+ const key = `${row.symbol}|${row.file}`;
902
+ if (seen.has(key)) continue;
903
+ seen.add(key);
904
+ merged.push(row);
905
+ }
906
+ return applyLimit(merged, opts.limit);
907
+ }
908
+ function getCallerRowsForSymbol(db, symbol, opts = {}) {
909
+ const match = getFullSymbolMatch(db, symbol);
910
+ if (!match) {
911
+ return [];
912
+ }
913
+ const primary = db.all(
914
+ `SELECT DISTINCT caller_gs.symbol AS caller_symbol, caller_d.relative_path AS caller_file
915
+ FROM mentions m
916
+ JOIN chunks c ON m.chunk_id = c.id
917
+ JOIN documents ref_d ON c.document_id = ref_d.id
918
+ JOIN defn_enclosing_ranges caller_der
919
+ ON caller_der.document_id = ref_d.id
920
+ AND c.start_line >= caller_der.start_line
921
+ AND c.end_line <= caller_der.end_line
922
+ JOIN global_symbols caller_gs ON caller_der.symbol_id = caller_gs.id
923
+ JOIN documents caller_d ON caller_der.document_id = caller_d.id
924
+ WHERE m.symbol_id = ?
925
+ AND m.role != 1
926
+ AND caller_gs.id != ?
927
+ ${db.symbolNoiseFor("caller_gs")}
928
+ ${db.pathExclusionsFor("caller_d")}
929
+ ORDER BY caller_d.relative_path
930
+ ${opts.limit ? "LIMIT ?" : ""}`,
931
+ ...callerQueryParams(match, opts.limit)
932
+ ).filter((row) => !db.isIgnored(row.caller_file)).map((row) => ({
933
+ symbol: row.caller_symbol,
934
+ file: row.caller_file
935
+ }));
936
+ const sourceFallback = getPythonSourceCallerRows(db, match, opts.limit);
937
+ if (sourceFallback.length === 0) {
938
+ return primary;
939
+ }
940
+ const merged = sourceFallback.length > 0 ? [...sourceFallback] : [];
941
+ const seen = new Set(merged.map((row) => `${row.symbol}|${row.file}`));
942
+ for (const row of primary) {
943
+ const key = `${row.symbol}|${row.file}`;
944
+ if (seen.has(key)) continue;
945
+ if (isFunctionLikeSymbol(row.symbol) || merged.length === 0) {
946
+ seen.add(key);
947
+ merged.push(row);
948
+ }
949
+ }
950
+ return applyLimit(merged, opts.limit);
951
+ }
952
+ function getSourceReferenceSites(db, symbol) {
953
+ const match = getFullSymbolMatch(db, symbol);
954
+ if (!match) {
955
+ return [];
956
+ }
957
+ const identifier = leafName(match.symbol);
958
+ if (!identifier || !hasUniqueLeafDefinition(db, identifier, match.symbolId)) {
959
+ return [];
960
+ }
961
+ const documents = db.all(
962
+ `SELECT relative_path
963
+ FROM documents
964
+ WHERE 1 = 1
965
+ ${db.pathExclusionsFor("documents")}
966
+ ORDER BY relative_path`
967
+ );
968
+ const sites = [];
969
+ const seen = /* @__PURE__ */ new Set();
970
+ for (const document of documents) {
971
+ if (db.isIgnored(document.relative_path)) continue;
972
+ const lines = findIdentifierLines(db, document.relative_path, identifier, document.relative_path === match.relativePath ? { excludeStartLine: match.startLine, excludeEndLine: match.endLine } : {});
973
+ for (const line of lines) {
974
+ const enclosing = db.get(
975
+ `SELECT gs.symbol
976
+ FROM defn_enclosing_ranges der
977
+ JOIN global_symbols gs ON der.symbol_id = gs.id
978
+ JOIN documents d ON der.document_id = d.id
979
+ WHERE d.relative_path = ?
980
+ AND der.start_line <= ?
981
+ AND der.end_line >= ?
982
+ ORDER BY (der.end_line - der.start_line) ASC
983
+ LIMIT 1`,
984
+ document.relative_path,
985
+ line,
986
+ line
987
+ );
988
+ const key = `${document.relative_path}|${line}|${enclosing?.symbol ?? ""}`;
989
+ if (seen.has(key)) continue;
990
+ seen.add(key);
991
+ sites.push({
992
+ file: document.relative_path,
993
+ line,
994
+ enclosingSymbol: enclosing?.symbol ?? null
995
+ });
996
+ }
997
+ }
998
+ return sites;
999
+ }
1000
+ function calleeQueryParams(symbol, limit) {
1001
+ const params = [
1002
+ symbol.documentId,
1003
+ symbol.startLine,
1004
+ symbol.endLine,
1005
+ symbol.symbolId
1006
+ ];
1007
+ if (typeof limit === "number") {
1008
+ params.push(limit);
1009
+ }
1010
+ return params;
1011
+ }
1012
+ function callerQueryParams(symbol, limit) {
1013
+ const params = [symbol.symbolId, symbol.symbolId];
1014
+ if (typeof limit === "number") {
1015
+ params.push(limit);
1016
+ }
1017
+ return params;
1018
+ }
1019
+ function getPythonSourceCalleeRows(db, symbol, limit) {
1020
+ const match = getFullSymbolMatch(db, symbol);
1021
+ if (!match || !isPythonDocument(db, match.relativePath)) {
1022
+ return [];
1023
+ }
1024
+ const definitions = getDefinitionsForFile(db, match.relativePath);
1025
+ const current = definitions.find((definition) => definition.symbolId === match.symbolId);
1026
+ if (!current) {
1027
+ return [];
1028
+ }
1029
+ const imports = getSourceImports(db, match.relativePath);
1030
+ const bindings = new Map(
1031
+ getSourceConstructorBindings(db, match.relativePath, {
1032
+ startLine: match.startLine,
1033
+ endLine: match.endLine
1034
+ }).map((binding) => [binding.localName, binding.typeName])
1035
+ );
1036
+ const rows = [];
1037
+ const seen = /* @__PURE__ */ new Set();
1038
+ for (const call of getSourceCalls(db, match.relativePath, {
1039
+ startLine: match.startLine,
1040
+ endLine: match.endLine
1041
+ })) {
1042
+ const resolved = resolvePythonCallTarget(
1043
+ db,
1044
+ current,
1045
+ definitions,
1046
+ imports,
1047
+ bindings,
1048
+ call.receiverName,
1049
+ call.calleeName
1050
+ );
1051
+ if (!resolved || resolved.symbolId === match.symbolId || db.isIgnored(resolved.relativePath)) continue;
1052
+ const chunkId = 1e9 + call.line;
1053
+ const key = `${resolved.symbol}|${resolved.relativePath}|${chunkId}`;
1054
+ if (seen.has(key)) continue;
1055
+ seen.add(key);
1056
+ rows.push({
1057
+ symbol: resolved.symbol,
1058
+ file: resolved.relativePath,
1059
+ chunkId
1060
+ });
1061
+ }
1062
+ return applyLimit(rows, limit);
1063
+ }
1064
+ function getJavaScriptSourceCalleeRows(db, symbol, limit) {
1065
+ const match = getFullSymbolMatch(db, symbol);
1066
+ if (!match || !isJavaScriptDocument(db, match.relativePath)) {
1067
+ return [];
1068
+ }
1069
+ const definitions = getDefinitionsForFile(db, match.relativePath);
1070
+ const current = definitions.find((definition) => definition.symbolId === match.symbolId);
1071
+ if (!current) {
1072
+ return [];
1073
+ }
1074
+ const imports = getSourceImports(db, match.relativePath);
1075
+ const bindings = new Map(
1076
+ getSourceConstructorBindings(db, match.relativePath, {
1077
+ startLine: match.startLine,
1078
+ endLine: match.endLine
1079
+ }).map((binding) => [binding.localName, binding.typeName])
1080
+ );
1081
+ const rows = [];
1082
+ const seen = /* @__PURE__ */ new Set();
1083
+ for (const call of getSourceCalls(db, match.relativePath, {
1084
+ startLine: match.startLine,
1085
+ endLine: match.endLine
1086
+ })) {
1087
+ const resolved = resolveJavaScriptCallTarget(
1088
+ db,
1089
+ current,
1090
+ definitions,
1091
+ imports,
1092
+ bindings,
1093
+ call.receiverName,
1094
+ call.calleeName
1095
+ );
1096
+ if (!resolved || resolved.symbolId === match.symbolId || db.isIgnored(resolved.relativePath)) continue;
1097
+ const chunkId = 1e9 + call.line;
1098
+ const key = `${resolved.symbol}|${resolved.relativePath}|${chunkId}`;
1099
+ if (seen.has(key)) continue;
1100
+ seen.add(key);
1101
+ rows.push({
1102
+ symbol: resolved.symbol,
1103
+ file: resolved.relativePath,
1104
+ chunkId
1105
+ });
1106
+ }
1107
+ return applyLimit(rows, limit);
1108
+ }
1109
+ function getSourceBackedCalleeRows(db, symbol, limit) {
1110
+ const match = getFullSymbolMatch(db, symbol);
1111
+ if (!match) {
1112
+ return [];
1113
+ }
1114
+ if (isPythonDocument(db, match.relativePath)) {
1115
+ return getPythonSourceCalleeRows(db, match, limit);
1116
+ }
1117
+ if (isJavaScriptDocument(db, match.relativePath)) {
1118
+ return getJavaScriptSourceCalleeRows(db, match, limit);
1119
+ }
1120
+ return [];
1121
+ }
1122
+ function getPythonSourceCallerRows(db, target, limit) {
1123
+ if (!isPythonDocument(db, target.relativePath)) {
1124
+ return [];
1125
+ }
1126
+ const rows = [];
1127
+ const seen = /* @__PURE__ */ new Set();
1128
+ for (const candidate of getAllFunctionLikeDefinitions(db)) {
1129
+ if (candidate.symbolId === target.symbolId) continue;
1130
+ const callees = getPythonSourceCalleeRows(db, candidate);
1131
+ if (!callees.some((callee) => callee.symbol === target.symbol)) continue;
1132
+ const key = `${candidate.symbol}|${candidate.relativePath}`;
1133
+ if (seen.has(key) || db.isIgnored(candidate.relativePath)) continue;
1134
+ seen.add(key);
1135
+ rows.push({
1136
+ symbol: candidate.symbol,
1137
+ file: candidate.relativePath
1138
+ });
1139
+ if (typeof limit === "number" && rows.length >= limit) {
1140
+ break;
1141
+ }
1142
+ }
1143
+ return rows;
1144
+ }
1145
+ function getFullSymbolMatch(db, symbol) {
1146
+ if ("symbol" in symbol && "relativePath" in symbol) {
1147
+ return symbol;
1148
+ }
1149
+ const row = db.get(
1150
+ `SELECT gs.symbol, d.relative_path
1151
+ FROM global_symbols gs
1152
+ JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
1153
+ JOIN documents d ON der.document_id = d.id
1154
+ WHERE gs.id = ?
1155
+ LIMIT 1`,
1156
+ symbol.symbolId
1157
+ );
1158
+ if (!row) {
1159
+ return null;
1160
+ }
1161
+ return {
1162
+ ...symbol,
1163
+ symbol: row.symbol,
1164
+ relativePath: row.relative_path
1165
+ };
1166
+ }
1167
+ function getDefinitionsForFile(db, relativePath) {
1168
+ let cache = FILE_DEFINITION_CACHE.get(db);
1169
+ if (!cache) {
1170
+ cache = /* @__PURE__ */ new Map();
1171
+ FILE_DEFINITION_CACHE.set(db, cache);
1172
+ }
1173
+ const cached = cache.get(relativePath);
1174
+ if (cached) {
1175
+ return cached;
1176
+ }
1177
+ const definitions = db.all(
1178
+ `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
1179
+ FROM global_symbols gs
1180
+ JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
1181
+ JOIN documents d ON der.document_id = d.id
1182
+ WHERE d.relative_path = ?
1183
+ ${db.symbolNoiseFor("gs")}
1184
+ ORDER BY der.start_line, der.end_line`,
1185
+ relativePath
1186
+ ).map((row) => ({
1187
+ symbolId: row.id,
1188
+ symbol: row.symbol,
1189
+ documentId: row.document_id,
1190
+ startLine: row.start_line,
1191
+ endLine: row.end_line,
1192
+ relativePath: row.relative_path,
1193
+ leaf: leafName(row.symbol),
1194
+ parentTypeName: parentTypeName(row.symbol),
1195
+ isFunctionLike: isFunctionLikeSymbol(row.symbol),
1196
+ isTypeLike: leafSuffix(row.symbol) === "type"
1197
+ }));
1198
+ cache.set(relativePath, definitions);
1199
+ return definitions;
1200
+ }
1201
+ function getAllFunctionLikeDefinitions(db) {
1202
+ const rows = db.all(
1203
+ `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
1204
+ FROM global_symbols gs
1205
+ JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
1206
+ JOIN documents d ON der.document_id = d.id
1207
+ WHERE 1 = 1
1208
+ ${db.pathExclusionsFor("d")}
1209
+ ${db.symbolNoiseFor("gs")}
1210
+ ORDER BY d.relative_path, der.start_line`
1211
+ );
1212
+ return rows.filter((row) => !db.isIgnored(row.relative_path)).filter((row) => isFunctionLikeSymbol(row.symbol)).map((row) => ({
1213
+ symbolId: row.id,
1214
+ symbol: row.symbol,
1215
+ documentId: row.document_id,
1216
+ startLine: row.start_line,
1217
+ endLine: row.end_line,
1218
+ relativePath: row.relative_path,
1219
+ leaf: leafName(row.symbol),
1220
+ parentTypeName: parentTypeName(row.symbol),
1221
+ isFunctionLike: true,
1222
+ isTypeLike: false
1223
+ }));
1224
+ }
1225
+ function resolvePythonCallTarget(db, current, currentFileDefinitions, imports, constructorBindings, receiverName, calleeName) {
1226
+ if (receiverName === "self" || receiverName === "cls") {
1227
+ return findDefinitionByName(currentFileDefinitions, calleeName, current.parentTypeName, ["function"]);
1228
+ }
1229
+ if (receiverName) {
1230
+ const inferredType = constructorBindings.get(receiverName);
1231
+ if (inferredType) {
1232
+ const boundMethod = findDefinitionByName(currentFileDefinitions, calleeName, inferredType, ["function"]);
1233
+ if (boundMethod) {
1234
+ return boundMethod;
1235
+ }
1236
+ for (const entry of imports) {
1237
+ if (entry.localName !== inferredType || !entry.sourcePath) continue;
1238
+ const importedDefinitions = getDefinitionsForFile(db, entry.sourcePath);
1239
+ const importedMethod = findDefinitionByName(importedDefinitions, calleeName, entry.importedName, ["function"]);
1240
+ if (importedMethod) {
1241
+ return importedMethod;
1242
+ }
1243
+ }
1244
+ }
1245
+ const localClassMethod = findDefinitionByName(currentFileDefinitions, calleeName, receiverName, ["function"]);
1246
+ if (localClassMethod) {
1247
+ return localClassMethod;
1248
+ }
1249
+ const namespaceImport = imports.find((entry) => entry.localName === receiverName && entry.sourcePath);
1250
+ if (namespaceImport?.sourcePath) {
1251
+ const importedDefinitions = getDefinitionsForFile(db, namespaceImport.sourcePath);
1252
+ const importedTypeMethod = namespaceImport.kind === "named" ? findDefinitionByName(importedDefinitions, calleeName, namespaceImport.importedName, ["function"]) : null;
1253
+ if (importedTypeMethod) {
1254
+ return importedTypeMethod;
1255
+ }
1256
+ return findDefinitionByName(importedDefinitions, calleeName, null, ["function", "type"]);
1257
+ }
1258
+ return null;
1259
+ }
1260
+ const importedBinding = imports.find((entry) => entry.localName === calleeName && entry.sourcePath);
1261
+ if (importedBinding?.sourcePath) {
1262
+ const importedDefinitions = getDefinitionsForFile(db, importedBinding.sourcePath);
1263
+ const importedName = importedBinding.importedName === "*" ? calleeName : importedBinding.importedName;
1264
+ const importedDefinition = findDefinitionByName(importedDefinitions, importedName, null, ["function", "type"]);
1265
+ if (importedDefinition) {
1266
+ return importedDefinition;
1267
+ }
1268
+ }
1269
+ return findDefinitionByName(currentFileDefinitions, calleeName, null, ["function", "type"]);
1270
+ }
1271
+ function resolveJavaScriptCallTarget(db, current, currentFileDefinitions, imports, constructorBindings, receiverName, calleeName) {
1272
+ if (receiverName === "this") {
1273
+ return findDefinitionByName(currentFileDefinitions, calleeName, current.parentTypeName, ["function"]);
1274
+ }
1275
+ if (receiverName) {
1276
+ const inferredType = constructorBindings.get(receiverName);
1277
+ if (inferredType) {
1278
+ const boundMethod = findDefinitionByName(currentFileDefinitions, calleeName, inferredType, ["function"]);
1279
+ if (boundMethod) {
1280
+ return boundMethod;
1281
+ }
1282
+ for (const entry of imports) {
1283
+ if (entry.localName !== inferredType || !entry.sourcePath) continue;
1284
+ const importedDefinitions = getDefinitionsForFile(db, entry.sourcePath);
1285
+ const importedMethod = findDefinitionByName(importedDefinitions, calleeName, entry.importedName, ["function"]);
1286
+ if (importedMethod) {
1287
+ return importedMethod;
1288
+ }
1289
+ }
1290
+ }
1291
+ const localClassMethod = findDefinitionByName(currentFileDefinitions, calleeName, receiverName, ["function"]);
1292
+ if (localClassMethod) {
1293
+ return localClassMethod;
1294
+ }
1295
+ const namespaceImport = imports.find((entry) => entry.localName === receiverName && entry.sourcePath);
1296
+ if (namespaceImport?.sourcePath) {
1297
+ const importedDefinitions = getDefinitionsForFile(db, namespaceImport.sourcePath);
1298
+ if (namespaceImport.kind === "named" || namespaceImport.kind === "default") {
1299
+ const importedTypeMethod = findDefinitionByName(
1300
+ importedDefinitions,
1301
+ calleeName,
1302
+ namespaceImport.importedName,
1303
+ ["function"]
1304
+ );
1305
+ if (importedTypeMethod) {
1306
+ return importedTypeMethod;
1307
+ }
1308
+ }
1309
+ return findDefinitionByName(importedDefinitions, calleeName, null, ["function", "type"]);
1310
+ }
1311
+ return null;
1312
+ }
1313
+ const importedBinding = imports.find((entry) => entry.localName === calleeName && entry.sourcePath && entry.kind !== "namespace");
1314
+ if (importedBinding?.sourcePath) {
1315
+ const importedDefinitions = getDefinitionsForFile(db, importedBinding.sourcePath);
1316
+ const importedName = importedBinding.importedName === "default" ? importedBinding.localName ?? calleeName : importedBinding.importedName;
1317
+ const importedDefinition = findDefinitionByName(importedDefinitions, importedName, null, ["function", "type"]);
1318
+ if (importedDefinition) {
1319
+ return importedDefinition;
1320
+ }
1321
+ }
1322
+ return findDefinitionByName(currentFileDefinitions, calleeName, null, ["function", "type"]);
1323
+ }
1324
+ function findDefinitionByName(definitions, leaf, parentType, preference) {
1325
+ const candidates = definitions.filter((definition) => definition.leaf === leaf && definition.parentTypeName === parentType);
1326
+ if (candidates.length === 0) {
1327
+ return null;
1328
+ }
1329
+ for (const preferred of preference) {
1330
+ const match = candidates.find((candidate) => preferred === "function" ? candidate.isFunctionLike : candidate.isTypeLike);
1331
+ if (match) {
1332
+ return match;
1333
+ }
1334
+ }
1335
+ return candidates[0] ?? null;
1336
+ }
1337
+ function hasUniqueLeafDefinition(db, leaf, symbolId) {
1338
+ const rows = db.all(
1339
+ `SELECT id, symbol
1340
+ FROM global_symbols
1341
+ WHERE symbol LIKE ?
1342
+ LIMIT 50`,
1343
+ `%${leaf}%`
1344
+ );
1345
+ let count = 0;
1346
+ for (const row of rows) {
1347
+ if (leafName(row.symbol) !== leaf) continue;
1348
+ count++;
1349
+ if (count > 1 && row.id !== symbolId) {
1350
+ return false;
1351
+ }
1352
+ }
1353
+ return count === 1;
1354
+ }
1355
+ function parentTypeName(rawSymbol) {
1356
+ const parsed = parseSymbol(rawSymbol);
1357
+ if ("kind" in parsed) {
1358
+ return null;
1359
+ }
1360
+ for (let index = parsed.descriptors.length - 2; index >= 0; index--) {
1361
+ const descriptor = parsed.descriptors[index];
1362
+ if (descriptor?.suffix === "type") {
1363
+ return descriptor.name;
1364
+ }
1365
+ }
1366
+ return null;
1367
+ }
1368
+ function isPythonDocument(db, relativePath) {
1369
+ const row = db.get(
1370
+ `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
1371
+ relativePath
1372
+ );
1373
+ return row?.language === "python" || relativePath.endsWith(".py") || relativePath.endsWith(".pyi");
1374
+ }
1375
+ function isJavaScriptDocument(db, relativePath) {
1376
+ const row = db.get(
1377
+ `SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
1378
+ relativePath
1379
+ );
1380
+ return row?.language === "typescript" || row?.language === "javascript" || /\.(?:[cm]?[jt]sx?)$/.test(relativePath);
1381
+ }
1382
+ function applyLimit(values, limit) {
1383
+ return typeof limit === "number" ? values.slice(0, limit) : values;
1384
+ }
1385
+ function resolveDocumentCandidates(db, filePattern, opts) {
1386
+ const normalizedPattern = normalizeLookupPath(filePattern);
1387
+ if (!normalizedPattern) {
1388
+ return [];
1389
+ }
1390
+ const rows = db.all(
1391
+ `SELECT relative_path
1392
+ FROM documents
1393
+ WHERE 1 = 1
1394
+ ${db.pathExclusionsFor("documents")}
1395
+ ORDER BY relative_path`
1396
+ );
1397
+ const scored = rows.filter((row) => !db.isIgnored(row.relative_path)).map((row) => ({
1398
+ relativePath: row.relative_path,
1399
+ score: scoreDocumentPath(row.relative_path, normalizedPattern)
1400
+ })).filter((row) => row.score > 0).sort((a, b) => b.score - a.score || a.relativePath.localeCompare(b.relativePath));
1401
+ if (scored.length === 0) {
1402
+ return [];
1403
+ }
1404
+ const exactish = scored.filter((row) => row.score >= 800);
1405
+ if (exactish.length > 0) {
1406
+ return opts.allowMultiple ? exactish : [exactish[0]];
1407
+ }
1408
+ return opts.allowMultiple ? scored : [scored[0]];
1409
+ }
1410
+ function scoreDocumentPath(relativePath, rawPattern) {
1411
+ const normalizedPath = normalizeLookupPath(relativePath);
1412
+ const pathBase = basename(normalizedPath);
1413
+ const patternBase = basename(rawPattern);
1414
+ let score = 0;
1415
+ if (normalizedPath === rawPattern) score += 1200;
1416
+ if (normalizedPath.endsWith(`/${rawPattern}`)) score += 1100;
1417
+ if (pathBase === patternBase) score += 900;
1418
+ if (normalizedPath.startsWith(`${rawPattern}/`)) score += 850;
1419
+ if (normalizedPath.includes(rawPattern)) score += 250;
1420
+ return score;
1421
+ }
1422
+ function normalizeLookupPath(filePattern) {
1423
+ return filePattern.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
1424
+ }
1425
+ function uniquePatterns(patterns) {
1426
+ return [...new Set(patterns)];
1427
+ }
1428
+
1429
+ export {
1430
+ getSourceImports,
1431
+ TEST_SUPPORT_PATH_PATTERNS,
1432
+ testFileExclusionSql,
1433
+ buildFileDepGraph,
1434
+ findFirstSymbolMatch,
1435
+ findExactSymbolMatch,
1436
+ resolveIndexedFile,
1437
+ resolveIndexedPaths,
1438
+ getCalleeRowsForSymbol,
1439
+ getCallerRowsForSymbol,
1440
+ getSourceReferenceSites
1441
+ };
1442
+ //# sourceMappingURL=chunk-AXQKUYKF.js.map