gitnexus 1.6.3-rc.8 → 1.6.3

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 (285) hide show
  1. package/README.md +21 -5
  2. package/dist/_shared/graph/types.d.ts +16 -0
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +20 -2
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/index.js +11 -0
  7. package/dist/_shared/index.js.map +1 -1
  8. package/dist/_shared/scope-resolution/def-index.js +2 -2
  9. package/dist/_shared/scope-resolution/def-index.js.map +1 -1
  10. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
  11. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
  12. package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
  13. package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
  14. package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
  15. package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
  17. package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
  18. package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
  19. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
  20. package/dist/_shared/scope-resolution/parsed-file.js +54 -0
  21. package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
  22. package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
  23. package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/position-index.js +2 -2
  25. package/dist/_shared/scope-resolution/position-index.js.map +1 -1
  26. package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
  27. package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
  28. package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
  29. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
  30. package/dist/_shared/scope-resolution/reference-site.js +24 -0
  31. package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
  32. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
  33. package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
  34. package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
  35. package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
  36. package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
  37. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
  38. package/dist/_shared/scope-resolution/registries/context.js +44 -0
  39. package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
  40. package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
  41. package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
  42. package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
  43. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
  44. package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
  45. package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
  46. package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
  47. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
  48. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
  49. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
  50. package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
  51. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
  52. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
  53. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
  54. package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
  55. package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
  56. package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
  57. package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
  58. package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
  59. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
  60. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
  61. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
  62. package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
  63. package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
  64. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
  65. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
  66. package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
  67. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
  68. package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
  69. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  70. package/dist/_shared/scope-resolution/scope-tree.js +3 -2
  71. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  72. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
  73. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
  74. package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
  75. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
  76. package/dist/_shared/scope-resolution/types.d.ts +11 -0
  77. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  78. package/dist/cli/ai-context.js +35 -4
  79. package/dist/cli/analyze.d.ts +27 -0
  80. package/dist/cli/analyze.js +31 -1
  81. package/dist/cli/clean.js +19 -1
  82. package/dist/cli/group.js +73 -0
  83. package/dist/cli/index-repo.js +8 -1
  84. package/dist/cli/index.js +26 -1
  85. package/dist/cli/list.js +11 -1
  86. package/dist/cli/remove.d.ts +30 -0
  87. package/dist/cli/remove.js +99 -0
  88. package/dist/cli/setup.js +185 -57
  89. package/dist/cli/tool.d.ts +5 -0
  90. package/dist/cli/tool.js +42 -0
  91. package/dist/config/ignore-service.d.ts +9 -0
  92. package/dist/config/ignore-service.js +80 -13
  93. package/dist/core/embedding-mode.d.ts +30 -0
  94. package/dist/core/embedding-mode.js +30 -0
  95. package/dist/core/embeddings/ast-utils.js +22 -22
  96. package/dist/core/embeddings/chunker.js +30 -25
  97. package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
  98. package/dist/core/embeddings/embedding-pipeline.js +15 -6
  99. package/dist/core/embeddings/text-generator.d.ts +1 -1
  100. package/dist/core/embeddings/text-generator.js +33 -24
  101. package/dist/core/embeddings/types.d.ts +43 -1
  102. package/dist/core/embeddings/types.js +101 -29
  103. package/dist/core/git-staleness.d.ts +18 -0
  104. package/dist/core/git-staleness.js +108 -0
  105. package/dist/core/graph/graph.js +115 -20
  106. package/dist/core/graph/types.d.ts +12 -1
  107. package/dist/core/group/config-parser.d.ts +4 -0
  108. package/dist/core/group/config-parser.js +18 -1
  109. package/dist/core/group/cross-impact.d.ts +41 -0
  110. package/dist/core/group/cross-impact.js +441 -0
  111. package/dist/core/group/extractors/http-patterns/php.js +126 -18
  112. package/dist/core/group/group-path-utils.d.ts +17 -0
  113. package/dist/core/group/group-path-utils.js +40 -0
  114. package/dist/core/group/resolve-at-member.d.ts +10 -0
  115. package/dist/core/group/resolve-at-member.js +31 -0
  116. package/dist/core/group/service.d.ts +9 -0
  117. package/dist/core/group/service.js +259 -25
  118. package/dist/core/group/types.d.ts +30 -0
  119. package/dist/core/ingestion/ast-cache.d.ts +16 -1
  120. package/dist/core/ingestion/ast-cache.js +14 -2
  121. package/dist/core/ingestion/call-processor.js +9 -0
  122. package/dist/core/ingestion/emit-references.d.ts +88 -0
  123. package/dist/core/ingestion/emit-references.js +229 -0
  124. package/dist/core/ingestion/filesystem-walker.js +6 -4
  125. package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
  126. package/dist/core/ingestion/finalize-orchestrator.js +139 -0
  127. package/dist/core/ingestion/framework-detection.js +6 -2
  128. package/dist/core/ingestion/import-processor.js +4 -0
  129. package/dist/core/ingestion/import-resolvers/python.js +9 -6
  130. package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
  131. package/dist/core/ingestion/import-target-adapter.js +95 -0
  132. package/dist/core/ingestion/language-provider.d.ts +36 -33
  133. package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
  134. package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
  135. package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
  136. package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
  137. package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
  138. package/dist/core/ingestion/languages/csharp/arity.js +37 -0
  139. package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
  140. package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
  141. package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
  142. package/dist/core/ingestion/languages/csharp/captures.js +249 -0
  143. package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
  144. package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
  145. package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
  146. package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
  147. package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
  148. package/dist/core/ingestion/languages/csharp/index.js +82 -0
  149. package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
  150. package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
  151. package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
  152. package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
  153. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
  154. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
  155. package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
  156. package/dist/core/ingestion/languages/csharp/query.js +515 -0
  157. package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
  158. package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
  159. package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
  160. package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
  161. package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
  162. package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
  163. package/dist/core/ingestion/languages/csharp.js +14 -0
  164. package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
  165. package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
  166. package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
  167. package/dist/core/ingestion/languages/python/arity.js +38 -0
  168. package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
  169. package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
  170. package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
  171. package/dist/core/ingestion/languages/python/captures.js +106 -0
  172. package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
  173. package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
  174. package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
  175. package/dist/core/ingestion/languages/python/import-target.js +99 -0
  176. package/dist/core/ingestion/languages/python/index.d.ts +80 -0
  177. package/dist/core/ingestion/languages/python/index.js +80 -0
  178. package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
  179. package/dist/core/ingestion/languages/python/interpret.js +191 -0
  180. package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
  181. package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
  182. package/dist/core/ingestion/languages/python/query.d.ts +9 -0
  183. package/dist/core/ingestion/languages/python/query.js +267 -0
  184. package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
  185. package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
  186. package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
  187. package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
  188. package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
  189. package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
  190. package/dist/core/ingestion/languages/python.js +14 -0
  191. package/dist/core/ingestion/model/method-registry.d.ts +9 -0
  192. package/dist/core/ingestion/model/method-registry.js +4 -0
  193. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
  194. package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
  195. package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
  196. package/dist/core/ingestion/model/semantic-model.js +55 -0
  197. package/dist/core/ingestion/mro-processor.js +38 -22
  198. package/dist/core/ingestion/parsing-processor.d.ts +18 -1
  199. package/dist/core/ingestion/parsing-processor.js +45 -11
  200. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  201. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  202. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
  203. package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
  204. package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
  205. package/dist/core/ingestion/pipeline.js +2 -1
  206. package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
  207. package/dist/core/ingestion/registry-primary-flag.js +111 -0
  208. package/dist/core/ingestion/resolve-references.d.ts +63 -0
  209. package/dist/core/ingestion/resolve-references.js +175 -0
  210. package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
  211. package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
  212. package/dist/core/ingestion/scope-extractor.d.ts +86 -0
  213. package/dist/core/ingestion/scope-extractor.js +758 -0
  214. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
  215. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
  216. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
  217. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
  218. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
  219. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
  220. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
  221. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
  222. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
  223. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
  224. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
  225. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
  226. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
  227. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
  228. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
  229. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
  230. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
  231. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
  232. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
  233. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
  234. package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
  235. package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
  236. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
  237. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
  238. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
  239. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
  240. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
  241. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
  242. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
  243. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
  244. package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
  245. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
  246. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
  247. package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
  248. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
  249. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
  250. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
  251. package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
  252. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
  253. package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
  254. package/dist/core/ingestion/shadow-harness.d.ts +113 -0
  255. package/dist/core/ingestion/shadow-harness.js +148 -0
  256. package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
  257. package/dist/core/ingestion/utils/ast-helpers.js +70 -0
  258. package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
  259. package/dist/core/ingestion/utils/max-file-size.js +52 -0
  260. package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
  261. package/dist/core/ingestion/workers/parse-worker.js +57 -21
  262. package/dist/core/lbug/lbug-adapter.d.ts +22 -2
  263. package/dist/core/lbug/lbug-adapter.js +58 -14
  264. package/dist/core/lbug/pool-adapter.d.ts +17 -0
  265. package/dist/core/lbug/pool-adapter.js +24 -14
  266. package/dist/core/run-analyze.d.ts +32 -0
  267. package/dist/core/run-analyze.js +74 -19
  268. package/dist/core/search/bm25-index.d.ts +18 -0
  269. package/dist/core/search/bm25-index.js +125 -12
  270. package/dist/core/tree-sitter/parser-loader.js +6 -1
  271. package/dist/mcp/local/local-backend.d.ts +67 -3
  272. package/dist/mcp/local/local-backend.js +296 -34
  273. package/dist/mcp/resources.d.ts +31 -0
  274. package/dist/mcp/resources.js +100 -17
  275. package/dist/mcp/tools.d.ts +4 -1
  276. package/dist/mcp/tools.js +75 -54
  277. package/dist/server/api.js +6 -2
  278. package/dist/storage/git.d.ts +49 -0
  279. package/dist/storage/git.js +111 -0
  280. package/dist/storage/repo-manager.d.ts +246 -1
  281. package/dist/storage/repo-manager.js +391 -9
  282. package/package.json +7 -6
  283. package/scripts/bench-scope-resolution.ts +134 -0
  284. package/scripts/ci-list-migrated-languages.ts +24 -0
  285. package/skills/gitnexus-cli.md +1 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Graph edge emission primitives.
3
+ *
4
+ * Two functions:
5
+ * - `mapReferenceKindToEdgeType` — translate a scope-resolution
6
+ * `Reference.kind` into the corresponding graph edge type.
7
+ * - `tryEmitEdge` — given a reference site + target def, resolve
8
+ * caller + target to graph ids and emit the edge with
9
+ * language-provided reason text, dedup-keyed by
10
+ * `(edgeType, callerId, targetId, line, col)`.
11
+ *
12
+ * Next-consumer contract: any language provider can call `tryEmitEdge`
13
+ * from its own post-pass to emit edges it resolves Python-specific
14
+ * (or TypeScript-specific, etc.) logic. The dedup key is
15
+ * language-agnostic — no language needs to change it.
16
+ */
17
+ import { resolveCallerGraphId, resolveDefGraphId } from '../graph-bridge/ids.js';
18
+ /**
19
+ * Map a `Reference.kind` to a graph edge type. `import-use` is dropped
20
+ * (no edge type today — provenance lives on the IMPORTS edge emitted
21
+ * by `emitImportEdges`).
22
+ */
23
+ export function mapReferenceKindToEdgeType(kind) {
24
+ switch (kind) {
25
+ case 'call':
26
+ return 'CALLS';
27
+ case 'read':
28
+ case 'write':
29
+ return 'ACCESSES';
30
+ case 'inherits':
31
+ return 'EXTENDS';
32
+ case 'type-reference':
33
+ return 'USES';
34
+ case 'import-use':
35
+ return undefined;
36
+ default:
37
+ return undefined;
38
+ }
39
+ }
40
+ /**
41
+ * Resolve caller + target to graph ids and emit the edge. Returns true
42
+ * if the edge was emitted (not deduped, not skipped).
43
+ *
44
+ * `seen` is a language-shared dedup set keyed by
45
+ * `${edgeType}:${callerGraphId}->${targetGraphId}:${line}:${col}` so
46
+ * multiple language-specific post-passes can share it and never
47
+ * double-emit a resolution one of them already produced.
48
+ */
49
+ export function tryEmitEdge(graph, scopes, nodeLookup, site, targetDef, reason, seen, confidence = 0.85, collapseByCallerTarget = false) {
50
+ const callerGraphId = resolveCallerGraphId(site.inScope, scopes, nodeLookup);
51
+ const targetGraphId = resolveDefGraphId(targetDef.filePath, targetDef, nodeLookup);
52
+ const edgeType = mapReferenceKindToEdgeType(site.kind);
53
+ if (callerGraphId === undefined)
54
+ return false;
55
+ if (targetGraphId === undefined)
56
+ return false;
57
+ if (edgeType === undefined)
58
+ return false;
59
+ // CALLS edges may collapse to `(caller, target)` granularity when
60
+ // the provider opts in (C# matches legacy DAG behavior this way).
61
+ // Write/read ACCESSES keep per-site dedup so multiple writes to the
62
+ // same field on different lines produce distinct edges.
63
+ const useCollapsed = collapseByCallerTarget && edgeType === 'CALLS';
64
+ const dedupKey = useCollapsed
65
+ ? `${edgeType}:${callerGraphId}->${targetGraphId}`
66
+ : `${edgeType}:${callerGraphId}->${targetGraphId}:${site.atRange.startLine}:${site.atRange.startCol}`;
67
+ if (seen.has(dedupKey))
68
+ return false;
69
+ seen.add(dedupKey);
70
+ graph.addRelationship({
71
+ id: `rel:${dedupKey}`,
72
+ sourceId: callerGraphId,
73
+ targetId: targetGraphId,
74
+ type: edgeType,
75
+ confidence,
76
+ reason,
77
+ });
78
+ return true;
79
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Scope-resolution → legacy graph-node ID bridging.
3
+ *
4
+ * Two functions:
5
+ * - `resolveDefGraphId` — turn a scope-resolution `SymbolDefinition`
6
+ * into the graph's node id for the corresponding legacy node.
7
+ * - `resolveCallerGraphId` — walk a scope chain from a reference
8
+ * site upward to find the enclosing function/method/class and
9
+ * return its graph-node id. Falls back to the File node for
10
+ * module-level calls so those still get an edge source.
11
+ *
12
+ * Next-consumer contract: language-agnostic. Any OO language with
13
+ * file-level module semantics (TypeScript, Java, Go, Kotlin) can
14
+ * reuse `resolveCallerGraphId` as-is. Languages with different
15
+ * top-level semantics (COBOL programs, Rust crate modules) may want
16
+ * a different file-level fallback — cross that bridge when they
17
+ * migrate.
18
+ */
19
+ import type { NodeLabel, ScopeId, SymbolDefinition } from '../../../../_shared/index.js';
20
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
21
+ import { type GraphNodeLookup } from '../graph-bridge/node-lookup.js';
22
+ /**
23
+ * Look up a `SymbolDefinition` in the graph node lookup.
24
+ *
25
+ * Tries the type-prefixed fully-qualified key FIRST. That's the only
26
+ * correct key when:
27
+ * - Two classes in the same file define a method with the same
28
+ * simple name (`class User: def save` + `class Document: def save`).
29
+ * - A top-level function and a class method share a simple name
30
+ * (`def save` + `class User: def save` — the Function's qualifier
31
+ * is just `save`, which would alias the Method's simple-key slot
32
+ * without the type prefix).
33
+ *
34
+ * Falls back to the simple name for definitions whose qualifier the
35
+ * lookup didn't capture (rare, but keeps cross-file simple-name
36
+ * resolution working for languages that don't yet synthesize
37
+ * qualifiers).
38
+ */
39
+ export declare function resolveDefGraphId(filePath: string, def: {
40
+ qualifiedName?: string;
41
+ type?: NodeLabel;
42
+ parameterTypes?: readonly string[];
43
+ }, nodeLookup: GraphNodeLookup): string | undefined;
44
+ /** Derive the simple (unqualified) name of a def from its `qualifiedName`. */
45
+ export declare function simpleQualifiedName(def: SymbolDefinition): string | undefined;
46
+ /**
47
+ * Walk the scope chain from `startScope` upward looking for the first
48
+ * scope whose `ownedDefs` contains a Function/Method/Class — that's
49
+ * our caller anchor. Translate via `nodeLookup` to the graph-node ID.
50
+ *
51
+ * Module-level references (e.g. Python `u = models.User()` at top
52
+ * level) have no enclosing function/method/class. Fall back to the
53
+ * File node for the scope's filePath so those calls still get an
54
+ * edge source. Matches legacy DAG behavior where module-level CALLS
55
+ * edges originate from the file symbol.
56
+ */
57
+ export declare function resolveCallerGraphId(startScope: ScopeId, scopes: ScopeResolutionIndexes, nodeLookup: GraphNodeLookup): string | undefined;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Scope-resolution → legacy graph-node ID bridging.
3
+ *
4
+ * Two functions:
5
+ * - `resolveDefGraphId` — turn a scope-resolution `SymbolDefinition`
6
+ * into the graph's node id for the corresponding legacy node.
7
+ * - `resolveCallerGraphId` — walk a scope chain from a reference
8
+ * site upward to find the enclosing function/method/class and
9
+ * return its graph-node id. Falls back to the File node for
10
+ * module-level calls so those still get an edge source.
11
+ *
12
+ * Next-consumer contract: language-agnostic. Any OO language with
13
+ * file-level module semantics (TypeScript, Java, Go, Kotlin) can
14
+ * reuse `resolveCallerGraphId` as-is. Languages with different
15
+ * top-level semantics (COBOL programs, Rust crate modules) may want
16
+ * a different file-level fallback — cross that bridge when they
17
+ * migrate.
18
+ */
19
+ import { generateId } from '../../../../lib/utils.js';
20
+ import { isLinkableLabel, qualifiedKey, simpleKey, } from '../graph-bridge/node-lookup.js';
21
+ /**
22
+ * Look up a `SymbolDefinition` in the graph node lookup.
23
+ *
24
+ * Tries the type-prefixed fully-qualified key FIRST. That's the only
25
+ * correct key when:
26
+ * - Two classes in the same file define a method with the same
27
+ * simple name (`class User: def save` + `class Document: def save`).
28
+ * - A top-level function and a class method share a simple name
29
+ * (`def save` + `class User: def save` — the Function's qualifier
30
+ * is just `save`, which would alias the Method's simple-key slot
31
+ * without the type prefix).
32
+ *
33
+ * Falls back to the simple name for definitions whose qualifier the
34
+ * lookup didn't capture (rare, but keeps cross-file simple-name
35
+ * resolution working for languages that don't yet synthesize
36
+ * qualifiers).
37
+ */
38
+ export function resolveDefGraphId(filePath, def, nodeLookup) {
39
+ const qn = def.qualifiedName;
40
+ if (qn === undefined || qn.length === 0)
41
+ return undefined;
42
+ if (def.type !== undefined) {
43
+ // Overload disambiguation: when the def carries parameter types,
44
+ // try the parameter-typed key first so same-name same-arity
45
+ // overloads route to their distinct graph nodes.
46
+ if (def.type === 'Method' &&
47
+ def.parameterTypes !== undefined &&
48
+ def.parameterTypes.length > 0) {
49
+ const pKey = qualifiedKey(filePath, def.type, `${qn}~${def.parameterTypes.join(',')}`);
50
+ const pHit = nodeLookup.get(pKey);
51
+ if (pHit !== undefined)
52
+ return pHit;
53
+ }
54
+ const qualifiedHit = nodeLookup.get(qualifiedKey(filePath, def.type, qn));
55
+ if (qualifiedHit !== undefined)
56
+ return qualifiedHit;
57
+ }
58
+ const simpleName = qn.lastIndexOf('.') === -1 ? qn : qn.slice(qn.lastIndexOf('.') + 1);
59
+ return nodeLookup.get(simpleKey(filePath, simpleName));
60
+ }
61
+ /** Derive the simple (unqualified) name of a def from its `qualifiedName`. */
62
+ export function simpleQualifiedName(def) {
63
+ const q = def.qualifiedName;
64
+ if (q === undefined || q.length === 0)
65
+ return undefined;
66
+ const dot = q.lastIndexOf('.');
67
+ return dot === -1 ? q : q.slice(dot + 1);
68
+ }
69
+ /**
70
+ * Walk the scope chain from `startScope` upward looking for the first
71
+ * scope whose `ownedDefs` contains a Function/Method/Class — that's
72
+ * our caller anchor. Translate via `nodeLookup` to the graph-node ID.
73
+ *
74
+ * Module-level references (e.g. Python `u = models.User()` at top
75
+ * level) have no enclosing function/method/class. Fall back to the
76
+ * File node for the scope's filePath so those calls still get an
77
+ * edge source. Matches legacy DAG behavior where module-level CALLS
78
+ * edges originate from the file symbol.
79
+ */
80
+ export function resolveCallerGraphId(startScope, scopes, nodeLookup) {
81
+ let current = startScope;
82
+ const visited = new Set();
83
+ let lastFilePath;
84
+ while (current !== null) {
85
+ if (visited.has(current))
86
+ return undefined;
87
+ visited.add(current);
88
+ const scope = scopes.scopeTree.getScope(current);
89
+ if (scope === undefined)
90
+ break;
91
+ lastFilePath = scope.filePath;
92
+ // Prefer Function/Method anchors; fall back to Class.
93
+ const fnDef = scope.ownedDefs.find((d) => d.type === 'Function' || d.type === 'Method' || d.type === 'Constructor');
94
+ if (fnDef !== undefined) {
95
+ const id = resolveDefGraphId(scope.filePath, fnDef, nodeLookup);
96
+ if (id !== undefined)
97
+ return id;
98
+ }
99
+ const classDef = scope.ownedDefs.find((d) => isLinkableLabel(d.type));
100
+ if (classDef !== undefined) {
101
+ const id = resolveDefGraphId(scope.filePath, classDef, nodeLookup);
102
+ if (id !== undefined)
103
+ return id;
104
+ }
105
+ current = scope.parent;
106
+ }
107
+ // Module-level calls — fall back to the File node for the scope's filePath.
108
+ if (lastFilePath !== undefined) {
109
+ return generateId('File', lastFilePath);
110
+ }
111
+ return undefined;
112
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * File→File IMPORTS edge emission from a finalized `ImportEdge` map.
3
+ *
4
+ * Deduplicates by `(sourceFile, targetFile)` so multi-symbol imports
5
+ * from the same module collapse to a single edge — matching the
6
+ * legacy schema.
7
+ *
8
+ * Next-consumer contract: language-agnostic. Any provider with a
9
+ * scope-resolution ImportEdge stream emits File→File edges via this
10
+ * single function. The `reason` defaults to
11
+ * `'scope-resolution: import'`; provider may override if downstream
12
+ * filters on reason.
13
+ */
14
+ import type { ImportEdge, ScopeId } from '../../../../_shared/index.js';
15
+ import type { KnowledgeGraph } from '../../../graph/types.js';
16
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
17
+ export declare function emitImportEdges(graph: KnowledgeGraph, imports: ReadonlyMap<ScopeId, readonly ImportEdge[]>, scopeTree: ScopeResolutionIndexes['scopeTree'], reason?: string): number;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * File→File IMPORTS edge emission from a finalized `ImportEdge` map.
3
+ *
4
+ * Deduplicates by `(sourceFile, targetFile)` so multi-symbol imports
5
+ * from the same module collapse to a single edge — matching the
6
+ * legacy schema.
7
+ *
8
+ * Next-consumer contract: language-agnostic. Any provider with a
9
+ * scope-resolution ImportEdge stream emits File→File edges via this
10
+ * single function. The `reason` defaults to
11
+ * `'scope-resolution: import'`; provider may override if downstream
12
+ * filters on reason.
13
+ */
14
+ import { generateId } from '../../../../lib/utils.js';
15
+ export function emitImportEdges(graph, imports, scopeTree, reason = 'scope-resolution: import') {
16
+ const seen = new Set();
17
+ let emitted = 0;
18
+ for (const [scopeId, edges] of imports) {
19
+ const scope = scopeTree.getScope(scopeId);
20
+ if (scope === undefined)
21
+ continue;
22
+ const sourceFile = scope.filePath;
23
+ for (const edge of edges) {
24
+ if (edge.targetFile === null)
25
+ continue;
26
+ if (edge.targetFile === sourceFile)
27
+ continue;
28
+ const dedupKey = `${sourceFile}->${edge.targetFile}`;
29
+ if (seen.has(dedupKey))
30
+ continue;
31
+ seen.add(dedupKey);
32
+ const sourceId = generateId('File', sourceFile);
33
+ const targetId = generateId('File', edge.targetFile);
34
+ graph.addRelationship({
35
+ id: generateId('IMPORTS', dedupKey),
36
+ sourceId,
37
+ targetId,
38
+ type: 'IMPORTS',
39
+ confidence: 1.0,
40
+ reason,
41
+ });
42
+ emitted++;
43
+ }
44
+ }
45
+ return emitted;
46
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Wrap a `DefId → ancestor DefId[]` MRO map in the shared
3
+ * `MethodDispatchIndex` shape so it slots into
4
+ * `ScopeResolutionIndexes.methodDispatch`.
5
+ *
6
+ * `finalizeScopeModel` builds an empty `MethodDispatchIndex` by design
7
+ * (per the comment in `finalize-orchestrator.ts`). Per-language
8
+ * providers compute MRO their own way (Python C3 walk, Java class
9
+ * hierarchy, Ruby mixin chains, etc.) and use this bridge to plug the
10
+ * result back into the shared index shape.
11
+ *
12
+ * Next-consumer contract: any language that computes its own MRO map
13
+ * calls `buildPopulatedMethodDispatch(mroByOwnerDefId)` and assigns the
14
+ * result to `indexes.methodDispatch`. Interface-implementer tracking
15
+ * (`implsByInterfaceDefId`) stays empty in V1 — providers that need it
16
+ * can extend the return shape without breaking existing consumers.
17
+ */
18
+ import type { MethodDispatchIndex } from '../../../../_shared/index.js';
19
+ export declare function buildPopulatedMethodDispatch(mroByDefId: ReadonlyMap<string, readonly string[]>): MethodDispatchIndex;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Wrap a `DefId → ancestor DefId[]` MRO map in the shared
3
+ * `MethodDispatchIndex` shape so it slots into
4
+ * `ScopeResolutionIndexes.methodDispatch`.
5
+ *
6
+ * `finalizeScopeModel` builds an empty `MethodDispatchIndex` by design
7
+ * (per the comment in `finalize-orchestrator.ts`). Per-language
8
+ * providers compute MRO their own way (Python C3 walk, Java class
9
+ * hierarchy, Ruby mixin chains, etc.) and use this bridge to plug the
10
+ * result back into the shared index shape.
11
+ *
12
+ * Next-consumer contract: any language that computes its own MRO map
13
+ * calls `buildPopulatedMethodDispatch(mroByOwnerDefId)` and assigns the
14
+ * result to `indexes.methodDispatch`. Interface-implementer tracking
15
+ * (`implsByInterfaceDefId`) stays empty in V1 — providers that need it
16
+ * can extend the return shape without breaking existing consumers.
17
+ */
18
+ const EMPTY_DEFS = Object.freeze([]);
19
+ export function buildPopulatedMethodDispatch(mroByDefId) {
20
+ return {
21
+ mroByOwnerDefId: mroByDefId,
22
+ implsByInterfaceDefId: new Map(),
23
+ mroFor(ownerDefId) {
24
+ return mroByDefId.get(ownerDefId) ?? EMPTY_DEFS;
25
+ },
26
+ implementorsOf() {
27
+ return EMPTY_DEFS;
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Build a `(filePath, name) → graphNodeId` lookup over the graph's
3
+ * Function/Method/Class/Constructor nodes. Two keys per node:
4
+ *
5
+ * - simple name (`User` / `save`) — legacy fallback
6
+ * - qualified name when derivable from the node id (`User.save`)
7
+ *
8
+ * The qualified key is the authoritative one when two classes in the
9
+ * same file define a method with the same simple name
10
+ * (`class User: def save` + `class Document: def save`). Without it,
11
+ * the simple-name key collides and every `document.save()` CALLS edge
12
+ * would silently target `User.save`. Method node ids encode the
13
+ * qualifier (`Method:file.py:User.save#1`), so we parse it back out.
14
+ *
15
+ * Language-agnostic seam. Any language provider migrating to the
16
+ * registry-primary path can consume this to translate scope-resolution
17
+ * `SymbolDefinition.nodeId` values into the legacy graph-node ID
18
+ * format that downstream consumers (queries, edges, MCP) expect.
19
+ */
20
+ import type { NodeLabel } from '../../../../_shared/index.js';
21
+ import type { KnowledgeGraph } from '../../../graph/types.js';
22
+ export type GraphNodeLookup = ReadonlyMap<string, string>;
23
+ /**
24
+ * Build a qualified-key string in a separate keyspace from simple-key
25
+ * strings. Prefix `<q>` can't appear in a valid filePath on any OS, so
26
+ * no collision between the two keyspaces is possible.
27
+ *
28
+ * Includes the node label so a top-level `def save` (Function,
29
+ * qualifier = `save`) doesn't alias a class method `User.save` (Method,
30
+ * simple name = `save`) whose Function-typed qualifier would collapse
31
+ * to the same simple-key slot in a single map.
32
+ */
33
+ export declare function qualifiedKey(filePath: string, label: NodeLabel, qualifiedName: string): string;
34
+ /** Simple-name key (legacy fallback keyspace — no `<q>` prefix). */
35
+ export declare function simpleKey(filePath: string, name: string): string;
36
+ export declare function buildGraphNodeLookup(graph: KnowledgeGraph): GraphNodeLookup;
37
+ export declare function isLinkableLabel(label: NodeLabel): boolean;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Build a `(filePath, name) → graphNodeId` lookup over the graph's
3
+ * Function/Method/Class/Constructor nodes. Two keys per node:
4
+ *
5
+ * - simple name (`User` / `save`) — legacy fallback
6
+ * - qualified name when derivable from the node id (`User.save`)
7
+ *
8
+ * The qualified key is the authoritative one when two classes in the
9
+ * same file define a method with the same simple name
10
+ * (`class User: def save` + `class Document: def save`). Without it,
11
+ * the simple-name key collides and every `document.save()` CALLS edge
12
+ * would silently target `User.save`. Method node ids encode the
13
+ * qualifier (`Method:file.py:User.save#1`), so we parse it back out.
14
+ *
15
+ * Language-agnostic seam. Any language provider migrating to the
16
+ * registry-primary path can consume this to translate scope-resolution
17
+ * `SymbolDefinition.nodeId` values into the legacy graph-node ID
18
+ * format that downstream consumers (queries, edges, MCP) expect.
19
+ */
20
+ /**
21
+ * Parse a qualified name out of a Function/Method node id.
22
+ *
23
+ * Node id format: `${label}:${filePath}:${qualifiedName}${arityTag}`,
24
+ * where `arityTag` is `#<n>` (or empty). Strips the known-length
25
+ * label + filePath prefix so colons inside `filePath` (Windows
26
+ * `C:\...`) don't break the parse. Returns `undefined` when the id
27
+ * doesn't match the expected shape.
28
+ */
29
+ function parseQualifiedFromId(id, label, filePath) {
30
+ const prefix = `${label}:${filePath}:`;
31
+ if (!id.startsWith(prefix))
32
+ return undefined;
33
+ const suffix = id.slice(prefix.length);
34
+ if (suffix.length === 0)
35
+ return undefined;
36
+ const hash = suffix.indexOf('#');
37
+ return hash === -1 ? suffix : suffix.slice(0, hash);
38
+ }
39
+ /**
40
+ * Build a qualified-key string in a separate keyspace from simple-key
41
+ * strings. Prefix `<q>` can't appear in a valid filePath on any OS, so
42
+ * no collision between the two keyspaces is possible.
43
+ *
44
+ * Includes the node label so a top-level `def save` (Function,
45
+ * qualifier = `save`) doesn't alias a class method `User.save` (Method,
46
+ * simple name = `save`) whose Function-typed qualifier would collapse
47
+ * to the same simple-key slot in a single map.
48
+ */
49
+ export function qualifiedKey(filePath, label, qualifiedName) {
50
+ return `<q>:${filePath}::${label}::${qualifiedName}`;
51
+ }
52
+ /** Simple-name key (legacy fallback keyspace — no `<q>` prefix). */
53
+ export function simpleKey(filePath, name) {
54
+ return `${filePath}::${name}`;
55
+ }
56
+ export function buildGraphNodeLookup(graph) {
57
+ const lookup = new Map();
58
+ for (const node of graph.iterNodes()) {
59
+ const props = node.properties;
60
+ if (props.filePath === undefined || props.name === undefined)
61
+ continue;
62
+ if (!isLinkableLabel(node.label))
63
+ continue;
64
+ // Primary key: fully-qualified name + label, in a separate
65
+ // keyspace from simple names. Class nodes carry `qualifiedName`
66
+ // in their properties (set by the parsing processor).
67
+ // Method/Function nodes do not, so derive the qualifier from the
68
+ // node id — that's where the parse-phase encoded it. Including
69
+ // the label avoids a collision when a free Function's qualifier
70
+ // happens to equal a Method's simple name (e.g. top-level
71
+ // `def save` vs `class User: def save`).
72
+ const qualified = props.qualifiedName ?? parseQualifiedFromId(node.id, node.label, props.filePath);
73
+ if (qualified !== undefined && qualified.length > 0) {
74
+ const qKey = qualifiedKey(props.filePath, node.label, qualified);
75
+ if (!lookup.has(qKey))
76
+ lookup.set(qKey, node.id);
77
+ // Overload-disambiguating key: include parameter types so two
78
+ // same-arity overloads (e.g. `Lookup(int)` vs `Lookup(string)`)
79
+ // map to distinct graph nodes. Legacy parse-phase encodes the
80
+ // type tag into the node id; we register both that node id and
81
+ // a parameter-types-suffixed key so resolveDefGraphId can find
82
+ // the right overload by matching its def's parameterTypes.
83
+ const pTypes = props.parameterTypes;
84
+ if (pTypes !== undefined && pTypes.length > 0 && node.label === 'Method') {
85
+ const pKey = qualifiedKey(props.filePath, node.label, `${qualified}~${pTypes.join(',')}`);
86
+ // Each overload is unique — set unconditionally.
87
+ lookup.set(pKey, node.id);
88
+ }
89
+ }
90
+ // Fallback key: simple name. First-wins within a file — used when
91
+ // the caller doesn't know the qualifier (unqualified free-call
92
+ // fallback, cross-file resolution where MethodRegistry already
93
+ // disambiguated the owner).
94
+ const sKey = simpleKey(props.filePath, props.name);
95
+ if (!lookup.has(sKey))
96
+ lookup.set(sKey, node.id);
97
+ }
98
+ return lookup;
99
+ }
100
+ export function isLinkableLabel(label) {
101
+ return (label === 'Function' ||
102
+ label === 'Method' ||
103
+ label === 'Constructor' ||
104
+ label === 'Class' ||
105
+ label === 'Interface' ||
106
+ label === 'Struct' ||
107
+ label === 'Enum' ||
108
+ // Variable / Property are linkable too — receiver-bound write/read
109
+ // ACCESSES edges target field nodes (e.g. `user.name = "x"` →
110
+ // ACCESSES edge to User's `name` Variable/Property node).
111
+ label === 'Variable' ||
112
+ label === 'Property');
113
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Translate the resolved `ReferenceIndex` into legacy graph edges.
3
+ *
4
+ * Per reference:
5
+ * 1. Resolve `fromScope` → caller graph-node id by walking the scope
6
+ * chain looking for an enclosing Function/Method/Class.
7
+ * 2. Resolve `toDef` → target graph-node id via `nodeLookup`.
8
+ * 3. Emit the edge (`CALLS` / `READS` / `WRITES` / `EXTENDS` / `USES`)
9
+ * with the standard reason format.
10
+ *
11
+ * Skips (without throwing) when either side fails to map — either side
12
+ * may legitimately not exist as a graph node (e.g. a resolved target
13
+ * lives in an external file that wasn't ingested into the graph).
14
+ *
15
+ * Next-consumer contract: this function is the canonical bridge from
16
+ * a shared `ReferenceIndex` into per-language graph edges. Every
17
+ * registry-primary language provider calls this exactly once with its
18
+ * `referenceIndex` output and its own `nodeLookup`.
19
+ */
20
+ import type { Reference, ScopeId } from '../../../../_shared/index.js';
21
+ import type { KnowledgeGraph } from '../../../graph/types.js';
22
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
23
+ import type { GraphNodeLookup } from '../graph-bridge/node-lookup.js';
24
+ /**
25
+ * Optional opaque skip key — providers may pre-emit edges (e.g. via
26
+ * receiver-bound post-passes) and want this loop to skip references at
27
+ * the same source position so the shared resolver's potentially-wrong
28
+ * fallback resolution doesn't fight the precise emission. The key is
29
+ * `${filePath}:${startLine}:${startCol}`.
30
+ */
31
+ type ReferenceSiteSkipSet = ReadonlySet<string>;
32
+ export declare function emitReferencesViaLookup(graph: KnowledgeGraph, scopes: ScopeResolutionIndexes, referenceIndex: {
33
+ readonly bySourceScope: ReadonlyMap<ScopeId, readonly Reference[]>;
34
+ }, nodeLookup: GraphNodeLookup, skipSites?: ReferenceSiteSkipSet): {
35
+ emitted: number;
36
+ skipped: number;
37
+ };
38
+ export {};
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Translate the resolved `ReferenceIndex` into legacy graph edges.
3
+ *
4
+ * Per reference:
5
+ * 1. Resolve `fromScope` → caller graph-node id by walking the scope
6
+ * chain looking for an enclosing Function/Method/Class.
7
+ * 2. Resolve `toDef` → target graph-node id via `nodeLookup`.
8
+ * 3. Emit the edge (`CALLS` / `READS` / `WRITES` / `EXTENDS` / `USES`)
9
+ * with the standard reason format.
10
+ *
11
+ * Skips (without throwing) when either side fails to map — either side
12
+ * may legitimately not exist as a graph node (e.g. a resolved target
13
+ * lives in an external file that wasn't ingested into the graph).
14
+ *
15
+ * Next-consumer contract: this function is the canonical bridge from
16
+ * a shared `ReferenceIndex` into per-language graph edges. Every
17
+ * registry-primary language provider calls this exactly once with its
18
+ * `referenceIndex` output and its own `nodeLookup`.
19
+ */
20
+ import { resolveCallerGraphId, resolveDefGraphId } from '../graph-bridge/ids.js';
21
+ import { mapReferenceKindToEdgeType } from '../graph-bridge/edges.js';
22
+ export function emitReferencesViaLookup(graph, scopes, referenceIndex, nodeLookup, skipSites) {
23
+ let emitted = 0;
24
+ let skipped = 0;
25
+ const seen = new Set();
26
+ for (const [fromScope, refs] of referenceIndex.bySourceScope) {
27
+ const callerGraphId = resolveCallerGraphId(fromScope, scopes, nodeLookup);
28
+ if (callerGraphId === undefined) {
29
+ skipped += refs.length;
30
+ continue;
31
+ }
32
+ const fromScopeMeta = scopes.scopeTree.getScope(fromScope);
33
+ const fromFilePath = fromScopeMeta?.filePath;
34
+ for (const ref of refs) {
35
+ if (skipSites !== undefined && fromFilePath !== undefined) {
36
+ const siteKey = `${fromFilePath}:${ref.atRange.startLine}:${ref.atRange.startCol}`;
37
+ if (skipSites.has(siteKey)) {
38
+ skipped++;
39
+ continue;
40
+ }
41
+ }
42
+ const targetDef = scopes.defs.get(ref.toDef);
43
+ if (targetDef === undefined) {
44
+ skipped++;
45
+ continue;
46
+ }
47
+ const targetGraphId = resolveDefGraphId(targetDef.filePath, targetDef, nodeLookup);
48
+ if (targetGraphId === undefined) {
49
+ skipped++;
50
+ continue;
51
+ }
52
+ const edgeType = mapReferenceKindToEdgeType(ref.kind);
53
+ if (edgeType === undefined) {
54
+ skipped++;
55
+ continue;
56
+ }
57
+ const dedupKey = `${edgeType}:${callerGraphId}->${targetGraphId}:${ref.atRange.startLine}:${ref.atRange.startCol}`;
58
+ if (seen.has(dedupKey))
59
+ continue;
60
+ seen.add(dedupKey);
61
+ graph.addRelationship({
62
+ id: `rel:${dedupKey}`,
63
+ sourceId: callerGraphId,
64
+ targetId: targetGraphId,
65
+ type: edgeType,
66
+ confidence: ref.confidence,
67
+ reason: `scope-resolution: ${ref.kind}`,
68
+ });
69
+ emitted++;
70
+ }
71
+ }
72
+ return { emitted, skipped };
73
+ }