gitnexus 1.4.8 → 1.4.9

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 (211) hide show
  1. package/README.md +7 -0
  2. package/dist/cli/index-repo.d.ts +15 -0
  3. package/dist/cli/index-repo.js +115 -0
  4. package/dist/cli/index.js +11 -2
  5. package/dist/cli/setup.js +12 -9
  6. package/dist/cli/wiki.d.ts +4 -0
  7. package/dist/cli/wiki.js +174 -53
  8. package/dist/config/supported-languages.d.ts +7 -5
  9. package/dist/config/supported-languages.js +6 -4
  10. package/dist/core/graph/graph.js +9 -1
  11. package/dist/core/graph/types.d.ts +10 -2
  12. package/dist/core/ingestion/call-processor.d.ts +18 -1
  13. package/dist/core/ingestion/call-processor.js +297 -38
  14. package/dist/core/ingestion/call-routing.d.ts +3 -18
  15. package/dist/core/ingestion/call-routing.js +0 -19
  16. package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
  17. package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
  18. package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
  19. package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
  20. package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
  21. package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
  22. package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
  23. package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
  24. package/dist/core/ingestion/cobol-processor.d.ts +54 -0
  25. package/dist/core/ingestion/cobol-processor.js +1186 -0
  26. package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
  27. package/dist/core/ingestion/entry-point-scoring.js +18 -4
  28. package/dist/core/ingestion/export-detection.d.ts +47 -8
  29. package/dist/core/ingestion/export-detection.js +29 -50
  30. package/dist/core/ingestion/field-extractor.d.ts +29 -0
  31. package/dist/core/ingestion/field-extractor.js +25 -0
  32. package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
  33. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
  34. package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
  35. package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
  36. package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
  37. package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
  38. package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
  39. package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
  40. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
  41. package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
  42. package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
  43. package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
  44. package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
  45. package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
  46. package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
  47. package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
  48. package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
  49. package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
  50. package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
  51. package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
  52. package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
  53. package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
  54. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
  55. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
  56. package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
  57. package/dist/core/ingestion/field-extractors/generic.js +111 -0
  58. package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
  59. package/dist/core/ingestion/field-extractors/typescript.js +291 -0
  60. package/dist/core/ingestion/field-types.d.ts +59 -0
  61. package/dist/core/ingestion/field-types.js +2 -0
  62. package/dist/core/ingestion/framework-detection.d.ts +87 -0
  63. package/dist/core/ingestion/framework-detection.js +65 -2
  64. package/dist/core/ingestion/heritage-processor.js +15 -17
  65. package/dist/core/ingestion/import-processor.d.ts +9 -10
  66. package/dist/core/ingestion/import-processor.js +59 -14
  67. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
  68. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
  69. package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
  70. package/dist/core/ingestion/import-resolvers/dart.js +44 -0
  71. package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
  72. package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
  73. package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +9 -1
  74. package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.js +56 -0
  75. package/dist/core/ingestion/{resolvers → import-resolvers}/php.d.ts +6 -10
  76. package/dist/core/ingestion/{resolvers → import-resolvers}/php.js +7 -2
  77. package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
  78. package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
  79. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
  80. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
  81. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
  82. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
  83. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
  84. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
  85. package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
  86. package/dist/core/ingestion/import-resolvers/swift.js +23 -0
  87. package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
  88. package/dist/core/ingestion/import-resolvers/types.js +6 -0
  89. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +0 -3
  90. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +0 -9
  91. package/dist/core/ingestion/language-config.d.ts +4 -1
  92. package/dist/core/ingestion/language-provider.d.ts +121 -0
  93. package/dist/core/ingestion/language-provider.js +24 -0
  94. package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
  95. package/dist/core/ingestion/languages/c-cpp.js +71 -0
  96. package/dist/core/ingestion/languages/cobol.d.ts +1 -0
  97. package/dist/core/ingestion/languages/cobol.js +26 -0
  98. package/dist/core/ingestion/languages/csharp.d.ts +8 -0
  99. package/dist/core/ingestion/languages/csharp.js +49 -0
  100. package/dist/core/ingestion/languages/dart.d.ts +12 -0
  101. package/dist/core/ingestion/languages/dart.js +58 -0
  102. package/dist/core/ingestion/languages/go.d.ts +11 -0
  103. package/dist/core/ingestion/languages/go.js +28 -0
  104. package/dist/core/ingestion/languages/index.d.ts +38 -0
  105. package/dist/core/ingestion/languages/index.js +63 -0
  106. package/dist/core/ingestion/languages/java.d.ts +9 -0
  107. package/dist/core/ingestion/languages/java.js +29 -0
  108. package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
  109. package/dist/core/ingestion/languages/kotlin.js +53 -0
  110. package/dist/core/ingestion/languages/php.d.ts +8 -0
  111. package/dist/core/ingestion/languages/php.js +145 -0
  112. package/dist/core/ingestion/languages/python.d.ts +12 -0
  113. package/dist/core/ingestion/languages/python.js +39 -0
  114. package/dist/core/ingestion/languages/ruby.d.ts +9 -0
  115. package/dist/core/ingestion/languages/ruby.js +44 -0
  116. package/dist/core/ingestion/languages/rust.d.ts +12 -0
  117. package/dist/core/ingestion/languages/rust.js +44 -0
  118. package/dist/core/ingestion/languages/swift.d.ts +12 -0
  119. package/dist/core/ingestion/languages/swift.js +133 -0
  120. package/dist/core/ingestion/languages/typescript.d.ts +10 -0
  121. package/dist/core/ingestion/languages/typescript.js +60 -0
  122. package/dist/core/ingestion/mro-processor.js +14 -15
  123. package/dist/core/ingestion/{named-binding-extraction.d.ts → named-binding-processor.d.ts} +0 -9
  124. package/dist/core/ingestion/named-binding-processor.js +42 -0
  125. package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
  126. package/dist/core/ingestion/named-bindings/csharp.js +37 -0
  127. package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
  128. package/dist/core/ingestion/named-bindings/java.js +29 -0
  129. package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
  130. package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
  131. package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
  132. package/dist/core/ingestion/named-bindings/php.js +61 -0
  133. package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
  134. package/dist/core/ingestion/named-bindings/python.js +49 -0
  135. package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
  136. package/dist/core/ingestion/named-bindings/rust.js +64 -0
  137. package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
  138. package/dist/core/ingestion/named-bindings/types.js +6 -0
  139. package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
  140. package/dist/core/ingestion/named-bindings/typescript.js +58 -0
  141. package/dist/core/ingestion/parsing-processor.d.ts +5 -1
  142. package/dist/core/ingestion/parsing-processor.js +115 -16
  143. package/dist/core/ingestion/pipeline.js +925 -424
  144. package/dist/core/ingestion/resolution-context.js +1 -1
  145. package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
  146. package/dist/core/ingestion/route-extractors/expo.js +36 -0
  147. package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
  148. package/dist/core/ingestion/route-extractors/middleware.js +143 -0
  149. package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
  150. package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
  151. package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
  152. package/dist/core/ingestion/route-extractors/php.js +21 -0
  153. package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
  154. package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
  155. package/dist/core/ingestion/tree-sitter-queries.d.ts +8 -7
  156. package/dist/core/ingestion/tree-sitter-queries.js +231 -9
  157. package/dist/core/ingestion/type-env.d.ts +14 -17
  158. package/dist/core/ingestion/type-env.js +66 -14
  159. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +1 -1
  160. package/dist/core/ingestion/type-extractors/csharp.js +1 -1
  161. package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
  162. package/dist/core/ingestion/type-extractors/dart.js +371 -0
  163. package/dist/core/ingestion/type-extractors/jvm.js +1 -1
  164. package/dist/core/ingestion/type-extractors/shared.d.ts +1 -13
  165. package/dist/core/ingestion/type-extractors/shared.js +9 -102
  166. package/dist/core/ingestion/type-extractors/swift.js +334 -4
  167. package/dist/core/ingestion/type-extractors/types.d.ts +3 -1
  168. package/dist/core/ingestion/{ast-helpers.d.ts → utils/ast-helpers.d.ts} +16 -13
  169. package/dist/core/ingestion/{ast-helpers.js → utils/ast-helpers.js} +111 -32
  170. package/dist/core/ingestion/{call-analysis.js → utils/call-analysis.js} +37 -0
  171. package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
  172. package/dist/core/ingestion/utils/event-loop.js +5 -0
  173. package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
  174. package/dist/core/ingestion/utils/language-detection.js +70 -0
  175. package/dist/core/ingestion/utils/verbose.d.ts +1 -0
  176. package/dist/core/ingestion/utils/verbose.js +7 -0
  177. package/dist/core/ingestion/workers/parse-worker.d.ts +43 -2
  178. package/dist/core/ingestion/workers/parse-worker.js +361 -150
  179. package/dist/core/lbug/csv-generator.js +34 -1
  180. package/dist/core/lbug/lbug-adapter.js +6 -0
  181. package/dist/core/lbug/schema.d.ts +5 -3
  182. package/dist/core/lbug/schema.js +39 -2
  183. package/dist/core/tree-sitter/parser-loader.js +7 -1
  184. package/dist/core/wiki/cursor-client.d.ts +31 -0
  185. package/dist/core/wiki/cursor-client.js +127 -0
  186. package/dist/core/wiki/generator.d.ts +28 -9
  187. package/dist/core/wiki/generator.js +115 -18
  188. package/dist/core/wiki/graph-queries.d.ts +4 -0
  189. package/dist/core/wiki/graph-queries.js +7 -1
  190. package/dist/core/wiki/llm-client.d.ts +2 -0
  191. package/dist/core/wiki/llm-client.js +8 -4
  192. package/dist/core/wiki/prompts.d.ts +3 -3
  193. package/dist/core/wiki/prompts.js +6 -0
  194. package/dist/mcp/core/lbug-adapter.d.ts +5 -0
  195. package/dist/mcp/core/lbug-adapter.js +11 -1
  196. package/dist/mcp/local/local-backend.d.ts +16 -5
  197. package/dist/mcp/local/local-backend.js +711 -74
  198. package/dist/mcp/tools.js +71 -2
  199. package/dist/storage/repo-manager.d.ts +3 -0
  200. package/package.json +17 -16
  201. package/dist/core/ingestion/import-resolution.d.ts +0 -101
  202. package/dist/core/ingestion/import-resolution.js +0 -251
  203. package/dist/core/ingestion/named-binding-extraction.js +0 -373
  204. package/dist/core/ingestion/resolvers/index.d.ts +0 -18
  205. package/dist/core/ingestion/resolvers/index.js +0 -13
  206. package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
  207. package/dist/core/ingestion/type-extractors/index.js +0 -31
  208. package/dist/core/ingestion/utils.d.ts +0 -20
  209. package/dist/core/ingestion/utils.js +0 -242
  210. package/scripts/patch-tree-sitter-swift.cjs +0 -74
  211. /package/dist/core/ingestion/{call-analysis.d.ts → utils/call-analysis.d.ts} +0 -0
@@ -1,14 +1,37 @@
1
1
  import Parser from 'tree-sitter';
2
2
  import { isLanguageAvailable, loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
3
- import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
3
+ import { getProvider, getProviderForFile, providersWithImplicitWiring } from './languages/index.js';
4
4
  import { generateId } from '../../lib/utils.js';
5
- import { getLanguageFromFilename, isVerboseIngestionEnabled, yieldToEventLoop } from './utils.js';
5
+ import { getLanguageFromFilename } from './utils/language-detection.js';
6
+ import { isVerboseIngestionEnabled } from './utils/verbose.js';
7
+ import { yieldToEventLoop } from './utils/event-loop.js';
6
8
  import { getTreeSitterBufferSize } from './constants.js';
7
9
  import { loadImportConfigs } from './language-config.js';
8
- import { buildSuffixIndex } from './resolvers/index.js';
9
- import { callRouters } from './call-routing.js';
10
- import { importResolvers, namedBindingExtractors, preprocessImportPath } from './import-resolution.js';
10
+ import { buildSuffixIndex } from './import-resolvers/utils.js';
11
11
  const isDev = process.env.NODE_ENV === 'development';
12
+ /** Group files by provider (only those with implicit import wiring), then call each wirer
13
+ * with its own language's files. O(n) over files, O(1) per provider lookup. */
14
+ function wireImplicitImports(files, importMap, addImportEdge, projectConfig) {
15
+ if (providersWithImplicitWiring.length === 0)
16
+ return;
17
+ const grouped = new Map();
18
+ for (const file of files) {
19
+ const provider = getProviderForFile(file);
20
+ if (!provider?.implicitImportWirer)
21
+ continue;
22
+ let list = grouped.get(provider);
23
+ if (!list) {
24
+ list = [];
25
+ grouped.set(provider, list);
26
+ }
27
+ list.push(file);
28
+ }
29
+ for (const [provider, langFiles] of grouped) {
30
+ if (langFiles.length > 1) {
31
+ provider.implicitImportWirer(langFiles, importMap, addImportEdge, projectConfig);
32
+ }
33
+ }
34
+ }
12
35
  /**
13
36
  * Check if a file path is directly inside a package directory identified by its suffix.
14
37
  * Used by the symbol resolver for Go and C# directory-level import matching.
@@ -21,6 +44,7 @@ export function isFileInPackageDir(filePath, dirSuffix) {
21
44
  const afterDir = normalized.substring(normalized.indexOf(dirSuffix) + dirSuffix.length);
22
45
  return !afterDir.includes('/');
23
46
  }
47
+ // ImportResolutionContext is defined in ./import-resolvers/types.ts — re-exported here for consumers.
24
48
  export function buildImportResolutionContext(allPaths) {
25
49
  const allFileList = allPaths;
26
50
  const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/'));
@@ -29,7 +53,25 @@ export function buildImportResolutionContext(allPaths) {
29
53
  return { allFilePaths, allFileList, normalizedFileList, index, resolveCache: new Map() };
30
54
  }
31
55
  // Config loaders extracted to ./language-config.ts (Phase 2 refactor)
32
- // Resolver dispatch tables are in ./import-resolution.ts imported above
56
+ // Resolver types are in ./import-resolvers/types.ts; named binding types in ./named-bindings/types.ts
57
+ // ============================================================================
58
+ // Import path preprocessing
59
+ // ============================================================================
60
+ /**
61
+ * Clean and preprocess a raw import source text into a resolved import path.
62
+ * Strips quotes/angle brackets (universal) and applies provider-specific
63
+ * transformations (currently only Kotlin wildcard import detection).
64
+ */
65
+ export function preprocessImportPath(sourceText, importNode, provider) {
66
+ const cleaned = sourceText.replace(/['"<>]/g, '');
67
+ // Defense-in-depth: reject null bytes and control characters (matches Ruby call-routing pattern)
68
+ if (!cleaned || cleaned.length > 2048 || /[\x00-\x1f]/.test(cleaned))
69
+ return null;
70
+ if (provider.importPathPreprocessor) {
71
+ return provider.importPathPreprocessor(cleaned, importNode);
72
+ }
73
+ return cleaned;
74
+ }
33
75
  /** Create IMPORTS edge helpers that share a resolved-count tracker. */
34
76
  function createImportEdgeHelpers(graph, importMap) {
35
77
  let totalImportsResolved = 0;
@@ -176,7 +218,8 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
176
218
  }
177
219
  continue;
178
220
  }
179
- const queryStr = LANGUAGE_QUERIES[language];
221
+ const provider = getProvider(language);
222
+ const queryStr = provider.treeSitterQueries;
180
223
  if (!queryStr)
181
224
  continue;
182
225
  // 2. ALWAYS load the language before querying (parser is stateful)
@@ -228,12 +271,12 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
228
271
  }
229
272
  return;
230
273
  }
231
- const rawImportPath = preprocessImportPath(sourceNode.text, captureMap['import'], language);
274
+ const rawImportPath = preprocessImportPath(sourceNode.text, captureMap['import'], provider);
232
275
  if (!rawImportPath)
233
276
  return;
234
277
  totalImportsFound++;
235
- const result = importResolvers[language](rawImportPath, file.path, resolveCtx);
236
- const extractor = namedBindingExtractors[language];
278
+ const result = provider.importResolver(rawImportPath, file.path, resolveCtx);
279
+ const extractor = provider.namedBindingExtractor;
237
280
  const bindings = namedImportMap && extractor ? extractor(captureMap['import']) : undefined;
238
281
  applyImportResult(result, file.path, importMap, packageMap, addImportEdge, addImportGraphEdge, bindings, namedImportMap, moduleAliasMap);
239
282
  }
@@ -241,11 +284,10 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
241
284
  if (captureMap['call']) {
242
285
  const callNameNode = captureMap['call.name'];
243
286
  if (callNameNode) {
244
- const callRouter = callRouters[language];
245
- const routed = callRouter(callNameNode.text, captureMap['call']);
287
+ const routed = provider.callRouter?.(callNameNode.text, captureMap['call']);
246
288
  if (routed && routed.kind === 'import') {
247
289
  totalImportsFound++;
248
- const result = importResolvers[language](routed.importPath, file.path, resolveCtx);
290
+ const result = provider.importResolver(routed.importPath, file.path, resolveCtx);
249
291
  applyImportResult(result, file.path, importMap, packageMap, addImportEdge, addImportGraphEdge);
250
292
  }
251
293
  }
@@ -253,6 +295,7 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
253
295
  });
254
296
  // Tree is now owned by the LRU cache — no manual delete needed
255
297
  }
298
+ wireImplicitImports(allFileList, importMap, addImportEdge, configs);
256
299
  if (skippedByLang && skippedByLang.size > 0) {
257
300
  for (const [lang, count] of skippedByLang.entries()) {
258
301
  console.warn(`[ingestion] Skipped ${count} ${lang} file(s) in import processing — ${lang} parser not available.`);
@@ -296,11 +339,13 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
296
339
  }
297
340
  for (const imp of fileImports) {
298
341
  totalImportsFound++;
299
- const result = importResolvers[imp.language](imp.rawImportPath, filePath, resolveCtx);
342
+ const provider = getProvider(imp.language);
343
+ const result = provider.importResolver(imp.rawImportPath, filePath, resolveCtx);
300
344
  applyImportResult(result, filePath, importMap, packageMap, addImportEdge, addImportGraphEdge, imp.namedBindings, namedImportMap, moduleAliasMap);
301
345
  }
302
346
  }
303
347
  onProgress?.(totalFiles, totalFiles);
348
+ wireImplicitImports(files.map(f => f.path), importMap, addImportEdge, configs);
304
349
  if (isDev) {
305
350
  console.log(`📊 Import processing (fast path): ${getResolvedCount()}/${totalImportsFound} imports resolved to graph edges`);
306
351
  }
@@ -3,20 +3,17 @@
3
3
  * Handles using-directive resolution via .csproj root namespace stripping.
4
4
  */
5
5
  import type { SuffixIndex } from './utils.js';
6
- /** C# project config parsed from .csproj files */
7
- export interface CSharpProjectConfig {
8
- /** Root namespace from <RootNamespace> or assembly name (default: project directory name) */
9
- rootNamespace: string;
10
- /** Directory containing the .csproj file */
11
- projectDir: string;
12
- }
6
+ import type { ImportResult, ResolveCtx } from './types.js';
7
+ import type { CSharpProjectConfig } from '../language-config.js';
13
8
  /**
14
- * Resolve a C# using-directive import path to matching .cs files.
9
+ * Resolve a C# using-directive import path to matching .cs files (low-level helper).
15
10
  * Tries single-file match first, then directory match for namespace imports.
16
11
  */
17
- export declare function resolveCSharpImport(importPath: string, csharpConfigs: CSharpProjectConfig[], normalizedFileList: string[], allFileList: string[], index?: SuffixIndex): string[];
12
+ export declare function resolveCSharpImportInternal(importPath: string, csharpConfigs: CSharpProjectConfig[], normalizedFileList: string[], allFileList: string[], index?: SuffixIndex): string[];
18
13
  /**
19
14
  * Compute the directory suffix for a C# namespace import (for PackageMap).
20
15
  * Returns a suffix like "/ProjectDir/Models/" or null if no config matches.
21
16
  */
22
17
  export declare function resolveCSharpNamespaceDir(importPath: string, csharpConfigs: CSharpProjectConfig[]): string | null;
18
+ /** C#: namespace-based resolution via .csproj configs, with suffix-match fallback. */
19
+ export declare function resolveCSharpImport(rawImportPath: string, filePath: string, ctx: ResolveCtx): ImportResult;
@@ -3,11 +3,13 @@
3
3
  * Handles using-directive resolution via .csproj root namespace stripping.
4
4
  */
5
5
  import { suffixResolve } from './utils.js';
6
+ import { SupportedLanguages } from '../../../config/supported-languages.js';
7
+ import { resolveStandard } from './standard.js';
6
8
  /**
7
- * Resolve a C# using-directive import path to matching .cs files.
9
+ * Resolve a C# using-directive import path to matching .cs files (low-level helper).
8
10
  * Tries single-file match first, then directory match for namespace imports.
9
11
  */
10
- export function resolveCSharpImport(importPath, csharpConfigs, normalizedFileList, allFileList, index) {
12
+ export function resolveCSharpImportInternal(importPath, csharpConfigs, normalizedFileList, allFileList, index) {
11
13
  const namespacePath = importPath.replace(/\./g, '/');
12
14
  const results = [];
13
15
  for (const config of csharpConfigs) {
@@ -107,3 +109,19 @@ export function resolveCSharpNamespaceDir(importPath, csharpConfigs) {
107
109
  }
108
110
  return null;
109
111
  }
112
+ /** C#: namespace-based resolution via .csproj configs, with suffix-match fallback. */
113
+ export function resolveCSharpImport(rawImportPath, filePath, ctx) {
114
+ const csharpConfigs = ctx.configs.csharpConfigs;
115
+ if (csharpConfigs.length > 0) {
116
+ const resolvedFiles = resolveCSharpImportInternal(rawImportPath, csharpConfigs, ctx.normalizedFileList, ctx.allFileList, ctx.index);
117
+ if (resolvedFiles.length > 1) {
118
+ const dirSuffix = resolveCSharpNamespaceDir(rawImportPath, csharpConfigs);
119
+ if (dirSuffix) {
120
+ return { kind: 'package', files: resolvedFiles, dirSuffix };
121
+ }
122
+ }
123
+ if (resolvedFiles.length > 0)
124
+ return { kind: 'files', files: resolvedFiles };
125
+ }
126
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.CSharp);
127
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Dart import resolution.
3
+ * Handles package: imports (local packages) and relative imports.
4
+ * SDK imports (dart:*) and external packages are skipped.
5
+ */
6
+ import type { ImportResult, ResolveCtx } from './types.js';
7
+ export declare function resolveDartImport(rawImportPath: string, filePath: string, ctx: ResolveCtx): ImportResult;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Dart import resolution.
3
+ * Handles package: imports (local packages) and relative imports.
4
+ * SDK imports (dart:*) and external packages are skipped.
5
+ */
6
+ import { resolveStandard } from './standard.js';
7
+ import { SupportedLanguages } from '../../../config/supported-languages.js';
8
+ export function resolveDartImport(rawImportPath, filePath, ctx) {
9
+ // Strip surrounding quotes from configurable_uri capture
10
+ const stripped = rawImportPath.replace(/^['"]|['"]$/g, '');
11
+ // Skip dart: SDK imports (dart:async, dart:io, etc.)
12
+ if (stripped.startsWith('dart:'))
13
+ return null;
14
+ // Local package: imports → resolve to lib/<path>
15
+ if (stripped.startsWith('package:')) {
16
+ const slashIdx = stripped.indexOf('/');
17
+ if (slashIdx === -1)
18
+ return null;
19
+ const relPath = stripped.slice(slashIdx + 1);
20
+ const candidates = [`lib/${relPath}`, relPath];
21
+ const files = [];
22
+ for (const candidate of candidates) {
23
+ for (const fp of ctx.allFileList) {
24
+ if (fp.endsWith('/' + candidate) || fp === candidate) {
25
+ files.push(fp);
26
+ break;
27
+ }
28
+ }
29
+ if (files.length > 0)
30
+ break;
31
+ }
32
+ if (files.length > 0)
33
+ return { kind: 'files', files };
34
+ return null;
35
+ }
36
+ // Relative imports — use standard resolution.
37
+ // Dart relative imports don't require a leading "./" (e.g. `import 'models.dart'`).
38
+ // The standard resolver only recognises paths starting with "." as relative, so
39
+ // prepend "./" when the path doesn't already start with "." to ensure correct
40
+ // same-directory resolution (without this, "models.dart" would be mangled by the
41
+ // generic dot-to-slash conversion intended for Java-style package imports).
42
+ const relPath = stripped.startsWith('.') ? stripped : './' + stripped;
43
+ return resolveStandard(relPath, filePath, ctx, SupportedLanguages.Dart);
44
+ }
@@ -2,11 +2,8 @@
2
2
  * Go package import resolution.
3
3
  * Handles Go module path-based package imports.
4
4
  */
5
- /** Go module config parsed from go.mod */
6
- export interface GoModuleConfig {
7
- /** Module path (e.g., "github.com/user/repo") */
8
- modulePath: string;
9
- }
5
+ import type { ImportResult, ResolveCtx } from './types.js';
6
+ import type { GoModuleConfig } from '../language-config.js';
10
7
  /**
11
8
  * Extract the package directory suffix from a Go import path.
12
9
  * Returns the suffix string (e.g., "/internal/auth/") or null if invalid.
@@ -17,3 +14,5 @@ export declare function resolveGoPackageDir(importPath: string, goModule: GoModu
17
14
  * Returns an array of file paths.
18
15
  */
19
16
  export declare function resolveGoPackage(importPath: string, goModule: GoModuleConfig, normalizedFileList: string[], allFileList: string[]): string[];
17
+ /** Go: package-level imports via go.mod module path. */
18
+ export declare function resolveGoImport(rawImportPath: string, filePath: string, ctx: ResolveCtx): ImportResult;
@@ -2,6 +2,8 @@
2
2
  * Go package import resolution.
3
3
  * Handles Go module path-based package imports.
4
4
  */
5
+ import { SupportedLanguages } from '../../../config/supported-languages.js';
6
+ import { resolveStandard } from './standard.js';
5
7
  /**
6
8
  * Extract the package directory suffix from a Go import path.
7
9
  * Returns the suffix string (e.g., "/internal/auth/") or null if invalid.
@@ -40,3 +42,18 @@ export function resolveGoPackage(importPath, goModule, normalizedFileList, allFi
40
42
  }
41
43
  return matches;
42
44
  }
45
+ /** Go: package-level imports via go.mod module path. */
46
+ export function resolveGoImport(rawImportPath, filePath, ctx) {
47
+ const goModule = ctx.configs.goModule;
48
+ if (goModule && rawImportPath.startsWith(goModule.modulePath)) {
49
+ const pkgSuffix = resolveGoPackageDir(rawImportPath, goModule);
50
+ if (pkgSuffix) {
51
+ const pkgFiles = resolveGoPackage(rawImportPath, goModule, ctx.normalizedFileList, ctx.allFileList);
52
+ if (pkgFiles.length > 0) {
53
+ return { kind: 'package', files: pkgFiles, dirSuffix: pkgSuffix };
54
+ }
55
+ }
56
+ // Fall through if no files found (package might be external)
57
+ }
58
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Go);
59
+ }
@@ -3,7 +3,8 @@
3
3
  * Handles wildcard imports, member/static imports, and Kotlin-specific patterns.
4
4
  */
5
5
  import type { SuffixIndex } from './utils.js';
6
- import type { SyntaxNode } from '../utils.js';
6
+ import type { SyntaxNode } from '../utils/ast-helpers.js';
7
+ import type { ImportResult, ResolveCtx } from './types.js';
7
8
  /** Kotlin file extensions for JVM resolver reuse */
8
9
  export declare const KOTLIN_EXTENSIONS: readonly string[];
9
10
  /**
@@ -22,3 +23,10 @@ export declare function resolveJvmWildcard(importPath: string, normalizedFileLis
22
23
  * Kotlin: "com.example.Constants.VALUE" -> resolve "com.example.Constants"
23
24
  */
24
25
  export declare function resolveJvmMemberImport(importPath: string, normalizedFileList: string[], allFileList: string[], extensions: readonly string[], index?: SuffixIndex): string | null;
26
+ /** Java: JVM wildcard -> member import -> standard fallthrough */
27
+ export declare function resolveJavaImport(rawImportPath: string, filePath: string, ctx: ResolveCtx): ImportResult;
28
+ /**
29
+ * Kotlin: JVM wildcard/member with Java-interop fallback -> top-level function imports -> standard.
30
+ * Kotlin can import from .kt/.kts files OR from .java files (Java interop).
31
+ */
32
+ export declare function resolveKotlinImport(rawImportPath: string, filePath: string, ctx: ResolveCtx): ImportResult;
@@ -2,6 +2,8 @@
2
2
  * JVM import resolution (Java + Kotlin).
3
3
  * Handles wildcard imports, member/static imports, and Kotlin-specific patterns.
4
4
  */
5
+ import { SupportedLanguages } from '../../../config/supported-languages.js';
6
+ import { resolveStandard } from './standard.js';
5
7
  /** Kotlin file extensions for JVM resolver reuse */
6
8
  export const KOTLIN_EXTENSIONS = ['.kt', '.kts'];
7
9
  /**
@@ -101,3 +103,57 @@ export function resolveJvmMemberImport(importPath, normalizedFileList, allFileLi
101
103
  }
102
104
  return null;
103
105
  }
106
+ /** Java: JVM wildcard -> member import -> standard fallthrough */
107
+ export function resolveJavaImport(rawImportPath, filePath, ctx) {
108
+ if (rawImportPath.endsWith('.*')) {
109
+ const matchedFiles = resolveJvmWildcard(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
110
+ if (matchedFiles.length > 0)
111
+ return { kind: 'files', files: matchedFiles };
112
+ }
113
+ else {
114
+ const memberResolved = resolveJvmMemberImport(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
115
+ if (memberResolved)
116
+ return { kind: 'files', files: [memberResolved] };
117
+ }
118
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Java);
119
+ }
120
+ /**
121
+ * Kotlin: JVM wildcard/member with Java-interop fallback -> top-level function imports -> standard.
122
+ * Kotlin can import from .kt/.kts files OR from .java files (Java interop).
123
+ */
124
+ export function resolveKotlinImport(rawImportPath, filePath, ctx) {
125
+ if (rawImportPath.endsWith('.*')) {
126
+ const matchedFiles = resolveJvmWildcard(rawImportPath, ctx.normalizedFileList, ctx.allFileList, KOTLIN_EXTENSIONS, ctx.index);
127
+ if (matchedFiles.length === 0) {
128
+ const javaMatches = resolveJvmWildcard(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
129
+ if (javaMatches.length > 0)
130
+ return { kind: 'files', files: javaMatches };
131
+ }
132
+ if (matchedFiles.length > 0)
133
+ return { kind: 'files', files: matchedFiles };
134
+ }
135
+ else {
136
+ let memberResolved = resolveJvmMemberImport(rawImportPath, ctx.normalizedFileList, ctx.allFileList, KOTLIN_EXTENSIONS, ctx.index);
137
+ if (!memberResolved) {
138
+ memberResolved = resolveJvmMemberImport(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
139
+ }
140
+ if (memberResolved)
141
+ return { kind: 'files', files: [memberResolved] };
142
+ // Kotlin: top-level function imports (e.g. import models.getUser) have only 2 segments,
143
+ // which resolveJvmMemberImport skips (requires >=3). Fall back to package-directory scan
144
+ // for lowercase last segments (function/property imports). Uppercase last segments
145
+ // (class imports like models.User) fall through to standard suffix resolution.
146
+ const segments = rawImportPath.split('.');
147
+ const lastSeg = segments[segments.length - 1];
148
+ if (segments.length >= 2 && lastSeg[0] && lastSeg[0] === lastSeg[0].toLowerCase()) {
149
+ const pkgWildcard = segments.slice(0, -1).join('.') + '.*';
150
+ let dirFiles = resolveJvmWildcard(pkgWildcard, ctx.normalizedFileList, ctx.allFileList, KOTLIN_EXTENSIONS, ctx.index);
151
+ if (dirFiles.length === 0) {
152
+ dirFiles = resolveJvmWildcard(pkgWildcard, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
153
+ }
154
+ if (dirFiles.length > 0)
155
+ return { kind: 'files', files: dirFiles };
156
+ }
157
+ }
158
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Kotlin);
159
+ }
@@ -3,16 +3,10 @@
3
3
  * Handles use-statement resolution via composer.json autoload mappings.
4
4
  */
5
5
  import type { SuffixIndex } from './utils.js';
6
- /** PHP Composer PSR-4 autoload config */
7
- export interface ComposerConfig {
8
- /** Map of namespace prefix -> directory (e.g., "App\\" -> "app/") */
9
- psr4: Map<string, string>;
10
- /** PSR-4 entries sorted by namespace length descending (longest match wins).
11
- * Cached once at config load time to avoid re-sorting on every import. */
12
- psr4Sorted?: readonly [string, string][];
13
- }
6
+ import type { ImportResult, ResolveCtx } from './types.js';
7
+ import type { ComposerConfig } from '../language-config.js';
14
8
  /**
15
- * Resolve a PHP use-statement import path using PSR-4 mappings.
9
+ * Resolve a PHP use-statement import path using PSR-4 mappings (low-level helper).
16
10
  * e.g. "App\Http\Controllers\UserController" -> "app/Http/Controllers/UserController.php"
17
11
  *
18
12
  * For function/constant imports (use function App\Models\getUser), the last
@@ -26,4 +20,6 @@ export interface ComposerConfig {
26
20
  * a known limitation — PHP function imports cannot be resolved to a specific file
27
21
  * without parsing all candidate files.
28
22
  */
29
- export declare function resolvePhpImport(importPath: string, composerConfig: ComposerConfig | null, allFiles: Set<string>, normalizedFileList: string[], allFileList: string[], index?: SuffixIndex): string | null;
23
+ export declare function resolvePhpImportInternal(importPath: string, composerConfig: ComposerConfig | null, allFiles: Set<string>, normalizedFileList: string[], allFileList: string[], index?: SuffixIndex): string | null;
24
+ /** PHP: namespace-based resolution via composer.json PSR-4. */
25
+ export declare function resolvePhpImport(rawImportPath: string, _filePath: string, ctx: ResolveCtx): ImportResult;
@@ -12,7 +12,7 @@ function getSortedPsr4(config) {
12
12
  return config.psr4Sorted;
13
13
  }
14
14
  /**
15
- * Resolve a PHP use-statement import path using PSR-4 mappings.
15
+ * Resolve a PHP use-statement import path using PSR-4 mappings (low-level helper).
16
16
  * e.g. "App\Http\Controllers\UserController" -> "app/Http/Controllers/UserController.php"
17
17
  *
18
18
  * For function/constant imports (use function App\Models\getUser), the last
@@ -26,7 +26,7 @@ function getSortedPsr4(config) {
26
26
  * a known limitation — PHP function imports cannot be resolved to a specific file
27
27
  * without parsing all candidate files.
28
28
  */
29
- export function resolvePhpImport(importPath, composerConfig, allFiles, normalizedFileList, allFileList, index) {
29
+ export function resolvePhpImportInternal(importPath, composerConfig, allFiles, normalizedFileList, allFileList, index) {
30
30
  // Normalize: replace backslashes with forward slashes
31
31
  const normalized = importPath.replace(/\\/g, '/');
32
32
  // Reject path traversal attempts (defense-in-depth — walker whitelist also prevents this)
@@ -73,3 +73,8 @@ export function resolvePhpImport(importPath, composerConfig, allFiles, normalize
73
73
  const pathParts = normalized.split('/').filter(Boolean);
74
74
  return suffixResolve(pathParts, normalizedFileList, allFileList, index);
75
75
  }
76
+ /** PHP: namespace-based resolution via composer.json PSR-4. */
77
+ export function resolvePhpImport(rawImportPath, _filePath, ctx) {
78
+ const resolved = resolvePhpImportInternal(rawImportPath, ctx.configs.composerConfig, ctx.allFilePaths, ctx.normalizedFileList, ctx.allFileList, ctx.index);
79
+ return resolved ? { kind: 'files', files: [resolved] } : null;
80
+ }
@@ -2,8 +2,9 @@
2
2
  * Python import resolution — PEP 328 relative imports and proximity-based bare imports.
3
3
  * Import system spec: PEP 302 (original), PEP 451 (current).
4
4
  */
5
+ import type { ImportResult, ResolveCtx } from './types.js';
5
6
  /**
6
- * Resolve a Python import to a file path.
7
+ * Resolve a Python import to a file path (low-level helper).
7
8
  *
8
9
  * 1. Relative (PEP 328): `.module`, `..module` — 1 dot = current package, each extra dot goes up one level.
9
10
  * 2. Proximity bare import: static heuristic — checks the importer's own directory first.
@@ -12,8 +13,13 @@
12
13
  * Checks package (__init__.py) before module (.py), matching CPython's finder order (PEP 451 §4).
13
14
  * Coexistence of both is physically impossible (same name = file vs directory), so the order
14
15
  * only matters for spec compliance.
15
- * Note: namespace packages (PEP 420, directory without __init__.py) are not handled.
16
+ * Note: implicit namespace packages (Python 3.3+, directory without __init__.py) are not handled.
16
17
  *
17
18
  * Returns null to let the caller fall through to suffixResolve.
18
19
  */
19
- export declare function resolvePythonImport(currentFile: string, importPath: string, allFiles: Set<string>): string | null;
20
+ export declare function resolvePythonImportInternal(currentFile: string, importPath: string, allFiles: Set<string>): string | null;
21
+ /**
22
+ * Python: relative imports (PEP 328) + proximity-based bare imports.
23
+ * Falls through to standard suffix resolution when proximity finds no match.
24
+ */
25
+ export declare function resolvePythonImport(rawImportPath: string, filePath: string, ctx: ResolveCtx): ImportResult;
@@ -3,8 +3,10 @@
3
3
  * Import system spec: PEP 302 (original), PEP 451 (current).
4
4
  */
5
5
  import { tryResolveWithExtensions } from './utils.js';
6
+ import { SupportedLanguages } from '../../../config/supported-languages.js';
7
+ import { resolveStandard } from './standard.js';
6
8
  /**
7
- * Resolve a Python import to a file path.
9
+ * Resolve a Python import to a file path (low-level helper).
8
10
  *
9
11
  * 1. Relative (PEP 328): `.module`, `..module` — 1 dot = current package, each extra dot goes up one level.
10
12
  * 2. Proximity bare import: static heuristic — checks the importer's own directory first.
@@ -13,11 +15,11 @@ import { tryResolveWithExtensions } from './utils.js';
13
15
  * Checks package (__init__.py) before module (.py), matching CPython's finder order (PEP 451 §4).
14
16
  * Coexistence of both is physically impossible (same name = file vs directory), so the order
15
17
  * only matters for spec compliance.
16
- * Note: namespace packages (PEP 420, directory without __init__.py) are not handled.
18
+ * Note: implicit namespace packages (Python 3.3+, directory without __init__.py) are not handled.
17
19
  *
18
20
  * Returns null to let the caller fall through to suffixResolve.
19
21
  */
20
- export function resolvePythonImport(currentFile, importPath, allFiles) {
22
+ export function resolvePythonImportInternal(currentFile, importPath, allFiles) {
21
23
  // Relative import — PEP 328 (https://peps.python.org/pep-0328/)
22
24
  if (importPath.startsWith('.')) {
23
25
  const dotMatch = importPath.match(/^(\.+)(.*)/);
@@ -48,5 +50,35 @@ export function resolvePythonImport(currentFile, importPath, allFiles) {
48
50
  return `${importerDir}/${pathLike}/__init__.py`;
49
51
  if (allFiles.has(`${importerDir}/${pathLike}.py`))
50
52
  return `${importerDir}/${pathLike}.py`;
53
+ // Ancestor directory walk — Python resolves bare imports against sys.path entries,
54
+ // which typically includes the project root and package directories. Walk up from the
55
+ // importer's directory to find the module in an ancestor, preferring the closest match.
56
+ // This prevents cross-language misresolution (e.g., Python `from middleware import X`
57
+ // resolving to a TypeScript middleware.ts via suffix matching). Issue #417.
58
+ const dirParts = importerDir.split('/');
59
+ for (let i = dirParts.length - 1; i >= 0; i--) {
60
+ const ancestorDir = dirParts.slice(0, i).join('/');
61
+ const prefix = ancestorDir ? `${ancestorDir}/` : '';
62
+ if (allFiles.has(`${prefix}${pathLike}/__init__.py`))
63
+ return `${prefix}${pathLike}/__init__.py`;
64
+ if (allFiles.has(`${prefix}${pathLike}.py`))
65
+ return `${prefix}${pathLike}.py`;
66
+ }
51
67
  return null;
52
68
  }
69
+ /**
70
+ * Python: relative imports (PEP 328) + proximity-based bare imports.
71
+ * Falls through to standard suffix resolution when proximity finds no match.
72
+ */
73
+ export function resolvePythonImport(rawImportPath, filePath, ctx) {
74
+ const resolved = resolvePythonImportInternal(filePath, rawImportPath, ctx.allFilePaths);
75
+ if (resolved) {
76
+ // Store in resolveCache so other files importing the same module skip the
77
+ // ancestor walk. The cache key matches resolveStandard's convention.
78
+ ctx.resolveCache.set(`${filePath}::${rawImportPath}`, resolved);
79
+ return { kind: 'files', files: [resolved] };
80
+ }
81
+ if (rawImportPath.startsWith('.'))
82
+ return null; // relative but unresolved -- don't suffix-match
83
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Python);
84
+ }
@@ -3,10 +3,13 @@
3
3
  * Handles path resolution for Ruby's require and require_relative calls.
4
4
  */
5
5
  import type { SuffixIndex } from './utils.js';
6
+ import type { ImportResult, ResolveCtx } from './types.js';
6
7
  /**
7
- * Resolve a Ruby require/require_relative path to a matching .rb file.
8
+ * Resolve a Ruby require/require_relative path to a matching .rb file (low-level helper).
8
9
  *
9
10
  * require_relative paths are pre-normalized to './' prefix by the caller.
10
11
  * require paths use suffix matching (gem-style paths like 'json', 'net/http').
11
12
  */
12
- export declare function resolveRubyImport(importPath: string, normalizedFileList: string[], allFileList: string[], index?: SuffixIndex): string | null;
13
+ export declare function resolveRubyImportInternal(importPath: string, normalizedFileList: string[], allFileList: string[], index?: SuffixIndex): string | null;
14
+ /** Ruby: require / require_relative. */
15
+ export declare function resolveRubyImport(rawImportPath: string, _filePath: string, ctx: ResolveCtx): ImportResult;
@@ -4,12 +4,17 @@
4
4
  */
5
5
  import { suffixResolve } from './utils.js';
6
6
  /**
7
- * Resolve a Ruby require/require_relative path to a matching .rb file.
7
+ * Resolve a Ruby require/require_relative path to a matching .rb file (low-level helper).
8
8
  *
9
9
  * require_relative paths are pre-normalized to './' prefix by the caller.
10
10
  * require paths use suffix matching (gem-style paths like 'json', 'net/http').
11
11
  */
12
- export function resolveRubyImport(importPath, normalizedFileList, allFileList, index) {
12
+ export function resolveRubyImportInternal(importPath, normalizedFileList, allFileList, index) {
13
13
  const pathParts = importPath.replace(/^\.\//, '').split('/').filter(Boolean);
14
14
  return suffixResolve(pathParts, normalizedFileList, allFileList, index);
15
15
  }
16
+ /** Ruby: require / require_relative. */
17
+ export function resolveRubyImport(rawImportPath, _filePath, ctx) {
18
+ const resolved = resolveRubyImportInternal(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ctx.index);
19
+ return resolved ? { kind: 'files', files: [resolved] } : null;
20
+ }
@@ -2,14 +2,17 @@
2
2
  * Rust module import resolution.
3
3
  * Handles crate::, super::, self:: prefix paths and :: separators.
4
4
  */
5
+ import type { ImportResult, ResolveCtx } from './types.js';
5
6
  /**
6
- * Resolve Rust use-path to a file.
7
+ * Resolve Rust use-path to a file (low-level helper).
7
8
  * Handles crate::, super::, self:: prefixes and :: path separators.
8
9
  */
9
- export declare function resolveRustImport(currentFile: string, importPath: string, allFiles: Set<string>): string | null;
10
+ export declare function resolveRustImportInternal(currentFile: string, importPath: string, allFiles: Set<string>): string | null;
10
11
  /**
11
12
  * Try to resolve a Rust module path to a file.
12
13
  * Tries: path.rs, path/mod.rs, and with the last segment stripped
13
14
  * (last segment might be a symbol name, not a module).
14
15
  */
15
16
  export declare function tryRustModulePath(modulePath: string, allFiles: Set<string>): string | null;
17
+ /** Rust: expand grouped imports: use {crate::a, crate::b} and use crate::models::{User, Repo}. */
18
+ export declare function resolveRustImport(rawImportPath: string, filePath: string, ctx: ResolveCtx): ImportResult;