gitnexus 1.5.3 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) 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 +2 -1
  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/grpc-extractor.d.ts +16 -0
  43. package/dist/core/group/extractors/grpc-extractor.js +264 -0
  44. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  45. package/dist/core/group/extractors/http-route-extractor.js +428 -0
  46. package/dist/core/group/extractors/topic-extractor.d.ts +9 -0
  47. package/dist/core/group/extractors/topic-extractor.js +234 -0
  48. package/dist/core/group/matching.d.ts +13 -0
  49. package/dist/core/group/matching.js +198 -0
  50. package/dist/core/group/normalization.d.ts +3 -0
  51. package/dist/core/group/normalization.js +115 -0
  52. package/dist/core/group/service-boundary-detector.d.ts +8 -0
  53. package/dist/core/group/service-boundary-detector.js +155 -0
  54. package/dist/core/group/service.d.ts +46 -0
  55. package/dist/core/group/service.js +160 -0
  56. package/dist/core/group/storage.d.ts +9 -0
  57. package/dist/core/group/storage.js +91 -0
  58. package/dist/core/group/sync.d.ts +21 -0
  59. package/dist/core/group/sync.js +148 -0
  60. package/dist/core/group/types.d.ts +130 -0
  61. package/dist/core/group/types.js +1 -0
  62. package/dist/core/ingestion/binding-accumulator.d.ts +207 -0
  63. package/dist/core/ingestion/binding-accumulator.js +332 -0
  64. package/dist/core/ingestion/call-processor.d.ts +155 -24
  65. package/dist/core/ingestion/call-processor.js +1129 -247
  66. package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
  67. package/dist/core/ingestion/class-extractors/generic.js +135 -0
  68. package/dist/core/ingestion/class-types.d.ts +34 -0
  69. package/dist/core/ingestion/class-types.js +1 -0
  70. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
  71. package/dist/core/ingestion/entry-point-scoring.js +1 -0
  72. package/dist/core/ingestion/field-types.d.ts +2 -2
  73. package/dist/core/ingestion/filesystem-walker.js +8 -0
  74. package/dist/core/ingestion/framework-detection.d.ts +1 -0
  75. package/dist/core/ingestion/framework-detection.js +1 -0
  76. package/dist/core/ingestion/heritage-processor.d.ts +8 -15
  77. package/dist/core/ingestion/heritage-processor.js +15 -28
  78. package/dist/core/ingestion/import-processor.d.ts +1 -11
  79. package/dist/core/ingestion/import-processor.js +0 -12
  80. package/dist/core/ingestion/import-resolvers/utils.js +1 -0
  81. package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
  82. package/dist/core/ingestion/import-resolvers/vue.js +9 -0
  83. package/dist/core/ingestion/language-provider.d.ts +6 -3
  84. package/dist/core/ingestion/languages/c-cpp.js +168 -1
  85. package/dist/core/ingestion/languages/csharp.js +20 -0
  86. package/dist/core/ingestion/languages/dart.js +26 -4
  87. package/dist/core/ingestion/languages/go.js +22 -0
  88. package/dist/core/ingestion/languages/index.d.ts +1 -0
  89. package/dist/core/ingestion/languages/index.js +2 -0
  90. package/dist/core/ingestion/languages/java.js +17 -0
  91. package/dist/core/ingestion/languages/kotlin.js +24 -1
  92. package/dist/core/ingestion/languages/php.js +23 -11
  93. package/dist/core/ingestion/languages/python.js +9 -0
  94. package/dist/core/ingestion/languages/ruby.js +28 -0
  95. package/dist/core/ingestion/languages/rust.js +38 -0
  96. package/dist/core/ingestion/languages/swift.js +31 -0
  97. package/dist/core/ingestion/languages/typescript.d.ts +1 -0
  98. package/dist/core/ingestion/languages/typescript.js +52 -3
  99. package/dist/core/ingestion/languages/vue.d.ts +13 -0
  100. package/dist/core/ingestion/languages/vue.js +81 -0
  101. package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
  102. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  103. package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
  104. package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
  105. package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
  106. package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
  107. package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
  108. package/dist/core/ingestion/method-extractors/configs/jvm.js +13 -4
  109. package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
  110. package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
  111. package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
  112. package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
  113. package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
  114. package/dist/core/ingestion/method-extractors/configs/ruby.js +285 -0
  115. package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
  116. package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
  117. package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
  118. package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
  119. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
  120. package/dist/core/ingestion/method-extractors/generic.js +38 -15
  121. package/dist/core/ingestion/method-types.d.ts +25 -0
  122. package/dist/core/ingestion/model/field-registry.d.ts +18 -0
  123. package/dist/core/ingestion/model/field-registry.js +22 -0
  124. package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
  125. package/dist/core/ingestion/model/heritage-map.js +159 -0
  126. package/dist/core/ingestion/model/index.d.ts +20 -0
  127. package/dist/core/ingestion/model/index.js +41 -0
  128. package/dist/core/ingestion/model/method-registry.d.ts +62 -0
  129. package/dist/core/ingestion/model/method-registry.js +130 -0
  130. package/dist/core/ingestion/model/registration-table.d.ts +139 -0
  131. package/dist/core/ingestion/model/registration-table.js +224 -0
  132. package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
  133. package/dist/core/ingestion/model/resolution-context.js +337 -0
  134. package/dist/core/ingestion/model/resolve.d.ts +56 -0
  135. package/dist/core/ingestion/model/resolve.js +242 -0
  136. package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
  137. package/dist/core/ingestion/model/semantic-model.js +120 -0
  138. package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
  139. package/dist/core/ingestion/model/symbol-table.js +206 -0
  140. package/dist/core/ingestion/model/type-registry.d.ts +39 -0
  141. package/dist/core/ingestion/model/type-registry.js +62 -0
  142. package/dist/core/ingestion/mro-processor.d.ts +4 -3
  143. package/dist/core/ingestion/mro-processor.js +310 -106
  144. package/dist/core/ingestion/parsing-processor.d.ts +5 -4
  145. package/dist/core/ingestion/parsing-processor.js +210 -85
  146. package/dist/core/ingestion/pipeline.d.ts +2 -0
  147. package/dist/core/ingestion/pipeline.js +192 -68
  148. package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
  149. package/dist/core/ingestion/tree-sitter-queries.js +21 -0
  150. package/dist/core/ingestion/type-env.d.ts +15 -2
  151. package/dist/core/ingestion/type-env.js +163 -102
  152. package/dist/core/ingestion/type-extractors/csharp.js +17 -0
  153. package/dist/core/ingestion/type-extractors/jvm.js +11 -0
  154. package/dist/core/ingestion/type-extractors/php.js +0 -55
  155. package/dist/core/ingestion/type-extractors/ruby.js +0 -32
  156. package/dist/core/ingestion/type-extractors/swift.js +13 -0
  157. package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
  158. package/dist/core/ingestion/type-extractors/typescript.js +66 -69
  159. package/dist/core/ingestion/utils/ast-helpers.d.ts +33 -43
  160. package/dist/core/ingestion/utils/ast-helpers.js +129 -572
  161. package/dist/core/ingestion/utils/method-props.d.ts +32 -0
  162. package/dist/core/ingestion/utils/method-props.js +147 -0
  163. package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
  164. package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
  165. package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
  166. package/dist/core/ingestion/workers/parse-worker.js +463 -198
  167. package/dist/core/lbug/lbug-adapter.d.ts +6 -0
  168. package/dist/core/lbug/lbug-adapter.js +68 -3
  169. package/dist/core/lbug/pool-adapter.d.ts +76 -0
  170. package/dist/core/lbug/pool-adapter.js +522 -0
  171. package/dist/core/run-analyze.d.ts +2 -0
  172. package/dist/core/run-analyze.js +1 -1
  173. package/dist/core/search/bm25-index.js +1 -1
  174. package/dist/core/tree-sitter/parser-loader.js +1 -0
  175. package/dist/core/wiki/graph-queries.js +1 -1
  176. package/dist/mcp/core/embedder.js +6 -5
  177. package/dist/mcp/core/lbug-adapter.d.ts +3 -63
  178. package/dist/mcp/core/lbug-adapter.js +3 -484
  179. package/dist/mcp/local/local-backend.d.ts +31 -2
  180. package/dist/mcp/local/local-backend.js +255 -46
  181. package/dist/mcp/resources.js +5 -4
  182. package/dist/mcp/staleness.d.ts +3 -13
  183. package/dist/mcp/staleness.js +2 -31
  184. package/dist/mcp/tools.js +80 -4
  185. package/dist/server/analyze-job.d.ts +2 -0
  186. package/dist/server/analyze-job.js +4 -0
  187. package/dist/server/api.d.ts +20 -1
  188. package/dist/server/api.js +306 -71
  189. package/dist/server/git-clone.d.ts +2 -1
  190. package/dist/server/git-clone.js +98 -5
  191. package/dist/storage/git.d.ts +13 -0
  192. package/dist/storage/git.js +25 -0
  193. package/dist/storage/repo-manager.js +1 -1
  194. package/package.json +8 -2
  195. package/scripts/patch-tree-sitter-swift.cjs +78 -0
  196. package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
  197. package/dist/core/ingestion/named-binding-processor.js +0 -42
  198. package/dist/core/ingestion/resolution-context.d.ts +0 -58
  199. package/dist/core/ingestion/resolution-context.js +0 -135
  200. package/dist/core/ingestion/symbol-table.d.ts +0 -79
  201. package/dist/core/ingestion/symbol-table.js +0 -115
@@ -0,0 +1,332 @@
1
+ /**
2
+ * BindingAccumulator — read-append-only accumulator that collects TypeEnv
3
+ * bindings across files in the GitNexus analyzer pipeline.
4
+ *
5
+ * **Current behavior (both execution paths):** The accumulator carries only
6
+ * file-scope (`scope = ''`) entries. Function-scope bindings are stripped
7
+ * at both write sites:
8
+ *
9
+ * - **Worker path**: `parse-worker.ts` serializes only
10
+ * `typeEnv.fileScope()` entries across the IPC boundary.
11
+ * - **Sequential path**: `type-env.ts::flush()` iterates only the FILE_SCOPE
12
+ * entry of the env map and writes `BindingEntry` records with
13
+ * `scope: ''` hardcoded.
14
+ *
15
+ * The narrowing exists because function-scope bindings have zero downstream
16
+ * consumers today and were previously costing ~4.9 MB of heap + IPC on
17
+ * every pipeline run. See `type-env.ts::flush()` and the `FileScopeBindings`
18
+ * JSDoc in `parse-worker.ts` for the paired Phase 9 reversion checklist.
19
+ *
20
+ * **Historical quality asymmetry (Phase 9 consideration):** Even though
21
+ * both paths now carry only file-scope data, the two paths were built
22
+ * under different resolution capabilities, and a future Phase 9 reverter
23
+ * that widens them back to all scopes will inherit that asymmetry:
24
+ *
25
+ * - **Sequential path** had (and would regain) access to the full
26
+ * `SymbolTable` and `importedBindings`, so its bindings benefit from
27
+ * Tier 2 cross-file propagation.
28
+ * - **Worker path** runs without `SymbolTable` / `importedBindings` and
29
+ * can only produce Tier 0 (annotation-declared) and local Tier 1
30
+ * (same-file constructor inference) bindings.
31
+ *
32
+ * Phase 9 consumers that trust every entry equally will silently produce
33
+ * worse results for large repos (worker-dominant) than small ones
34
+ * (sequential-dominant). If Phase 9 needs homogeneous quality, either
35
+ * (a) tag entries with their tier at insert time so consumers can filter,
36
+ * or (b) post-process worker-path entries through a follow-up resolution
37
+ * pass after the main-thread `SymbolTable` is complete.
38
+ *
39
+ * **Lifecycle contract**: `append → finalize → consume → dispose`. See
40
+ * `finalize()` and `dispose()` for the state machine. Disposal is
41
+ * orthogonal to finalization: either order is legal.
42
+ */
43
+ /**
44
+ * Merge file-scope bindings from a (finalized) `BindingAccumulator` into an
45
+ * `exportedTypeMap` for symbols whose graph nodes are marked as exported.
46
+ *
47
+ * This is the single source of truth for the worker-path ExportedTypeMap
48
+ * enrichment loop. Previously the logic lived inline in `pipeline.ts` and
49
+ * the test suite reimplemented it as a `runEnrichmentLoop` helper — a
50
+ * drift-prone pattern that meant tests could pass while production regressed.
51
+ * Extracting it here makes the production code call the same function the
52
+ * tests call.
53
+ *
54
+ * **Node ID candidate order**: `Function:{filePath}:{name}` →
55
+ * `Variable:{filePath}:{name}` → `Const:{filePath}:{name}`. First match wins.
56
+ *
57
+ * **Tier 0 priority**: if `exportedTypeMap` already has an entry for a
58
+ * `(filePath, name)` pair, the accumulator entry does NOT overwrite it —
59
+ * the SymbolTable tier-0 pass is authoritative. Without this guard, a
60
+ * worker-path binding could clobber a higher-quality type from SymbolTable.
61
+ *
62
+ * **Finalize precondition**: the accumulator should be finalized before
63
+ * calling this function. The lifecycle contract is
64
+ * `append → finalize → enrich → dispose`. Finalization is not asserted
65
+ * here (the test suite and pipeline both honor it separately), but any
66
+ * append happening concurrently with this enrichment would be a lifecycle
67
+ * bug at the caller level.
68
+ *
69
+ * @returns The number of new entries written into `exportedTypeMap`
70
+ * (0 on empty accumulator or when every candidate was filtered
71
+ * out by the export check or the Tier 0 guard).
72
+ */
73
+ export function enrichExportedTypeMap(bindingAccumulator, graph, exportedTypeMap) {
74
+ if (bindingAccumulator.fileCount === 0)
75
+ return 0;
76
+ let enriched = 0;
77
+ for (const filePath of bindingAccumulator.files()) {
78
+ for (const [name, type] of bindingAccumulator.fileScopeEntries(filePath)) {
79
+ // Three-candidate-ID lookup mirrors the sequential-path export check
80
+ // in `collectExportedBindings()` (call-processor.ts).
81
+ const functionNodeId = `Function:${filePath}:${name}`;
82
+ const variableNodeId = `Variable:${filePath}:${name}`;
83
+ const constNodeId = `Const:${filePath}:${name}`;
84
+ const node = graph.getNode(functionNodeId) ??
85
+ graph.getNode(variableNodeId) ??
86
+ graph.getNode(constNodeId);
87
+ if (!node?.properties?.isExported)
88
+ continue;
89
+ let fileExports = exportedTypeMap.get(filePath);
90
+ if (!fileExports) {
91
+ fileExports = new Map();
92
+ exportedTypeMap.set(filePath, fileExports);
93
+ }
94
+ // Tier 0 priority: SymbolTable-populated entries are authoritative.
95
+ if (!fileExports.has(name)) {
96
+ fileExports.set(name, type);
97
+ enriched++;
98
+ }
99
+ }
100
+ }
101
+ return enriched;
102
+ }
103
+ const ENTRY_OVERHEAD = 64; // bytes per entry (object overhead + property refs)
104
+ const MAP_ENTRY_OVERHEAD = 80; // bytes per file entry in the map
105
+ export class BindingAccumulator {
106
+ // Storage is split into two parallel maps so file-scope reads are fast.
107
+ // - _allByFile holds every BindingEntry (used by getFile, memory estimate).
108
+ // - _fileScopeByFile is a nested Map<filePath, Map<varName, typeName>> for
109
+ // O(1) point-lookup via fileScopeGet(). For iteration-based consumers
110
+ // (enrichExportedTypeMap), fileScopeEntries() iterates the inner Map.
111
+ // Both maps carry the same key set modulo the `scope === ''` precondition:
112
+ // _allByFile has a key as soon as any entry is appended; _fileScopeByFile
113
+ // only has a key once a file-scope entry arrives. Code that iterates via
114
+ // files() uses _allByFile so files with only function-scope entries
115
+ // remain visible.
116
+ //
117
+ // Note: Map.set semantics mean a duplicate varName for the same file
118
+ // overwrites the previous value (last-write-wins). This is the correct
119
+ // behavior — duplicate top-level bindings in the same file shouldn't
120
+ // happen in well-formed source, and if they do the last declaration
121
+ // is typically the one the compiler sees.
122
+ _allByFile = new Map();
123
+ _fileScopeByFile = new Map();
124
+ _totalBindings = 0;
125
+ _finalized = false;
126
+ _disposed = false;
127
+ /**
128
+ * Append bindings for a file. Safe to call multiple times for the same file.
129
+ * Throws if the accumulator has been finalized. Skips if entries is empty.
130
+ *
131
+ * The `entries` parameter is `readonly` — this method never mutates the
132
+ * caller's array. Internally, the first `appendFile` call per filePath
133
+ * makes a defensive copy (`slice()`), and subsequent calls push into the
134
+ * accumulator's own storage.
135
+ */
136
+ appendFile(filePath, entries) {
137
+ if (this._finalized) {
138
+ throw new Error('[BindingAccumulator] appendFile after finalize — no further appends allowed');
139
+ }
140
+ if (entries.length === 0) {
141
+ return;
142
+ }
143
+ // Contract consistency: if this accumulator was previously disposed
144
+ // without being finalized, `dispose()` is documented to leave it
145
+ // "behaving like a fresh one" for subsequent appends. Clear the
146
+ // `_disposed` flag here so the `disposed` getter tracks the actual
147
+ // live state, not a stale signal from the prior lifecycle cycle.
148
+ if (this._disposed) {
149
+ this._disposed = false;
150
+ }
151
+ // Note on the file-scope-only invariant:
152
+ // The accumulator does NOT reject function-scope entries at this
153
+ // boundary. The narrowing contract is enforced by the two production
154
+ // write sites — `parse-worker.ts` (which uses `typeEnv.fileScope()`
155
+ // and hardcodes `scope: ''` in the pipeline adapter) and
156
+ // `type-env.ts::flush()` (which iterates only `env.get(FILE_SCOPE)`).
157
+ // The class JSDoc documents the invariant and the Phase 9 reversion
158
+ // path. Making `appendFile` runtime-reject non-file-scope entries
159
+ // would break the accumulator's own storage-split tests which
160
+ // legitimately exercise mixed-scope entries. If a future write path
161
+ // violates the invariant, tests should fail via missing exports in
162
+ // the enrichment loop, not via an assertion here.
163
+ // All-scope store.
164
+ const existingAll = this._allByFile.get(filePath);
165
+ if (existingAll !== undefined) {
166
+ for (const e of entries) {
167
+ existingAll.push(e);
168
+ }
169
+ }
170
+ else {
171
+ this._allByFile.set(filePath, entries.slice());
172
+ }
173
+ // File-scope fast-path store (nested Map for O(1) point-lookup via fileScopeGet).
174
+ // Populated lazily on first file-scope entry per file.
175
+ let fileScopeMap = this._fileScopeByFile.get(filePath);
176
+ for (const e of entries) {
177
+ if (e.scope === '') {
178
+ if (fileScopeMap === undefined) {
179
+ fileScopeMap = new Map();
180
+ this._fileScopeByFile.set(filePath, fileScopeMap);
181
+ }
182
+ fileScopeMap.set(e.varName, e.typeName);
183
+ }
184
+ }
185
+ this._totalBindings += entries.length;
186
+ }
187
+ /** Lock the accumulator — no further appends. Idempotent. */
188
+ finalize() {
189
+ // Dev-mode invariant: verify the parallel storage split is consistent.
190
+ // `_fileScopeByFile` must be a proper projection of `_allByFile`
191
+ // where the outer key is a subset and the inner entries are exactly
192
+ // the `scope === ''` subset of `_allByFile[key]`. A drift would
193
+ // indicate a bug in `appendFile()` where one map was updated but
194
+ // not the other.
195
+ if (process.env.NODE_ENV !== 'production' && !this._finalized) {
196
+ for (const [filePath, fileScopeMap] of this._fileScopeByFile) {
197
+ const allEntries = this._allByFile.get(filePath);
198
+ if (allEntries === undefined) {
199
+ throw new Error(`[BindingAccumulator] storage split drift: file ${filePath} has file-scope entries ` +
200
+ `but no _allByFile entry`);
201
+ }
202
+ // Count unique file-scope varNames in _allByFile (to match Map dedup
203
+ // semantics in _fileScopeByFile where Map.set deduplicates same-name).
204
+ const projectedNames = new Set(allEntries.filter((e) => e.scope === '').map((e) => e.varName));
205
+ if (projectedNames.size !== fileScopeMap.size) {
206
+ throw new Error(`[BindingAccumulator] storage split drift: file ${filePath} has ` +
207
+ `${fileScopeMap.size} file-scope names in Map but ${projectedNames.size} unique ` +
208
+ `file-scope varNames in _allByFile`);
209
+ }
210
+ }
211
+ }
212
+ this._finalized = true;
213
+ }
214
+ /**
215
+ * Release the accumulator's heap footprint. Clears both internal storage
216
+ * maps and resets `_totalBindings` to zero. Idempotent and orthogonal to
217
+ * `finalize()` — calling `dispose()` does not change the finalized state.
218
+ *
219
+ * Post-dispose contract: all read methods return empty/undefined state
220
+ * matching a never-appended-to accumulator. Specifically:
221
+ * - `fileCount === 0`
222
+ * - `totalBindings === 0`
223
+ * - `files()` yields an empty iterator
224
+ * - `getFile(x)` returns `undefined` for all `x`
225
+ * - `fileScopeEntries(x)` returns `[]` for all `x`
226
+ * - `estimateMemoryBytes()` returns `0`
227
+ *
228
+ * If `dispose()` is called **before** `finalize()`, subsequent `appendFile`
229
+ * calls succeed — the accumulator behaves like a fresh one. If called
230
+ * **after** `finalize()`, subsequent `appendFile` calls throw the existing
231
+ * "finalized" error.
232
+ *
233
+ * Lifecycle note: the pipeline disposes the accumulator after both Phase 9
234
+ * consumers (`processCallsFromExtracted`, `processAssignmentsFromExtracted`)
235
+ * and the ExportedTypeMap enrichment loop have completed, so the heap is
236
+ * released before Phase 14 (`runCrossFileBindingPropagation`) and
237
+ * `runGraphAnalysisPhases` begin their long-running work.
238
+ */
239
+ dispose() {
240
+ this._allByFile.clear();
241
+ this._fileScopeByFile.clear();
242
+ this._totalBindings = 0;
243
+ this._disposed = true;
244
+ }
245
+ /** Get all bindings for a file, or undefined if the file is unknown. */
246
+ getFile(filePath) {
247
+ return this._allByFile.get(filePath);
248
+ }
249
+ /**
250
+ * Get only scope='' (file-level) entries as [varName, typeName] tuples.
251
+ * For iteration-based consumers (e.g., `enrichExportedTypeMap`).
252
+ * Returns an empty array for an unknown file.
253
+ *
254
+ * O(1) map lookup + O(n_file_scope) tuple reconstruction from the inner
255
+ * Map. Does NOT walk function-scope entries.
256
+ *
257
+ * For point-lookup consumers (e.g., Phase 9 fallback), prefer
258
+ * `fileScopeGet(filePath, name)` — O(1) with no allocation.
259
+ */
260
+ fileScopeEntries(filePath) {
261
+ const map = this._fileScopeByFile.get(filePath);
262
+ return map ? [...map.entries()] : [];
263
+ }
264
+ /**
265
+ * O(1) point-lookup for a single file-scope binding by (filePath, name).
266
+ * Returns the typeName if found, `undefined` otherwise.
267
+ *
268
+ * This is the preferred lookup path for Phase 9 consumers that resolve
269
+ * a single callee's return type — avoids the O(n_file_scope) iteration
270
+ * and defensive-copy allocation of `fileScopeEntries()`.
271
+ */
272
+ fileScopeGet(filePath, name) {
273
+ return this._fileScopeByFile.get(filePath)?.get(name);
274
+ }
275
+ /** Iterate over all file paths in insertion order. */
276
+ files() {
277
+ return this._allByFile.keys();
278
+ }
279
+ /** Number of distinct files with at least one binding. */
280
+ get fileCount() {
281
+ return this._allByFile.size;
282
+ }
283
+ /** Total number of binding entries across all files. */
284
+ get totalBindings() {
285
+ return this._totalBindings;
286
+ }
287
+ /** Whether the accumulator has been finalized. */
288
+ get finalized() {
289
+ return this._finalized;
290
+ }
291
+ /**
292
+ * Whether the accumulator has been disposed. Exposed for symmetry with
293
+ * `finalized` so debug tooling and future Phase 9 consumers can detect a
294
+ * disposed accumulator without inspecting empty state heuristically.
295
+ *
296
+ * Disposal and finalization are orthogonal: a disposed accumulator may or
297
+ * may not be finalized, and vice versa. See `dispose()` for the full
298
+ * lifecycle contract.
299
+ */
300
+ get disposed() {
301
+ return this._disposed;
302
+ }
303
+ /**
304
+ * Rough memory estimate in bytes (intentionally pessimistic).
305
+ * Formula: sum of (ENTRY_OVERHEAD + char bytes of scope+varName+typeName) per entry
306
+ * + MAP_ENTRY_OVERHEAD + char bytes of filePath per file.
307
+ *
308
+ * Note: V8 stores all-ASCII strings as Latin-1 (1 byte/char) and only upgrades
309
+ * to UCS-2 (2 bytes/char) for non-Latin-1 code points. Source paths and type names
310
+ * are typically all-ASCII, so actual heap cost is roughly half what this returns.
311
+ * The pessimistic factor is intentional — better to over-budget than under-budget.
312
+ *
313
+ * **⚠ Cost profile**: O(totalBindings) — iterates every entry in
314
+ * `_allByFile` and reads three string `.length` properties per entry.
315
+ * At a typical repo scale (10k files × ~20 file-scope bindings) this is
316
+ * ~200k property reads per call. Call at most once per pipeline run,
317
+ * NOT per file, per chunk, or per progress tick. The current single
318
+ * call site is the dev-mode telemetry log at the pipeline finalize
319
+ * seam. Adding a per-file-progress caller would silently make it
320
+ * quadratic in repo size.
321
+ */
322
+ estimateMemoryBytes() {
323
+ let total = 0;
324
+ for (const [filePath, entries] of this._allByFile) {
325
+ total += MAP_ENTRY_OVERHEAD + filePath.length * 2;
326
+ for (const e of entries) {
327
+ total += ENTRY_OVERHEAD + (e.scope.length + e.varName.length + e.typeName.length) * 2;
328
+ }
329
+ }
330
+ return total;
331
+ }
332
+ }
@@ -1,13 +1,26 @@
1
1
  import { KnowledgeGraph } from '../graph/types.js';
2
2
  import { ASTCache } from './ast-cache.js';
3
- import type { SymbolTable } from './symbol-table.js';
4
- import type { ResolutionContext } from './resolution-context.js';
5
- import type { ExtractedCall, ExtractedAssignment, ExtractedHeritage, ExtractedRoute, ExtractedFetchCall, FileConstructorBindings } from './workers/parse-worker.js';
3
+ import type { SymbolDefinition, SymbolTableReader } from './model/symbol-table.js';
4
+ import type { ResolutionContext } from './model/resolution-context.js';
5
+ import type { TieredCandidates } from './model/resolution-context.js';
6
+ import type { TypeEnvironment } from './type-env.js';
7
+ import type { HeritageMap } from './model/heritage-map.js';
8
+ import type { BindingAccumulator } from './binding-accumulator.js';
9
+ import type { ExtractedCall, ExtractedAssignment, ExtractedRoute, ExtractedFetchCall, FileConstructorBindings } from './workers/parse-worker.js';
10
+ import type { ExtractedHeritage } from './model/heritage-map.js';
11
+ import type { LiteralTypeInferrer } from './type-extractors/types.js';
12
+ import type { SyntaxNode } from './utils/ast-helpers.js';
6
13
  /** Per-file resolved type bindings for exported symbols.
7
14
  * Populated during call processing, consumed by Phase 14 re-resolution pass. */
8
15
  export type ExportedTypeMap = Map<string, Map<string, string>>;
9
16
  /** Build a map of imported callee names → return types for cross-file call-result binding.
10
- * Consulted ONLY when SymbolTable has no unambiguous local match (local-first principle). */
17
+ * Consulted ONLY when SymbolTable has no unambiguous local match (local-first principle).
18
+ *
19
+ * Overlapping mechanism (1 of 3): this is the SymbolTable-backed path.
20
+ * See also:
21
+ * 2. collectExportedBindings (~line 168) / enrichExportedTypeMap — TypeEnv + graph isExported
22
+ * 3. Phase 9 fallback in verifyConstructorBindings (~line 563) — namedImportMap + BindingAccumulator
23
+ * A future cleanup should merge these into a single resolution pass. */
11
24
  export declare function buildImportedReturnTypes(filePath: string, namedImportMap: ReadonlyMap<string, ReadonlyMap<string, {
12
25
  sourcePath: string;
13
26
  exportedName: string;
@@ -31,7 +44,7 @@ export declare function buildImportedRawReturnTypes(filePath: string, namedImpor
31
44
  /** Build ExportedTypeMap from graph nodes — used for worker path where TypeEnv
32
45
  * is not available in the main thread. Collects returnType/declaredType from
33
46
  * exported symbols that have callables with known return types. */
34
- export declare function buildExportedTypeMapFromGraph(graph: KnowledgeGraph, symbolTable: SymbolTable): ExportedTypeMap;
47
+ export declare function buildExportedTypeMapFromGraph(graph: KnowledgeGraph, symbolTable: SymbolTableReader): ExportedTypeMap;
35
48
  /** Seed cross-file receiver types into pre-extracted call records.
36
49
  * Fills missing receiverTypeName for single-hop imported variables
37
50
  * using ExportedTypeMap + namedImportMap — zero disk I/O, zero AST re-parsing.
@@ -42,24 +55,15 @@ export declare function seedCrossFileReceiverTypes(calls: ExtractedCall[], named
42
55
  }>>, exportedTypeMap: ReadonlyMap<string, ReadonlyMap<string, string>>): {
43
56
  enrichedCount: number;
44
57
  };
45
- /** Maps interface/abstract-class name → set of file paths of direct implementors. */
46
- export type ImplementorMap = ReadonlyMap<string, ReadonlySet<string>>;
47
58
  /**
48
- * Build an ImplementorMap from extracted heritage data.
49
- * Only direct `implements` relationships are tracked (transitive not needed for
50
- * the common Java/Kotlin/C# interface dispatch pattern).
51
- * `extends` is ignored — dispatch keyed on abstract class bases is not modeled here.
59
+ * Resolution result with confidence scoring
52
60
  */
53
- /**
54
- * Maps interface name → file paths of classes that implement it (direct only).
55
- * When `ctx` is set, `kind: 'extends'` rows are classified like heritage-processor
56
- * (C#/Java base_list: class vs interface parents share one capture name).
57
- */
58
- export declare const buildImplementorMap: (heritage: readonly ExtractedHeritage[], ctx?: ResolutionContext) => Map<string, Set<string>>;
59
- /**
60
- * Merge a chunk's implementor map into the global accumulator.
61
- */
62
- export declare const mergeImplementorMaps: (target: Map<string, Set<string>>, source: ReadonlyMap<string, ReadonlySet<string>>) => void;
61
+ interface ResolveResult {
62
+ nodeId: string;
63
+ confidence: number;
64
+ reason: string;
65
+ returnType?: string;
66
+ }
63
67
  export declare const processCalls: (graph: KnowledgeGraph, files: {
64
68
  path: string;
65
69
  content: string;
@@ -70,18 +74,144 @@ importedBindingsMap?: ReadonlyMap<string, ReadonlyMap<string, string>>,
70
74
  * Consulted ONLY when SymbolTable has no unambiguous match (local-first principle). */
71
75
  importedReturnTypesMap?: ReadonlyMap<string, ReadonlyMap<string, string>>,
72
76
  /** Phase 14 E3: cross-file RAW return types for for-loop element extraction. Keyed by filePath → Map<calleeName, rawReturnType>. */
73
- importedRawReturnTypesMap?: ReadonlyMap<string, ReadonlyMap<string, string>>, implementorMap?: ImplementorMap) => Promise<ExtractedHeritage[]>;
77
+ importedRawReturnTypesMap?: ReadonlyMap<string, ReadonlyMap<string, string>>, heritageMap?: HeritageMap, bindingAccumulator?: BindingAccumulator) => Promise<ExtractedHeritage[]>;
78
+ /** Per-file cache for module-alias widening. Cleared between files. */
79
+ type WidenCache = Map<string, readonly SymbolDefinition[]>;
80
+ /**
81
+ * Optional hints for overload disambiguation via argument literal types.
82
+ * Only available on the sequential path (has AST); worker path passes undefined.
83
+ *
84
+ * @internal Exported so tests can exercise the D0 skip-condition path without
85
+ * constructing a real SyntaxNode. Do not use outside `call-processor.ts`
86
+ * and its unit tests.
87
+ */
88
+ export interface OverloadHints {
89
+ callNode: SyntaxNode;
90
+ inferLiteralType: LiteralTypeInferrer;
91
+ typeEnv?: TypeEnvironment;
92
+ }
93
+ /** @internal Exported for unit tests. Do not use outside tests. */
94
+ export declare const _resolveCallTargetForTesting: (call: Pick<ExtractedCall, "calledName" | "argCount" | "callForm" | "receiverTypeName" | "receiverName">, currentFile: string, ctx: ResolutionContext, opts?: {
95
+ overloadHints?: OverloadHints;
96
+ widenCache?: WidenCache;
97
+ preComputedArgTypes?: (string | undefined)[];
98
+ heritageMap?: HeritageMap;
99
+ }) => ResolveResult | null;
100
+ /**
101
+ * Resolve a member call using owner-scoped + MRO resolution only (no fuzzy lookup).
102
+ * Used for `obj.method()` calls where the receiver type is known.
103
+ *
104
+ * Delegates to {@link resolveMethodByOwner} which performs an O(1) owner-scoped
105
+ * method lookup and, when a {@link HeritageMap} is provided, walks the MRO chain
106
+ * via {@link lookupMethodByOwnerWithMRO}.
107
+ *
108
+ * {@link resolveCallTarget} delegates here for member calls.
109
+ *
110
+ * **SEMANTIC CHANGE (2026-04-09):** The confidence tier reflects how the
111
+ * owner TYPE was resolved, not how the method NAME was resolved globally.
112
+ * more accurate for owner-scoped resolution (the discriminant IS the class,
113
+ * not the method name). Downstream consumers that filter CALLS edges by
114
+ * confidence threshold may see shifted values on otherwise-unchanged code.
115
+ * See the "returns result with correct confidence tier" tests below for the
116
+ * locked-in behavior.
117
+ *
118
+ * **Performance:** Callers that only need the return type (e.g. `walkMixedChain`)
119
+ * should call {@link resolveMethodByOwner} directly and use the `.def.returnType`
120
+ * field instead, to avoid building a throwaway `ResolveResult`.
121
+ *
122
+ * @param ownerType - The receiver's type name (e.g. 'User')
123
+ * @param methodName - The method being called (e.g. 'save')
124
+ * @param currentFile - File path of the call site
125
+ * @param ctx - Resolution context
126
+ * @param heritageMap - Optional heritage map for MRO-aware ancestor walking
127
+ */
128
+ export declare const resolveMemberCall: (ownerType: string, methodName: string, currentFile: string, ctx: ResolutionContext, heritageMap?: HeritageMap, argCount?: number) => ResolveResult | null;
129
+ /**
130
+ * Resolve a free-function call using `lookupExact` (same-file) + import-scoped
131
+ * resolution via `ctx.resolve()`.
132
+ *
133
+ * Used for `foo()`, `doStuff()` — unqualified calls with no receiver.
134
+ * Also handles Swift/Kotlin implicit constructors (`User()` without `new`)
135
+ * by delegating to {@link resolveStaticCall} when the tiered pool contains
136
+ * class-like targets.
137
+ *
138
+ * {@link resolveCallTarget} delegates here for `callForm === 'free'`.
139
+ *
140
+ * `resolveFreeCall` does not take a `widenCache` parameter. Free calls
141
+ * have no receiver type and rely exclusively on the tiered pool
142
+ * from `ctx.resolve()`.
143
+ *
144
+ * @param calledName - The called function name (e.g. 'doStuff')
145
+ * @param filePath - File path of the call site
146
+ * @param ctx - Resolution context
147
+ * @param argCount - Optional argument count for arity filtering
148
+ * @param tieredOverride - Pre-computed tiered candidates from an upstream
149
+ * `ctx.resolve` call. When provided, skips the redundant
150
+ * lookup inside this function.
151
+ * @param overloadHints - Optional AST-based overload disambiguation hints
152
+ * @param preComputedArgTypes - Optional pre-computed argument types (worker path)
153
+ */
154
+ export declare const resolveFreeCall: (calledName: string, filePath: string, ctx: ResolutionContext, argCount?: number, tieredOverride?: TieredCandidates, overloadHints?: OverloadHints, preComputedArgTypes?: (string | undefined)[]) => ResolveResult | null;
155
+ /**
156
+ * Resolve a constructor or static call using class-scoped lookup (no fuzzy lookup).
157
+ * Used for `new User()` / `User()` calls where the calledName targets a class.
158
+ *
159
+ * Uses {@link TypeRegistry.lookupClassByName} for O(1) class lookup and
160
+ * {@link MethodRegistry.lookupMethodByOwner} for constructor resolution.
161
+ * {@link resolveCallTarget} delegates here for constructor and free-form calls
162
+ * that target a class.
163
+ *
164
+ * Resolution strategy:
165
+ * 1. `lookupClassByName(className)` — O(1) pre-check; bail early if no class exists.
166
+ * 2. `ctx.resolve(className, currentFile)` — import-scoped tier for confidence.
167
+ * 3. Filter to class-like candidates via `CLASS_LIKE_TYPES` and walk each
168
+ * with `lookupMethodByOwner(classNodeId, className, argCount)` — O(1)
169
+ * constructor lookup. Only accept results with `type === 'Constructor'`.
170
+ * 4. If step 3 found nothing and the tiered pool contains ownerless
171
+ * `Constructor` nodes (common in some extractors), bail out so
172
+ * `filterCallableCandidates` downstream handles Constructor-vs-Class
173
+ * preference correctly.
174
+ * 5. Class-node fallback: filter `classCandidates` through
175
+ * `INSTANTIABLE_CLASS_TYPES` and return the sole survivor when there is
176
+ * exactly one. Null-route on zero survivors (Interface / Trait / Impl
177
+ * stripped) or multiple (homonym ambiguity).
178
+ *
179
+ * @param className - The class name (e.g. 'User'). Also used as the method
180
+ * name for the `lookupMethodByOwner` scan, because the
181
+ * only constructor-shaped call we handle today is
182
+ * `ClassName(...)` / `new ClassName(...)`. Named
183
+ * constructors like Dart `User.fromJson()` arrive as
184
+ * member calls and route through `resolveMemberCall`,
185
+ * so this function does not yet need a separate
186
+ * `methodName` parameter. Revisit if a language surfaces
187
+ * a static-method-shaped call with a distinct member
188
+ * name.
189
+ * @param currentFile - File path of the call site
190
+ * @param ctx - Resolution context
191
+ * @param argCount - Optional argument count for arity filtering
192
+ * @param tieredOverride - Pre-computed tiered candidates for `className` from
193
+ * an upstream `ctx.resolve` call. When provided, skips
194
+ * the redundant lookup inside this function. Leave
195
+ * unset for direct callers without a prior resolution.
196
+ */
197
+ export declare const resolveStaticCall: (className: string, currentFile: string, ctx: ResolutionContext, argCount?: number, tieredOverride?: TieredCandidates, overloadHints?: OverloadHints, preComputedArgTypes?: (string | undefined)[]) => ResolveResult | null;
74
198
  /**
75
199
  * Fast path: resolve pre-extracted call sites from workers.
76
200
  * No AST parsing — workers already extracted calledName + sourceId.
201
+ *
202
+ * @param bindingAccumulator Phase 9: optional accumulator carrying file-scope
203
+ * TypeEnv bindings from all worker-processed files. When the SymbolTable has
204
+ * no return type for a cross-file callee, `verifyConstructorBindings` falls
205
+ * back to the accumulator via `namedImportMap` to bind the variable to the
206
+ * callee's resolved type (e.g. `var x = getUser()` → `x: User`).
77
207
  */
78
- export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void, constructorBindings?: FileConstructorBindings[], implementorMap?: ImplementorMap) => Promise<void>;
208
+ export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void, constructorBindings?: FileConstructorBindings[], heritageMap?: HeritageMap, bindingAccumulator?: BindingAccumulator) => Promise<void>;
79
209
  /**
80
210
  * Resolve pre-extracted field write assignments to ACCESSES {reason: 'write'} edges.
81
211
  * Accepts optional constructorBindings for return-type-aware receiver inference,
82
212
  * mirroring processCallsFromExtracted's verified binding lookup.
83
213
  */
84
- export declare const processAssignmentsFromExtracted: (graph: KnowledgeGraph, assignments: ExtractedAssignment[], ctx: ResolutionContext, constructorBindings?: FileConstructorBindings[]) => void;
214
+ export declare const processAssignmentsFromExtracted: (graph: KnowledgeGraph, assignments: ExtractedAssignment[], ctx: ResolutionContext, constructorBindings?: FileConstructorBindings[], bindingAccumulator?: BindingAccumulator) => void;
85
215
  /**
86
216
  * Resolve pre-extracted Laravel routes to CALLS edges from route files to controller methods.
87
217
  */
@@ -103,3 +233,4 @@ export declare const extractFetchCallsFromFiles: (files: {
103
233
  path: string;
104
234
  content: string;
105
235
  }[], astCache: ASTCache) => Promise<ExtractedFetchCall[]>;
236
+ export {};