types.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. * Reference Resolution Types
  3. *
  4. * Types for the reference resolution system.
  5. */
  6. import { EdgeKind, Language, Node, ReferenceKind } from '../types';
  7. /**
  8. * An unresolved reference from extraction
  9. */
  10. export interface UnresolvedRef {
  11. /** ID of the source node containing the reference */
  12. fromNodeId: string;
  13. /** The name being referenced */
  14. referenceName: string;
  15. /** Type of reference */
  16. referenceKind: ReferenceKind;
  17. /** Line where reference occurs */
  18. line: number;
  19. /** Column where reference occurs */
  20. column: number;
  21. /** File path where reference occurs */
  22. filePath: string;
  23. /** Language of the source file */
  24. language: Language;
  25. /** Possible qualified names it might resolve to */
  26. candidates?: string[];
  27. /** `unresolved_refs.id` when loaded from the database — post-pass cleanup
  28. * targets exactly this row instead of every same-key sibling (#1269). */
  29. rowId?: number;
  30. }
  31. /**
  32. * A resolved reference
  33. */
  34. export interface ResolvedRef {
  35. /** Original unresolved reference */
  36. original: UnresolvedRef;
  37. /** ID of the target node */
  38. targetNodeId: string;
  39. /** Confidence score (0-1) */
  40. confidence: number;
  41. /** How it was resolved */
  42. resolvedBy: 'exact-match' | 'import' | 'qualified-name' | 'framework' | 'fuzzy' | 'instance-method' | 'file-path' | 'function-ref';
  43. /**
  44. * Edge kind the edge should carry when it is NOT the ref's own kind — a
  45. * framework that turns a `calls` ref into a `navigates` edge, for example.
  46. * The original kind is still recorded on the edge as `metadata.refKind`, so
  47. * re-resolution after a target is removed reconstructs the ref faithfully.
  48. */
  49. edgeKind?: EdgeKind;
  50. /** Extra metadata the strategy wants persisted on the edge (`href`, …). */
  51. metadata?: Record<string, unknown>;
  52. /**
  53. * The OTHER targets, when one reference names several.
  54. *
  55. * A navigation whose destination is a conditional reaches every arm —
  56. * `!isAdmin ? keyword ? '/search/…' : '/page/…' : '/admin/…'` is one call
  57. * and three screens — and drawing only the first would hide two places the
  58. * code goes. `createEdges` fans these out into an edge apiece, sharing this
  59. * resolution's kind and confidence; each carries its own metadata.
  60. *
  61. * The reference itself still resolves ONCE, so the resolution pipeline's
  62. * bookkeeping — cleanup by row id, counts, re-resolution — is unchanged.
  63. */
  64. alsoTargets?: { targetNodeId: string; metadata?: Record<string, unknown> }[];
  65. }
  66. /**
  67. * Result of resolution attempt
  68. */
  69. export interface ResolutionResult {
  70. /** Successfully resolved references */
  71. resolved: ResolvedRef[];
  72. /** References that couldn't be resolved */
  73. unresolved: UnresolvedRef[];
  74. /** Statistics */
  75. stats: {
  76. total: number;
  77. resolved: number;
  78. unresolved: number;
  79. byMethod: Record<string, number>;
  80. };
  81. }
  82. /**
  83. * Context for resolution - provides access to the graph
  84. */
  85. export interface ResolutionContext {
  86. /** Get all nodes in a file */
  87. getNodesInFile(filePath: string): Node[];
  88. /** Get all nodes by name */
  89. getNodesByName(name: string): Node[];
  90. /** Get all nodes by qualified name */
  91. getNodesByQualifiedName(qualifiedName: string): Node[];
  92. /** Get all nodes of a kind */
  93. getNodesByKind(kind: Node['kind']): Node[];
  94. /**
  95. * Stream nodes of a kind one at a time instead of materializing (and, unlike
  96. * `getNodesByKind`, without populating the resolver's per-kind array cache).
  97. * For unbounded kinds (`function`, `method`, `struct`) on a symbol-dense
  98. * project the full array is gigabytes — the dynamic-edge synthesizers must
  99. * use this so their memory stays O(1) in node count (#610, #1212). Optional
  100. * so minimal test contexts compile; callers fall back to getNodesByKind.
  101. */
  102. iterateNodesByKind?(kind: Node['kind']): IterableIterator<Node>;
  103. /** Check if a file exists */
  104. fileExists(filePath: string): boolean;
  105. /** Read file content */
  106. readFile(filePath: string): string | null;
  107. /**
  108. * `readFile(filePath)` split into lines, LRU-cached per file. Receiver-type
  109. * inference scans source lines for EVERY `receiver.method()` ref; splitting
  110. * the whole file per ref made that O(refs-in-file × file-size) — ~20% of
  111. * total index CPU on a Java-heavy repo and a driver of the #1122 watchdog
  112. * kill on large ones. Optional so external/test contexts compile without it;
  113. * callers fall back to splitting `readFile` themselves.
  114. */
  115. getFileLines?(filePath: string): string[] | null;
  116. /**
  117. * The method-definition nodes matching `typeName::methodName` in `language` —
  118. * exactly `resolveMethodOnType`'s kind/language/qualifiedName-suffix filter,
  119. * LRU-cached per (language, type, method). The uncached path re-fetches every
  120. * node sharing the METHOD name (unbounded — tens of thousands on a collision-
  121. * heavy Java repo) and re-scans it per ref, the dominant term in the #1122
  122. * watchdog kill. Cached entries hold only the small filtered result; per-ref
  123. * disambiguation (import FQN, call-site file) stays in the caller so a cached
  124. * entry is valid from any call site. Optional for external/test contexts.
  125. */
  126. getMethodMatches?(typeName: string, methodName: string, language: Language): Node[];
  127. /** Get project root */
  128. getProjectRoot(): string;
  129. /** Get all files */
  130. getAllFiles(): string[];
  131. /** Get nodes by lowercase name (O(1) lookup for fuzzy matching) */
  132. getNodesByLowerName(lowerName: string): Node[];
  133. /**
  134. * Direct supertypes of the type named `typeName` (same language): the classes
  135. * it extends and the interfaces / protocols / traits it implements/conforms to,
  136. * by simple name. Backed by the resolved `implements`/`extends` edges, so it is
  137. * EMPTY during the first resolution pass (edges aren't built yet) and populated
  138. * afterward — the conformance pass uses it to resolve a chained method defined
  139. * on a supertype the receiver type conforms to (e.g. a protocol-extension
  140. * method). Optional so external/test contexts compile without it.
  141. */
  142. getSupertypes?(typeName: string, language: Language): string[];
  143. /**
  144. * Look up a node by its id. Lets matchers derive the FROM-symbol's
  145. * enclosing-class scope (Swift implicit-self method scoping, `this.X`
  146. * member resolution). Optional so external/test contexts compile
  147. * without it.
  148. */
  149. getNodeById?(id: string): Node | null;
  150. /** Get cached import mappings for a file */
  151. getImportMappings(filePath: string, language: Language): ImportMapping[];
  152. /**
  153. * Project import-path aliases (tsconfig/jsconfig `paths`). Returns
  154. * `null` when the project doesn't define any. Cached per resolver
  155. * instance — safe to call from any resolver code path. Optional so
  156. * existing test fixtures and external context implementations
  157. * compile without modification; production resolver implements it.
  158. */
  159. getProjectAliases?(): import('./path-aliases').AliasMap | null;
  160. /**
  161. * Go module info from `go.mod` at the project root. Returns `null`
  162. * when the project has no `go.mod` (non-Go projects, pre-modules
  163. * Go code, or projects whose modules live in subdirectories). Used
  164. * by the Go branch of import resolution to distinguish in-module
  165. * cross-package imports from third-party packages.
  166. */
  167. getGoModule?(): import('./go-module').GoModule | null;
  168. /**
  169. * Monorepo workspace member packages, keyed by declared package name.
  170. * Returns `null` for single-package repos (no `workspaces` field).
  171. * Lets the resolver treat `@scope/ui/sub` as a local import into the
  172. * member's directory instead of an external npm package (#629).
  173. */
  174. getWorkspacePackages?(): import('./workspace-packages').WorkspacePackages | null;
  175. /**
  176. * Re-exports declared by a file (`export { x } from './other'`,
  177. * `export * from './other'`). Empty array when the file has none.
  178. * Optional so older callers compile; the import resolver follows
  179. * re-export chains when this is provided.
  180. */
  181. getReExports?(filePath: string, language: Language): ReExport[];
  182. /**
  183. * List immediate subdirectories of `relativePath` (relative to the
  184. * project root). Returns an empty array when the path doesn't exist
  185. * or isn't a directory. Used by framework resolvers that need to
  186. * walk build-system metadata (e.g. Cargo workspace globs). Optional
  187. * so external context implementations and test fixtures compile
  188. * without modification.
  189. */
  190. listDirectories?(relativePath: string): string[];
  191. /**
  192. * C/C++ include search directories (relative to project root),
  193. * extracted from compile_commands.json or discovered by heuristic.
  194. * Used by resolveCppIncludePath to search -I directories when
  195. * relative resolution fails. Optional so existing callers compile.
  196. */
  197. getCppIncludeDirs?(): string[];
  198. }
  199. /**
  200. * Result of framework-specific file extraction.
  201. */
  202. export interface FrameworkExtractionResult {
  203. /** Framework-specific nodes (e.g. routes) */
  204. nodes: Node[];
  205. /** Framework-specific unresolved references (e.g. route -> handler) */
  206. references: UnresolvedRef[];
  207. }
  208. /**
  209. * Framework-specific resolver
  210. */
  211. export interface FrameworkResolver {
  212. /** Framework name */
  213. name: string;
  214. /** Languages this framework applies to. If omitted, applies to all languages. */
  215. languages?: Language[];
  216. /** Detect if project uses this framework (project-level, called once at startup) */
  217. detect(context: ResolutionContext): boolean;
  218. /** Resolve a reference using framework-specific patterns */
  219. resolve(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null;
  220. /**
  221. * Opt a reference NAME through the resolver's name-exists pre-filter, even when
  222. * no node is named that. Needed for dynamic dispatch where the call target is
  223. * an attribute/descriptor, not a declared symbol (e.g. Django's
  224. * `self._iterable_class(...)`, React effect callbacks). Returning true lets the
  225. * ref reach `resolve()` instead of being dropped for having no name match.
  226. */
  227. claimsReference?(name: string): boolean;
  228. /**
  229. * Extract framework-specific nodes and references from a file.
  230. *
  231. * Returns route nodes, middleware nodes, etc., plus unresolved references
  232. * that link those nodes to handlers (view classes, controller methods,
  233. * included modules). Unresolved references flow into the normal resolution
  234. * pipeline; the framework's own `resolve()` is one of the strategies tried.
  235. */
  236. extract?(filePath: string, content: string): FrameworkExtractionResult;
  237. /**
  238. * Cross-file finalization pass, called once after all per-file extraction
  239. * completes (and again on every incremental sync). Used by frameworks where
  240. * a symbol's final representation depends on a sibling file the per-file
  241. * `extract()` never saw — e.g. NestJS's `RouterModule.register([...])`
  242. * sets route prefixes for controllers declared elsewhere.
  243. *
  244. * Implementations return route/etc. nodes with mutated fields (typically
  245. * `name`); the orchestrator persists each via `updateNode`. The node `id`
  246. * MUST be preserved so existing edges (route → handler, etc.) stay intact;
  247. * `qualifiedName` SHOULD be preserved so the pass stays idempotent — a
  248. * second run can recover the original in-file form from `qualifiedName`.
  249. */
  250. postExtract?(context: ResolutionContext): Node[];
  251. }
  252. /**
  253. * Import mapping from a file
  254. */
  255. export interface ImportMapping {
  256. /** Local name used in the file */
  257. localName: string;
  258. /** Original exported name (may differ due to aliasing) */
  259. exportedName: string;
  260. /** Source module/path */
  261. source: string;
  262. /** Whether it's a default import */
  263. isDefault: boolean;
  264. /** Whether it's a namespace import (import * as X) */
  265. isNamespace: boolean;
  266. /** Resolved file path (if local) */
  267. resolvedPath?: string;
  268. }
  269. /**
  270. * Re-export from a file: `export { x } from './other'` or
  271. * `export * from './other'`. Used by the resolver to chase
  272. * symbols through barrel files.
  273. */
  274. export type ReExport =
  275. | {
  276. kind: 'named';
  277. /** Name as exported by THIS file. */
  278. exportedName: string;
  279. /** Name in the upstream module (differs when renamed: `as`). */
  280. originalName: string;
  281. /** Module specifier of the upstream module. */
  282. source: string;
  283. }
  284. | {
  285. kind: 'wildcard';
  286. /** Module specifier of the upstream module. */
  287. source: string;
  288. };