gitnexus 1.5.3 → 1.6.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 (304) hide show
  1. package/README.md +10 -0
  2. package/dist/_shared/graph/types.d.ts +1 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +1 -0
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/language-detection.d.ts.map +1 -1
  7. package/dist/_shared/language-detection.js +2 -0
  8. package/dist/_shared/language-detection.js.map +1 -1
  9. package/dist/_shared/languages.d.ts +1 -0
  10. package/dist/_shared/languages.d.ts.map +1 -1
  11. package/dist/_shared/languages.js +1 -0
  12. package/dist/_shared/languages.js.map +1 -1
  13. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  14. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.js +3 -1
  16. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  17. package/dist/_shared/mro-strategy.d.ts +19 -0
  18. package/dist/_shared/mro-strategy.d.ts.map +1 -0
  19. package/dist/_shared/mro-strategy.js +2 -0
  20. package/dist/_shared/mro-strategy.js.map +1 -0
  21. package/dist/cli/ai-context.d.ts +1 -0
  22. package/dist/cli/ai-context.js +28 -4
  23. package/dist/cli/analyze.d.ts +2 -0
  24. package/dist/cli/analyze.js +30 -4
  25. package/dist/cli/group.d.ts +2 -0
  26. package/dist/cli/group.js +233 -0
  27. package/dist/cli/index.js +3 -0
  28. package/dist/cli/serve.js +4 -1
  29. package/dist/cli/setup.js +34 -3
  30. package/dist/config/ignore-service.js +8 -3
  31. package/dist/core/augmentation/engine.js +1 -1
  32. package/dist/core/git-staleness.d.ts +13 -0
  33. package/dist/core/git-staleness.js +29 -0
  34. package/dist/core/group/bridge-db.d.ts +82 -0
  35. package/dist/core/group/bridge-db.js +460 -0
  36. package/dist/core/group/bridge-schema.d.ts +27 -0
  37. package/dist/core/group/bridge-schema.js +55 -0
  38. package/dist/core/group/config-parser.d.ts +3 -0
  39. package/dist/core/group/config-parser.js +83 -0
  40. package/dist/core/group/contract-extractor.d.ts +7 -0
  41. package/dist/core/group/contract-extractor.js +1 -0
  42. package/dist/core/group/extractors/fs-utils.d.ts +10 -0
  43. package/dist/core/group/extractors/fs-utils.js +24 -0
  44. package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
  45. package/dist/core/group/extractors/grpc-extractor.js +386 -0
  46. package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
  47. package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
  48. package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
  49. package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
  50. package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
  51. package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
  52. package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
  53. package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
  54. package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
  55. package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
  56. package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
  57. package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
  58. package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
  59. package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
  60. package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
  61. package/dist/core/group/extractors/http-patterns/go.js +215 -0
  62. package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
  63. package/dist/core/group/extractors/http-patterns/index.js +44 -0
  64. package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
  65. package/dist/core/group/extractors/http-patterns/java.js +253 -0
  66. package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
  67. package/dist/core/group/extractors/http-patterns/node.js +354 -0
  68. package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
  69. package/dist/core/group/extractors/http-patterns/php.js +70 -0
  70. package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
  71. package/dist/core/group/extractors/http-patterns/python.js +133 -0
  72. package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
  73. package/dist/core/group/extractors/http-patterns/types.js +1 -0
  74. package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
  75. package/dist/core/group/extractors/http-route-extractor.js +391 -0
  76. package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
  77. package/dist/core/group/extractors/manifest-extractor.js +235 -0
  78. package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
  79. package/dist/core/group/extractors/topic-extractor.js +97 -0
  80. package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
  81. package/dist/core/group/extractors/topic-patterns/go.js +120 -0
  82. package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
  83. package/dist/core/group/extractors/topic-patterns/index.js +38 -0
  84. package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
  85. package/dist/core/group/extractors/topic-patterns/java.js +80 -0
  86. package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
  87. package/dist/core/group/extractors/topic-patterns/node.js +155 -0
  88. package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
  89. package/dist/core/group/extractors/topic-patterns/python.js +116 -0
  90. package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
  91. package/dist/core/group/extractors/topic-patterns/types.js +10 -0
  92. package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
  93. package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
  94. package/dist/core/group/matching.d.ts +13 -0
  95. package/dist/core/group/matching.js +198 -0
  96. package/dist/core/group/normalization.d.ts +3 -0
  97. package/dist/core/group/normalization.js +115 -0
  98. package/dist/core/group/service-boundary-detector.d.ts +8 -0
  99. package/dist/core/group/service-boundary-detector.js +155 -0
  100. package/dist/core/group/service.d.ts +46 -0
  101. package/dist/core/group/service.js +160 -0
  102. package/dist/core/group/storage.d.ts +9 -0
  103. package/dist/core/group/storage.js +91 -0
  104. package/dist/core/group/sync.d.ts +21 -0
  105. package/dist/core/group/sync.js +148 -0
  106. package/dist/core/group/types.d.ts +130 -0
  107. package/dist/core/group/types.js +1 -0
  108. package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
  109. package/dist/core/ingestion/binding-accumulator.js +336 -0
  110. package/dist/core/ingestion/call-processor.d.ts +155 -24
  111. package/dist/core/ingestion/call-processor.js +1129 -247
  112. package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
  113. package/dist/core/ingestion/class-extractors/generic.js +135 -0
  114. package/dist/core/ingestion/class-types.d.ts +34 -0
  115. package/dist/core/ingestion/class-types.js +1 -0
  116. package/dist/core/ingestion/cobol-processor.d.ts +1 -1
  117. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
  118. package/dist/core/ingestion/entry-point-scoring.js +1 -0
  119. package/dist/core/ingestion/field-types.d.ts +2 -2
  120. package/dist/core/ingestion/filesystem-walker.js +8 -0
  121. package/dist/core/ingestion/framework-detection.d.ts +1 -0
  122. package/dist/core/ingestion/framework-detection.js +1 -0
  123. package/dist/core/ingestion/heritage-processor.d.ts +8 -15
  124. package/dist/core/ingestion/heritage-processor.js +15 -28
  125. package/dist/core/ingestion/import-processor.d.ts +1 -11
  126. package/dist/core/ingestion/import-processor.js +1 -13
  127. package/dist/core/ingestion/import-resolvers/utils.js +1 -0
  128. package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
  129. package/dist/core/ingestion/import-resolvers/vue.js +9 -0
  130. package/dist/core/ingestion/language-config.js +1 -1
  131. package/dist/core/ingestion/language-provider.d.ts +14 -3
  132. package/dist/core/ingestion/languages/c-cpp.js +168 -1
  133. package/dist/core/ingestion/languages/csharp.js +20 -0
  134. package/dist/core/ingestion/languages/dart.js +26 -4
  135. package/dist/core/ingestion/languages/go.js +22 -0
  136. package/dist/core/ingestion/languages/index.d.ts +1 -0
  137. package/dist/core/ingestion/languages/index.js +2 -0
  138. package/dist/core/ingestion/languages/java.js +17 -0
  139. package/dist/core/ingestion/languages/kotlin.js +24 -1
  140. package/dist/core/ingestion/languages/php.js +23 -11
  141. package/dist/core/ingestion/languages/python.js +9 -0
  142. package/dist/core/ingestion/languages/ruby.js +43 -0
  143. package/dist/core/ingestion/languages/rust.js +38 -0
  144. package/dist/core/ingestion/languages/swift.js +31 -0
  145. package/dist/core/ingestion/languages/typescript.d.ts +1 -0
  146. package/dist/core/ingestion/languages/typescript.js +52 -3
  147. package/dist/core/ingestion/languages/vue.d.ts +13 -0
  148. package/dist/core/ingestion/languages/vue.js +81 -0
  149. package/dist/core/ingestion/markdown-processor.d.ts +1 -1
  150. package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
  151. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  152. package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
  153. package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
  154. package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
  155. package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
  156. package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
  157. package/dist/core/ingestion/method-extractors/configs/jvm.js +14 -4
  158. package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
  159. package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
  160. package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
  161. package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
  162. package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
  163. package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
  164. package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
  165. package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
  166. package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
  167. package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
  168. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
  169. package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
  170. package/dist/core/ingestion/method-extractors/generic.js +84 -17
  171. package/dist/core/ingestion/method-types.d.ts +29 -0
  172. package/dist/core/ingestion/model/field-registry.d.ts +18 -0
  173. package/dist/core/ingestion/model/field-registry.js +22 -0
  174. package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
  175. package/dist/core/ingestion/model/heritage-map.js +159 -0
  176. package/dist/core/ingestion/model/index.d.ts +20 -0
  177. package/dist/core/ingestion/model/index.js +41 -0
  178. package/dist/core/ingestion/model/method-registry.d.ts +62 -0
  179. package/dist/core/ingestion/model/method-registry.js +130 -0
  180. package/dist/core/ingestion/model/registration-table.d.ts +139 -0
  181. package/dist/core/ingestion/model/registration-table.js +224 -0
  182. package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
  183. package/dist/core/ingestion/model/resolution-context.js +337 -0
  184. package/dist/core/ingestion/model/resolve.d.ts +56 -0
  185. package/dist/core/ingestion/model/resolve.js +297 -0
  186. package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
  187. package/dist/core/ingestion/model/semantic-model.js +120 -0
  188. package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
  189. package/dist/core/ingestion/model/symbol-table.js +206 -0
  190. package/dist/core/ingestion/model/type-registry.d.ts +39 -0
  191. package/dist/core/ingestion/model/type-registry.js +62 -0
  192. package/dist/core/ingestion/mro-processor.d.ts +5 -4
  193. package/dist/core/ingestion/mro-processor.js +311 -107
  194. package/dist/core/ingestion/parsing-processor.d.ts +5 -4
  195. package/dist/core/ingestion/parsing-processor.js +224 -87
  196. package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
  197. package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
  198. package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
  199. package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
  200. package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
  201. package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
  202. package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
  203. package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
  204. package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
  205. package/dist/core/ingestion/pipeline-phases/index.js +22 -0
  206. package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
  207. package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
  208. package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
  209. package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
  210. package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
  211. package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
  212. package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
  213. package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
  214. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
  215. package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
  216. package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
  217. package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
  218. package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
  219. package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
  220. package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
  221. package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
  222. package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
  223. package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
  224. package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
  225. package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
  226. package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
  227. package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
  228. package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
  229. package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
  230. package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
  231. package/dist/core/ingestion/pipeline-phases/types.js +37 -0
  232. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +35 -0
  233. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +174 -0
  234. package/dist/core/ingestion/pipeline.d.ts +18 -10
  235. package/dist/core/ingestion/pipeline.js +66 -1410
  236. package/dist/core/ingestion/process-processor.js +1 -1
  237. package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
  238. package/dist/core/ingestion/tree-sitter-queries.js +90 -0
  239. package/dist/core/ingestion/type-env.d.ts +15 -2
  240. package/dist/core/ingestion/type-env.js +163 -102
  241. package/dist/core/ingestion/type-extractors/csharp.js +17 -0
  242. package/dist/core/ingestion/type-extractors/jvm.js +11 -0
  243. package/dist/core/ingestion/type-extractors/php.js +0 -55
  244. package/dist/core/ingestion/type-extractors/ruby.js +0 -32
  245. package/dist/core/ingestion/type-extractors/swift.js +13 -0
  246. package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
  247. package/dist/core/ingestion/type-extractors/typescript.js +66 -69
  248. package/dist/core/ingestion/utils/ast-helpers.d.ts +32 -44
  249. package/dist/core/ingestion/utils/ast-helpers.js +157 -573
  250. package/dist/core/ingestion/utils/env.d.ts +10 -0
  251. package/dist/core/ingestion/utils/env.js +10 -0
  252. package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
  253. package/dist/core/ingestion/utils/graph-sort.js +100 -0
  254. package/dist/core/ingestion/utils/method-props.d.ts +32 -0
  255. package/dist/core/ingestion/utils/method-props.js +147 -0
  256. package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
  257. package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
  258. package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
  259. package/dist/core/ingestion/workers/parse-worker.js +469 -200
  260. package/dist/core/lbug/lbug-adapter.d.ts +6 -0
  261. package/dist/core/lbug/lbug-adapter.js +134 -27
  262. package/dist/core/lbug/pool-adapter.d.ts +76 -0
  263. package/dist/core/lbug/pool-adapter.js +522 -0
  264. package/dist/core/run-analyze.d.ts +2 -0
  265. package/dist/core/run-analyze.js +1 -1
  266. package/dist/core/search/bm25-index.js +1 -1
  267. package/dist/core/tree-sitter/parser-loader.js +1 -0
  268. package/dist/core/wiki/graph-queries.js +1 -1
  269. package/dist/mcp/core/embedder.js +6 -5
  270. package/dist/mcp/core/lbug-adapter.d.ts +3 -63
  271. package/dist/mcp/core/lbug-adapter.js +3 -484
  272. package/dist/mcp/local/local-backend.d.ts +31 -2
  273. package/dist/mcp/local/local-backend.js +255 -46
  274. package/dist/mcp/resources.js +5 -4
  275. package/dist/mcp/staleness.d.ts +3 -13
  276. package/dist/mcp/staleness.js +2 -31
  277. package/dist/mcp/tools.js +80 -4
  278. package/dist/server/analyze-job.d.ts +2 -0
  279. package/dist/server/analyze-job.js +4 -0
  280. package/dist/server/api.d.ts +20 -1
  281. package/dist/server/api.js +306 -71
  282. package/dist/server/git-clone.d.ts +2 -1
  283. package/dist/server/git-clone.js +98 -5
  284. package/dist/storage/git.d.ts +13 -0
  285. package/dist/storage/git.js +25 -0
  286. package/dist/storage/repo-manager.js +1 -1
  287. package/package.json +9 -3
  288. package/scripts/patch-tree-sitter-swift.cjs +78 -0
  289. package/vendor/tree-sitter-proto/binding.gyp +30 -0
  290. package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
  291. package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
  292. package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
  293. package/vendor/tree-sitter-proto/package.json +18 -0
  294. package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
  295. package/vendor/tree-sitter-proto/src/parser.c +10149 -0
  296. package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
  297. package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
  298. package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
  299. package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
  300. package/dist/core/ingestion/named-binding-processor.js +0 -42
  301. package/dist/core/ingestion/resolution-context.d.ts +0 -58
  302. package/dist/core/ingestion/resolution-context.js +0 -135
  303. package/dist/core/ingestion/symbol-table.d.ts +0 -79
  304. package/dist/core/ingestion/symbol-table.js +0 -115
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Registration Dispatch Table
3
+ *
4
+ * Behavior-grouped O(1) dispatch table for routing `SymbolTable.add()`
5
+ * registrations into the semantic registries. Replaces the cascading
6
+ * `if/else` ladder in `symbol-table.ts` with a `Map<NodeLabel, RoutingDecision>`
7
+ * whose entries point to closure-captured hooks.
8
+ *
9
+ * ## Ownership diagram
10
+ *
11
+ * SemanticModel
12
+ * ├── types (TypeRegistry) ← classLikeHook / implHook write here
13
+ * ├── methods (MethodRegistry) ← methodHook writes here
14
+ * ├── fields (FieldRegistry) ← propertyHook writes here
15
+ * └── symbols (SymbolTable) ← owns fileIndex + callableByName,
16
+ * calls dispatch() in add()
17
+ *
18
+ * ## Behavior groups (5 hooks, 13 table entries)
19
+ *
20
+ * | Group | NodeLabel values | Hook | Skip callable? |
21
+ * |---------------|---------------------------------------------------|--------------|----------------|
22
+ * | class-like | Class, Struct, Interface, Enum, Record, Trait | classLikeHook | no |
23
+ * | method-like | Method, Constructor | methodHook | no |
24
+ * | property | Property | propertyHook | YES |
25
+ * | impl-block | Impl | implHook | no |
26
+ * | callable-only | Function, Macro, Delegate | (no entry) | no |
27
+ *
28
+ * Every other `NodeLabel` is "inert" — reached by `fileIndex` only. No
29
+ * specialized registry, no callable index append.
30
+ *
31
+ * ## How to add a new NodeLabel
32
+ *
33
+ * 1. Add the variant to the `NodeLabel` union in `gitnexus-shared/src/graph/types.ts`.
34
+ * 2. Decide which behavior group it belongs to by asking "which lookups must
35
+ * return this symbol?" (not "what language feature is it?"). A new Swift
36
+ * `Extension` is class-like if you want owner-scoped method lookup on it;
37
+ * a new Kotlin `Object` is class-like for the same reason.
38
+ * 3. Either:
39
+ * - Add a table entry here pointing at one of the existing hooks, OR
40
+ * - Add it to `CALLABLE_ONLY_LABELS` if it is a free callable, OR
41
+ * - Add it to `INERT_LABELS` if it's metadata-only (File, Folder, Decorator,
42
+ * etc.) — never queried via owner/class lookups.
43
+ * 4. If none of the above fit — the new kind needs a brand-new registry —
44
+ * design the registry first in `model/`, then add a new hook closure
45
+ * and table entries. Update `DISPATCH_LABELS` / the exhaustiveness guard
46
+ * accordingly.
47
+ *
48
+ * The runtime exhaustiveness guard in `symbol-table.ts` will warn if a
49
+ * `NodeLabel` is missing from all three sets.
50
+ */
51
+ import { FREE_CALLABLE_TYPES } from './symbol-table.js';
52
+ /**
53
+ * **Single source of truth** for NodeLabel classification. Every NodeLabel
54
+ * has exactly one behavior category — enforced at compile time by the
55
+ * `as const satisfies Record<NodeLabel, LabelBehavior>` combo:
56
+ *
57
+ * - **Completeness** — `Record<NodeLabel, LabelBehavior>` requires every
58
+ * NodeLabel to be a key. Missing a label fails to compile with
59
+ * "Property 'X' is missing in type ..." naming the drifted label.
60
+ * - **No extras** — `satisfies` performs excess-property checking on
61
+ * object literals, so a non-NodeLabel string key fails to compile.
62
+ * - **No duplicates** — object keys are unique by construction. A label
63
+ * cannot be classified into two categories by accident.
64
+ * - **Valid values** — `LabelBehavior` is a narrow union, so a typo in
65
+ * the category name fails to compile.
66
+ *
67
+ * Adding a new NodeLabel to `gitnexus-shared`: TypeScript will flag this
68
+ * file as incomplete. Add the new label with its behavior category and
69
+ * the three `*_LABELS` Sets + `ALL_NODE_LABELS` array below are derived
70
+ * automatically — no separate list to update, no runtime drift detection
71
+ * needed.
72
+ *
73
+ * NOTE: `Type` and `CodeElement` are inert wrappers for language features
74
+ * that don't yet have a dedicated registry (typedefs, synthesized dynamic
75
+ * calls). If future work needs owner-scoped lookup for them, change their
76
+ * category to `'dispatch'` and add a hook in `createRegistrationTable`.
77
+ * Do not special-case them inside `SymbolTable.add()`.
78
+ */
79
+ const LABEL_BEHAVIOR = {
80
+ // dispatch — owner-scoped registry writes
81
+ Class: 'dispatch',
82
+ Struct: 'dispatch',
83
+ Interface: 'dispatch',
84
+ Enum: 'dispatch',
85
+ Record: 'dispatch',
86
+ Trait: 'dispatch',
87
+ Method: 'dispatch',
88
+ Constructor: 'dispatch',
89
+ Property: 'dispatch',
90
+ Impl: 'dispatch',
91
+ // callable-only — file index + callableByName, no owner scope
92
+ Function: 'callable-only',
93
+ Macro: 'callable-only',
94
+ Delegate: 'callable-only',
95
+ // inert — file index only
96
+ Project: 'inert',
97
+ Package: 'inert',
98
+ Module: 'inert',
99
+ Folder: 'inert',
100
+ File: 'inert',
101
+ Variable: 'inert',
102
+ Decorator: 'inert',
103
+ Import: 'inert',
104
+ Type: 'inert',
105
+ CodeElement: 'inert',
106
+ Community: 'inert',
107
+ Process: 'inert',
108
+ Typedef: 'inert',
109
+ Union: 'inert',
110
+ Namespace: 'inert',
111
+ TypeAlias: 'inert',
112
+ Const: 'inert',
113
+ Static: 'inert',
114
+ Annotation: 'inert',
115
+ Template: 'inert',
116
+ Section: 'inert',
117
+ Route: 'inert',
118
+ Tool: 'inert',
119
+ };
120
+ // ---------------------------------------------------------------------------
121
+ // Derived runtime collections — all keyed off LABEL_BEHAVIOR
122
+ // ---------------------------------------------------------------------------
123
+ /**
124
+ * All known NodeLabels, derived from the keys of `LABEL_BEHAVIOR`. The
125
+ * `satisfies Record<NodeLabel, LabelBehavior>` bijection above proves
126
+ * that `Object.keys(LABEL_BEHAVIOR)` is exactly the NodeLabel set —
127
+ * the cast to `NodeLabel[]` is sound, not a type-system bypass.
128
+ *
129
+ * Consumers (e.g., the semantic-model barrel re-export for tests) can
130
+ * rely on this list being complete by construction. No runtime drift
131
+ * check is needed or possible — the type system is the proof.
132
+ */
133
+ export const ALL_NODE_LABELS = Object.keys(LABEL_BEHAVIOR);
134
+ const labelsWithBehavior = (behavior) => ALL_NODE_LABELS.filter((label) => LABEL_BEHAVIOR[label] === behavior);
135
+ /**
136
+ * NodeLabel values that are free callables — appear in `callableByName`
137
+ * but have no owner-scoped specialized registry. Alias of
138
+ * {@link FREE_CALLABLE_TYPES} exported here for taxonomy-test use. The
139
+ * compile-time cross-invariant on `LABEL_BEHAVIOR` above guarantees the
140
+ * alias and the LABEL_BEHAVIOR `callable-only` classification cannot
141
+ * drift.
142
+ */
143
+ export const CALLABLE_ONLY_LABELS = FREE_CALLABLE_TYPES;
144
+ /**
145
+ * NodeLabel values that touch only the file index — no specialized
146
+ * registry, no callable index.
147
+ */
148
+ export const INERT_LABELS = new Set(labelsWithBehavior('inert'));
149
+ /**
150
+ * NodeLabel values that have a dispatch table entry. `createRegistrationTable`
151
+ * below must provide a hook for exactly this set — the test file's behavior-
152
+ * group tests and the integration tests pin the hook↔label correspondence.
153
+ */
154
+ export const DISPATCH_LABELS = new Set(labelsWithBehavior('dispatch'));
155
+ // ---------------------------------------------------------------------------
156
+ // Factory
157
+ // ---------------------------------------------------------------------------
158
+ /**
159
+ * Build the dispatch table. Must be called once per `createSymbolTable`
160
+ * invocation so each hook closes over that SymbolTable's injected
161
+ * registries. Reusing a single module-level instance would cause hooks
162
+ * to write into the wrong SemanticModel.
163
+ */
164
+ export const createRegistrationTable = (deps) => {
165
+ const { types, methods, fields } = deps;
166
+ // Hook 1: class-like — Class, Struct, Interface, Enum, Record, Trait.
167
+ // Shared reference — six table entries point at this one closure.
168
+ const classLikeHook = (name, def) => {
169
+ const qualifiedKey = def.qualifiedName ?? name;
170
+ types.registerClass(name, qualifiedKey, def);
171
+ };
172
+ // Hook 2: method-like — Method, Constructor. Silently skipped if the
173
+ // caller did not provide an ownerId (Property without ownerId is
174
+ // treated the same way).
175
+ const methodHook = (name, def) => {
176
+ if (def.ownerId) {
177
+ methods.register(def.ownerId, name, def);
178
+ }
179
+ };
180
+ // Hook 3: property — Property. Silently skipped without ownerId.
181
+ // Property is not in `FREE_CALLABLE_TYPES`, so `SymbolTable.add()` already
182
+ // excludes it from `callableByName`; common property names like
183
+ // `id` / `name` / `type` never pollute the callable index.
184
+ const propertyHook = (name, def) => {
185
+ if (def.ownerId) {
186
+ fields.register(def.ownerId, name, def);
187
+ }
188
+ };
189
+ // Hook 4: impl-block — Rust `impl` blocks. Kept separate from classLikeHook
190
+ // because heritage resolution must not treat Impls as class candidates
191
+ // (an Impl is not a parent type, it's an ancillary dispatch table).
192
+ const implHook = (name, def) => {
193
+ types.registerImpl(name, def);
194
+ };
195
+ // Single source of truth for the label → hook mapping. The
196
+ // `satisfies Record<DispatchLabel, RegistrationHook>` intersection
197
+ // fails at build time if (a) any label classified as 'dispatch' in
198
+ // `LABEL_BEHAVIOR` is missing here, or (b) any key here is not
199
+ // classified as 'dispatch'. This is the compile-time twin of the
200
+ // runtime taxonomy — no drift possible.
201
+ const dispatchByLabel = {
202
+ // class-like — six labels share the single `classLikeHook` closure,
203
+ // kept in lockstep with `CLASS_TYPES_TUPLE` via the
204
+ // `Record<ClassLikeLabel, 'dispatch'>` cross-invariant on
205
+ // `LABEL_BEHAVIOR`.
206
+ Class: classLikeHook,
207
+ Struct: classLikeHook,
208
+ Interface: classLikeHook,
209
+ Enum: classLikeHook,
210
+ Record: classLikeHook,
211
+ Trait: classLikeHook,
212
+ // method-like — routed via dispatch-key normalization in
213
+ // `wrappedAdd` so Function+ownerId also reaches `methodHook`.
214
+ Method: methodHook,
215
+ Constructor: methodHook,
216
+ // property — callable-index exclusion is enforced by
217
+ // `SymbolTable.add()` (Property is not in `FREE_CALLABLE_TYPES`).
218
+ Property: propertyHook,
219
+ // impl-block — Rust `impl` blocks. Separate from classLikeHook because
220
+ // heritage resolution must not treat Impls as class candidates.
221
+ Impl: implHook,
222
+ };
223
+ return new Map(Object.entries(dispatchByLabel));
224
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Resolution Context
3
+ *
4
+ * Single implementation of tiered name resolution.
5
+ *
6
+ * Resolution tiers (highest confidence first):
7
+ * 1. Same file (lookupExactAll — authoritative)
8
+ * 2a-named. Named binding chain (walkBindingChain via NamedImportMap)
9
+ * 2a. Import-scoped (iterate importedFiles with lookupExactAll per file)
10
+ * 2b. Package-scoped (iterate indexed files matching package dir with lookupExactAll)
11
+ * 3. Global (lookupClassByName + lookupImplByName + lookupCallableByName — consumers must check count)
12
+ *
13
+ * Each tier queries the minimum necessary scope directly:
14
+ * - Tier 2a iterates the caller's import set (O(imports) × O(1) lookupExactAll).
15
+ * - Tier 2b iterates all indexed files filtered by package dir
16
+ * (O(files) × O(1) lookupExactAll — avoids a global name scan).
17
+ * - Tier 3 combines lookupClassByName + lookupImplByName + lookupCallableByName
18
+ * (three O(1) index lookups with a narrow, type-specific result set).
19
+ */
20
+ import type { SymbolDefinition } from './symbol-table.js';
21
+ import type { MutableSemanticModel } from './semantic-model.js';
22
+ /**
23
+ * A single named binding in a source file (e.g. `import { User as U }`).
24
+ * Stores both the resolved source path and the original exported name so
25
+ * that aliased imports can resolve U → User in the source file.
26
+ */
27
+ export interface NamedImportBinding {
28
+ sourcePath: string;
29
+ exportedName: string;
30
+ }
31
+ /**
32
+ * Map<ImportingFilePath, Map<LocalName, NamedImportBinding>>.
33
+ *
34
+ * Tracks which specific names a file imports from which sources (TS / Python
35
+ * / Rust / Java-static / ...). Used to tighten Tier 2a resolution:
36
+ * `import { User } from './models'` means only `User` (not `Repo`) is
37
+ * visible from models.ts via this import.
38
+ */
39
+ export type NamedImportMap = Map<string, Map<string, NamedImportBinding>>;
40
+ /**
41
+ * Check if a file path is directly inside a package directory identified by
42
+ * its suffix. Used by Tier 2b package-scoped resolution (Go / C#).
43
+ */
44
+ export declare function isFileInPackageDir(filePath: string, dirSuffix: string): boolean;
45
+ /** Resolution tier for tracking, logging, and test assertions. */
46
+ export type ResolutionTier = 'same-file' | 'import-scoped' | 'global';
47
+ /** Tier-selected candidates with metadata. */
48
+ export interface TieredCandidates {
49
+ readonly candidates: readonly SymbolDefinition[];
50
+ readonly tier: ResolutionTier;
51
+ }
52
+ /** Confidence scores per resolution tier. */
53
+ export declare const TIER_CONFIDENCE: Record<ResolutionTier, number>;
54
+ export type ImportMap = Map<string, Set<string>>;
55
+ export type PackageMap = Map<string, Set<string>>;
56
+ /** Maps callerFile → (moduleAlias → sourceFilePath) for Python namespace imports.
57
+ * e.g. `import models` in app.py → moduleAliasMap.get('app.py')?.get('models') === 'models.py' */
58
+ export type ModuleAliasMap = Map<string, Map<string, string>>;
59
+ export interface ResolutionContext {
60
+ /**
61
+ * The only resolution API. Returns all candidates at the winning tier.
62
+ *
63
+ * Tier 3 ('global') returns ALL candidates regardless of count —
64
+ * consumers must check candidates.length and refuse ambiguous matches.
65
+ */
66
+ resolve(name: string, fromFile: string): TieredCandidates | null;
67
+ /** Semantic model — the top-level container for types, methods, fields,
68
+ * and the nested file/callable SymbolTable. Typed as
69
+ * {@link MutableSemanticModel} because `ResolutionContext` is the
70
+ * lifecycle owner — the pipeline registers symbols through it during
71
+ * the fan-out phase. Resolvers that only query should annotate their
72
+ * own fields as {@link SemanticModel} to drop write access. */
73
+ readonly model: MutableSemanticModel;
74
+ /** Raw maps — used by import-processor to populate import data. */
75
+ readonly importMap: ImportMap;
76
+ readonly packageMap: PackageMap;
77
+ readonly namedImportMap: NamedImportMap;
78
+ /** Module-alias map for Python namespace imports: callerFile → (alias → sourceFile). */
79
+ readonly moduleAliasMap: ModuleAliasMap;
80
+ enableCache(filePath: string): void;
81
+ clearCache(): void;
82
+ getStats(): {
83
+ fileCount: number;
84
+ cacheHits: number;
85
+ cacheMisses: number;
86
+ tierSameFile: number;
87
+ tierImportScoped: number;
88
+ tierGlobal: number;
89
+ tierMiss: number;
90
+ };
91
+ clear(): void;
92
+ }
93
+ export declare const createResolutionContext: () => ResolutionContext;
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Resolution Context
3
+ *
4
+ * Single implementation of tiered name resolution.
5
+ *
6
+ * Resolution tiers (highest confidence first):
7
+ * 1. Same file (lookupExactAll — authoritative)
8
+ * 2a-named. Named binding chain (walkBindingChain via NamedImportMap)
9
+ * 2a. Import-scoped (iterate importedFiles with lookupExactAll per file)
10
+ * 2b. Package-scoped (iterate indexed files matching package dir with lookupExactAll)
11
+ * 3. Global (lookupClassByName + lookupImplByName + lookupCallableByName — consumers must check count)
12
+ *
13
+ * Each tier queries the minimum necessary scope directly:
14
+ * - Tier 2a iterates the caller's import set (O(imports) × O(1) lookupExactAll).
15
+ * - Tier 2b iterates all indexed files filtered by package dir
16
+ * (O(files) × O(1) lookupExactAll — avoids a global name scan).
17
+ * - Tier 3 combines lookupClassByName + lookupImplByName + lookupCallableByName
18
+ * (three O(1) index lookups with a narrow, type-specific result set).
19
+ */
20
+ import { createSemanticModel } from './semantic-model.js';
21
+ /**
22
+ * Check if a file path is directly inside a package directory identified by
23
+ * its suffix. Used by Tier 2b package-scoped resolution (Go / C#).
24
+ */
25
+ export function isFileInPackageDir(filePath, dirSuffix) {
26
+ // Prepend '/' so paths like "internal/auth/service.go" match suffix "/internal/auth/"
27
+ const normalized = '/' + filePath.replace(/\\/g, '/');
28
+ if (!normalized.includes(dirSuffix))
29
+ return false;
30
+ const afterDir = normalized.substring(normalized.indexOf(dirSuffix) + dirSuffix.length);
31
+ return !afterDir.includes('/');
32
+ }
33
+ /** Maximum re-export hops walkBindingChain will follow before giving up.
34
+ * A hard cap is needed to defend against pathological cycles that slip
35
+ * past the `visited` Set (e.g. a binding chain whose key is equal by
36
+ * string value but visits distinct modules). Five hops covers the
37
+ * common TypeScript monorepo pattern (component → pkg/index →
38
+ * packages/index → root/index → types/index). Chains longer than this
39
+ * fall through to Tier 2a-import / Tier 2b / Tier 3 resolution, which
40
+ * is a silent false-negative that the caller may or may not recover
41
+ * from. If a real repo hits this limit, raise it — there is no
42
+ * correctness reason to keep it at exactly 5. */
43
+ const MAX_BINDING_CHAIN_DEPTH = 5;
44
+ /**
45
+ * Walk a named-binding re-export chain through NamedImportMap.
46
+ *
47
+ * When file A imports { User } from B, and B re-exports { User } from C,
48
+ * the NamedImportMap for A points to B, but B has no User definition.
49
+ * This function follows the chain: A → B → C until a definition is found.
50
+ *
51
+ * Returns the definitions found at the end of the chain, or null if the
52
+ * chain breaks (missing binding, circular reference, or
53
+ * {@link MAX_BINDING_CHAIN_DEPTH} exceeded). Internal to
54
+ * resolution-context — not exported from the model barrel.
55
+ */
56
+ function walkBindingChain(name, currentFilePath, symbolTable, namedImportMap) {
57
+ // Fast exit: most files have no named imports at all. Skip the Set
58
+ // allocation + loop entry on the common empty-binding path so resolve()
59
+ // stays allocation-free for the typical call site.
60
+ const firstBindings = namedImportMap.get(currentFilePath);
61
+ if (!firstBindings)
62
+ return null;
63
+ const firstBinding = firstBindings.get(name);
64
+ if (!firstBinding)
65
+ return null;
66
+ let lookupFile = currentFilePath;
67
+ let lookupName = name;
68
+ const visited = new Set();
69
+ for (let depth = 0; depth < MAX_BINDING_CHAIN_DEPTH; depth++) {
70
+ const bindings = depth === 0 ? firstBindings : namedImportMap.get(lookupFile);
71
+ if (!bindings)
72
+ return null;
73
+ const binding = depth === 0 ? firstBinding : bindings.get(lookupName);
74
+ if (!binding)
75
+ return null;
76
+ const key = `${binding.sourcePath}:${binding.exportedName}`;
77
+ if (visited.has(key))
78
+ return null; // circular
79
+ visited.add(key);
80
+ const targetName = binding.exportedName;
81
+ const resolvedDefs = symbolTable.lookupExactAll(binding.sourcePath, targetName);
82
+ if (resolvedDefs.length > 0)
83
+ return resolvedDefs;
84
+ // No definition in source file → follow re-export chain
85
+ lookupFile = binding.sourcePath;
86
+ lookupName = targetName;
87
+ }
88
+ return null;
89
+ }
90
+ /** Confidence scores per resolution tier. */
91
+ export const TIER_CONFIDENCE = {
92
+ 'same-file': 0.95,
93
+ 'import-scoped': 0.9,
94
+ global: 0.5,
95
+ };
96
+ export const createResolutionContext = () => {
97
+ const model = createSemanticModel();
98
+ const symbols = model.symbols;
99
+ const importMap = new Map();
100
+ const packageMap = new Map();
101
+ const namedImportMap = new Map();
102
+ const moduleAliasMap = new Map();
103
+ // Inverted index: packageDirSuffix → Set<filePath>.
104
+ // Built lazily on first Tier 2b hit — one-time cost of O(totalFiles ×
105
+ // allUniqueDirSuffixes) isFileInPackageDir calls across the entire
106
+ // packageMap, amortized over the pipeline run. Subsequent Tier 2b
107
+ // resolutions are O(callerPackages × filesInPackage × O(1)).
108
+ let packageDirIndex = null;
109
+ // Per-file cache state
110
+ let cacheFile = null;
111
+ let cache = null;
112
+ let cacheHits = 0;
113
+ let cacheMisses = 0;
114
+ // Tier hit counters — replaces the lost fuzzyCallCount diagnostic
115
+ let tierSameFile = 0;
116
+ let tierImportScoped = 0;
117
+ let tierGlobal = 0;
118
+ let tierMiss = 0;
119
+ // --- Core resolution (single implementation of tier logic) ---
120
+ const resolveUncached = (name, fromFile) => {
121
+ // Tier 1: Same file — authoritative match (returns all overloads)
122
+ const localDefs = symbols.lookupExactAll(fromFile, name);
123
+ if (localDefs.length > 0) {
124
+ tierSameFile++;
125
+ return { candidates: localDefs, tier: 'same-file' };
126
+ }
127
+ // Tier 2a-named: Named binding chain (aliased / re-exported imports)
128
+ // Checked before import-scoped so that `import { User as U }` resolves
129
+ // correctly even when lookupExactAll on the alias name returns nothing.
130
+ const chainResult = walkBindingChain(name, fromFile, symbols, namedImportMap);
131
+ if (chainResult && chainResult.length > 0) {
132
+ tierImportScoped++;
133
+ return { candidates: chainResult, tier: 'import-scoped' };
134
+ }
135
+ // Tier 2a: Import-scoped — iterate the caller's imported files directly.
136
+ // O(importedFiles) × O(1) lookupExactAll — no global name scan needed.
137
+ const importedFiles = importMap.get(fromFile);
138
+ if (importedFiles) {
139
+ const importedDefs = [];
140
+ for (const file of importedFiles) {
141
+ importedDefs.push(...symbols.lookupExactAll(file, name));
142
+ }
143
+ if (importedDefs.length > 0) {
144
+ tierImportScoped++;
145
+ return { candidates: importedDefs, tier: 'import-scoped' };
146
+ }
147
+ }
148
+ // Tier 2b: Package-scoped — look up files in the caller's imported package
149
+ // directories via an inverted index (packageDirSuffix → Set<filePath>),
150
+ // then do O(1) lookupExactAll per file. The inverted index is built lazily
151
+ // on first Tier 2b hit by scanning symbols.getFiles() once, making
152
+ // subsequent Tier 2b resolutions O(packages × filesInPackage) instead of
153
+ // O(allFiles × packages).
154
+ const importedPackages = packageMap.get(fromFile);
155
+ if (importedPackages) {
156
+ // Lazily build the inverted index on first use. For each indexed file,
157
+ // test it against isFileInPackageDir for all known dirSuffixes collected
158
+ // from packageMap. This scans all files once (instead of per-resolution)
159
+ // and produces a dirSuffix → Set<filePath> map.
160
+ if (!packageDirIndex) {
161
+ // Collect all unique dir suffixes across the entire packageMap
162
+ const allDirSuffixes = new Set();
163
+ for (const dirs of packageMap.values()) {
164
+ for (const d of dirs)
165
+ allDirSuffixes.add(d);
166
+ }
167
+ packageDirIndex = new Map();
168
+ for (const file of symbols.getFiles()) {
169
+ for (const dirSuffix of allDirSuffixes) {
170
+ if (isFileInPackageDir(file, dirSuffix)) {
171
+ let files = packageDirIndex.get(dirSuffix);
172
+ if (!files) {
173
+ files = new Set();
174
+ packageDirIndex.set(dirSuffix, files);
175
+ }
176
+ files.add(file);
177
+ }
178
+ }
179
+ }
180
+ }
181
+ const packageDefs = [];
182
+ for (const dirSuffix of importedPackages) {
183
+ const filesInDir = packageDirIndex.get(dirSuffix);
184
+ if (filesInDir) {
185
+ for (const file of filesInDir) {
186
+ packageDefs.push(...symbols.lookupExactAll(file, name));
187
+ }
188
+ }
189
+ }
190
+ if (packageDefs.length > 0) {
191
+ tierImportScoped++;
192
+ return { candidates: packageDefs, tier: 'import-scoped' };
193
+ }
194
+ }
195
+ // Tier 3: Global — targeted O(1) index lookups for each symbol category.
196
+ // Class-like symbols (Class, Struct, Interface, Enum, Record, Trait) are
197
+ // covered by lookupClassByName; Rust impl blocks by lookupImplByName
198
+ // (separate to avoid polluting heritage resolution); free callables
199
+ // (Function, Macro, Delegate) by lookupCallableByName; owner-scoped
200
+ // methods and constructors by `model.methods.lookupMethodByName`.
201
+ //
202
+ // FREE_CALLABLE_TYPES excludes Method/Constructor, so strictly-labeled
203
+ // methods are disjoint between the two indexes.
204
+ //
205
+ // Partial-state caveat: Python/Rust/Kotlin class methods are emitted
206
+ // as Function + ownerId — `rawSymbols.add` routes them through both
207
+ // the Function callable index AND, via the dispatch-key normalization
208
+ // in `wrappedAdd`, the method registry. The same `SymbolDefinition`
209
+ // reference lands in both `callableDefs` and `methodDefs`, so the
210
+ // Set-based dedup below is required.
211
+ //
212
+ // Known exclusion: TypeAlias, Const, and Variable are NOT reachable at
213
+ // Tier 3 — they don't belong to any of the indexes. TypeAlias is not
214
+ // a call target; Const/Variable are resolved via import or same-file
215
+ // tiers. Macro (C/C++) and Delegate (C#) stay in the callable index
216
+ // since call-processor.ts treats them as callable targets.
217
+ const classDefs = model.types.lookupClassByName(name);
218
+ const implDefs = model.types.lookupImplByName(name);
219
+ const callableDefs = symbols.lookupCallableByName(name);
220
+ const methodDefs = model.methods.lookupMethodByName(name);
221
+ if (classDefs.length === 0 &&
222
+ implDefs.length === 0 &&
223
+ callableDefs.length === 0 &&
224
+ methodDefs.length === 0) {
225
+ tierMiss++;
226
+ return null;
227
+ }
228
+ // Fast path: if no `Function + ownerId` class method was ever
229
+ // registered into the method registry (the only source of
230
+ // cross-index duplication), the callable and method indexes are
231
+ // guaranteed disjoint and we can concat without dedup.
232
+ if (!model.methods.hasFunctionMethods) {
233
+ const globalDefs = [
234
+ ...classDefs,
235
+ ...implDefs,
236
+ ...callableDefs,
237
+ ...methodDefs,
238
+ ];
239
+ tierGlobal++;
240
+ return { candidates: globalDefs, tier: 'global' };
241
+ }
242
+ // Slow path: dedup by nodeId because the same SymbolDefinition
243
+ // reference can land in both `callableDefs` (via the Function
244
+ // callable-index gate) and `methodDefs` (via the dispatch-key
245
+ // normalization routing Function+ownerId into MethodRegistry).
246
+ // Dedup covers all four index reads so any nodeId overlap (even
247
+ // theoretical ones between classDefs/implDefs) is caught.
248
+ const globalDefs = [];
249
+ const seen = new Set();
250
+ const pushUnique = (pool) => {
251
+ for (const def of pool) {
252
+ if (seen.has(def.nodeId))
253
+ continue;
254
+ seen.add(def.nodeId);
255
+ globalDefs.push(def);
256
+ }
257
+ };
258
+ pushUnique(classDefs);
259
+ pushUnique(implDefs);
260
+ pushUnique(callableDefs);
261
+ pushUnique(methodDefs);
262
+ tierGlobal++;
263
+ return { candidates: globalDefs, tier: 'global' };
264
+ };
265
+ const resolve = (name, fromFile) => {
266
+ // Check cache (only when enabled AND fromFile matches cached file)
267
+ if (cache && cacheFile === fromFile) {
268
+ if (cache.has(name)) {
269
+ cacheHits++;
270
+ return cache.get(name);
271
+ }
272
+ cacheMisses++;
273
+ }
274
+ const result = resolveUncached(name, fromFile);
275
+ // Store in cache if active and file matches
276
+ if (cache && cacheFile === fromFile) {
277
+ cache.set(name, result);
278
+ }
279
+ return result;
280
+ };
281
+ // --- Cache lifecycle ---
282
+ const enableCache = (filePath) => {
283
+ cacheFile = filePath;
284
+ if (!cache)
285
+ cache = new Map();
286
+ else
287
+ cache.clear();
288
+ };
289
+ const clearCache = () => {
290
+ cacheFile = null;
291
+ // Reuse the Map instance — just clear entries to reduce GC pressure at scale.
292
+ cache?.clear();
293
+ // Note: packageDirIndex is NOT invalidated here. It is built lazily on
294
+ // first Tier 2b hit and remains valid across file boundaries because
295
+ // packageMap and the symbol file set are append-only during the calls
296
+ // phase (all parsing/import processing completes before resolution).
297
+ // Invalidating per-file would destroy the amortization benefit — the
298
+ // O(files × dirs) rebuild would run per-file instead of once.
299
+ // Full invalidation happens in clear() (pipeline reset).
300
+ };
301
+ const getStats = () => ({
302
+ ...symbols.getStats(),
303
+ cacheHits,
304
+ cacheMisses,
305
+ tierSameFile,
306
+ tierImportScoped,
307
+ tierGlobal,
308
+ tierMiss,
309
+ });
310
+ const clear = () => {
311
+ model.clear();
312
+ importMap.clear();
313
+ packageMap.clear();
314
+ namedImportMap.clear();
315
+ moduleAliasMap.clear();
316
+ packageDirIndex = null; // invalidate — will rebuild on next Tier 2b hit
317
+ clearCache();
318
+ cacheHits = 0;
319
+ cacheMisses = 0;
320
+ tierSameFile = 0;
321
+ tierImportScoped = 0;
322
+ tierGlobal = 0;
323
+ tierMiss = 0;
324
+ };
325
+ return {
326
+ resolve,
327
+ model,
328
+ importMap,
329
+ packageMap,
330
+ namedImportMap,
331
+ moduleAliasMap,
332
+ enableCache,
333
+ clearCache,
334
+ getStats,
335
+ clear,
336
+ };
337
+ };