@colbymchenry/codegraph 0.6.6 → 0.7.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 (221) hide show
  1. package/README.md +180 -502
  2. package/dist/bin/codegraph.d.ts +0 -5
  3. package/dist/bin/codegraph.d.ts.map +1 -1
  4. package/dist/bin/codegraph.js +217 -263
  5. package/dist/bin/codegraph.js.map +1 -1
  6. package/dist/bin/uninstall.d.ts +0 -1
  7. package/dist/bin/uninstall.d.ts.map +1 -1
  8. package/dist/bin/uninstall.js +3 -29
  9. package/dist/bin/uninstall.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +0 -3
  12. package/dist/config.js.map +1 -1
  13. package/dist/context/index.d.ts +3 -5
  14. package/dist/context/index.d.ts.map +1 -1
  15. package/dist/context/index.js +497 -46
  16. package/dist/context/index.js.map +1 -1
  17. package/dist/db/migrations.d.ts +1 -1
  18. package/dist/db/migrations.d.ts.map +1 -1
  19. package/dist/db/migrations.js +10 -1
  20. package/dist/db/migrations.js.map +1 -1
  21. package/dist/db/queries.d.ts +53 -0
  22. package/dist/db/queries.d.ts.map +1 -1
  23. package/dist/db/queries.js +244 -24
  24. package/dist/db/queries.js.map +1 -1
  25. package/dist/db/schema.sql +1 -16
  26. package/dist/errors.d.ts +1 -1
  27. package/dist/errors.d.ts.map +1 -1
  28. package/dist/errors.js +1 -7
  29. package/dist/errors.js.map +1 -1
  30. package/dist/extraction/dfm-extractor.d.ts +31 -0
  31. package/dist/extraction/dfm-extractor.d.ts.map +1 -0
  32. package/dist/extraction/dfm-extractor.js +151 -0
  33. package/dist/extraction/dfm-extractor.js.map +1 -0
  34. package/dist/extraction/grammars.d.ts +9 -1
  35. package/dist/extraction/grammars.d.ts.map +1 -1
  36. package/dist/extraction/grammars.js +34 -2
  37. package/dist/extraction/grammars.js.map +1 -1
  38. package/dist/extraction/index.d.ts +7 -1
  39. package/dist/extraction/index.d.ts.map +1 -1
  40. package/dist/extraction/index.js +373 -29
  41. package/dist/extraction/index.js.map +1 -1
  42. package/dist/extraction/languages/c-cpp.d.ts +4 -0
  43. package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
  44. package/dist/extraction/languages/c-cpp.js +126 -0
  45. package/dist/extraction/languages/c-cpp.js.map +1 -0
  46. package/dist/extraction/languages/csharp.d.ts +3 -0
  47. package/dist/extraction/languages/csharp.d.ts.map +1 -0
  48. package/dist/extraction/languages/csharp.js +72 -0
  49. package/dist/extraction/languages/csharp.js.map +1 -0
  50. package/dist/extraction/languages/dart.d.ts +3 -0
  51. package/dist/extraction/languages/dart.d.ts.map +1 -0
  52. package/dist/extraction/languages/dart.js +192 -0
  53. package/dist/extraction/languages/dart.js.map +1 -0
  54. package/dist/extraction/languages/go.d.ts +3 -0
  55. package/dist/extraction/languages/go.d.ts.map +1 -0
  56. package/dist/extraction/languages/go.js +58 -0
  57. package/dist/extraction/languages/go.js.map +1 -0
  58. package/dist/extraction/languages/index.d.ts +10 -0
  59. package/dist/extraction/languages/index.d.ts.map +1 -0
  60. package/dist/extraction/languages/index.js +43 -0
  61. package/dist/extraction/languages/index.js.map +1 -0
  62. package/dist/extraction/languages/java.d.ts +3 -0
  63. package/dist/extraction/languages/java.d.ts.map +1 -0
  64. package/dist/extraction/languages/java.js +64 -0
  65. package/dist/extraction/languages/java.js.map +1 -0
  66. package/dist/extraction/languages/javascript.d.ts +3 -0
  67. package/dist/extraction/languages/javascript.d.ts.map +1 -0
  68. package/dist/extraction/languages/javascript.js +90 -0
  69. package/dist/extraction/languages/javascript.js.map +1 -0
  70. package/dist/extraction/languages/kotlin.d.ts +3 -0
  71. package/dist/extraction/languages/kotlin.d.ts.map +1 -0
  72. package/dist/extraction/languages/kotlin.js +253 -0
  73. package/dist/extraction/languages/kotlin.js.map +1 -0
  74. package/dist/extraction/languages/pascal.d.ts +3 -0
  75. package/dist/extraction/languages/pascal.d.ts.map +1 -0
  76. package/dist/extraction/languages/pascal.js +66 -0
  77. package/dist/extraction/languages/pascal.js.map +1 -0
  78. package/dist/extraction/languages/php.d.ts +3 -0
  79. package/dist/extraction/languages/php.d.ts.map +1 -0
  80. package/dist/extraction/languages/php.js +107 -0
  81. package/dist/extraction/languages/php.js.map +1 -0
  82. package/dist/extraction/languages/python.d.ts +3 -0
  83. package/dist/extraction/languages/python.d.ts.map +1 -0
  84. package/dist/extraction/languages/python.js +56 -0
  85. package/dist/extraction/languages/python.js.map +1 -0
  86. package/dist/extraction/languages/ruby.d.ts +3 -0
  87. package/dist/extraction/languages/ruby.d.ts.map +1 -0
  88. package/dist/extraction/languages/ruby.js +114 -0
  89. package/dist/extraction/languages/ruby.js.map +1 -0
  90. package/dist/extraction/languages/rust.d.ts +3 -0
  91. package/dist/extraction/languages/rust.d.ts.map +1 -0
  92. package/dist/extraction/languages/rust.js +109 -0
  93. package/dist/extraction/languages/rust.js.map +1 -0
  94. package/dist/extraction/languages/swift.d.ts +3 -0
  95. package/dist/extraction/languages/swift.d.ts.map +1 -0
  96. package/dist/extraction/languages/swift.js +91 -0
  97. package/dist/extraction/languages/swift.js.map +1 -0
  98. package/dist/extraction/languages/typescript.d.ts +3 -0
  99. package/dist/extraction/languages/typescript.d.ts.map +1 -0
  100. package/dist/extraction/languages/typescript.js +129 -0
  101. package/dist/extraction/languages/typescript.js.map +1 -0
  102. package/dist/extraction/liquid-extractor.d.ts +52 -0
  103. package/dist/extraction/liquid-extractor.d.ts.map +1 -0
  104. package/dist/extraction/liquid-extractor.js +313 -0
  105. package/dist/extraction/liquid-extractor.js.map +1 -0
  106. package/dist/extraction/parse-worker.d.ts +8 -0
  107. package/dist/extraction/parse-worker.d.ts.map +1 -0
  108. package/dist/extraction/parse-worker.js +57 -0
  109. package/dist/extraction/parse-worker.js.map +1 -0
  110. package/dist/extraction/svelte-extractor.d.ts +47 -0
  111. package/dist/extraction/svelte-extractor.d.ts.map +1 -0
  112. package/dist/extraction/svelte-extractor.js +230 -0
  113. package/dist/extraction/svelte-extractor.js.map +1 -0
  114. package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
  115. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
  116. package/dist/extraction/tree-sitter-helpers.js +103 -0
  117. package/dist/extraction/tree-sitter-helpers.js.map +1 -0
  118. package/dist/extraction/tree-sitter-types.d.ts +179 -0
  119. package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
  120. package/dist/extraction/tree-sitter-types.js +10 -0
  121. package/dist/extraction/tree-sitter-types.js.map +1 -0
  122. package/dist/extraction/tree-sitter.d.ts +67 -125
  123. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  124. package/dist/extraction/tree-sitter.js +1052 -1860
  125. package/dist/extraction/tree-sitter.js.map +1 -1
  126. package/dist/graph/traversal.d.ts.map +1 -1
  127. package/dist/graph/traversal.js +20 -2
  128. package/dist/graph/traversal.js.map +1 -1
  129. package/dist/index.d.ts +29 -53
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +88 -117
  132. package/dist/index.js.map +1 -1
  133. package/dist/installer/claude-md-template.d.ts +1 -1
  134. package/dist/installer/claude-md-template.d.ts.map +1 -1
  135. package/dist/installer/claude-md-template.js +15 -15
  136. package/dist/installer/config-writer.d.ts +2 -13
  137. package/dist/installer/config-writer.d.ts.map +1 -1
  138. package/dist/installer/config-writer.js +4 -87
  139. package/dist/installer/config-writer.js.map +1 -1
  140. package/dist/installer/index.d.ts +3 -4
  141. package/dist/installer/index.d.ts.map +1 -1
  142. package/dist/installer/index.js +118 -127
  143. package/dist/installer/index.js.map +1 -1
  144. package/dist/mcp/index.d.ts +5 -0
  145. package/dist/mcp/index.d.ts.map +1 -1
  146. package/dist/mcp/index.js +25 -4
  147. package/dist/mcp/index.js.map +1 -1
  148. package/dist/mcp/tools.d.ts +33 -0
  149. package/dist/mcp/tools.d.ts.map +1 -1
  150. package/dist/mcp/tools.js +405 -26
  151. package/dist/mcp/tools.js.map +1 -1
  152. package/dist/mcp/transport.d.ts.map +1 -1
  153. package/dist/mcp/transport.js +0 -2
  154. package/dist/mcp/transport.js.map +1 -1
  155. package/dist/resolution/frameworks/csharp.js +29 -84
  156. package/dist/resolution/frameworks/csharp.js.map +1 -1
  157. package/dist/resolution/frameworks/express.js +44 -48
  158. package/dist/resolution/frameworks/express.js.map +1 -1
  159. package/dist/resolution/frameworks/go.js +34 -70
  160. package/dist/resolution/frameworks/go.js.map +1 -1
  161. package/dist/resolution/frameworks/java.js +29 -87
  162. package/dist/resolution/frameworks/java.js.map +1 -1
  163. package/dist/resolution/frameworks/laravel.js +6 -6
  164. package/dist/resolution/frameworks/laravel.js.map +1 -1
  165. package/dist/resolution/frameworks/python.js +33 -98
  166. package/dist/resolution/frameworks/python.js.map +1 -1
  167. package/dist/resolution/frameworks/react.js +53 -76
  168. package/dist/resolution/frameworks/react.js.map +1 -1
  169. package/dist/resolution/frameworks/ruby.js +12 -24
  170. package/dist/resolution/frameworks/ruby.js.map +1 -1
  171. package/dist/resolution/frameworks/rust.js +26 -66
  172. package/dist/resolution/frameworks/rust.js.map +1 -1
  173. package/dist/resolution/frameworks/svelte.js +11 -31
  174. package/dist/resolution/frameworks/svelte.js.map +1 -1
  175. package/dist/resolution/frameworks/swift.js +42 -160
  176. package/dist/resolution/frameworks/swift.js.map +1 -1
  177. package/dist/resolution/index.d.ts +19 -6
  178. package/dist/resolution/index.d.ts.map +1 -1
  179. package/dist/resolution/index.js +300 -144
  180. package/dist/resolution/index.js.map +1 -1
  181. package/dist/resolution/name-matcher.d.ts +5 -0
  182. package/dist/resolution/name-matcher.d.ts.map +1 -1
  183. package/dist/resolution/name-matcher.js +148 -8
  184. package/dist/resolution/name-matcher.js.map +1 -1
  185. package/dist/resolution/types.d.ts +1 -1
  186. package/dist/resolution/types.d.ts.map +1 -1
  187. package/dist/search/query-utils.d.ts +26 -1
  188. package/dist/search/query-utils.d.ts.map +1 -1
  189. package/dist/search/query-utils.js +209 -9
  190. package/dist/search/query-utils.js.map +1 -1
  191. package/dist/sync/index.d.ts +2 -4
  192. package/dist/sync/index.d.ts.map +1 -1
  193. package/dist/sync/index.js +4 -3
  194. package/dist/sync/index.js.map +1 -1
  195. package/dist/sync/watcher.d.ts +81 -0
  196. package/dist/sync/watcher.d.ts.map +1 -0
  197. package/dist/sync/watcher.js +184 -0
  198. package/dist/sync/watcher.js.map +1 -0
  199. package/dist/types.d.ts +2 -2
  200. package/dist/types.d.ts.map +1 -1
  201. package/dist/types.js +0 -1
  202. package/dist/types.js.map +1 -1
  203. package/dist/ui/shimmer-progress.d.ts +11 -0
  204. package/dist/ui/shimmer-progress.d.ts.map +1 -0
  205. package/dist/ui/shimmer-progress.js +90 -0
  206. package/dist/ui/shimmer-progress.js.map +1 -0
  207. package/dist/ui/shimmer-worker.d.ts +2 -0
  208. package/dist/ui/shimmer-worker.d.ts.map +1 -0
  209. package/dist/ui/shimmer-worker.js +112 -0
  210. package/dist/ui/shimmer-worker.js.map +1 -0
  211. package/dist/ui/types.d.ts +17 -0
  212. package/dist/ui/types.d.ts.map +1 -0
  213. package/dist/ui/types.js +3 -0
  214. package/dist/ui/types.js.map +1 -0
  215. package/dist/vectors/embedder.js +1 -1
  216. package/dist/vectors/embedder.js.map +1 -1
  217. package/dist/visualizer/server.d.ts.map +1 -1
  218. package/dist/visualizer/server.js +3 -11
  219. package/dist/visualizer/server.js.map +1 -1
  220. package/package.json +7 -12
  221. package/scripts/postinstall.js +0 -68
@@ -45,13 +45,88 @@ exports.ReferenceResolver = void 0;
45
45
  exports.createResolver = createResolver;
46
46
  const fs = __importStar(require("fs"));
47
47
  const path = __importStar(require("path"));
48
- const sentry_1 = require("../sentry");
49
48
  const name_matcher_1 = require("./name-matcher");
50
49
  const import_resolver_1 = require("./import-resolver");
51
50
  const frameworks_1 = require("./frameworks");
52
51
  const errors_1 = require("../errors");
53
52
  // Re-export types
54
53
  __exportStar(require("./types"), exports);
54
+ // Pre-built Sets for O(1) built-in lookups (allocated once, shared across all instances)
55
+ const JS_BUILT_INS = new Set([
56
+ 'console', 'window', 'document', 'global', 'process',
57
+ 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean',
58
+ 'Date', 'Math', 'JSON', 'RegExp', 'Error', 'Map', 'Set',
59
+ 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
60
+ 'fetch', 'require', 'module', 'exports', '__dirname', '__filename',
61
+ ]);
62
+ const REACT_HOOKS = new Set([
63
+ 'useState', 'useEffect', 'useContext', 'useReducer', 'useCallback',
64
+ 'useMemo', 'useRef', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
65
+ ]);
66
+ const PYTHON_BUILT_INS = new Set([
67
+ 'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
68
+ 'open', 'input', 'type', 'isinstance', 'hasattr', 'getattr', 'setattr',
69
+ 'super', 'self', 'cls', 'None', 'True', 'False',
70
+ ]);
71
+ const PYTHON_BUILT_IN_TYPES = new Set([
72
+ 'list', 'dict', 'set', 'tuple', 'str', 'int', 'float', 'bool',
73
+ 'bytes', 'bytearray', 'frozenset', 'object', 'super',
74
+ ]);
75
+ const PYTHON_BUILT_IN_METHODS = new Set([
76
+ 'append', 'extend', 'insert', 'remove', 'pop', 'clear', 'sort', 'reverse', 'copy',
77
+ 'update', 'keys', 'values', 'items', 'get',
78
+ 'add', 'discard', 'union', 'intersection', 'difference',
79
+ 'split', 'join', 'strip', 'lstrip', 'rstrip', 'replace', 'lower', 'upper',
80
+ 'startswith', 'endswith', 'find', 'index', 'count', 'encode', 'decode',
81
+ 'format', 'isdigit', 'isalpha', 'isalnum',
82
+ 'read', 'write', 'readline', 'readlines', 'close', 'flush', 'seek',
83
+ ]);
84
+ const GO_STDLIB_PACKAGES = new Set([
85
+ 'fmt', 'os', 'io', 'net', 'http', 'log', 'math', 'sort', 'sync',
86
+ 'time', 'path', 'bytes', 'strings', 'strconv', 'errors', 'context',
87
+ 'json', 'xml', 'csv', 'html', 'template', 'regexp', 'reflect',
88
+ 'runtime', 'testing', 'flag', 'bufio', 'crypto', 'encoding',
89
+ 'filepath', 'hash', 'mime', 'rand', 'signal', 'sql', 'syscall',
90
+ 'unicode', 'unsafe', 'atomic', 'binary', 'debug', 'exec', 'heap',
91
+ 'ring', 'scanner', 'tar', 'zip', 'gzip', 'zlib', 'tls', 'url',
92
+ 'user', 'pprof', 'trace', 'ast', 'build', 'parser', 'printer',
93
+ 'token', 'types', 'cgo', 'plugin', 'race', 'ioutil',
94
+ // Kubernetes-common stdlib aliases
95
+ 'utilruntime', 'utilwait', 'utilnet',
96
+ ]);
97
+ const GO_BUILT_INS = new Set([
98
+ 'make', 'new', 'len', 'cap', 'append', 'copy', 'delete', 'close',
99
+ 'panic', 'recover', 'print', 'println', 'complex', 'real', 'imag',
100
+ 'error', 'nil', 'true', 'false', 'iota',
101
+ 'int', 'int8', 'int16', 'int32', 'int64',
102
+ 'uint', 'uint8', 'uint16', 'uint32', 'uint64', 'uintptr',
103
+ 'float32', 'float64', 'complex64', 'complex128',
104
+ 'string', 'bool', 'byte', 'rune', 'any',
105
+ ]);
106
+ const PASCAL_UNIT_PREFIXES = [
107
+ 'System.', 'Winapi.', 'Vcl.', 'Fmx.', 'Data.', 'Datasnap.',
108
+ 'Soap.', 'Xml.', 'Web.', 'REST.', 'FireDAC.', 'IBX.',
109
+ 'IdHTTP', 'IdTCP', 'IdSSL',
110
+ ];
111
+ const PASCAL_BUILT_INS = new Set([
112
+ 'System', 'SysUtils', 'Classes', 'Types', 'Variants', 'StrUtils',
113
+ 'Math', 'DateUtils', 'IOUtils', 'Generics.Collections', 'Generics.Defaults',
114
+ 'Rtti', 'TypInfo', 'SyncObjs', 'RegularExpressions',
115
+ 'SysInit', 'Windows', 'Messages', 'Graphics', 'Controls', 'Forms',
116
+ 'Dialogs', 'StdCtrls', 'ExtCtrls', 'ComCtrls', 'Menus', 'ActnList',
117
+ 'WriteLn', 'Write', 'ReadLn', 'Read', 'Inc', 'Dec', 'Ord', 'Chr',
118
+ 'Length', 'SetLength', 'High', 'Low', 'Assigned', 'FreeAndNil',
119
+ 'Format', 'IntToStr', 'StrToInt', 'FloatToStr', 'StrToFloat',
120
+ 'Trim', 'UpperCase', 'LowerCase', 'Pos', 'Copy', 'Delete', 'Insert',
121
+ 'Now', 'Date', 'Time', 'DateToStr', 'StrToDate',
122
+ 'Raise', 'Exit', 'Break', 'Continue', 'Abort',
123
+ 'True', 'False', 'nil', 'Self', 'Result',
124
+ 'Create', 'Destroy', 'Free',
125
+ 'TObject', 'TComponent', 'TPersistent', 'TInterfacedObject',
126
+ 'TList', 'TStringList', 'TStrings', 'TStream', 'TMemoryStream', 'TFileStream',
127
+ 'Exception', 'EAbort', 'EConvertError', 'EAccessViolation',
128
+ 'IInterface', 'IUnknown',
129
+ ]);
55
130
  /**
56
131
  * Reference Resolver
57
132
  *
@@ -62,14 +137,13 @@ class ReferenceResolver {
62
137
  queries;
63
138
  context;
64
139
  frameworks = [];
65
- nodeCache = new Map();
66
- fileCache = new Map();
67
- nameCache = new Map();
68
- qualifiedNameCache = new Map();
69
- kindCache = new Map();
70
- nodeByIdCache = new Map();
71
- lowerNameCache = new Map();
140
+ nodeCache = new Map(); // per-file node cache (bounded)
141
+ fileCache = new Map(); // per-file content cache (bounded)
72
142
  importMappingCache = new Map();
143
+ nameCache = new Map(); // name → nodes cache
144
+ lowerNameCache = new Map(); // lower(name) → nodes cache
145
+ qualifiedNameCache = new Map(); // qualified_name → nodes cache
146
+ knownNames = null; // all known symbol names for fast pre-filtering
73
147
  knownFiles = null;
74
148
  cachesWarmed = false;
75
149
  constructor(projectRoot, queries) {
@@ -85,52 +159,18 @@ class ReferenceResolver {
85
159
  this.clearCaches();
86
160
  }
87
161
  /**
88
- * Pre-load all nodes into memory maps for fast lookup during resolution.
89
- * This eliminates repeated SQLite queries and provides the core speedup.
162
+ * Pre-build lightweight caches for resolution.
163
+ * Node lookups are now handled by indexed SQLite queries instead of
164
+ * loading all nodes into memory (which caused OOM on large codebases).
165
+ * We cache the set of known symbol names for fast pre-filtering.
90
166
  */
91
167
  warmCaches() {
92
168
  if (this.cachesWarmed)
93
169
  return;
94
- const allNodes = this.queries.getAllNodes();
95
- for (const node of allNodes) {
96
- // Index by name
97
- const byName = this.nameCache.get(node.name);
98
- if (byName) {
99
- byName.push(node);
100
- }
101
- else {
102
- this.nameCache.set(node.name, [node]);
103
- }
104
- // Index by qualified name
105
- const byQName = this.qualifiedNameCache.get(node.qualifiedName);
106
- if (byQName) {
107
- byQName.push(node);
108
- }
109
- else {
110
- this.qualifiedNameCache.set(node.qualifiedName, [node]);
111
- }
112
- // Index by kind
113
- const byKind = this.kindCache.get(node.kind);
114
- if (byKind) {
115
- byKind.push(node);
116
- }
117
- else {
118
- this.kindCache.set(node.kind, [node]);
119
- }
120
- // Index by ID
121
- this.nodeByIdCache.set(node.id, node);
122
- // Index by lowercase name (for fuzzy matching)
123
- const lowerName = node.name.toLowerCase();
124
- const byLower = this.lowerNameCache.get(lowerName);
125
- if (byLower) {
126
- byLower.push(node);
127
- }
128
- else {
129
- this.lowerNameCache.set(lowerName, [node]);
130
- }
131
- }
132
- // Pre-build known files set from index
133
- this.knownFiles = new Set(this.queries.getAllFiles().map((f) => f.path));
170
+ // Only cache the set of known file paths (lightweight string set)
171
+ this.knownFiles = new Set(this.queries.getAllFilePaths());
172
+ // Cache all distinct symbol names for fast pre-filtering (just strings, not full nodes)
173
+ this.knownNames = new Set(this.queries.getAllNodeNames());
134
174
  this.cachesWarmed = true;
135
175
  }
136
176
  /**
@@ -139,12 +179,11 @@ class ReferenceResolver {
139
179
  clearCaches() {
140
180
  this.nodeCache.clear();
141
181
  this.fileCache.clear();
182
+ this.importMappingCache.clear();
142
183
  this.nameCache.clear();
143
- this.qualifiedNameCache.clear();
144
- this.kindCache.clear();
145
- this.nodeByIdCache.clear();
146
184
  this.lowerNameCache.clear();
147
- this.importMappingCache.clear();
185
+ this.qualifiedNameCache.clear();
186
+ this.knownNames = null;
148
187
  this.knownFiles = null;
149
188
  this.cachesWarmed = false;
150
189
  }
@@ -160,26 +199,22 @@ class ReferenceResolver {
160
199
  return this.nodeCache.get(filePath);
161
200
  },
162
201
  getNodesByName: (name) => {
163
- // Use warm cache if available, otherwise fall back to search
164
- if (this.cachesWarmed) {
165
- return this.nameCache.get(name) ?? [];
166
- }
167
- return this.queries.searchNodes(name, { limit: 100 }).map((r) => r.node);
202
+ const cached = this.nameCache.get(name);
203
+ if (cached !== undefined)
204
+ return cached;
205
+ const result = this.queries.getNodesByName(name);
206
+ this.nameCache.set(name, result);
207
+ return result;
168
208
  },
169
209
  getNodesByQualifiedName: (qualifiedName) => {
170
- // Use warm cache if available, otherwise fall back to search + filter
171
- if (this.cachesWarmed) {
172
- return this.qualifiedNameCache.get(qualifiedName) ?? [];
173
- }
174
- return this.queries
175
- .searchNodes(qualifiedName, { limit: 50 })
176
- .filter((r) => r.node.qualifiedName === qualifiedName)
177
- .map((r) => r.node);
210
+ const cached = this.qualifiedNameCache.get(qualifiedName);
211
+ if (cached !== undefined)
212
+ return cached;
213
+ const result = this.queries.getNodesByQualifiedNameExact(qualifiedName);
214
+ this.qualifiedNameCache.set(qualifiedName, result);
215
+ return result;
178
216
  },
179
217
  getNodesByKind: (kind) => {
180
- if (this.cachesWarmed) {
181
- return this.kindCache.get(kind) ?? [];
182
- }
183
218
  return this.queries.getNodesByKind(kind);
184
219
  },
185
220
  fileExists: (filePath) => {
@@ -196,7 +231,6 @@ class ReferenceResolver {
196
231
  return fs.existsSync(fullPath);
197
232
  }
198
233
  catch (error) {
199
- (0, sentry_1.captureException)(error, { operation: 'resolution-file-exists', filePath });
200
234
  (0, errors_1.logDebug)('Error checking file existence', { filePath, error: String(error) });
201
235
  return false;
202
236
  }
@@ -212,7 +246,6 @@ class ReferenceResolver {
212
246
  return content;
213
247
  }
214
248
  catch (error) {
215
- (0, sentry_1.captureException)(error, { operation: 'resolution-read-file', filePath });
216
249
  (0, errors_1.logDebug)('Failed to read file for resolution', { filePath, error: String(error) });
217
250
  this.fileCache.set(filePath, null);
218
251
  return null;
@@ -220,14 +253,15 @@ class ReferenceResolver {
220
253
  },
221
254
  getProjectRoot: () => this.projectRoot,
222
255
  getAllFiles: () => {
223
- return this.queries.getAllFiles().map((f) => f.path);
256
+ return this.queries.getAllFilePaths();
224
257
  },
225
258
  getNodesByLowerName: (lowerName) => {
226
- if (this.cachesWarmed) {
227
- return this.lowerNameCache.get(lowerName) ?? [];
228
- }
229
- // Fallback: scan all nodes (expensive, but only used if cache not warm)
230
- return this.queries.getAllNodes().filter((n) => n.name.toLowerCase() === lowerName);
259
+ const cached = this.lowerNameCache.get(lowerName);
260
+ if (cached !== undefined)
261
+ return cached;
262
+ const result = this.queries.getNodesByLowerName(lowerName);
263
+ this.lowerNameCache.set(lowerName, result);
264
+ return result;
231
265
  },
232
266
  getImportMappings: (filePath, language) => {
233
267
  const cacheKey = filePath;
@@ -300,6 +334,45 @@ class ReferenceResolver {
300
334
  },
301
335
  };
302
336
  }
337
+ /**
338
+ * Check if a reference name has any possible match in the codebase.
339
+ * Uses the pre-built knownNames set to skip expensive resolution
340
+ * for names that definitely don't exist as symbols.
341
+ */
342
+ hasAnyPossibleMatch(name) {
343
+ if (!this.knownNames)
344
+ return true; // no pre-filter available
345
+ // Direct name match
346
+ if (this.knownNames.has(name))
347
+ return true;
348
+ // For qualified names like "obj.method" or "Class::method", check the parts
349
+ const dotIdx = name.indexOf('.');
350
+ if (dotIdx > 0) {
351
+ const receiver = name.substring(0, dotIdx);
352
+ const member = name.substring(dotIdx + 1);
353
+ if (this.knownNames.has(receiver) || this.knownNames.has(member))
354
+ return true;
355
+ // Also check capitalized receiver (instance-method resolution)
356
+ const capitalized = receiver.charAt(0).toUpperCase() + receiver.slice(1);
357
+ if (this.knownNames.has(capitalized))
358
+ return true;
359
+ }
360
+ const colonIdx = name.indexOf('::');
361
+ if (colonIdx > 0) {
362
+ const receiver = name.substring(0, colonIdx);
363
+ const member = name.substring(colonIdx + 2);
364
+ if (this.knownNames.has(receiver) || this.knownNames.has(member))
365
+ return true;
366
+ }
367
+ // For path-like references (e.g., "snippets/drawer-menu.liquid"), check the filename
368
+ const slashIdx = name.lastIndexOf('/');
369
+ if (slashIdx > 0) {
370
+ const fileName = name.substring(slashIdx + 1);
371
+ if (this.knownNames.has(fileName))
372
+ return true;
373
+ }
374
+ return false;
375
+ }
303
376
  /**
304
377
  * Resolve a single reference
305
378
  */
@@ -308,6 +381,10 @@ class ReferenceResolver {
308
381
  if (this.isBuiltInOrExternal(ref)) {
309
382
  return null;
310
383
  }
384
+ // Fast pre-filter: skip if no symbol with this name exists anywhere
385
+ if (!this.hasAnyPossibleMatch(ref.referenceName)) {
386
+ return null;
387
+ }
311
388
  const candidates = [];
312
389
  // Strategy 1: Try framework-specific resolution
313
390
  for (const framework of this.frameworks) {
@@ -339,17 +416,30 @@ class ReferenceResolver {
339
416
  * Create edges from resolved references
340
417
  */
341
418
  createEdges(resolved) {
342
- return resolved.map((ref) => ({
343
- source: ref.original.fromNodeId,
344
- target: ref.targetNodeId,
345
- kind: ref.original.referenceKind,
346
- line: ref.original.line,
347
- column: ref.original.column,
348
- metadata: {
349
- confidence: ref.confidence,
350
- resolvedBy: ref.resolvedBy,
351
- },
352
- }));
419
+ return resolved.map((ref) => {
420
+ let kind = ref.original.referenceKind;
421
+ // Promote "extends" to "implements" when a class/struct targets an interface
422
+ if (kind === 'extends') {
423
+ const targetNode = this.queries.getNodeById(ref.targetNodeId);
424
+ if (targetNode && (targetNode.kind === 'interface' || targetNode.kind === 'protocol')) {
425
+ const sourceNode = this.queries.getNodeById(ref.original.fromNodeId);
426
+ if (sourceNode && sourceNode.kind !== 'interface' && sourceNode.kind !== 'protocol') {
427
+ kind = 'implements';
428
+ }
429
+ }
430
+ }
431
+ return {
432
+ source: ref.original.fromNodeId,
433
+ target: ref.targetNodeId,
434
+ kind,
435
+ line: ref.original.line,
436
+ column: ref.original.column,
437
+ metadata: {
438
+ confidence: ref.confidence,
439
+ resolvedBy: ref.resolvedBy,
440
+ },
441
+ };
442
+ });
353
443
  }
354
444
  /**
355
445
  * Resolve and persist edges to database
@@ -362,8 +452,82 @@ class ReferenceResolver {
362
452
  if (edges.length > 0) {
363
453
  this.queries.insertEdges(edges);
364
454
  }
455
+ // Clean up resolved refs from unresolved_refs table so metrics are accurate
456
+ if (result.resolved.length > 0) {
457
+ this.queries.deleteSpecificResolvedReferences(result.resolved.map((r) => ({
458
+ fromNodeId: r.original.fromNodeId,
459
+ referenceName: r.original.referenceName,
460
+ referenceKind: r.original.referenceKind,
461
+ })));
462
+ }
365
463
  return result;
366
464
  }
465
+ /**
466
+ * Resolve and persist in batches to keep memory bounded.
467
+ * Processes unresolved references in chunks, persisting edges and cleaning
468
+ * up resolved refs after each batch to avoid accumulating large arrays.
469
+ */
470
+ async resolveAndPersistBatched(onProgress, batchSize = 5000) {
471
+ this.warmCaches();
472
+ const total = this.queries.getUnresolvedReferencesCount();
473
+ let processed = 0;
474
+ const aggregateStats = {
475
+ total: 0,
476
+ resolved: 0,
477
+ unresolved: 0,
478
+ byMethod: {},
479
+ };
480
+ // Process in batches. We always read from offset 0 because resolved refs
481
+ // are deleted after each batch, shifting the remaining rows forward.
482
+ while (true) {
483
+ const batch = this.queries.getUnresolvedReferencesBatch(0, batchSize);
484
+ if (batch.length === 0)
485
+ break;
486
+ const result = this.resolveAll(batch);
487
+ // Persist edges immediately
488
+ const edges = this.createEdges(result.resolved);
489
+ if (edges.length > 0) {
490
+ this.queries.insertEdges(edges);
491
+ }
492
+ // Clean up resolved refs so they don't appear in the next batch
493
+ if (result.resolved.length > 0) {
494
+ this.queries.deleteSpecificResolvedReferences(result.resolved.map((r) => ({
495
+ fromNodeId: r.original.fromNodeId,
496
+ referenceName: r.original.referenceName,
497
+ referenceKind: r.original.referenceKind,
498
+ })));
499
+ }
500
+ // Delete unresolvable refs from this batch to avoid re-processing them
501
+ if (result.unresolved.length > 0) {
502
+ this.queries.deleteSpecificResolvedReferences(result.unresolved.map((r) => ({
503
+ fromNodeId: r.fromNodeId,
504
+ referenceName: r.referenceName,
505
+ referenceKind: r.referenceKind,
506
+ })));
507
+ }
508
+ // Aggregate stats
509
+ aggregateStats.total += result.stats.total;
510
+ aggregateStats.resolved += result.stats.resolved;
511
+ aggregateStats.unresolved += result.stats.unresolved;
512
+ for (const [method, count] of Object.entries(result.stats.byMethod)) {
513
+ aggregateStats.byMethod[method] = (aggregateStats.byMethod[method] || 0) + count;
514
+ }
515
+ processed += batch.length;
516
+ onProgress?.(processed, total);
517
+ // Yield so progress UI can render between batches
518
+ await new Promise(resolve => setImmediate(resolve));
519
+ // If nothing was resolved or removed in this batch, we'd loop forever
520
+ // on the same rows. Break to avoid infinite loop.
521
+ if (result.resolved.length === 0 && result.unresolved.length === batch.length) {
522
+ break;
523
+ }
524
+ }
525
+ return {
526
+ resolved: [],
527
+ unresolved: [],
528
+ stats: aggregateStats,
529
+ };
530
+ }
367
531
  /**
368
532
  * Get detected frameworks
369
533
  */
@@ -375,67 +539,67 @@ class ReferenceResolver {
375
539
  */
376
540
  isBuiltInOrExternal(ref) {
377
541
  const name = ref.referenceName;
542
+ const isJsTs = ref.language === 'typescript' || ref.language === 'javascript'
543
+ || ref.language === 'tsx' || ref.language === 'jsx';
378
544
  // JavaScript/TypeScript built-ins
379
- const jsBuiltIns = [
380
- 'console', 'window', 'document', 'global', 'process',
381
- 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean',
382
- 'Date', 'Math', 'JSON', 'RegExp', 'Error', 'Map', 'Set',
383
- 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
384
- 'fetch', 'require', 'module', 'exports', '__dirname', '__filename',
385
- ];
386
- if (jsBuiltIns.includes(name)) {
545
+ if (isJsTs && JS_BUILT_INS.has(name)) {
387
546
  return true;
388
547
  }
389
- // Common library calls
390
- if (name.startsWith('console.') || name.startsWith('Math.') || name.startsWith('JSON.')) {
548
+ // Common JS/TS library calls (console.log, Math.floor, JSON.parse)
549
+ if (isJsTs && (name.startsWith('console.') || name.startsWith('Math.') || name.startsWith('JSON.'))) {
391
550
  return true;
392
551
  }
393
552
  // React hooks from React itself
394
- const reactHooks = ['useState', 'useEffect', 'useContext', 'useReducer', 'useCallback', 'useMemo', 'useRef', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue'];
395
- if (reactHooks.includes(name)) {
553
+ if (isJsTs && REACT_HOOKS.has(name)) {
396
554
  return true;
397
555
  }
398
- // Python built-ins
399
- const pythonBuiltIns = [
400
- 'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
401
- 'open', 'input', 'type', 'isinstance', 'hasattr', 'getattr', 'setattr',
402
- 'super', 'self', 'cls', 'None', 'True', 'False',
403
- ];
404
- if (ref.language === 'python' && pythonBuiltIns.includes(name)) {
556
+ // Python built-ins (bare calls only — dotted calls like console.print are method calls)
557
+ if (ref.language === 'python' && PYTHON_BUILT_INS.has(name)) {
405
558
  return true;
406
559
  }
560
+ // Python built-in method calls (e.g., list.extend, dict.update)
561
+ if (ref.language === 'python') {
562
+ const dotIdx = name.indexOf('.');
563
+ if (dotIdx > 0) {
564
+ const receiver = name.substring(0, dotIdx);
565
+ const method = name.substring(dotIdx + 1);
566
+ // Filter calls on built-in types (list.append, dict.update, etc.)
567
+ if (PYTHON_BUILT_IN_TYPES.has(receiver)) {
568
+ return true;
569
+ }
570
+ // Filter built-in methods on non-class receivers
571
+ // (e.g., items.append where items is a local list variable)
572
+ // But allow if the capitalized receiver matches a known codebase class
573
+ if (PYTHON_BUILT_IN_METHODS.has(method)) {
574
+ const capitalized = receiver.charAt(0).toUpperCase() + receiver.slice(1);
575
+ if (!this.knownNames?.has(capitalized)) {
576
+ return true;
577
+ }
578
+ }
579
+ }
580
+ if (PYTHON_BUILT_IN_METHODS.has(name)) {
581
+ return true;
582
+ }
583
+ }
584
+ // Go standard library packages — refs like "fmt.Println", "http.ListenAndServe", etc.
585
+ if (ref.language === 'go') {
586
+ const dotIdx = name.indexOf('.');
587
+ if (dotIdx > 0) {
588
+ const pkg = name.substring(0, dotIdx);
589
+ if (GO_STDLIB_PACKAGES.has(pkg)) {
590
+ return true;
591
+ }
592
+ }
593
+ if (GO_BUILT_INS.has(name)) {
594
+ return true;
595
+ }
596
+ }
407
597
  // Pascal/Delphi built-ins and standard library units
408
598
  if (ref.language === 'pascal') {
409
- // Standard RTL/VCL/FMX unit prefixes — these are external dependencies
410
- const pascalUnitPrefixes = [
411
- 'System.', 'Winapi.', 'Vcl.', 'Fmx.', 'Data.', 'Datasnap.',
412
- 'Soap.', 'Xml.', 'Web.', 'REST.', 'FireDAC.', 'IBX.',
413
- 'IdHTTP', 'IdTCP', 'IdSSL',
414
- ];
415
- if (pascalUnitPrefixes.some((p) => name.startsWith(p))) {
599
+ if (PASCAL_UNIT_PREFIXES.some((p) => name.startsWith(p))) {
416
600
  return true;
417
601
  }
418
- // Common standalone RTL units and built-in identifiers
419
- const pascalBuiltIns = [
420
- 'System', 'SysUtils', 'Classes', 'Types', 'Variants', 'StrUtils',
421
- 'Math', 'DateUtils', 'IOUtils', 'Generics.Collections', 'Generics.Defaults',
422
- 'Rtti', 'TypInfo', 'SyncObjs', 'RegularExpressions',
423
- 'SysInit', 'Windows', 'Messages', 'Graphics', 'Controls', 'Forms',
424
- 'Dialogs', 'StdCtrls', 'ExtCtrls', 'ComCtrls', 'Menus', 'ActnList',
425
- 'WriteLn', 'Write', 'ReadLn', 'Read', 'Inc', 'Dec', 'Ord', 'Chr',
426
- 'Length', 'SetLength', 'High', 'Low', 'Assigned', 'FreeAndNil',
427
- 'Format', 'IntToStr', 'StrToInt', 'FloatToStr', 'StrToFloat',
428
- 'Trim', 'UpperCase', 'LowerCase', 'Pos', 'Copy', 'Delete', 'Insert',
429
- 'Now', 'Date', 'Time', 'DateToStr', 'StrToDate',
430
- 'Raise', 'Exit', 'Break', 'Continue', 'Abort',
431
- 'True', 'False', 'nil', 'Self', 'Result',
432
- 'Create', 'Destroy', 'Free',
433
- 'TObject', 'TComponent', 'TPersistent', 'TInterfacedObject',
434
- 'TList', 'TStringList', 'TStrings', 'TStream', 'TMemoryStream', 'TFileStream',
435
- 'Exception', 'EAbort', 'EConvertError', 'EAccessViolation',
436
- 'IInterface', 'IUnknown',
437
- ];
438
- if (pascalBuiltIns.includes(name)) {
602
+ if (PASCAL_BUILT_INS.has(name)) {
439
603
  return true;
440
604
  }
441
605
  }
@@ -445,10 +609,6 @@ class ReferenceResolver {
445
609
  * Get file path from node ID
446
610
  */
447
611
  getFilePathFromNodeId(nodeId) {
448
- // Check warm cache first
449
- const cached = this.nodeByIdCache.get(nodeId);
450
- if (cached)
451
- return cached.filePath;
452
612
  const node = this.queries.getNodeById(nodeId);
453
613
  return node?.filePath || '';
454
614
  }
@@ -456,10 +616,6 @@ class ReferenceResolver {
456
616
  * Get language from node ID
457
617
  */
458
618
  getLanguageFromNodeId(nodeId) {
459
- // Check warm cache first
460
- const cached = this.nodeByIdCache.get(nodeId);
461
- if (cached)
462
- return cached.language;
463
619
  const node = this.queries.getNodeById(nodeId);
464
620
  return node?.language || 'unknown';
465
621
  }