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,56 @@
1
+ /**
2
+ * Deterministic Resolution Functions
3
+ *
4
+ * Pure functions that resolve methods across the inheritance hierarchy
5
+ * using only the SemanticModel registries and HeritageMap — NO dependency
6
+ * on resolution-context.ts (circular dependency risk).
7
+ */
8
+ import type { SymbolDefinition } from './symbol-table.js';
9
+ import type { SemanticModel } from './semantic-model.js';
10
+ import type { HeritageMap } from './heritage-map.js';
11
+ import type { MroStrategy } from '../../../_shared/index.js';
12
+ /**
13
+ * Gather all ancestor IDs in BFS / topological order.
14
+ * Returns the linearized list of ancestor IDs (excluding the class itself).
15
+ */
16
+ declare function gatherAncestors(classId: string, parentMap: Map<string, string[]>): string[];
17
+ /**
18
+ * Compute C3 linearization for a class given a parentMap.
19
+ * Returns an array of ancestor IDs in C3 order (excluding the class itself),
20
+ * or null if linearization fails (inconsistent or cyclic hierarchy).
21
+ *
22
+ * Used internally by `lookupMethodByOwnerWithMRO` for the Python MRO
23
+ * strategy and re-exported for mro-processor.ts (graph-level MRO emission).
24
+ */
25
+ export declare function c3Linearize(classId: string, parentMap: Map<string, string[]>, cache: Map<string, string[] | null>, inProgress?: Set<string>): string[] | null;
26
+ export { gatherAncestors };
27
+ /**
28
+ * Look up a method on an owner class, walking the parent chain via HeritageMap
29
+ * when the method isn't found on the direct owner.
30
+ *
31
+ * Respects the 5 per-language MRO strategies:
32
+ * - `first-wins`: BFS ancestor walk, first match wins (default)
33
+ * - `leftmost-base`: BFS ancestor walk, leftmost base in declaration order wins (C++);
34
+ * HeritageMap preserves insertion order matching source declaration,
35
+ * so BFS order is equivalent to leftmost-base semantics
36
+ * - `c3`: C3-linearized ancestor order, first match wins (Python)
37
+ * - `implements-split`: BFS ancestor walk, first match wins (Java/C#) —
38
+ * full ambiguity detection for multiple interface defaults
39
+ * is handled by computeMRO at graph level
40
+ * - `qualified-syntax`: No auto-resolution (Rust) — returns undefined
41
+ *
42
+ * Uses the `c3Linearize` defined in this file (also consumed by
43
+ * mro-processor.ts for graph-level MRO emission) for the `c3` strategy.
44
+ *
45
+ * Depends only on {@link SemanticModel} + {@link HeritageMap} + an
46
+ * {@link MroStrategy} literal — NO dependency on SymbolTable, the language
47
+ * registry, or resolution-context, which keeps the `model/` module free of
48
+ * cross-layer imports. Callers derive the strategy from their language
49
+ * provider before invoking this function.
50
+ *
51
+ * @internal This is the low-level MRO walker. Exported so call-processor's
52
+ * higher-level resolvers (and unit tests) can invoke it directly. Callers
53
+ * outside `core/ingestion/` should use the higher-level resolvers in
54
+ * call-processor.ts instead of depending on this function.
55
+ */
56
+ export declare const lookupMethodByOwnerWithMRO: (ownerNodeId: string, methodName: string, heritageMap: HeritageMap, model: SemanticModel, strategy: MroStrategy, argCount?: number) => SymbolDefinition | undefined;
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Deterministic Resolution Functions
3
+ *
4
+ * Pure functions that resolve methods across the inheritance hierarchy
5
+ * using only the SemanticModel registries and HeritageMap — NO dependency
6
+ * on resolution-context.ts (circular dependency risk).
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // MRO primitives.
10
+ //
11
+ // `c3Linearize` and its BFS helper `gatherAncestors` live here so the model
12
+ // layer stays a pure leaf — mro-processor.ts (graph-level MRO emission)
13
+ // imports `c3Linearize` from this file.
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Gather all ancestor IDs in BFS / topological order.
17
+ * Returns the linearized list of ancestor IDs (excluding the class itself).
18
+ */
19
+ function gatherAncestors(classId, parentMap) {
20
+ const visited = new Set();
21
+ const order = [];
22
+ const queue = [...(parentMap.get(classId) ?? [])];
23
+ while (queue.length > 0) {
24
+ const id = queue.shift();
25
+ if (visited.has(id))
26
+ continue;
27
+ visited.add(id);
28
+ order.push(id);
29
+ const grandparents = parentMap.get(id);
30
+ if (grandparents) {
31
+ for (const gp of grandparents) {
32
+ if (!visited.has(gp))
33
+ queue.push(gp);
34
+ }
35
+ }
36
+ }
37
+ return order;
38
+ }
39
+ /**
40
+ * Compute C3 linearization for a class given a parentMap.
41
+ * Returns an array of ancestor IDs in C3 order (excluding the class itself),
42
+ * or null if linearization fails (inconsistent or cyclic hierarchy).
43
+ *
44
+ * Used internally by `lookupMethodByOwnerWithMRO` for the Python MRO
45
+ * strategy and re-exported for mro-processor.ts (graph-level MRO emission).
46
+ */
47
+ export function c3Linearize(classId, parentMap, cache, inProgress) {
48
+ if (cache.has(classId))
49
+ return cache.get(classId);
50
+ // Cycle detection: if we're already computing this class, the hierarchy is cyclic
51
+ const visiting = inProgress ?? new Set();
52
+ if (visiting.has(classId)) {
53
+ cache.set(classId, null);
54
+ return null;
55
+ }
56
+ visiting.add(classId);
57
+ const directParents = parentMap.get(classId);
58
+ if (!directParents || directParents.length === 0) {
59
+ visiting.delete(classId);
60
+ cache.set(classId, []);
61
+ return [];
62
+ }
63
+ // Compute linearization for each parent first
64
+ const parentLinearizations = [];
65
+ for (const pid of directParents) {
66
+ const pLin = c3Linearize(pid, parentMap, cache, visiting);
67
+ if (pLin === null) {
68
+ visiting.delete(classId);
69
+ cache.set(classId, null);
70
+ return null;
71
+ }
72
+ parentLinearizations.push([pid, ...pLin]);
73
+ }
74
+ // Add the direct parents list as the final sequence
75
+ const sequences = [...parentLinearizations, [...directParents]];
76
+ const result = [];
77
+ while (sequences.some((s) => s.length > 0)) {
78
+ // Find a good head: one that doesn't appear in the tail of any other sequence
79
+ let head = null;
80
+ for (const seq of sequences) {
81
+ if (seq.length === 0)
82
+ continue;
83
+ const candidate = seq[0];
84
+ const inTail = sequences.some((other) => other.length > 1 && other.indexOf(candidate, 1) !== -1);
85
+ if (!inTail) {
86
+ head = candidate;
87
+ break;
88
+ }
89
+ }
90
+ if (head === null) {
91
+ // Inconsistent hierarchy
92
+ visiting.delete(classId);
93
+ cache.set(classId, null);
94
+ return null;
95
+ }
96
+ result.push(head);
97
+ // Remove the chosen head from all sequences
98
+ for (const seq of sequences) {
99
+ if (seq.length > 0 && seq[0] === head) {
100
+ seq.shift();
101
+ }
102
+ }
103
+ }
104
+ visiting.delete(classId);
105
+ cache.set(classId, result);
106
+ return result;
107
+ }
108
+ // `gatherAncestors` is exported so mro-processor.ts can reuse the same
109
+ // BFS traversal for graph-level MRO emission.
110
+ export { gatherAncestors };
111
+ // ---------------------------------------------------------------------------
112
+ // C3 linearization cache (per HeritageMap, auto-drained via WeakMap)
113
+ // ---------------------------------------------------------------------------
114
+ /**
115
+ * Per-HeritageMap cache of C3 linearization results keyed by owner nodeId.
116
+ *
117
+ * HeritageMap instances are immutable after construction, so C3 output is
118
+ * stable for the lifetime of a HeritageMap. WeakMap lets the cache auto-drain
119
+ * when the HeritageMap is garbage collected (end of ingestion run), so we
120
+ * never need to manually invalidate it.
121
+ *
122
+ * `null` is a sentinel for "C3 failed for this owner" (cyclic or inconsistent
123
+ * hierarchy) so we don't re-run the expensive linearization repeatedly.
124
+ */
125
+ const c3LinearizationCache = new WeakMap();
126
+ const getCachedC3Linearization = (ownerNodeId, heritageMap) => {
127
+ let perHmCache = c3LinearizationCache.get(heritageMap);
128
+ if (!perHmCache) {
129
+ perHmCache = new Map();
130
+ c3LinearizationCache.set(heritageMap, perHmCache);
131
+ }
132
+ const cached = perHmCache.get(ownerNodeId);
133
+ if (cached !== undefined)
134
+ return cached;
135
+ const parentMap = buildParentMapFromHeritage(ownerNodeId, heritageMap);
136
+ const result = c3Linearize(ownerNodeId, parentMap, new Map()) ?? null;
137
+ perHmCache.set(ownerNodeId, result);
138
+ return result;
139
+ };
140
+ // ---------------------------------------------------------------------------
141
+ // Heritage → parentMap conversion
142
+ // ---------------------------------------------------------------------------
143
+ /**
144
+ * Build a parentMap from HeritageMap for use with c3Linearize.
145
+ * Traverses the parent chain starting from startNodeId, collecting all
146
+ * parent→children relationships into a Map<string, string[]>.
147
+ *
148
+ * Uses a head-pointer BFS (queue[head++]) instead of Array.shift() to avoid
149
+ * O(n) per-dequeue re-indexing. For wide/shallow hierarchies common in
150
+ * large Java/C# codebases this keeps the walk linear in ancestor count.
151
+ */
152
+ const buildParentMapFromHeritage = (startNodeId, heritageMap) => {
153
+ const parentMap = new Map();
154
+ const visited = new Set();
155
+ const queue = [startNodeId];
156
+ let head = 0;
157
+ while (head < queue.length) {
158
+ const nodeId = queue[head++];
159
+ if (visited.has(nodeId))
160
+ continue;
161
+ visited.add(nodeId);
162
+ const parents = heritageMap.getParents(nodeId);
163
+ if (parents.length > 0) {
164
+ parentMap.set(nodeId, parents);
165
+ for (const p of parents) {
166
+ if (!visited.has(p))
167
+ queue.push(p);
168
+ }
169
+ }
170
+ }
171
+ return parentMap;
172
+ };
173
+ // ---------------------------------------------------------------------------
174
+ // MRO-aware method lookup
175
+ // ---------------------------------------------------------------------------
176
+ /**
177
+ * Look up a method on an owner class, walking the parent chain via HeritageMap
178
+ * when the method isn't found on the direct owner.
179
+ *
180
+ * Respects the 5 per-language MRO strategies:
181
+ * - `first-wins`: BFS ancestor walk, first match wins (default)
182
+ * - `leftmost-base`: BFS ancestor walk, leftmost base in declaration order wins (C++);
183
+ * HeritageMap preserves insertion order matching source declaration,
184
+ * so BFS order is equivalent to leftmost-base semantics
185
+ * - `c3`: C3-linearized ancestor order, first match wins (Python)
186
+ * - `implements-split`: BFS ancestor walk, first match wins (Java/C#) —
187
+ * full ambiguity detection for multiple interface defaults
188
+ * is handled by computeMRO at graph level
189
+ * - `qualified-syntax`: No auto-resolution (Rust) — returns undefined
190
+ *
191
+ * Uses the `c3Linearize` defined in this file (also consumed by
192
+ * mro-processor.ts for graph-level MRO emission) for the `c3` strategy.
193
+ *
194
+ * Depends only on {@link SemanticModel} + {@link HeritageMap} + an
195
+ * {@link MroStrategy} literal — NO dependency on SymbolTable, the language
196
+ * registry, or resolution-context, which keeps the `model/` module free of
197
+ * cross-layer imports. Callers derive the strategy from their language
198
+ * provider before invoking this function.
199
+ *
200
+ * @internal This is the low-level MRO walker. Exported so call-processor's
201
+ * higher-level resolvers (and unit tests) can invoke it directly. Callers
202
+ * outside `core/ingestion/` should use the higher-level resolvers in
203
+ * call-processor.ts instead of depending on this function.
204
+ */
205
+ export const lookupMethodByOwnerWithMRO = (ownerNodeId, methodName, heritageMap, model, strategy, argCount) => {
206
+ // Direct lookup first (child override — no walk needed).
207
+ // argCount is threaded through so arity-differing overloads on the direct
208
+ // owner can be disambiguated before the MRO walk starts.
209
+ const direct = model.methods.lookupMethodByOwner(ownerNodeId, methodName, argCount);
210
+ if (direct)
211
+ return direct;
212
+ // Rust: requires qualified syntax (<Type as Trait>::method), no auto-resolution
213
+ if (strategy === 'qualified-syntax')
214
+ return undefined;
215
+ // Determine ancestor walk order based on MRO strategy.
216
+ // readonly to accept the cached (frozen) c3 linearization without copying.
217
+ let ancestors;
218
+ if (strategy === 'c3') {
219
+ // C3 linearization (memoized per HeritageMap
220
+ // so repeated calls for the same owner within an ingestion run reuse the
221
+ // linearization instead of rebuilding the parent map and re-running C3).
222
+ // c3Linearize returns ancestors only (excludes the owner itself),
223
+ // matching heritageMap.getAncestors() semantics.
224
+ const c3Result = getCachedC3Linearization(ownerNodeId, heritageMap);
225
+ // Fall back to BFS order if C3 fails (cyclic or inconsistent hierarchy).
226
+ // Note: BFS order may not preserve Python MRO semantics in these edge
227
+ // cases, but cyclic/inconsistent hierarchies are invalid in Python anyway.
228
+ ancestors = c3Result ?? heritageMap.getAncestors(ownerNodeId);
229
+ }
230
+ else {
231
+ // first-wins, leftmost-base, implements-split: BFS order via HeritageMap
232
+ ancestors = heritageMap.getAncestors(ownerNodeId);
233
+ }
234
+ // Walk ancestors in MRO order — first match wins.
235
+ // argCount narrows overloaded ancestors the same way as the direct lookup.
236
+ for (const ancestorId of ancestors) {
237
+ const method = model.methods.lookupMethodByOwner(ancestorId, methodName, argCount);
238
+ if (method)
239
+ return method;
240
+ }
241
+ return undefined;
242
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Semantic Model
3
+ *
4
+ * Top-level orchestrator for all resolution-time data. Owns:
5
+ *
6
+ * - Three owner-scoped registries (types, methods, fields)
7
+ * - A nested SymbolTable (file + callable name indexes) wrapped so
8
+ * that `add()` fans out into the registries via the dispatch table
9
+ *
10
+ * ## DAG direction
11
+ *
12
+ * gitnexus-shared (NodeLabel) — leaf
13
+ * ↑
14
+ * symbol-table.ts — pure file/callable index
15
+ * ↑
16
+ * model/type-registry / method-registry / field-registry
17
+ * ↑
18
+ * model/registration-table.ts — dispatch table factory
19
+ * ↑
20
+ * model/semantic-model.ts — THIS FILE (orchestrator)
21
+ * ↑
22
+ * resolve.ts, call-processor.ts, resolution-context.ts, ...
23
+ *
24
+ * `symbol-table.ts` is a leaf — it never imports from `./model/`. This
25
+ * file (semantic-model.ts) is the ONLY place where SymbolTable and the
26
+ * owner-scoped registries are composed. Upstream consumers pass around
27
+ * the `SemanticModel` interface and reach into `.symbols` for file-scoped
28
+ * operations or `.types` / `.methods` / `.fields` for owner-scoped ones.
29
+ *
30
+ * ## Fan-out via wrapped add()
31
+ *
32
+ * `createSemanticModel()` creates a pure SymbolTable, creates the three
33
+ * registries, builds a dispatch table via `createRegistrationTable`, and
34
+ * exposes a SymbolTable-shaped façade whose `add()`:
35
+ *
36
+ * 1. Calls `rawSymbols.add()` — writes the fileIndex + callable index
37
+ * and returns the fully-built `SymbolDefinition`.
38
+ * 2. Runs pre-dispatch normalization (`Function`-with-`ownerId` routes
39
+ * as `Method`).
40
+ * 3. Looks up the dispatch table and invokes the hook, which writes to
41
+ * the appropriate owner-scoped registry.
42
+ *
43
+ * The wrapper is the only place where the two layers are combined. A
44
+ * direct `createSymbolTable()` caller (e.g. an isolated unit test) gets
45
+ * the pure, registry-free behavior — no surprises, no hidden side
46
+ * effects.
47
+ */
48
+ import type { TypeRegistry, MutableTypeRegistry } from './type-registry.js';
49
+ import type { MethodRegistry, MutableMethodRegistry } from './method-registry.js';
50
+ import type { FieldRegistry, MutableFieldRegistry } from './field-registry.js';
51
+ import type { SymbolTableReader, SymbolTableWriter } from './symbol-table.js';
52
+ /**
53
+ * Aggregated read-only view of the semantic registries plus the nested
54
+ * file/callable SymbolTable.
55
+ *
56
+ * `symbols` is typed as {@link SymbolTableReader} — consumers can query
57
+ * symbols but cannot register new ones or trigger a reset. Callers that
58
+ * need to register symbols or reset state must hold a
59
+ * {@link MutableSemanticModel} reference instead, which widens
60
+ * `symbols` back to {@link SymbolTableWriter} and adds `clear()` on the
61
+ * model itself.
62
+ *
63
+ * This segregation is the runtime half of the principle of least
64
+ * authority: a resolver that receives `SemanticModel` physically cannot
65
+ * mutate the index, so it cannot desync the leaf from the owner-scoped
66
+ * registries even accidentally.
67
+ */
68
+ export interface SemanticModel {
69
+ readonly types: TypeRegistry;
70
+ readonly methods: MethodRegistry;
71
+ readonly fields: FieldRegistry;
72
+ readonly symbols: SymbolTableReader;
73
+ }
74
+ /** Mutable variant — exposes the MutableX registries, a Writer-typed
75
+ * `symbols` facade, and a full-cascade reset. This is the interface
76
+ * held by the lifecycle owner (pipeline, resolution-context); resolvers
77
+ * that only query should hold the narrower {@link SemanticModel}. */
78
+ export interface MutableSemanticModel extends SemanticModel {
79
+ readonly types: MutableTypeRegistry;
80
+ readonly methods: MutableMethodRegistry;
81
+ readonly fields: MutableFieldRegistry;
82
+ readonly symbols: SymbolTableWriter;
83
+ /** Clear all registries AND the nested SymbolTable. */
84
+ clear(): void;
85
+ }
86
+ export declare const createSemanticModel: () => MutableSemanticModel;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Semantic Model
3
+ *
4
+ * Top-level orchestrator for all resolution-time data. Owns:
5
+ *
6
+ * - Three owner-scoped registries (types, methods, fields)
7
+ * - A nested SymbolTable (file + callable name indexes) wrapped so
8
+ * that `add()` fans out into the registries via the dispatch table
9
+ *
10
+ * ## DAG direction
11
+ *
12
+ * gitnexus-shared (NodeLabel) — leaf
13
+ * ↑
14
+ * symbol-table.ts — pure file/callable index
15
+ * ↑
16
+ * model/type-registry / method-registry / field-registry
17
+ * ↑
18
+ * model/registration-table.ts — dispatch table factory
19
+ * ↑
20
+ * model/semantic-model.ts — THIS FILE (orchestrator)
21
+ * ↑
22
+ * resolve.ts, call-processor.ts, resolution-context.ts, ...
23
+ *
24
+ * `symbol-table.ts` is a leaf — it never imports from `./model/`. This
25
+ * file (semantic-model.ts) is the ONLY place where SymbolTable and the
26
+ * owner-scoped registries are composed. Upstream consumers pass around
27
+ * the `SemanticModel` interface and reach into `.symbols` for file-scoped
28
+ * operations or `.types` / `.methods` / `.fields` for owner-scoped ones.
29
+ *
30
+ * ## Fan-out via wrapped add()
31
+ *
32
+ * `createSemanticModel()` creates a pure SymbolTable, creates the three
33
+ * registries, builds a dispatch table via `createRegistrationTable`, and
34
+ * exposes a SymbolTable-shaped façade whose `add()`:
35
+ *
36
+ * 1. Calls `rawSymbols.add()` — writes the fileIndex + callable index
37
+ * and returns the fully-built `SymbolDefinition`.
38
+ * 2. Runs pre-dispatch normalization (`Function`-with-`ownerId` routes
39
+ * as `Method`).
40
+ * 3. Looks up the dispatch table and invokes the hook, which writes to
41
+ * the appropriate owner-scoped registry.
42
+ *
43
+ * The wrapper is the only place where the two layers are combined. A
44
+ * direct `createSymbolTable()` caller (e.g. an isolated unit test) gets
45
+ * the pure, registry-free behavior — no surprises, no hidden side
46
+ * effects.
47
+ */
48
+ import { createTypeRegistry } from './type-registry.js';
49
+ import { createMethodRegistry } from './method-registry.js';
50
+ import { createFieldRegistry } from './field-registry.js';
51
+ import { createSymbolTable } from './symbol-table.js';
52
+ import { createRegistrationTable } from './registration-table.js';
53
+ // ---------------------------------------------------------------------------
54
+ // Factory
55
+ // ---------------------------------------------------------------------------
56
+ //
57
+ // NodeLabel taxonomy drift detection lives in `registration-table.ts` as a
58
+ // pure compile-time check — the `LABEL_BEHAVIOR` map is
59
+ // `Record<NodeLabel, LabelBehavior>` with `as const satisfies`, which proves
60
+ // coverage, uniqueness, and no-extra-keys at build time. No runtime guard
61
+ // is needed because drift is structurally impossible in the source.
62
+ export const createSemanticModel = () => {
63
+ // 1. Create the pure, registry-unaware SymbolTable leaf.
64
+ // rawSymbols is the only handle in the codebase whose type (the
65
+ // internal createSymbolTable return) includes `.clear()`. cascadeClear
66
+ // below reaches it here; no external caller receives this variable.
67
+ const rawSymbols = createSymbolTable();
68
+ // 2. Create the three owner-scoped registries.
69
+ const types = createTypeRegistry();
70
+ const methods = createMethodRegistry();
71
+ const fields = createFieldRegistry();
72
+ // 3. Build the dispatch table, closed over THIS instance's registries.
73
+ const dispatchTable = createRegistrationTable({ types, methods, fields });
74
+ // 4. Wrap rawSymbols so `add()` fans out into the registries via the
75
+ // dispatch table. See module JSDoc for the three-step contract.
76
+ const wrappedAdd = (filePath, name, nodeId, type, metadata) => {
77
+ const def = rawSymbols.add(filePath, name, nodeId, type, metadata);
78
+ // Function-with-ownerId (Python `def` in a class body, Rust trait
79
+ // method, Kotlin companion method) routes as Method. Keeps the
80
+ // dispatch table single-purpose.
81
+ const dispatchKey = type === 'Function' && metadata?.ownerId !== undefined ? 'Method' : type;
82
+ const hook = dispatchTable.get(dispatchKey);
83
+ if (hook) {
84
+ hook(name, def);
85
+ }
86
+ return def;
87
+ };
88
+ // Cascade clear: single source of truth for "reset the entire model".
89
+ // Wired into both `model.clear()` AND `model.symbols.clear()` so that a
90
+ // caller holding only a SymbolTable reference can't leave the
91
+ // owner-scoped registries populated while the file/callable indexes go
92
+ // empty (the phantom-resolution failure mode).
93
+ const cascadeClear = () => {
94
+ types.clear();
95
+ methods.clear();
96
+ fields.clear();
97
+ rawSymbols.clear();
98
+ };
99
+ // Writer-typed facade: exposes reads + add, but NO `clear` field.
100
+ // Callers holding a `SemanticModel.symbols` reference cannot desync
101
+ // the leaf indexes from the owner-scoped registries. Consumers that
102
+ // only query should widen their annotation to SymbolTableReader for
103
+ // least-authority clarity.
104
+ const symbols = {
105
+ add: wrappedAdd,
106
+ lookupExact: rawSymbols.lookupExact,
107
+ lookupExactFull: rawSymbols.lookupExactFull,
108
+ lookupExactAll: rawSymbols.lookupExactAll,
109
+ lookupCallableByName: rawSymbols.lookupCallableByName,
110
+ getFiles: rawSymbols.getFiles,
111
+ getStats: rawSymbols.getStats,
112
+ };
113
+ return {
114
+ types,
115
+ methods,
116
+ fields,
117
+ symbols,
118
+ clear: cascadeClear,
119
+ };
120
+ };