gitnexus 1.6.2-rc.8 → 1.6.2

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 (222) hide show
  1. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  2. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  3. package/dist/_shared/lbug/schema-constants.js +1 -0
  4. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  5. package/dist/_shared/mro-strategy.d.ts +38 -16
  6. package/dist/_shared/mro-strategy.d.ts.map +1 -1
  7. package/dist/cli/ai-context.js +0 -58
  8. package/dist/cli/analyze.js +3 -0
  9. package/dist/core/embeddings/ast-utils.d.ts +22 -0
  10. package/dist/core/embeddings/ast-utils.js +105 -0
  11. package/dist/core/embeddings/character-chunk.d.ts +12 -0
  12. package/dist/core/embeddings/character-chunk.js +43 -0
  13. package/dist/core/embeddings/chunker.d.ts +14 -0
  14. package/dist/core/embeddings/chunker.js +234 -0
  15. package/dist/core/embeddings/embedder.js +5 -0
  16. package/dist/core/embeddings/embedding-pipeline.d.ts +20 -24
  17. package/dist/core/embeddings/embedding-pipeline.js +176 -107
  18. package/dist/core/embeddings/line-index.d.ts +7 -0
  19. package/dist/core/embeddings/line-index.js +42 -0
  20. package/dist/core/embeddings/server-mapping.d.ts +15 -0
  21. package/dist/core/embeddings/server-mapping.js +33 -0
  22. package/dist/core/embeddings/structural-extractor.d.ts +15 -0
  23. package/dist/core/embeddings/structural-extractor.js +58 -0
  24. package/dist/core/embeddings/text-generator.d.ts +20 -13
  25. package/dist/core/embeddings/text-generator.js +151 -119
  26. package/dist/core/embeddings/types.d.ts +81 -3
  27. package/dist/core/embeddings/types.js +105 -3
  28. package/dist/core/group/extractors/http-patterns/node.js +130 -0
  29. package/dist/core/ingestion/call-extractors/configs/c-cpp.d.ts +3 -0
  30. package/dist/core/ingestion/call-extractors/configs/c-cpp.js +8 -0
  31. package/dist/core/ingestion/call-extractors/configs/csharp.d.ts +2 -0
  32. package/dist/core/ingestion/call-extractors/configs/csharp.js +6 -0
  33. package/dist/core/ingestion/call-extractors/configs/dart.d.ts +2 -0
  34. package/dist/core/ingestion/call-extractors/configs/dart.js +5 -0
  35. package/dist/core/ingestion/call-extractors/configs/go.d.ts +2 -0
  36. package/dist/core/ingestion/call-extractors/configs/go.js +5 -0
  37. package/dist/core/ingestion/call-extractors/configs/jvm.d.ts +3 -0
  38. package/dist/core/ingestion/call-extractors/configs/jvm.js +51 -0
  39. package/dist/core/ingestion/call-extractors/configs/php.d.ts +2 -0
  40. package/dist/core/ingestion/call-extractors/configs/php.js +5 -0
  41. package/dist/core/ingestion/call-extractors/configs/python.d.ts +2 -0
  42. package/dist/core/ingestion/call-extractors/configs/python.js +5 -0
  43. package/dist/core/ingestion/call-extractors/configs/ruby.d.ts +2 -0
  44. package/dist/core/ingestion/call-extractors/configs/ruby.js +5 -0
  45. package/dist/core/ingestion/call-extractors/configs/rust.d.ts +2 -0
  46. package/dist/core/ingestion/call-extractors/configs/rust.js +5 -0
  47. package/dist/core/ingestion/call-extractors/configs/swift.d.ts +2 -0
  48. package/dist/core/ingestion/call-extractors/configs/swift.js +5 -0
  49. package/dist/core/ingestion/call-extractors/configs/typescript-javascript.d.ts +3 -0
  50. package/dist/core/ingestion/call-extractors/configs/typescript-javascript.js +8 -0
  51. package/dist/core/ingestion/call-extractors/generic.d.ts +5 -0
  52. package/dist/core/ingestion/call-extractors/generic.js +59 -0
  53. package/dist/core/ingestion/call-processor.d.ts +2 -4
  54. package/dist/core/ingestion/call-processor.js +221 -89
  55. package/dist/core/ingestion/call-routing.d.ts +8 -12
  56. package/dist/core/ingestion/call-routing.js +13 -34
  57. package/dist/core/ingestion/call-types.d.ts +135 -0
  58. package/dist/core/ingestion/call-types.js +2 -0
  59. package/dist/core/ingestion/class-extractors/configs/c-cpp.d.ts +3 -0
  60. package/dist/core/ingestion/class-extractors/configs/c-cpp.js +11 -0
  61. package/dist/core/ingestion/class-extractors/configs/csharp.d.ts +2 -0
  62. package/dist/core/ingestion/class-extractors/configs/csharp.js +21 -0
  63. package/dist/core/ingestion/class-extractors/configs/dart.d.ts +2 -0
  64. package/dist/core/ingestion/class-extractors/configs/dart.js +7 -0
  65. package/dist/core/ingestion/class-extractors/configs/go.d.ts +2 -0
  66. package/dist/core/ingestion/class-extractors/configs/go.js +20 -0
  67. package/dist/core/ingestion/class-extractors/configs/jvm.d.ts +3 -0
  68. package/dist/core/ingestion/class-extractors/configs/jvm.js +35 -0
  69. package/dist/core/ingestion/class-extractors/configs/php.d.ts +2 -0
  70. package/dist/core/ingestion/class-extractors/configs/php.js +7 -0
  71. package/dist/core/ingestion/class-extractors/configs/python.d.ts +2 -0
  72. package/dist/core/ingestion/class-extractors/configs/python.js +7 -0
  73. package/dist/core/ingestion/class-extractors/configs/ruby.d.ts +2 -0
  74. package/dist/core/ingestion/class-extractors/configs/ruby.js +7 -0
  75. package/dist/core/ingestion/class-extractors/configs/rust.d.ts +2 -0
  76. package/dist/core/ingestion/class-extractors/configs/rust.js +7 -0
  77. package/dist/core/ingestion/class-extractors/configs/swift.d.ts +2 -0
  78. package/dist/core/ingestion/class-extractors/configs/swift.js +18 -0
  79. package/dist/core/ingestion/class-extractors/configs/typescript-javascript.d.ts +4 -0
  80. package/dist/core/ingestion/class-extractors/configs/typescript-javascript.js +28 -0
  81. package/dist/core/ingestion/field-types.d.ts +1 -1
  82. package/dist/core/ingestion/heritage-extractors/configs/go.d.ts +13 -0
  83. package/dist/core/ingestion/heritage-extractors/configs/go.js +20 -0
  84. package/dist/core/ingestion/heritage-extractors/configs/ruby.d.ts +18 -0
  85. package/dist/core/ingestion/heritage-extractors/configs/ruby.js +65 -0
  86. package/dist/core/ingestion/heritage-extractors/generic.d.ts +23 -0
  87. package/dist/core/ingestion/heritage-extractors/generic.js +47 -0
  88. package/dist/core/ingestion/heritage-processor.d.ts +9 -0
  89. package/dist/core/ingestion/heritage-processor.js +120 -85
  90. package/dist/core/ingestion/heritage-types.d.ts +73 -0
  91. package/dist/core/ingestion/heritage-types.js +2 -0
  92. package/dist/core/ingestion/import-resolvers/configs/c-cpp.d.ts +7 -0
  93. package/dist/core/ingestion/import-resolvers/configs/c-cpp.js +14 -0
  94. package/dist/core/ingestion/import-resolvers/configs/csharp.d.ts +8 -0
  95. package/dist/core/ingestion/import-resolvers/configs/csharp.js +27 -0
  96. package/dist/core/ingestion/import-resolvers/configs/dart.d.ts +17 -0
  97. package/dist/core/ingestion/import-resolvers/{dart.js → configs/dart.js} +26 -16
  98. package/dist/core/ingestion/import-resolvers/configs/go.d.ts +8 -0
  99. package/dist/core/ingestion/import-resolvers/configs/go.js +26 -0
  100. package/dist/core/ingestion/import-resolvers/configs/jvm.d.ts +13 -0
  101. package/dist/core/ingestion/import-resolvers/configs/jvm.js +68 -0
  102. package/dist/core/ingestion/import-resolvers/configs/php.d.ts +8 -0
  103. package/dist/core/ingestion/import-resolvers/configs/php.js +15 -0
  104. package/dist/core/ingestion/import-resolvers/configs/python.d.ts +12 -0
  105. package/dist/core/ingestion/import-resolvers/configs/python.js +41 -0
  106. package/dist/core/ingestion/import-resolvers/configs/ruby.d.ts +8 -0
  107. package/dist/core/ingestion/import-resolvers/configs/ruby.js +16 -0
  108. package/dist/core/ingestion/import-resolvers/configs/rust.d.ts +8 -0
  109. package/dist/core/ingestion/import-resolvers/configs/rust.js +54 -0
  110. package/dist/core/ingestion/import-resolvers/configs/swift.d.ts +8 -0
  111. package/dist/core/ingestion/import-resolvers/{swift.js → configs/swift.js} +10 -5
  112. package/dist/core/ingestion/import-resolvers/configs/typescript-javascript.d.ts +9 -0
  113. package/dist/core/ingestion/import-resolvers/configs/typescript-javascript.js +23 -0
  114. package/dist/core/ingestion/import-resolvers/csharp.d.ts +4 -5
  115. package/dist/core/ingestion/import-resolvers/csharp.js +4 -20
  116. package/dist/core/ingestion/import-resolvers/go.d.ts +4 -5
  117. package/dist/core/ingestion/import-resolvers/go.js +4 -19
  118. package/dist/core/ingestion/import-resolvers/jvm.d.ts +5 -10
  119. package/dist/core/ingestion/import-resolvers/jvm.js +5 -58
  120. package/dist/core/ingestion/import-resolvers/php.d.ts +4 -5
  121. package/dist/core/ingestion/import-resolvers/php.js +4 -7
  122. package/dist/core/ingestion/import-resolvers/python.d.ts +3 -6
  123. package/dist/core/ingestion/import-resolvers/python.js +3 -18
  124. package/dist/core/ingestion/import-resolvers/resolver-factory.d.ts +24 -0
  125. package/dist/core/ingestion/import-resolvers/resolver-factory.js +33 -0
  126. package/dist/core/ingestion/import-resolvers/ruby.d.ts +4 -5
  127. package/dist/core/ingestion/import-resolvers/ruby.js +4 -7
  128. package/dist/core/ingestion/import-resolvers/rust.d.ts +4 -5
  129. package/dist/core/ingestion/import-resolvers/rust.js +4 -47
  130. package/dist/core/ingestion/import-resolvers/standard.d.ts +3 -9
  131. package/dist/core/ingestion/import-resolvers/standard.js +7 -8
  132. package/dist/core/ingestion/import-resolvers/types.d.ts +24 -0
  133. package/dist/core/ingestion/language-provider.d.ts +80 -0
  134. package/dist/core/ingestion/languages/c-cpp.js +18 -12
  135. package/dist/core/ingestion/languages/csharp.js +13 -21
  136. package/dist/core/ingestion/languages/dart.js +13 -7
  137. package/dist/core/ingestion/languages/go.js +14 -20
  138. package/dist/core/ingestion/languages/java.js +13 -18
  139. package/dist/core/ingestion/languages/kotlin.js +13 -13
  140. package/dist/core/ingestion/languages/php.js +13 -7
  141. package/dist/core/ingestion/languages/python.js +13 -7
  142. package/dist/core/ingestion/languages/ruby.js +103 -22
  143. package/dist/core/ingestion/languages/rust.js +13 -7
  144. package/dist/core/ingestion/languages/swift.js +13 -18
  145. package/dist/core/ingestion/languages/typescript.js +18 -23
  146. package/dist/core/ingestion/languages/vue.js +13 -17
  147. package/dist/core/ingestion/model/heritage-map.d.ts +35 -0
  148. package/dist/core/ingestion/model/heritage-map.js +110 -9
  149. package/dist/core/ingestion/model/index.d.ts +2 -2
  150. package/dist/core/ingestion/model/index.js +1 -1
  151. package/dist/core/ingestion/model/resolve.d.ts +33 -28
  152. package/dist/core/ingestion/model/resolve.js +111 -27
  153. package/dist/core/ingestion/parsing-processor.d.ts +1 -2
  154. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +1 -0
  155. package/dist/core/ingestion/pipeline-phases/parse-impl.js +9 -3
  156. package/dist/core/ingestion/pipeline-phases/parse.d.ts +7 -0
  157. package/dist/core/ingestion/pipeline.d.ts +11 -0
  158. package/dist/core/ingestion/pipeline.js +9 -2
  159. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  160. package/dist/core/ingestion/tree-sitter-queries.js +81 -0
  161. package/dist/core/ingestion/type-env.d.ts +1 -1
  162. package/dist/core/ingestion/utils/ast-helpers.d.ts +1 -1
  163. package/dist/core/ingestion/utils/ast-helpers.js +22 -2
  164. package/dist/core/ingestion/utils/ruby-self-call.d.ts +52 -0
  165. package/dist/core/ingestion/utils/ruby-self-call.js +59 -0
  166. package/dist/core/ingestion/variable-extractors/configs/c-cpp.d.ts +3 -0
  167. package/dist/core/ingestion/variable-extractors/configs/c-cpp.js +81 -0
  168. package/dist/core/ingestion/variable-extractors/configs/csharp.d.ts +9 -0
  169. package/dist/core/ingestion/variable-extractors/configs/csharp.js +63 -0
  170. package/dist/core/ingestion/variable-extractors/configs/dart.d.ts +2 -0
  171. package/dist/core/ingestion/variable-extractors/configs/dart.js +94 -0
  172. package/dist/core/ingestion/variable-extractors/configs/go.d.ts +2 -0
  173. package/dist/core/ingestion/variable-extractors/configs/go.js +83 -0
  174. package/dist/core/ingestion/variable-extractors/configs/jvm.d.ts +18 -0
  175. package/dist/core/ingestion/variable-extractors/configs/jvm.js +115 -0
  176. package/dist/core/ingestion/variable-extractors/configs/php.d.ts +14 -0
  177. package/dist/core/ingestion/variable-extractors/configs/php.js +58 -0
  178. package/dist/core/ingestion/variable-extractors/configs/python.d.ts +2 -0
  179. package/dist/core/ingestion/variable-extractors/configs/python.js +101 -0
  180. package/dist/core/ingestion/variable-extractors/configs/ruby.d.ts +11 -0
  181. package/dist/core/ingestion/variable-extractors/configs/ruby.js +52 -0
  182. package/dist/core/ingestion/variable-extractors/configs/rust.d.ts +2 -0
  183. package/dist/core/ingestion/variable-extractors/configs/rust.js +76 -0
  184. package/dist/core/ingestion/variable-extractors/configs/swift.d.ts +2 -0
  185. package/dist/core/ingestion/variable-extractors/configs/swift.js +88 -0
  186. package/dist/core/ingestion/variable-extractors/configs/typescript-javascript.d.ts +3 -0
  187. package/dist/core/ingestion/variable-extractors/configs/typescript-javascript.js +83 -0
  188. package/dist/core/ingestion/variable-extractors/generic.d.ts +5 -0
  189. package/dist/core/ingestion/variable-extractors/generic.js +80 -0
  190. package/dist/core/ingestion/variable-types.d.ts +82 -0
  191. package/dist/core/ingestion/variable-types.js +2 -0
  192. package/dist/core/ingestion/workers/parse-worker.js +244 -217
  193. package/dist/core/ingestion/workers/worker-pool.js +3 -0
  194. package/dist/core/lbug/csv-generator.js +1 -0
  195. package/dist/core/lbug/lbug-adapter.d.ts +4 -5
  196. package/dist/core/lbug/lbug-adapter.js +38 -14
  197. package/dist/core/lbug/schema.d.ts +2 -1
  198. package/dist/core/lbug/schema.js +10 -1
  199. package/dist/core/run-analyze.js +6 -7
  200. package/dist/core/tree-sitter/parser-loader.d.ts +3 -0
  201. package/dist/core/tree-sitter/parser-loader.js +17 -8
  202. package/dist/mcp/core/embedder.js +5 -0
  203. package/dist/mcp/local/local-backend.js +29 -19
  204. package/dist/server/api.js +2 -0
  205. package/dist/types/pipeline.d.ts +6 -0
  206. package/package.json +8 -7
  207. package/scripts/build-tree-sitter-proto.cjs +82 -0
  208. package/vendor/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
  209. package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +104 -0
  210. package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +108 -0
  211. package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +104 -0
  212. package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +104 -0
  213. package/vendor/tree-sitter-proto/package.json +1 -7
  214. package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +0 -10
  215. package/dist/core/ingestion/call-sites/extract-language-call-site.js +0 -22
  216. package/dist/core/ingestion/call-sites/java.d.ts +0 -9
  217. package/dist/core/ingestion/call-sites/java.js +0 -30
  218. package/dist/core/ingestion/import-resolvers/dart.d.ts +0 -7
  219. package/dist/core/ingestion/import-resolvers/swift.d.ts +0 -7
  220. package/dist/core/ingestion/import-resolvers/vue.d.ts +0 -8
  221. package/dist/core/ingestion/import-resolvers/vue.js +0 -9
  222. package/scripts/preinstall-cleanup.cjs +0 -34
@@ -64,8 +64,12 @@ const DEFAULT_HERITAGE_STRATEGY = { defaultEdge: 'EXTENDS' };
64
64
  * paths) used by interface-dispatch in call resolution.
65
65
  */
66
66
  export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
67
- // childNodeId → Set<parentNodeId> (Set to deduplicate cross-chunk duplicates)
67
+ // childNodeId → insertion-ordered array of { parentId, kind }.
68
+ // Ordered array (not Set) because Ruby MRO walk depends on declaration
69
+ // order. A parallel `seen` map dedupes `(parentId, kind)` pairs without
70
+ // losing order.
68
71
  const directParents = new Map();
72
+ const seenParents = new Map();
69
73
  // interfaceName → Set<filePath> (implementor lookup for interface dispatch)
70
74
  const implementorFiles = new Map();
71
75
  for (const h of heritage) {
@@ -80,10 +84,23 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
80
84
  continue;
81
85
  let parents = directParents.get(child.nodeId);
82
86
  if (!parents) {
83
- parents = new Set();
87
+ parents = [];
84
88
  directParents.set(child.nodeId, parents);
85
89
  }
86
- parents.add(parent.nodeId);
90
+ let seen = seenParents.get(child.nodeId);
91
+ if (!seen) {
92
+ seen = new Set();
93
+ seenParents.set(child.nodeId, seen);
94
+ }
95
+ // Dedup by `parentId + kind` so the same parent under two different
96
+ // kinds (e.g. a module that is both included and prepended — legal
97
+ // Ruby though unusual) is recorded twice; the consumer needs both
98
+ // kinds in the walk. A single (parent, kind) pair is deduped.
99
+ const key = `${parent.nodeId}|${h.kind}`;
100
+ if (!seen.has(key)) {
101
+ seen.add(key);
102
+ parents.push({ parentId: parent.nodeId, kind: h.kind });
103
+ }
87
104
  }
88
105
  }
89
106
  }
@@ -120,9 +137,28 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
120
137
  }
121
138
  }
122
139
  // --- Public API ---------------------------------------------------
140
+ /** Internal helper: return the entries array (may be undefined). */
141
+ const entriesFor = (nodeId) => directParents.get(nodeId);
142
+ const getParentEntries = (childNodeId) => {
143
+ const entries = entriesFor(childNodeId);
144
+ return entries ?? [];
145
+ };
123
146
  const getParents = (childNodeId) => {
124
- const parents = directParents.get(childNodeId);
125
- return parents ? [...parents] : [];
147
+ const entries = entriesFor(childNodeId);
148
+ if (!entries)
149
+ return [];
150
+ // Deduplicate parent ids across kinds so the flat-string contract
151
+ // (used by non-Ruby MRO strategies and by the C3 linearizer) stays
152
+ // identical to its pre-kind-awareness behavior.
153
+ const out = [];
154
+ const seen = new Set();
155
+ for (const e of entries) {
156
+ if (!seen.has(e.parentId)) {
157
+ seen.add(e.parentId);
158
+ out.push(e.parentId);
159
+ }
160
+ }
161
+ return out;
126
162
  };
127
163
  const getAncestors = (childNodeId) => {
128
164
  const result = [];
@@ -139,11 +175,15 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
139
175
  visited.add(parentId);
140
176
  result.push(parentId);
141
177
  // Expand parent's own parents for next level
142
- const grandparents = directParents.get(parentId);
178
+ const grandparents = entriesFor(parentId);
143
179
  if (grandparents) {
180
+ const gpSeen = new Set();
144
181
  for (const gp of grandparents) {
145
- if (!visited.has(gp))
146
- nextFrontier.push(gp);
182
+ if (gpSeen.has(gp.parentId))
183
+ continue;
184
+ gpSeen.add(gp.parentId);
185
+ if (!visited.has(gp.parentId))
186
+ nextFrontier.push(gp.parentId);
147
187
  }
148
188
  }
149
189
  }
@@ -152,8 +192,69 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
152
192
  }
153
193
  return result;
154
194
  };
195
+ /**
196
+ * Lazy-computed per-owner split of direct parents into instance-dispatch
197
+ * (non-`extend`) and singleton-dispatch (`extend`-only) views. Memoized on
198
+ * first request so the `.filter()` pass happens at most once per owner per
199
+ * HeritageMap lifetime, not per call-site dispatch.
200
+ *
201
+ * Shared empty-array sentinels for owners with no entries in a given view
202
+ * avoid per-call allocation when the split is asymmetric (common Ruby case:
203
+ * a class has `include` but no `extend`, so its singleton view is empty).
204
+ */
205
+ const EMPTY_PARENT_ENTRIES = [];
206
+ const splitCache = new Map();
207
+ const splitForOwner = (childNodeId) => {
208
+ let cached = splitCache.get(childNodeId);
209
+ if (cached)
210
+ return cached;
211
+ const entries = entriesFor(childNodeId);
212
+ if (!entries || entries.length === 0) {
213
+ cached = { instance: EMPTY_PARENT_ENTRIES, singleton: EMPTY_PARENT_ENTRIES };
214
+ }
215
+ else {
216
+ const instance = [];
217
+ const singleton = [];
218
+ for (const e of entries) {
219
+ if (e.kind === 'extend')
220
+ singleton.push(e);
221
+ else
222
+ instance.push(e);
223
+ }
224
+ cached = {
225
+ instance: instance.length === 0 ? EMPTY_PARENT_ENTRIES : instance,
226
+ singleton: singleton.length === 0 ? EMPTY_PARENT_ENTRIES : singleton,
227
+ };
228
+ }
229
+ splitCache.set(childNodeId, cached);
230
+ return cached;
231
+ };
232
+ /**
233
+ * Instance-dispatch ancestry walk. Excludes `extend` (singleton-only).
234
+ * For kind-aware consumers (Ruby MRO): walks parents in source-insertion
235
+ * order. The consumer is responsible for interleaving self / reversing
236
+ * prepend order / etc. This method preserves raw declaration order.
237
+ *
238
+ * Result is cached per owner; repeat calls return the same array.
239
+ */
240
+ const getInstanceAncestry = (childNodeId) => splitForOwner(childNodeId).instance;
241
+ /**
242
+ * Singleton-dispatch ancestry walk. Only `extend` parents. For non-Ruby
243
+ * languages this is always empty (no language currently produces `extend`
244
+ * heritage records outside Ruby).
245
+ *
246
+ * Result is cached per owner; repeat calls return the same array.
247
+ */
248
+ const getSingletonAncestry = (childNodeId) => splitForOwner(childNodeId).singleton;
155
249
  const getImplementorFiles = (interfaceName) => {
156
250
  return implementorFiles.get(interfaceName) ?? EMPTY_SET;
157
251
  };
158
- return { getParents, getAncestors, getImplementorFiles };
252
+ return {
253
+ getParents,
254
+ getAncestors,
255
+ getParentEntries,
256
+ getInstanceAncestry,
257
+ getSingletonAncestry,
258
+ getImplementorFiles,
259
+ };
159
260
  };
@@ -10,11 +10,11 @@
10
10
  * `SymbolTable` by design.
11
11
  */
12
12
  export { type SemanticModel, type MutableSemanticModel, createSemanticModel, } from './semantic-model.js';
13
- export { type SymbolTableReader, type SymbolTableWriter, createSymbolTable, } from './symbol-table.js';
13
+ export { type SymbolTableReader, type SymbolTableWriter, createSymbolTable, type SymbolDefinition, type AddMetadata, CLASS_TYPES, CLASS_TYPES_TUPLE, type ClassLikeLabel, FREE_CALLABLE_TYPES, FREE_CALLABLE_TUPLE, type FreeCallableLabel, CALL_TARGET_TYPES, } from './symbol-table.js';
14
14
  export { type TypeRegistry, type MutableTypeRegistry, createTypeRegistry, } from './type-registry.js';
15
15
  export { type MethodRegistry, type MutableMethodRegistry, createMethodRegistry, } from './method-registry.js';
16
16
  export { type FieldRegistry, type MutableFieldRegistry, createFieldRegistry, } from './field-registry.js';
17
17
  export { lookupMethodByOwnerWithMRO } from './resolve.js';
18
18
  export { type NamedImportBinding, type NamedImportMap, isFileInPackageDir, } from './resolution-context.js';
19
- export { type ExtractedHeritage, type HeritageResolutionStrategy, type HeritageStrategyLookup, } from './heritage-map.js';
19
+ export { type ExtractedHeritage, type HeritageMap, type HeritageResolutionStrategy, type HeritageStrategyLookup, } from './heritage-map.js';
20
20
  export { CALLABLE_ONLY_LABELS, INERT_LABELS, DISPATCH_LABELS, ALL_NODE_LABELS, type LabelBehavior, } from './registration-table.js';
@@ -16,7 +16,7 @@ export { createSemanticModel, } from './semantic-model.js';
16
16
  // SymbolTable is exclusively owned by SemanticModel. Re-exported here
17
17
  // for the rare caller that needs the file/callable interface in
18
18
  // isolation (e.g. tests).
19
- export { createSymbolTable, } from './symbol-table.js';
19
+ export { createSymbolTable, CLASS_TYPES, CLASS_TYPES_TUPLE, FREE_CALLABLE_TYPES, FREE_CALLABLE_TUPLE, CALL_TARGET_TYPES, } from './symbol-table.js';
20
20
  // Type registry (classes, structs, interfaces, enums, records, impls)
21
21
  export { createTypeRegistry, } from './type-registry.js';
22
22
  // Method registry (owner-scoped methods with arity-aware overload lookup)
@@ -12,6 +12,9 @@ import type { MroStrategy } from '../../../_shared/index.js';
12
12
  /**
13
13
  * Gather all ancestor IDs in BFS / topological order.
14
14
  * Returns the linearized list of ancestor IDs (excluding the class itself).
15
+ *
16
+ * Uses a head-pointer BFS (`queue[head++]`) instead of `Array.shift()` to
17
+ * avoid O(n) per-dequeue re-indexing — matching `buildParentMapFromHeritage`.
15
18
  */
16
19
  declare function gatherAncestors(classId: string, parentMap: Map<string, string[]>): string[];
17
20
  /**
@@ -25,32 +28,34 @@ declare function gatherAncestors(classId: string, parentMap: Map<string, string[
25
28
  export declare function c3Linearize(classId: string, parentMap: Map<string, string[]>, cache: Map<string, string[] | null>, inProgress?: Set<string>): string[] | null;
26
29
  export { gatherAncestors };
27
30
  /**
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.
31
+ * DAG stage 5 helper: look up a method on an owner class via MRO walk.
32
+ *
33
+ * Low-level resolver; no dependency on SymbolTable, language registry, or
34
+ * resolution-context (keeps model/ layer free of cross-layer imports).
35
+ * All strategies respect `argCount` for overload narrowing.
36
+ * `ancestryOverride` replaces the default walk; caller must compute it correctly.
37
+ *
38
+ * Strategy summary (full docs in gitnexus-shared/mro-strategy.ts):
39
+ * - `first-wins` / `leftmost-base` / `implements-split`: BFS, first match wins.
40
+ * - `c3`: C3-linearized order; falls back to BFS on cycle/inconsistency.
41
+ * - `qualified-syntax`: returns undefined immediately (Rust requires explicit syntax).
42
+ * - `ruby-mixin`: kind-aware walk see inline comments below.
43
+ *
44
+ * Internal API: exported for call-processor resolvers and tests.
45
+ * External callers should use resolveMemberCall instead.
46
+ *
47
+ * @see gitnexus-shared/mro-strategy.ts § 'ruby-mixin'
48
+ * @see call-processor.ts § resolveMemberCall
49
+ */
50
+ export declare const lookupMethodByOwnerWithMRO: (ownerNodeId: string, methodName: string, heritageMap: HeritageMap, model: SemanticModel, strategy: MroStrategy, argCount?: number,
51
+ /**
52
+ * Optional pre-computed ancestry list. When provided, overrides the default
53
+ * per-strategy ancestry source. Primarily used by Ruby singleton dispatch:
54
+ * the caller supplies `heritageMap.getSingletonAncestry(ownerNodeId)` as
55
+ * node-id array so this walker resolves against `extend` providers only.
56
+ *
57
+ * For `ruby-mixin` strategy, passing an override switches the walker into
58
+ * a no-prepend-no-direct linear scan (the caller has already decided the
59
+ * order), which is the correct semantics for singleton dispatch.
55
60
  */
56
- export declare const lookupMethodByOwnerWithMRO: (ownerNodeId: string, methodName: string, heritageMap: HeritageMap, model: SemanticModel, strategy: MroStrategy, argCount?: number) => SymbolDefinition | undefined;
61
+ ancestryOverride?: readonly string[]) => SymbolDefinition | undefined;
@@ -15,13 +15,17 @@
15
15
  /**
16
16
  * Gather all ancestor IDs in BFS / topological order.
17
17
  * Returns the linearized list of ancestor IDs (excluding the class itself).
18
+ *
19
+ * Uses a head-pointer BFS (`queue[head++]`) instead of `Array.shift()` to
20
+ * avoid O(n) per-dequeue re-indexing — matching `buildParentMapFromHeritage`.
18
21
  */
19
22
  function gatherAncestors(classId, parentMap) {
20
23
  const visited = new Set();
21
24
  const order = [];
22
25
  const queue = [...(parentMap.get(classId) ?? [])];
23
- while (queue.length > 0) {
24
- const id = queue.shift();
26
+ let head = 0;
27
+ while (head < queue.length) {
28
+ const id = queue[head++];
25
29
  if (visited.has(id))
26
30
  continue;
27
31
  visited.add(id);
@@ -229,35 +233,112 @@ const buildParentMapFromHeritage = (startNodeId, heritageMap) => {
229
233
  // MRO-aware method lookup
230
234
  // ---------------------------------------------------------------------------
231
235
  /**
232
- * Look up a method on an owner class, walking the parent chain via HeritageMap
233
- * when the method isn't found on the direct owner.
236
+ * DAG stage 5 helper: look up a method on an owner class via MRO walk.
234
237
  *
235
- * Respects the 5 per-language MRO strategies:
236
- * - `first-wins`: BFS ancestor walk, first match wins (default)
237
- * - `leftmost-base`: BFS ancestor walk, leftmost base in declaration order wins (C++);
238
- * HeritageMap preserves insertion order matching source declaration,
239
- * so BFS order is equivalent to leftmost-base semantics
240
- * - `c3`: C3-linearized ancestor order, first match wins (Python)
241
- * - `implements-split`: BFS ancestor walk, first match wins (Java/C#) —
242
- * full ambiguity detection for multiple interface defaults
243
- * is handled by computeMRO at graph level
244
- * - `qualified-syntax`: No auto-resolution (Rust) — returns undefined
238
+ * Low-level resolver; no dependency on SymbolTable, language registry, or
239
+ * resolution-context (keeps model/ layer free of cross-layer imports).
240
+ * All strategies respect `argCount` for overload narrowing.
241
+ * `ancestryOverride` replaces the default walk; caller must compute it correctly.
245
242
  *
246
- * Uses the `c3Linearize` defined in this file (also consumed by
247
- * mro-processor.ts for graph-level MRO emission) for the `c3` strategy.
243
+ * Strategy summary (full docs in gitnexus-shared/mro-strategy.ts):
244
+ * - `first-wins` / `leftmost-base` / `implements-split`: BFS, first match wins.
245
+ * - `c3`: C3-linearized order; falls back to BFS on cycle/inconsistency.
246
+ * - `qualified-syntax`: returns undefined immediately (Rust requires explicit syntax).
247
+ * - `ruby-mixin`: kind-aware walk — see inline comments below.
248
248
  *
249
- * Depends only on {@link SemanticModel} + {@link HeritageMap} + an
250
- * {@link MroStrategy} literal NO dependency on SymbolTable, the language
251
- * registry, or resolution-context, which keeps the `model/` module free of
252
- * cross-layer imports. Callers derive the strategy from their language
253
- * provider before invoking this function.
249
+ * Internal API: exported for call-processor resolvers and tests.
250
+ * External callers should use resolveMemberCall instead.
254
251
  *
255
- * @internal This is the low-level MRO walker. Exported so call-processor's
256
- * higher-level resolvers (and unit tests) can invoke it directly. Callers
257
- * outside `core/ingestion/` should use the higher-level resolvers in
258
- * call-processor.ts instead of depending on this function.
252
+ * @see gitnexus-shared/mro-strategy.ts § 'ruby-mixin'
253
+ * @see call-processor.ts § resolveMemberCall
259
254
  */
260
- export const lookupMethodByOwnerWithMRO = (ownerNodeId, methodName, heritageMap, model, strategy, argCount) => {
255
+ export const lookupMethodByOwnerWithMRO = (ownerNodeId, methodName, heritageMap, model, strategy, argCount,
256
+ /**
257
+ * Optional pre-computed ancestry list. When provided, overrides the default
258
+ * per-strategy ancestry source. Primarily used by Ruby singleton dispatch:
259
+ * the caller supplies `heritageMap.getSingletonAncestry(ownerNodeId)` as
260
+ * node-id array so this walker resolves against `extend` providers only.
261
+ *
262
+ * For `ruby-mixin` strategy, passing an override switches the walker into
263
+ * a no-prepend-no-direct linear scan (the caller has already decided the
264
+ * order), which is the correct semantics for singleton dispatch.
265
+ */
266
+ ancestryOverride) => {
267
+ // ── Ruby mixin strategy ───────────────────────────────────────────
268
+ // Kind-aware walk — does NOT short-circuit on direct owner first (prepend beats direct).
269
+ // Instance dispatch: prepend (reverse) → direct → include (reverse) → transitive BFS.
270
+ // Singleton dispatch: caller supplies ancestryOverride (extend providers only);
271
+ // simple left-to-right scan. Miss NEVER falls through to file-scoped fallback.
272
+ // See gitnexus-shared/mro-strategy.ts § 'ruby-mixin' for full strategy docs.
273
+ if (strategy === 'ruby-mixin') {
274
+ if (ancestryOverride) {
275
+ // Singleton dispatch: scan pre-computed ancestry only. Miss null-routes.
276
+ for (const ancestorId of ancestryOverride) {
277
+ const method = model.methods.lookupMethodByOwner(ancestorId, methodName, argCount);
278
+ if (method)
279
+ return method;
280
+ }
281
+ return undefined;
282
+ }
283
+ // Instance dispatch — kind-aware walk per the pseudocode above.
284
+ const instanceEntries = heritageMap.getInstanceAncestry(ownerNodeId);
285
+ // Partition into prepend parents vs other parents (extends / include /
286
+ // implements / trait-impl), preserving declaration order within each.
287
+ const prependParents = [];
288
+ const otherParents = [];
289
+ for (const e of instanceEntries) {
290
+ if (e.kind === 'prepend')
291
+ prependParents.push(e.parentId);
292
+ else
293
+ otherParents.push(e.parentId);
294
+ }
295
+ // Step 1: Walk prepend parents in REVERSE declaration order (last-prepended wins).
296
+ for (let i = prependParents.length - 1; i >= 0; i--) {
297
+ const method = model.methods.lookupMethodByOwner(prependParents[i], methodName, argCount);
298
+ if (method)
299
+ return method;
300
+ }
301
+ // Step 2: Direct owner lookup (the class's own method).
302
+ // This is the only difference from other strategies — prepend beats direct.
303
+ const direct = model.methods.lookupMethodByOwner(ownerNodeId, methodName, argCount);
304
+ if (direct)
305
+ return direct;
306
+ // Step 3: Walk extends + include parents in REVERSE declaration order.
307
+ // (Ruby `include A; include B` puts B ahead of A in MRO.)
308
+ for (let i = otherParents.length - 1; i >= 0; i--) {
309
+ const method = model.methods.lookupMethodByOwner(otherParents[i], methodName, argCount);
310
+ if (method)
311
+ return method;
312
+ }
313
+ // Step 4: Transitive ancestors (a mixin that itself mixes in another module).
314
+ // Fall back to the BFS ancestor walk for depth > 1. Order is best-effort;
315
+ // Ruby's actual MRO for transitive mixins is rare and under-specified
316
+ // (documented in architecture docs as deferred work).
317
+ //
318
+ // O(1) skip-check via Sets:
319
+ // - `walkedDirect` covers parents already visited in steps 1-3.
320
+ // - `singletonOnly` covers direct `extend` providers: they belong to
321
+ // the singleton MRO and must NEVER appear in instance dispatch.
322
+ // Building Sets once before the BFS loop avoids O(n²) `Array.includes`
323
+ // on large mixin hierarchies.
324
+ const walkedDirect = new Set(prependParents);
325
+ for (const id of otherParents)
326
+ walkedDirect.add(id);
327
+ const singletonOnly = new Set(heritageMap.getSingletonAncestry(ownerNodeId).map((e) => e.parentId));
328
+ for (const ancestorId of heritageMap.getAncestors(ownerNodeId)) {
329
+ if (ancestorId === ownerNodeId)
330
+ continue;
331
+ if (walkedDirect.has(ancestorId))
332
+ continue;
333
+ if (singletonOnly.has(ancestorId))
334
+ continue;
335
+ const method = model.methods.lookupMethodByOwner(ancestorId, methodName, argCount);
336
+ if (method)
337
+ return method;
338
+ }
339
+ return undefined;
340
+ }
341
+ // ── Non-Ruby strategies: direct-owner-first short-circuit ─────────
261
342
  // Direct lookup first (child override — no walk needed).
262
343
  // argCount is threaded through so arity-differing overloads on the direct
263
344
  // owner can be disambiguated before the MRO walk starts.
@@ -270,7 +351,10 @@ export const lookupMethodByOwnerWithMRO = (ownerNodeId, methodName, heritageMap,
270
351
  // Determine ancestor walk order based on MRO strategy.
271
352
  // readonly to accept the cached (frozen) c3 linearization without copying.
272
353
  let ancestors;
273
- if (strategy === 'c3') {
354
+ if (ancestryOverride) {
355
+ ancestors = ancestryOverride;
356
+ }
357
+ else if (strategy === 'c3') {
274
358
  // C3 linearization (memoized per HeritageMap
275
359
  // so repeated calls for the same owner within an ingestion run reuse the
276
360
  // linearization instead of rebuilding the parent map and re-running C3).
@@ -1,9 +1,8 @@
1
1
  import { KnowledgeGraph } from '../graph/types.js';
2
- import type { SymbolTableWriter } from './model/symbol-table.js';
2
+ import type { SymbolTableWriter, ExtractedHeritage } from './model/index.js';
3
3
  import { ASTCache } from './ast-cache.js';
4
4
  import { WorkerPool } from './workers/worker-pool.js';
5
5
  import type { ExtractedImport, ExtractedCall, ExtractedAssignment, ExtractedRoute, ExtractedFetchCall, ExtractedDecoratorRoute, ExtractedToolDef, FileConstructorBindings, FileScopeBindings, ExtractedORMQuery } from './workers/parse-worker.js';
6
- import type { ExtractedHeritage } from './model/heritage-map.js';
7
6
  export type FileProgressCallback = (current: number, total: number, filePath: string) => void;
8
7
  export interface WorkerExtractedData {
9
8
  imports: ExtractedImport[];
@@ -43,5 +43,6 @@ export declare function runChunkedParseAndResolve(graph: KnowledgeGraph, scanned
43
43
  allORMQueries: ExtractedORMQuery[];
44
44
  bindingAccumulator: BindingAccumulator;
45
45
  resolutionContext: ReturnType<typeof createResolutionContext>;
46
+ usedWorkerPool: boolean;
46
47
  }>;
47
48
  export {};
@@ -98,9 +98,11 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
98
98
  message: `Parsing ${totalParseable} files in ${numChunks} chunk${numChunks !== 1 ? 's' : ''}...`,
99
99
  stats: { filesProcessed: 0, totalFiles: totalParseable, nodesCreated: graph.nodeCount },
100
100
  });
101
- // Don't spawn workers for tiny repos — overhead exceeds benefit
102
- const MIN_FILES_FOR_WORKERS = 15;
103
- const MIN_BYTES_FOR_WORKERS = 512 * 1024;
101
+ // Don't spawn workers for tiny repos — overhead exceeds benefit.
102
+ // Test suites may lower the thresholds via `options.workerThresholdsForTest`
103
+ // to exercise the worker-pool path with small fixtures; see PipelineOptions.
104
+ const MIN_FILES_FOR_WORKERS = options?.workerThresholdsForTest?.minFiles ?? 15;
105
+ const MIN_BYTES_FOR_WORKERS = options?.workerThresholdsForTest?.minBytes ?? 512 * 1024;
104
106
  const totalBytes = parseableScanned.reduce((s, f) => s + f.size, 0);
105
107
  // Create worker pool once, reuse across chunks
106
108
  let workerPool;
@@ -433,5 +435,9 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
433
435
  allORMQueries,
434
436
  bindingAccumulator,
435
437
  resolutionContext: ctx,
438
+ // Whether a worker pool was actually live for this run. False means the
439
+ // sequential fallback handled every chunk (either due to `skipWorkers`,
440
+ // the file-count/byte thresholds, or a pool-creation failure).
441
+ usedWorkerPool: workerPool !== undefined,
436
442
  };
437
443
  }
@@ -45,5 +45,12 @@ export interface ParseOutput {
45
45
  readonly allPathSet: ReadonlySet<string>;
46
46
  /** Pass-through: total file count for progress reporting. */
47
47
  totalFiles: number;
48
+ /**
49
+ * True if the parse phase spawned a live worker pool for this run.
50
+ * False means every chunk ran through the sequential fallback (skipWorkers,
51
+ * thresholds not met, or pool-creation failure). Primarily a test affordance:
52
+ * see `PipelineOptions.workerThresholdsForTest`.
53
+ */
54
+ readonly usedWorkerPool: boolean;
48
55
  }
49
56
  export declare const parsePhase: PipelinePhase<ParseOutput>;
@@ -21,5 +21,16 @@ export interface PipelineOptions {
21
21
  skipGraphPhases?: boolean;
22
22
  /** Force sequential parsing (no worker pool). Useful for testing the sequential path. */
23
23
  skipWorkers?: boolean;
24
+ /**
25
+ * @internal Test-only override for worker-pool gating thresholds.
26
+ * When unset, production defaults apply (15 files OR 512 KB total bytes).
27
+ * Setting either field lowers the corresponding threshold so small test
28
+ * fixtures can still exercise the worker-pool path. Do not use from
29
+ * production call sites.
30
+ */
31
+ workerThresholdsForTest?: {
32
+ minFiles?: number;
33
+ minBytes?: number;
34
+ };
24
35
  }
25
36
  export declare const runPipelineFromRepo: (repoPath: string, onProgress: (progress: PipelineProgress) => void, options?: PipelineOptions) => Promise<PipelineResult>;
@@ -58,7 +58,7 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
58
58
  pipelineStart,
59
59
  });
60
60
  // Extract final results for the PipelineResult contract
61
- const { totalFiles } = getPhaseOutput(results, 'parse');
61
+ const { totalFiles, usedWorkerPool } = getPhaseOutput(results, 'parse');
62
62
  let communityResult;
63
63
  let processResult;
64
64
  if (!options?.skipGraphPhases) {
@@ -77,5 +77,12 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
77
77
  nodesCreated: graph.nodeCount,
78
78
  },
79
79
  });
80
- return { graph, repoPath, totalFileCount: totalFiles, communityResult, processResult };
80
+ return {
81
+ graph,
82
+ repoPath,
83
+ totalFileCount: totalFiles,
84
+ communityResult,
85
+ processResult,
86
+ usedWorkerPool,
87
+ };
81
88
  };