@zuvia-software-solutions/code-mapper 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +215 -0
  2. package/dist/cli/ai-context.d.ts +19 -0
  3. package/dist/cli/ai-context.js +168 -0
  4. package/dist/cli/analyze.d.ts +7 -0
  5. package/dist/cli/analyze.js +325 -0
  6. package/dist/cli/augment.d.ts +7 -0
  7. package/dist/cli/augment.js +27 -0
  8. package/dist/cli/clean.d.ts +5 -0
  9. package/dist/cli/clean.js +56 -0
  10. package/dist/cli/eval-server.d.ts +25 -0
  11. package/dist/cli/eval-server.js +365 -0
  12. package/dist/cli/index.d.ts +6 -0
  13. package/dist/cli/index.js +102 -0
  14. package/dist/cli/lazy-action.d.ts +6 -0
  15. package/dist/cli/lazy-action.js +19 -0
  16. package/dist/cli/list.d.ts +2 -0
  17. package/dist/cli/list.js +27 -0
  18. package/dist/cli/mcp.d.ts +8 -0
  19. package/dist/cli/mcp.js +35 -0
  20. package/dist/cli/refresh.d.ts +12 -0
  21. package/dist/cli/refresh.js +165 -0
  22. package/dist/cli/serve.d.ts +5 -0
  23. package/dist/cli/serve.js +8 -0
  24. package/dist/cli/setup.d.ts +6 -0
  25. package/dist/cli/setup.js +218 -0
  26. package/dist/cli/status.d.ts +2 -0
  27. package/dist/cli/status.js +33 -0
  28. package/dist/cli/tool.d.ts +28 -0
  29. package/dist/cli/tool.js +87 -0
  30. package/dist/config/ignore-service.d.ts +32 -0
  31. package/dist/config/ignore-service.js +282 -0
  32. package/dist/config/supported-languages.d.ts +23 -0
  33. package/dist/config/supported-languages.js +52 -0
  34. package/dist/core/augmentation/engine.d.ts +22 -0
  35. package/dist/core/augmentation/engine.js +232 -0
  36. package/dist/core/embeddings/embedder.d.ts +35 -0
  37. package/dist/core/embeddings/embedder.js +171 -0
  38. package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
  39. package/dist/core/embeddings/embedding-pipeline.js +402 -0
  40. package/dist/core/embeddings/index.d.ts +5 -0
  41. package/dist/core/embeddings/index.js +6 -0
  42. package/dist/core/embeddings/text-generator.d.ts +20 -0
  43. package/dist/core/embeddings/text-generator.js +159 -0
  44. package/dist/core/embeddings/types.d.ts +60 -0
  45. package/dist/core/embeddings/types.js +23 -0
  46. package/dist/core/graph/graph.d.ts +4 -0
  47. package/dist/core/graph/graph.js +65 -0
  48. package/dist/core/graph/types.d.ts +69 -0
  49. package/dist/core/graph/types.js +3 -0
  50. package/dist/core/incremental/child-process.d.ts +8 -0
  51. package/dist/core/incremental/child-process.js +649 -0
  52. package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
  53. package/dist/core/incremental/refresh-coordinator.js +147 -0
  54. package/dist/core/incremental/types.d.ts +78 -0
  55. package/dist/core/incremental/types.js +153 -0
  56. package/dist/core/incremental/watcher.d.ts +63 -0
  57. package/dist/core/incremental/watcher.js +338 -0
  58. package/dist/core/ingestion/ast-cache.d.ts +12 -0
  59. package/dist/core/ingestion/ast-cache.js +34 -0
  60. package/dist/core/ingestion/call-processor.d.ts +34 -0
  61. package/dist/core/ingestion/call-processor.js +937 -0
  62. package/dist/core/ingestion/call-routing.d.ts +40 -0
  63. package/dist/core/ingestion/call-routing.js +97 -0
  64. package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
  65. package/dist/core/ingestion/cluster-enricher.js +151 -0
  66. package/dist/core/ingestion/community-processor.d.ts +26 -0
  67. package/dist/core/ingestion/community-processor.js +272 -0
  68. package/dist/core/ingestion/constants.d.ts +5 -0
  69. package/dist/core/ingestion/constants.js +8 -0
  70. package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
  71. package/dist/core/ingestion/entry-point-scoring.js +317 -0
  72. package/dist/core/ingestion/export-detection.d.ts +11 -0
  73. package/dist/core/ingestion/export-detection.js +203 -0
  74. package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
  75. package/dist/core/ingestion/filesystem-walker.js +64 -0
  76. package/dist/core/ingestion/framework-detection.d.ts +42 -0
  77. package/dist/core/ingestion/framework-detection.js +405 -0
  78. package/dist/core/ingestion/heritage-processor.d.ts +15 -0
  79. package/dist/core/ingestion/heritage-processor.js +237 -0
  80. package/dist/core/ingestion/import-processor.d.ts +31 -0
  81. package/dist/core/ingestion/import-processor.js +416 -0
  82. package/dist/core/ingestion/language-config.d.ts +32 -0
  83. package/dist/core/ingestion/language-config.js +161 -0
  84. package/dist/core/ingestion/mro-processor.d.ts +32 -0
  85. package/dist/core/ingestion/mro-processor.js +343 -0
  86. package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
  87. package/dist/core/ingestion/named-binding-extraction.js +343 -0
  88. package/dist/core/ingestion/parsing-processor.d.ts +20 -0
  89. package/dist/core/ingestion/parsing-processor.js +282 -0
  90. package/dist/core/ingestion/pipeline.d.ts +3 -0
  91. package/dist/core/ingestion/pipeline.js +416 -0
  92. package/dist/core/ingestion/process-processor.d.ts +42 -0
  93. package/dist/core/ingestion/process-processor.js +357 -0
  94. package/dist/core/ingestion/resolution-context.d.ts +40 -0
  95. package/dist/core/ingestion/resolution-context.js +171 -0
  96. package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
  97. package/dist/core/ingestion/resolvers/csharp.js +101 -0
  98. package/dist/core/ingestion/resolvers/go.d.ts +8 -0
  99. package/dist/core/ingestion/resolvers/go.js +33 -0
  100. package/dist/core/ingestion/resolvers/index.d.ts +14 -0
  101. package/dist/core/ingestion/resolvers/index.js +10 -0
  102. package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
  103. package/dist/core/ingestion/resolvers/jvm.js +74 -0
  104. package/dist/core/ingestion/resolvers/php.d.ts +7 -0
  105. package/dist/core/ingestion/resolvers/php.js +30 -0
  106. package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
  107. package/dist/core/ingestion/resolvers/ruby.js +13 -0
  108. package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
  109. package/dist/core/ingestion/resolvers/rust.js +62 -0
  110. package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
  111. package/dist/core/ingestion/resolvers/standard.js +144 -0
  112. package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
  113. package/dist/core/ingestion/resolvers/utils.js +113 -0
  114. package/dist/core/ingestion/structure-processor.d.ts +4 -0
  115. package/dist/core/ingestion/structure-processor.js +39 -0
  116. package/dist/core/ingestion/symbol-table.d.ts +34 -0
  117. package/dist/core/ingestion/symbol-table.js +48 -0
  118. package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
  119. package/dist/core/ingestion/tree-sitter-queries.js +691 -0
  120. package/dist/core/ingestion/type-env.d.ts +52 -0
  121. package/dist/core/ingestion/type-env.js +349 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
  123. package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
  124. package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
  125. package/dist/core/ingestion/type-extractors/csharp.js +224 -0
  126. package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
  127. package/dist/core/ingestion/type-extractors/go.js +261 -0
  128. package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
  129. package/dist/core/ingestion/type-extractors/index.js +30 -0
  130. package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
  131. package/dist/core/ingestion/type-extractors/jvm.js +386 -0
  132. package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
  133. package/dist/core/ingestion/type-extractors/php.js +280 -0
  134. package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
  135. package/dist/core/ingestion/type-extractors/python.js +175 -0
  136. package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
  137. package/dist/core/ingestion/type-extractors/ruby.js +218 -0
  138. package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
  139. package/dist/core/ingestion/type-extractors/rust.js +290 -0
  140. package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
  141. package/dist/core/ingestion/type-extractors/shared.js +322 -0
  142. package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
  143. package/dist/core/ingestion/type-extractors/swift.js +140 -0
  144. package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
  145. package/dist/core/ingestion/type-extractors/types.js +4 -0
  146. package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
  147. package/dist/core/ingestion/type-extractors/typescript.js +227 -0
  148. package/dist/core/ingestion/utils.d.ts +73 -0
  149. package/dist/core/ingestion/utils.js +992 -0
  150. package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
  151. package/dist/core/ingestion/workers/parse-worker.js +1055 -0
  152. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  153. package/dist/core/ingestion/workers/worker-pool.js +123 -0
  154. package/dist/core/lbug/csv-generator.d.ts +28 -0
  155. package/dist/core/lbug/csv-generator.js +355 -0
  156. package/dist/core/lbug/lbug-adapter.d.ts +96 -0
  157. package/dist/core/lbug/lbug-adapter.js +753 -0
  158. package/dist/core/lbug/schema.d.ts +46 -0
  159. package/dist/core/lbug/schema.js +402 -0
  160. package/dist/core/search/bm25-index.d.ts +20 -0
  161. package/dist/core/search/bm25-index.js +123 -0
  162. package/dist/core/search/hybrid-search.d.ts +32 -0
  163. package/dist/core/search/hybrid-search.js +131 -0
  164. package/dist/core/search/query-cache.d.ts +18 -0
  165. package/dist/core/search/query-cache.js +47 -0
  166. package/dist/core/search/query-expansion.d.ts +19 -0
  167. package/dist/core/search/query-expansion.js +75 -0
  168. package/dist/core/search/reranker.d.ts +29 -0
  169. package/dist/core/search/reranker.js +122 -0
  170. package/dist/core/search/types.d.ts +154 -0
  171. package/dist/core/search/types.js +51 -0
  172. package/dist/core/semantic/tsgo-service.d.ts +67 -0
  173. package/dist/core/semantic/tsgo-service.js +355 -0
  174. package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
  175. package/dist/core/tree-sitter/parser-loader.js +71 -0
  176. package/dist/lib/memory-guard.d.ts +35 -0
  177. package/dist/lib/memory-guard.js +70 -0
  178. package/dist/lib/utils.d.ts +3 -0
  179. package/dist/lib/utils.js +6 -0
  180. package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
  181. package/dist/mcp/compatible-stdio-transport.js +209 -0
  182. package/dist/mcp/core/embedder.d.ts +24 -0
  183. package/dist/mcp/core/embedder.js +168 -0
  184. package/dist/mcp/core/lbug-adapter.d.ts +29 -0
  185. package/dist/mcp/core/lbug-adapter.js +330 -0
  186. package/dist/mcp/local/local-backend.d.ts +188 -0
  187. package/dist/mcp/local/local-backend.js +2759 -0
  188. package/dist/mcp/resources.d.ts +22 -0
  189. package/dist/mcp/resources.js +379 -0
  190. package/dist/mcp/server.d.ts +10 -0
  191. package/dist/mcp/server.js +217 -0
  192. package/dist/mcp/staleness.d.ts +10 -0
  193. package/dist/mcp/staleness.js +25 -0
  194. package/dist/mcp/tools.d.ts +21 -0
  195. package/dist/mcp/tools.js +202 -0
  196. package/dist/server/api.d.ts +5 -0
  197. package/dist/server/api.js +340 -0
  198. package/dist/server/mcp-http.d.ts +7 -0
  199. package/dist/server/mcp-http.js +95 -0
  200. package/dist/storage/git.d.ts +6 -0
  201. package/dist/storage/git.js +35 -0
  202. package/dist/storage/repo-manager.d.ts +87 -0
  203. package/dist/storage/repo-manager.js +249 -0
  204. package/dist/types/pipeline.d.ts +35 -0
  205. package/dist/types/pipeline.js +20 -0
  206. package/hooks/claude/code-mapper-hook.cjs +238 -0
  207. package/hooks/claude/pre-tool-use.sh +79 -0
  208. package/hooks/claude/session-start.sh +42 -0
  209. package/models/mlx-embedder.py +185 -0
  210. package/package.json +100 -0
  211. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  212. package/vendor/leiden/index.cjs +355 -0
  213. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,992 @@
1
+ // code-mapper/src/core/ingestion/utils.ts
2
+ /**
3
+ * @file Shared AST utilities for code ingestion
4
+ * @description Tree-sitter node helpers, built-in name filtering, function/class extraction,
5
+ * call-form discrimination, and receiver resolution used across all ingestion modules
6
+ */
7
+ import { SupportedLanguages } from '../../config/supported-languages.js';
8
+ import { generateId } from '../../lib/utils.js';
9
+ import { extractSimpleTypeName } from './type-extractors/shared.js';
10
+ /** Ordered definition capture keys for tree-sitter query matches */
11
+ export const DEFINITION_CAPTURE_KEYS = [
12
+ 'definition.function',
13
+ 'definition.class',
14
+ 'definition.interface',
15
+ 'definition.method',
16
+ 'definition.struct',
17
+ 'definition.enum',
18
+ 'definition.namespace',
19
+ 'definition.module',
20
+ 'definition.trait',
21
+ 'definition.impl',
22
+ 'definition.type',
23
+ 'definition.const',
24
+ 'definition.static',
25
+ 'definition.typedef',
26
+ 'definition.macro',
27
+ 'definition.union',
28
+ 'definition.property',
29
+ 'definition.record',
30
+ 'definition.delegate',
31
+ 'definition.annotation',
32
+ 'definition.constructor',
33
+ 'definition.template',
34
+ ];
35
+ /** Extract the definition node from a tree-sitter query capture map */
36
+ export const getDefinitionNodeFromCaptures = (captureMap) => {
37
+ for (const key of DEFINITION_CAPTURE_KEYS) {
38
+ if (captureMap[key])
39
+ return captureMap[key];
40
+ }
41
+ return null;
42
+ };
43
+ /** Node types representing function/method definitions across languages */
44
+ export const FUNCTION_NODE_TYPES = new Set([
45
+ // TypeScript/JavaScript
46
+ 'function_declaration',
47
+ 'arrow_function',
48
+ 'function_expression',
49
+ 'method_definition',
50
+ 'generator_function_declaration',
51
+ // Python
52
+ 'function_definition',
53
+ // Common async variants
54
+ 'async_function_declaration',
55
+ 'async_arrow_function',
56
+ // Java
57
+ 'method_declaration',
58
+ 'constructor_declaration',
59
+ // C/C++
60
+ // 'function_definition' already included above
61
+ // Go
62
+ // 'method_declaration' already included from Java
63
+ // C#
64
+ 'local_function_statement',
65
+ // Rust
66
+ 'function_item',
67
+ 'impl_item', // Methods inside impl blocks
68
+ // PHP
69
+ 'anonymous_function',
70
+ // Kotlin
71
+ 'lambda_literal',
72
+ // Swift
73
+ 'init_declaration',
74
+ 'deinit_declaration',
75
+ // Ruby
76
+ 'method', // def foo
77
+ 'singleton_method', // def self.foo
78
+ ]);
79
+ /** Node types for standard function declarations that need C/C++ declarator handling */
80
+ export const FUNCTION_DECLARATION_TYPES = new Set([
81
+ 'function_declaration',
82
+ 'function_definition',
83
+ 'async_function_declaration',
84
+ 'generator_function_declaration',
85
+ 'function_item',
86
+ ]);
87
+ /** Built-in function/method names that should not be tracked as call targets */
88
+ export const BUILT_IN_NAMES = new Set([
89
+ // JavaScript/TypeScript
90
+ 'console', 'log', 'warn', 'error', 'info', 'debug',
91
+ 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
92
+ 'parseInt', 'parseFloat', 'isNaN', 'isFinite',
93
+ 'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
94
+ 'JSON', 'parse', 'stringify',
95
+ 'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
96
+ 'Map', 'Set', 'WeakMap', 'WeakSet',
97
+ 'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
98
+ 'Math', 'Date', 'RegExp', 'Error',
99
+ 'require', 'import', 'export', 'fetch', 'Response', 'Request',
100
+ 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
101
+ 'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
102
+ 'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
103
+ 'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
104
+ 'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
105
+ 'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
106
+ 'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
107
+ 'hasOwnProperty', 'toString', 'valueOf',
108
+ // Python
109
+ 'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
110
+ 'append', 'extend', 'update',
111
+ // NOTE: 'open', 'read', 'write', 'close' removed — these are real C POSIX syscalls
112
+ 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
113
+ 'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
114
+ // Kotlin stdlib
115
+ 'println', 'print', 'readLine', 'require', 'requireNotNull', 'check', 'assert', 'lazy', 'error',
116
+ 'listOf', 'mapOf', 'setOf', 'mutableListOf', 'mutableMapOf', 'mutableSetOf',
117
+ 'arrayOf', 'sequenceOf', 'also', 'apply', 'run', 'with', 'takeIf', 'takeUnless',
118
+ 'TODO', 'buildString', 'buildList', 'buildMap', 'buildSet',
119
+ 'repeat', 'synchronized',
120
+ // Kotlin coroutine builders & scope functions
121
+ 'launch', 'async', 'runBlocking', 'withContext', 'coroutineScope',
122
+ 'supervisorScope', 'delay',
123
+ // Kotlin Flow operators
124
+ 'flow', 'flowOf', 'collect', 'emit', 'onEach', 'catch',
125
+ 'buffer', 'conflate', 'distinctUntilChanged',
126
+ 'flatMapLatest', 'flatMapMerge', 'combine',
127
+ 'stateIn', 'shareIn', 'launchIn',
128
+ // Kotlin infix stdlib functions
129
+ 'to', 'until', 'downTo', 'step',
130
+ // C/C++ standard library
131
+ 'printf', 'fprintf', 'sprintf', 'snprintf', 'vprintf', 'vfprintf', 'vsprintf', 'vsnprintf',
132
+ 'scanf', 'fscanf', 'sscanf',
133
+ 'malloc', 'calloc', 'realloc', 'free', 'memcpy', 'memmove', 'memset', 'memcmp',
134
+ 'strlen', 'strcpy', 'strncpy', 'strcat', 'strncat', 'strcmp', 'strncmp', 'strstr', 'strchr', 'strrchr',
135
+ 'atoi', 'atol', 'atof', 'strtol', 'strtoul', 'strtoll', 'strtoull', 'strtod',
136
+ 'sizeof', 'offsetof', 'typeof',
137
+ 'assert', 'abort', 'exit', '_exit',
138
+ 'fopen', 'fclose', 'fread', 'fwrite', 'fseek', 'ftell', 'rewind', 'fflush', 'fgets', 'fputs',
139
+ // Linux kernel common macros/helpers (not real call targets)
140
+ 'likely', 'unlikely', 'BUG', 'BUG_ON', 'WARN', 'WARN_ON', 'WARN_ONCE',
141
+ 'IS_ERR', 'PTR_ERR', 'ERR_PTR', 'IS_ERR_OR_NULL',
142
+ 'ARRAY_SIZE', 'container_of', 'list_for_each_entry', 'list_for_each_entry_safe',
143
+ 'min', 'max', 'clamp', 'abs', 'swap',
144
+ 'pr_info', 'pr_warn', 'pr_err', 'pr_debug', 'pr_notice', 'pr_crit', 'pr_emerg',
145
+ 'printk', 'dev_info', 'dev_warn', 'dev_err', 'dev_dbg',
146
+ 'GFP_KERNEL', 'GFP_ATOMIC',
147
+ 'spin_lock', 'spin_unlock', 'spin_lock_irqsave', 'spin_unlock_irqrestore',
148
+ 'mutex_lock', 'mutex_unlock', 'mutex_init',
149
+ 'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
150
+ 'get', 'put',
151
+ // C# / .NET built-ins
152
+ 'Console', 'WriteLine', 'ReadLine', 'Write',
153
+ 'Task', 'Run', 'Wait', 'WhenAll', 'WhenAny', 'FromResult', 'Delay', 'ContinueWith',
154
+ 'ConfigureAwait', 'GetAwaiter', 'GetResult',
155
+ 'ToString', 'GetType', 'Equals', 'GetHashCode', 'ReferenceEquals',
156
+ 'Add', 'Remove', 'Contains', 'Clear', 'Count', 'Any', 'All',
157
+ 'Where', 'Select', 'SelectMany', 'OrderBy', 'OrderByDescending', 'GroupBy',
158
+ 'First', 'FirstOrDefault', 'Single', 'SingleOrDefault', 'Last', 'LastOrDefault',
159
+ 'ToList', 'ToArray', 'ToDictionary', 'AsEnumerable', 'AsQueryable',
160
+ 'Aggregate', 'Sum', 'Average', 'Min', 'Max', 'Distinct', 'Skip', 'Take',
161
+ 'String', 'Format', 'IsNullOrEmpty', 'IsNullOrWhiteSpace', 'Concat', 'Join',
162
+ 'Trim', 'TrimStart', 'TrimEnd', 'Split', 'Replace', 'StartsWith', 'EndsWith',
163
+ 'Convert', 'ToInt32', 'ToDouble', 'ToBoolean', 'ToByte',
164
+ 'Math', 'Abs', 'Ceiling', 'Floor', 'Round', 'Pow', 'Sqrt',
165
+ 'Dispose', 'Close',
166
+ 'TryParse', 'Parse',
167
+ 'AddRange', 'RemoveAt', 'RemoveAll', 'FindAll', 'Exists', 'TrueForAll',
168
+ 'ContainsKey', 'TryGetValue', 'AddOrUpdate',
169
+ 'Throw', 'ThrowIfNull',
170
+ // PHP built-ins
171
+ 'echo', 'isset', 'empty', 'unset', 'list', 'array', 'compact', 'extract',
172
+ 'count', 'strlen', 'strpos', 'strrpos', 'substr', 'strtolower', 'strtoupper', 'trim',
173
+ 'ltrim', 'rtrim', 'str_replace', 'str_contains', 'str_starts_with', 'str_ends_with',
174
+ 'sprintf', 'vsprintf', 'printf', 'number_format',
175
+ 'array_map', 'array_filter', 'array_reduce', 'array_push', 'array_pop', 'array_shift',
176
+ 'array_unshift', 'array_slice', 'array_splice', 'array_merge', 'array_keys', 'array_values',
177
+ 'array_key_exists', 'in_array', 'array_search', 'array_unique', 'usort', 'rsort',
178
+ 'json_encode', 'json_decode', 'serialize', 'unserialize',
179
+ 'intval', 'floatval', 'strval', 'boolval', 'is_null', 'is_string', 'is_int', 'is_array',
180
+ 'is_object', 'is_numeric', 'is_bool', 'is_float',
181
+ 'var_dump', 'print_r', 'var_export',
182
+ 'date', 'time', 'strtotime', 'mktime', 'microtime',
183
+ 'file_exists', 'file_get_contents', 'file_put_contents', 'is_file', 'is_dir',
184
+ 'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
185
+ 'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
186
+ 'dd', 'dump',
187
+ // Swift/iOS built-ins and standard library
188
+ 'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
189
+ 'assert', 'assertionFailure', 'NSLog',
190
+ 'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
191
+ 'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
192
+ 'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
193
+ 'type', 'MemoryLayout',
194
+ // Swift collection/string methods (common noise)
195
+ 'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
196
+ 'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
197
+ 'sorted', 'reversed', 'enumerated', 'joined', 'split',
198
+ 'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
199
+ 'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
200
+ // UIKit/Foundation common methods (noise in call graph)
201
+ 'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
202
+ 'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
203
+ 'addTarget', 'removeTarget', 'addGestureRecognizer',
204
+ 'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
205
+ 'NSLocalizedString', 'Bundle',
206
+ 'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
207
+ 'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
208
+ 'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
209
+ 'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
210
+ 'performSegue', 'prepare',
211
+ // GCD / async
212
+ 'DispatchQueue', 'async', 'sync', 'asyncAfter',
213
+ 'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
214
+ // Combine
215
+ 'sink', 'store', 'assign', 'receive', 'subscribe',
216
+ // Notification / KVO
217
+ 'addObserver', 'removeObserver', 'post', 'NotificationCenter',
218
+ // Rust standard library (common noise in call graphs)
219
+ 'unwrap', 'expect', 'unwrap_or', 'unwrap_or_else', 'unwrap_or_default',
220
+ 'ok', 'err', 'is_ok', 'is_err', 'map', 'map_err', 'and_then', 'or_else',
221
+ 'clone', 'to_string', 'to_owned', 'into', 'from', 'as_ref', 'as_mut',
222
+ 'iter', 'into_iter', 'collect', 'map', 'filter', 'fold', 'for_each',
223
+ 'len', 'is_empty', 'push', 'pop', 'insert', 'remove', 'contains',
224
+ 'format', 'write', 'writeln', 'panic', 'unreachable', 'todo', 'unimplemented',
225
+ 'vec', 'println', 'eprintln', 'dbg',
226
+ 'lock', 'read', 'write', 'try_lock',
227
+ 'spawn', 'join', 'sleep',
228
+ 'Some', 'None', 'Ok', 'Err',
229
+ // Ruby built-ins and Kernel methods
230
+ 'puts', 'p', 'pp', 'raise', 'fail',
231
+ 'require', 'require_relative', 'load', 'autoload',
232
+ 'include', 'extend', 'prepend',
233
+ 'attr_accessor', 'attr_reader', 'attr_writer',
234
+ 'public', 'private', 'protected', 'module_function',
235
+ 'lambda', 'proc', 'block_given?',
236
+ 'nil?', 'is_a?', 'kind_of?', 'instance_of?', 'respond_to?',
237
+ 'freeze', 'frozen?', 'dup', 'tap', 'yield_self',
238
+ // Ruby enumerables
239
+ 'each', 'select', 'reject', 'detect', 'collect',
240
+ 'inject', 'flat_map', 'each_with_object', 'each_with_index',
241
+ 'any?', 'all?', 'none?', 'count', 'first', 'last',
242
+ 'sort_by', 'min_by', 'max_by',
243
+ 'group_by', 'partition', 'compact', 'flatten', 'uniq',
244
+ ]);
245
+ /** Check if a name is a built-in or common noise that should be filtered out */
246
+ export const isBuiltInOrNoise = (name) => BUILT_IN_NAMES.has(name);
247
+ /** AST node types representing class-like containers (for HAS_METHOD edges) */
248
+ export const CLASS_CONTAINER_TYPES = new Set([
249
+ 'class_declaration', 'abstract_class_declaration',
250
+ 'interface_declaration', 'struct_declaration', 'record_declaration',
251
+ 'class_specifier', 'struct_specifier',
252
+ 'impl_item', 'trait_item',
253
+ 'class_definition',
254
+ 'trait_declaration',
255
+ 'protocol_declaration',
256
+ // Ruby
257
+ 'class',
258
+ 'module',
259
+ // Kotlin
260
+ 'object_declaration',
261
+ 'companion_object',
262
+ ]);
263
+ export const CONTAINER_TYPE_TO_LABEL = {
264
+ class_declaration: 'Class',
265
+ abstract_class_declaration: 'Class',
266
+ interface_declaration: 'Interface',
267
+ struct_declaration: 'Struct',
268
+ struct_specifier: 'Struct',
269
+ class_specifier: 'Class',
270
+ class_definition: 'Class',
271
+ impl_item: 'Impl',
272
+ trait_item: 'Trait',
273
+ trait_declaration: 'Trait',
274
+ record_declaration: 'Record',
275
+ protocol_declaration: 'Interface',
276
+ class: 'Class',
277
+ module: 'Module',
278
+ object_declaration: 'Class',
279
+ companion_object: 'Class',
280
+ };
281
+ /** Walk up AST to find enclosing class/struct/impl and return its generateId (handles Go receivers) */
282
+ export const findEnclosingClassId = (node, filePath) => {
283
+ let current = node.parent;
284
+ while (current) {
285
+ // Go: method_declaration has a receiver parameter with the struct type
286
+ if (current.type === 'method_declaration') {
287
+ const receiver = current.childForFieldName?.('receiver');
288
+ if (receiver) {
289
+ // receiver is a parameter_list: (u *User) or (u User)
290
+ const paramDecl = receiver.namedChildren?.find?.((c) => c.type === 'parameter_declaration');
291
+ if (paramDecl) {
292
+ const typeNode = paramDecl.childForFieldName?.('type');
293
+ if (typeNode) {
294
+ // Unwrap pointer_type (*User → User)
295
+ const inner = typeNode.type === 'pointer_type' ? typeNode.firstNamedChild : typeNode;
296
+ if (inner && (inner.type === 'type_identifier' || inner.type === 'identifier')) {
297
+ return generateId('Struct', `${filePath}:${inner.text}`);
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
303
+ if (CLASS_CONTAINER_TYPES.has(current.type)) {
304
+ // Rust impl_item: for `impl Trait for Struct {}`, pick the type after `for`
305
+ if (current.type === 'impl_item') {
306
+ const children = current.children ?? [];
307
+ const forIdx = children.findIndex((c) => c.text === 'for');
308
+ if (forIdx !== -1) {
309
+ const nameNode = children.slice(forIdx + 1).find((c) => c.type === 'type_identifier' || c.type === 'identifier');
310
+ if (nameNode) {
311
+ return generateId('Impl', `${filePath}:${nameNode.text}`);
312
+ }
313
+ }
314
+ // Fall through: plain `impl Struct {}` — use first type_identifier below
315
+ }
316
+ const nameNode = current.childForFieldName?.('name')
317
+ ?? current.children?.find((c) => c.type === 'type_identifier' || c.type === 'identifier' || c.type === 'name' || c.type === 'constant');
318
+ if (nameNode) {
319
+ const label = CONTAINER_TYPE_TO_LABEL[current.type] || 'Class';
320
+ return generateId(label, `${filePath}:${nameNode.text}`);
321
+ }
322
+ }
323
+ current = current.parent;
324
+ }
325
+ return null;
326
+ };
327
+ /** Extract function name and label from a function/method AST node (handles C/C++ qualified_identifier) */
328
+ export const extractFunctionName = (node) => {
329
+ let funcName = null;
330
+ let label = 'Function';
331
+ // Swift init/deinit
332
+ if (node.type === 'init_declaration' || node.type === 'deinit_declaration') {
333
+ return {
334
+ funcName: node.type === 'init_declaration' ? 'init' : 'deinit',
335
+ label: 'Constructor',
336
+ };
337
+ }
338
+ if (FUNCTION_DECLARATION_TYPES.has(node.type)) {
339
+ // C/C++: function_definition -> [pointer_declarator ->] function_declarator -> qualified_identifier/identifier
340
+ // Unwrap pointer_declarator / reference_declarator wrappers to reach function_declarator
341
+ let declarator = node.childForFieldName?.('declarator') ||
342
+ node.children?.find((c) => c.type === 'function_declarator');
343
+ while (declarator && (declarator.type === 'pointer_declarator' || declarator.type === 'reference_declarator')) {
344
+ declarator = declarator.childForFieldName?.('declarator') ||
345
+ declarator.children?.find((c) => c.type === 'function_declarator' || c.type === 'pointer_declarator' || c.type === 'reference_declarator');
346
+ }
347
+ if (declarator) {
348
+ const innerDeclarator = declarator.childForFieldName?.('declarator') ||
349
+ declarator.children?.find((c) => c.type === 'qualified_identifier' || c.type === 'identifier' || c.type === 'parenthesized_declarator');
350
+ if (innerDeclarator?.type === 'qualified_identifier') {
351
+ const nameNode = innerDeclarator.childForFieldName?.('name') ||
352
+ innerDeclarator.children?.find((c) => c.type === 'identifier');
353
+ if (nameNode?.text) {
354
+ funcName = nameNode.text;
355
+ label = 'Method';
356
+ }
357
+ }
358
+ else if (innerDeclarator?.type === 'identifier') {
359
+ funcName = innerDeclarator.text;
360
+ }
361
+ else if (innerDeclarator?.type === 'parenthesized_declarator') {
362
+ const nestedId = innerDeclarator.children?.find((c) => c.type === 'qualified_identifier' || c.type === 'identifier');
363
+ if (nestedId?.type === 'qualified_identifier') {
364
+ const nameNode = nestedId.childForFieldName?.('name') ||
365
+ nestedId.children?.find((c) => c.type === 'identifier');
366
+ if (nameNode?.text) {
367
+ funcName = nameNode.text;
368
+ label = 'Method';
369
+ }
370
+ }
371
+ else if (nestedId?.type === 'identifier') {
372
+ funcName = nestedId.text;
373
+ }
374
+ }
375
+ }
376
+ // Fallback for other languages (Kotlin uses simple_identifier, Swift uses simple_identifier)
377
+ if (!funcName) {
378
+ const nameNode = node.childForFieldName?.('name') ||
379
+ node.children?.find((c) => c.type === 'identifier' || c.type === 'property_identifier' || c.type === 'simple_identifier');
380
+ funcName = nameNode?.text;
381
+ }
382
+ }
383
+ else if (node.type === 'impl_item') {
384
+ const funcItem = node.children?.find((c) => c.type === 'function_item');
385
+ if (funcItem) {
386
+ const nameNode = funcItem.childForFieldName?.('name') ||
387
+ funcItem.children?.find((c) => c.type === 'identifier');
388
+ funcName = nameNode?.text;
389
+ label = 'Method';
390
+ }
391
+ }
392
+ else if (node.type === 'method_definition') {
393
+ const nameNode = node.childForFieldName?.('name') ||
394
+ node.children?.find((c) => c.type === 'property_identifier');
395
+ funcName = nameNode?.text;
396
+ label = 'Method';
397
+ }
398
+ else if (node.type === 'method_declaration' || node.type === 'constructor_declaration') {
399
+ const nameNode = node.childForFieldName?.('name') ||
400
+ node.children?.find((c) => c.type === 'identifier');
401
+ funcName = nameNode?.text;
402
+ label = 'Method';
403
+ }
404
+ else if (node.type === 'arrow_function' || node.type === 'function_expression') {
405
+ const parent = node.parent;
406
+ if (parent?.type === 'variable_declarator') {
407
+ const nameNode = parent.childForFieldName?.('name') ||
408
+ parent.children?.find((c) => c.type === 'identifier');
409
+ funcName = nameNode?.text;
410
+ }
411
+ }
412
+ else if (node.type === 'method' || node.type === 'singleton_method') {
413
+ const nameNode = node.childForFieldName?.('name') ||
414
+ node.children?.find((c) => c.type === 'identifier');
415
+ funcName = nameNode?.text;
416
+ label = 'Method';
417
+ }
418
+ return { funcName, label };
419
+ };
420
+ /** Yield control to the event loop to prevent UI freezes in hot loops */
421
+ export const yieldToEventLoop = () => new Promise(resolve => setImmediate(resolve));
422
+ // Ruby extensionless filenames recognised as Ruby source
423
+ const RUBY_EXTENSIONLESS_FILES = new Set(['Rakefile', 'Gemfile', 'Guardfile', 'Vagrantfile', 'Brewfile']);
424
+ /** Find a child of `childType` within a sibling node of `siblingType` (used for Kotlin AST) */
425
+ export const findSiblingChild = (parent, siblingType, childType) => {
426
+ for (let i = 0; i < parent.childCount; i++) {
427
+ const sibling = parent.child(i);
428
+ if (sibling?.type === siblingType) {
429
+ for (let j = 0; j < sibling.childCount; j++) {
430
+ const child = sibling.child(j);
431
+ if (child?.type === childType)
432
+ return child;
433
+ }
434
+ }
435
+ }
436
+ return null;
437
+ };
438
+ /** Map file extension to SupportedLanguage enum */
439
+ export const getLanguageFromFilename = (filename) => {
440
+ // TypeScript (including TSX)
441
+ if (filename.endsWith('.tsx'))
442
+ return SupportedLanguages.TypeScript;
443
+ if (filename.endsWith('.ts'))
444
+ return SupportedLanguages.TypeScript;
445
+ // JavaScript (including JSX)
446
+ if (filename.endsWith('.jsx'))
447
+ return SupportedLanguages.JavaScript;
448
+ if (filename.endsWith('.js'))
449
+ return SupportedLanguages.JavaScript;
450
+ // Python
451
+ if (filename.endsWith('.py'))
452
+ return SupportedLanguages.Python;
453
+ // Java
454
+ if (filename.endsWith('.java'))
455
+ return SupportedLanguages.Java;
456
+ // C source files
457
+ if (filename.endsWith('.c'))
458
+ return SupportedLanguages.C;
459
+ // C++ (all common extensions, including .h)
460
+ // .h is parsed as C++ because tree-sitter-cpp is a strict superset of C, so pure-C
461
+ // headers parse correctly, and C++ headers (classes, templates) are handled properly
462
+ if (filename.endsWith('.cpp') || filename.endsWith('.cc') || filename.endsWith('.cxx') ||
463
+ filename.endsWith('.h') || filename.endsWith('.hpp') || filename.endsWith('.hxx') || filename.endsWith('.hh'))
464
+ return SupportedLanguages.CPlusPlus;
465
+ // C#
466
+ if (filename.endsWith('.cs'))
467
+ return SupportedLanguages.CSharp;
468
+ // Go
469
+ if (filename.endsWith('.go'))
470
+ return SupportedLanguages.Go;
471
+ // Rust
472
+ if (filename.endsWith('.rs'))
473
+ return SupportedLanguages.Rust;
474
+ // Kotlin
475
+ if (filename.endsWith('.kt') || filename.endsWith('.kts'))
476
+ return SupportedLanguages.Kotlin;
477
+ // PHP (all common extensions)
478
+ if (filename.endsWith('.php') || filename.endsWith('.phtml') ||
479
+ filename.endsWith('.php3') || filename.endsWith('.php4') ||
480
+ filename.endsWith('.php5') || filename.endsWith('.php8')) {
481
+ return SupportedLanguages.PHP;
482
+ }
483
+ // Ruby (extensions)
484
+ if (filename.endsWith('.rb') || filename.endsWith('.rake') || filename.endsWith('.gemspec')) {
485
+ return SupportedLanguages.Ruby;
486
+ }
487
+ // Ruby (extensionless files)
488
+ const basename = filename.split('/').pop() || filename;
489
+ if (RUBY_EXTENSIONLESS_FILES.has(basename)) {
490
+ return SupportedLanguages.Ruby;
491
+ }
492
+ // Swift (extensions)
493
+ if (filename.endsWith('.swift'))
494
+ return SupportedLanguages.Swift;
495
+ return null;
496
+ };
497
+ const CALL_ARGUMENT_LIST_TYPES = new Set([
498
+ 'arguments',
499
+ 'argument_list',
500
+ 'value_arguments',
501
+ ]);
502
+ /** Extract parameter count and return type from an AST function/method node */
503
+ export const extractMethodSignature = (node) => {
504
+ let parameterCount = 0;
505
+ let returnType;
506
+ let isVariadic = false;
507
+ const paramTypes = [];
508
+ if (!node)
509
+ return { parameterCount, returnType };
510
+ const paramListTypes = new Set([
511
+ 'formal_parameters', 'parameters', 'parameter_list',
512
+ 'function_parameters', 'method_parameters', 'function_value_parameters',
513
+ ]);
514
+ // Node types that indicate variadic/rest parameters
515
+ const VARIADIC_PARAM_TYPES = new Set([
516
+ 'variadic_parameter_declaration', // Go: ...string
517
+ 'variadic_parameter', // Rust: extern "C" fn(...)
518
+ 'spread_parameter', // Java: Object... args
519
+ 'list_splat_pattern', // Python: *args
520
+ 'dictionary_splat_pattern', // Python: **kwargs
521
+ ]);
522
+ const findParameterList = (current) => {
523
+ for (const child of current.children) {
524
+ if (paramListTypes.has(child.type))
525
+ return child;
526
+ }
527
+ for (const child of current.children) {
528
+ const nested = findParameterList(child);
529
+ if (nested)
530
+ return nested;
531
+ }
532
+ return null;
533
+ };
534
+ const parameterList = (paramListTypes.has(node.type) ? node // node itself IS the parameter list (e.g. C# primary constructors)
535
+ : node.childForFieldName?.('parameters')
536
+ ?? findParameterList(node));
537
+ if (parameterList && paramListTypes.has(parameterList.type)) {
538
+ for (const param of parameterList.namedChildren) {
539
+ if (param.type === 'comment')
540
+ continue;
541
+ if (param.text === 'self' || param.text === '&self' || param.text === '&mut self' ||
542
+ param.type === 'self_parameter') {
543
+ continue;
544
+ }
545
+ // Check for variadic parameter types
546
+ if (VARIADIC_PARAM_TYPES.has(param.type)) {
547
+ isVariadic = true;
548
+ continue;
549
+ }
550
+ // TypeScript/JavaScript: rest parameter — required_parameter containing rest_pattern
551
+ if (param.type === 'required_parameter' || param.type === 'optional_parameter') {
552
+ for (const child of param.children) {
553
+ if (child.type === 'rest_pattern') {
554
+ isVariadic = true;
555
+ break;
556
+ }
557
+ }
558
+ if (isVariadic)
559
+ continue;
560
+ }
561
+ // Kotlin: vararg modifier on a regular parameter
562
+ if (param.type === 'parameter' || param.type === 'formal_parameter') {
563
+ const prev = param.previousSibling;
564
+ if (prev?.type === 'parameter_modifiers' && prev.text.includes('vararg')) {
565
+ isVariadic = true;
566
+ }
567
+ }
568
+ parameterCount++;
569
+ // Extract individual parameter type for DI dependency edges
570
+ const paramTypeNode = param.childForFieldName?.('type');
571
+ if (paramTypeNode) {
572
+ const typeName = extractSimpleTypeName(paramTypeNode);
573
+ if (typeName)
574
+ paramTypes.push(typeName);
575
+ }
576
+ }
577
+ // C/C++: bare `...` token in parameter list (not a named child — check all children)
578
+ if (!isVariadic) {
579
+ for (const child of parameterList.children) {
580
+ if (!child.isNamed && child.text === '...') {
581
+ isVariadic = true;
582
+ break;
583
+ }
584
+ }
585
+ }
586
+ }
587
+ // Return type extraction — language-specific field names
588
+ // Go: 'result' field is either a type_identifier or parameter_list (multi-return)
589
+ const goResult = node.childForFieldName?.('result');
590
+ if (goResult) {
591
+ if (goResult.type === 'parameter_list') {
592
+ // Multi-return: extract first parameter's type only (e.g. (*User, error) → *User)
593
+ const firstParam = goResult.firstNamedChild;
594
+ if (firstParam?.type === 'parameter_declaration') {
595
+ const typeNode = firstParam.childForFieldName('type');
596
+ if (typeNode)
597
+ returnType = typeNode.text;
598
+ }
599
+ else if (firstParam) {
600
+ // Unnamed return types: (string, error) — first child is a bare type node
601
+ returnType = firstParam.text;
602
+ }
603
+ }
604
+ else {
605
+ returnType = goResult.text;
606
+ }
607
+ }
608
+ // Rust: 'return_type' field — the value IS the type node (e.g. primitive_type, type_identifier)
609
+ // Skip if the node is a type_annotation (TS/Python), handled by the generic loop below
610
+ if (!returnType) {
611
+ const rustReturn = node.childForFieldName?.('return_type');
612
+ if (rustReturn && rustReturn.type !== 'type_annotation') {
613
+ returnType = rustReturn.text;
614
+ }
615
+ }
616
+ // C/C++: 'type' field on function_definition
617
+ if (!returnType) {
618
+ const cppType = node.childForFieldName?.('type');
619
+ if (cppType && cppType.text !== 'void') {
620
+ returnType = cppType.text;
621
+ }
622
+ }
623
+ // C#: 'returns' field on method_declaration
624
+ if (!returnType) {
625
+ const csReturn = node.childForFieldName?.('returns');
626
+ if (csReturn && csReturn.text !== 'void') {
627
+ returnType = csReturn.text;
628
+ }
629
+ }
630
+ // TS/Rust/Python/C#/Kotlin: type_annotation or return_type child
631
+ if (!returnType) {
632
+ for (const child of node.children) {
633
+ if (child.type === 'type_annotation' || child.type === 'return_type') {
634
+ const typeNode = child.children.find((c) => c.isNamed);
635
+ if (typeNode)
636
+ returnType = typeNode.text;
637
+ }
638
+ }
639
+ }
640
+ // Kotlin: fun getUser(): User — return type is a bare user_type child of
641
+ // function_declaration. The Kotlin grammar does NOT wrap it in type_annotation
642
+ // or return_type; it appears as a direct child after function_value_parameters
643
+ // Note: Kotlin uses function_value_parameters (not a field), so we find it by type
644
+ if (!returnType) {
645
+ let paramsEnd = -1;
646
+ for (let i = 0; i < node.childCount; i++) {
647
+ const child = node.child(i);
648
+ if (!child)
649
+ continue;
650
+ if (child.type === 'function_value_parameters' || child.type === 'value_parameters') {
651
+ paramsEnd = child.endIndex;
652
+ }
653
+ if (paramsEnd >= 0 && child.type === 'user_type' && child.startIndex > paramsEnd) {
654
+ returnType = child.text;
655
+ break;
656
+ }
657
+ }
658
+ }
659
+ if (isVariadic)
660
+ parameterCount = undefined;
661
+ return {
662
+ parameterCount,
663
+ returnType,
664
+ ...(paramTypes.length > 0 ? { parameterTypes: paramTypes } : {}),
665
+ };
666
+ };
667
+ /** Count direct arguments for a call expression across common tree-sitter grammars */
668
+ export const countCallArguments = (callNode) => {
669
+ if (!callNode)
670
+ return undefined;
671
+ // Direct field or direct child (most languages)
672
+ let argsNode = callNode.childForFieldName('arguments')
673
+ ?? callNode.children.find((child) => CALL_ARGUMENT_LIST_TYPES.has(child.type));
674
+ // Kotlin/Swift: call_expression → call_suffix → value_arguments
675
+ // Search one level deeper for languages that wrap arguments in a suffix node
676
+ if (!argsNode) {
677
+ for (const child of callNode.children) {
678
+ if (!child.isNamed)
679
+ continue;
680
+ const nested = child.children.find((gc) => CALL_ARGUMENT_LIST_TYPES.has(gc.type));
681
+ if (nested) {
682
+ argsNode = nested;
683
+ break;
684
+ }
685
+ }
686
+ }
687
+ if (!argsNode)
688
+ return undefined;
689
+ let count = 0;
690
+ for (const child of argsNode.children) {
691
+ if (!child.isNamed)
692
+ continue;
693
+ if (child.type === 'comment')
694
+ continue;
695
+ count++;
696
+ }
697
+ return count;
698
+ };
699
+ // Call-form discrimination
700
+ // AST node types indicating a member-access wrapper around the callee name
701
+ const MEMBER_ACCESS_NODE_TYPES = new Set([
702
+ 'member_expression', // TS/JS: obj.method()
703
+ 'attribute', // Python: obj.method()
704
+ 'member_access_expression', // C#: obj.Method()
705
+ 'field_expression', // Rust/C++: obj.method() / ptr->method()
706
+ 'selector_expression', // Go: obj.Method()
707
+ 'navigation_suffix', // Kotlin/Swift: obj.method() — nameNode sits inside navigation_suffix
708
+ 'member_binding_expression', // C#: user?.Method() — null-conditional access
709
+ ]);
710
+ // Call node types that are inherently constructor invocations
711
+ const CONSTRUCTOR_CALL_NODE_TYPES = new Set([
712
+ 'constructor_invocation', // Kotlin: Foo()
713
+ 'new_expression', // TS/JS/C++: new Foo()
714
+ 'object_creation_expression', // Java/C#/PHP: new Foo()
715
+ 'implicit_object_creation_expression', // C# 9: User u = new(...)
716
+ 'composite_literal', // Go: User{...}
717
+ 'struct_expression', // Rust: User { ... }
718
+ ]);
719
+ // Scoped/qualified call node types (Foo::new() in Rust, Foo::bar() in C++)
720
+ const SCOPED_CALL_NODE_TYPES = new Set([
721
+ 'scoped_identifier', // Rust: Foo::new()
722
+ 'qualified_identifier', // C++: ns::func()
723
+ ]);
724
+ /**
725
+ * Infer whether a captured call site is a free call, member call, or constructor
726
+ *
727
+ * Inspects the AST structure between callNode (@call) and nameNode (@call.name)
728
+ */
729
+ export const inferCallForm = (callNode, nameNode) => {
730
+ // 1. Constructor: callNode itself is a constructor invocation (Kotlin)
731
+ if (CONSTRUCTOR_CALL_NODE_TYPES.has(callNode.type)) {
732
+ return 'constructor';
733
+ }
734
+ // 2. Member call: nameNode's parent is a member-access wrapper
735
+ const nameParent = nameNode.parent;
736
+ if (nameParent && MEMBER_ACCESS_NODE_TYPES.has(nameParent.type)) {
737
+ return 'member';
738
+ }
739
+ // 3. PHP: the callNode itself distinguishes member vs free calls
740
+ if (callNode.type === 'member_call_expression' || callNode.type === 'nullsafe_member_call_expression') {
741
+ return 'member';
742
+ }
743
+ if (callNode.type === 'scoped_call_expression') {
744
+ return 'member'; // static call Foo::bar()
745
+ }
746
+ // 4. Java method_invocation: member if it has an 'object' field
747
+ if (callNode.type === 'method_invocation' && callNode.childForFieldName('object')) {
748
+ return 'member';
749
+ }
750
+ // 4b. Ruby call with receiver: obj.method
751
+ if (callNode.type === 'call' && callNode.childForFieldName('receiver')) {
752
+ return 'member';
753
+ }
754
+ // 5. Scoped calls (Rust Foo::new(), C++ ns::func()): treat as free
755
+ // The receiver is a type, not an instance — handled differently in Phase 3
756
+ if (nameParent && SCOPED_CALL_NODE_TYPES.has(nameParent.type)) {
757
+ return 'free';
758
+ }
759
+ // 6. Default: if nameNode is a direct child of callNode, it's a free call
760
+ if (nameNode.parent === callNode || nameParent?.parent === callNode) {
761
+ return 'free';
762
+ }
763
+ return undefined;
764
+ };
765
+ // Simple receiver identifier types (refuses complex expressions like getUser().save())
766
+ const SIMPLE_RECEIVER_TYPES = new Set([
767
+ 'identifier',
768
+ 'simple_identifier',
769
+ 'variable_name', // PHP $variable (tree-sitter-php)
770
+ 'name', // PHP name node
771
+ 'this', // TS/JS/Java/C# this.method()
772
+ 'self', // Rust/Python self.method()
773
+ 'super', // TS/JS/Java/Kotlin/Ruby super.method()
774
+ 'super_expression', // Kotlin wraps super in super_expression
775
+ 'base', // C# base.Method()
776
+ 'parent', // PHP parent::method()
777
+ 'constant', // Ruby CONSTANT.method() (uppercase identifiers)
778
+ ]);
779
+ /** Extract the receiver identifier for member calls (simple identifiers only) */
780
+ export const extractReceiverName = (nameNode) => {
781
+ const parent = nameNode.parent;
782
+ if (!parent)
783
+ return undefined;
784
+ // PHP: member_call_expression / nullsafe_member_call_expression — receiver is on the callNode
785
+ // Java: method_invocation — receiver is the 'object' field on callNode
786
+ // For these, parent of nameNode is the call itself, so check the call's object field
787
+ const callNode = parent.parent ?? parent;
788
+ let receiver = null;
789
+ // Try standard field names used across grammars
790
+ receiver = parent.childForFieldName('object') // TS/JS member_expression, Python attribute, PHP, Java
791
+ ?? parent.childForFieldName('value') // Rust field_expression
792
+ ?? parent.childForFieldName('operand') // Go selector_expression
793
+ ?? parent.childForFieldName('expression') // C# member_access_expression
794
+ ?? parent.childForFieldName('argument'); // C++ field_expression
795
+ // Java method_invocation: 'object' field is on the callNode, not on nameNode's parent
796
+ if (!receiver && callNode.type === 'method_invocation') {
797
+ receiver = callNode.childForFieldName('object');
798
+ }
799
+ // PHP: member_call_expression has 'object' on the call node
800
+ if (!receiver && (callNode.type === 'member_call_expression' || callNode.type === 'nullsafe_member_call_expression')) {
801
+ receiver = callNode.childForFieldName('object');
802
+ }
803
+ // Ruby: call node has 'receiver' field
804
+ if (!receiver && parent.type === 'call') {
805
+ receiver = parent.childForFieldName('receiver');
806
+ }
807
+ // PHP scoped_call_expression (parent::method(), self::method()):
808
+ // nameNode's direct parent IS the scoped_call_expression (name is a direct child)
809
+ if (!receiver && (parent.type === 'scoped_call_expression' || callNode.type === 'scoped_call_expression')) {
810
+ const scopedCall = parent.type === 'scoped_call_expression' ? parent : callNode;
811
+ receiver = scopedCall.childForFieldName('scope');
812
+ // relative_scope wraps 'parent'/'self'/'static' — unwrap to get the keyword
813
+ if (receiver?.type === 'relative_scope') {
814
+ receiver = receiver.firstChild;
815
+ }
816
+ }
817
+ // C# null-conditional: user?.Save() → conditional_access_expression wraps member_binding_expression
818
+ if (!receiver && parent.type === 'member_binding_expression') {
819
+ const condAccess = parent.parent;
820
+ if (condAccess?.type === 'conditional_access_expression') {
821
+ receiver = condAccess.firstNamedChild;
822
+ }
823
+ }
824
+ // Kotlin/Swift: navigation_expression target is the first child
825
+ if (!receiver && parent.type === 'navigation_suffix') {
826
+ const navExpr = parent.parent;
827
+ if (navExpr?.type === 'navigation_expression') {
828
+ // First named child is the target (receiver)
829
+ for (const child of navExpr.children) {
830
+ if (child.isNamed && child !== parent) {
831
+ receiver = child;
832
+ break;
833
+ }
834
+ }
835
+ }
836
+ }
837
+ if (!receiver)
838
+ return undefined;
839
+ // Only capture simple identifiers — refuse complex expressions
840
+ if (SIMPLE_RECEIVER_TYPES.has(receiver.type)) {
841
+ return receiver.text;
842
+ }
843
+ // Python super().method(): receiver is a call node `super()` — extract the function name
844
+ if (receiver.type === 'call') {
845
+ const func = receiver.childForFieldName('function');
846
+ if (func?.text === 'super')
847
+ return 'super';
848
+ }
849
+ return undefined;
850
+ };
851
+ /**
852
+ * Extract the raw receiver AST node for a member call (including chained calls)
853
+ *
854
+ * Unlike extractReceiverName, returns the receiver node regardless of its type
855
+ */
856
+ export const extractReceiverNode = (nameNode) => {
857
+ const parent = nameNode.parent;
858
+ if (!parent)
859
+ return undefined;
860
+ const callNode = parent.parent ?? parent;
861
+ let receiver = null;
862
+ receiver = parent.childForFieldName('object')
863
+ ?? parent.childForFieldName('value')
864
+ ?? parent.childForFieldName('operand')
865
+ ?? parent.childForFieldName('expression')
866
+ ?? parent.childForFieldName('argument');
867
+ if (!receiver && callNode.type === 'method_invocation') {
868
+ receiver = callNode.childForFieldName('object');
869
+ }
870
+ if (!receiver && (callNode.type === 'member_call_expression' || callNode.type === 'nullsafe_member_call_expression')) {
871
+ receiver = callNode.childForFieldName('object');
872
+ }
873
+ if (!receiver && parent.type === 'call') {
874
+ receiver = parent.childForFieldName('receiver');
875
+ }
876
+ if (!receiver && (parent.type === 'scoped_call_expression' || callNode.type === 'scoped_call_expression')) {
877
+ const scopedCall = parent.type === 'scoped_call_expression' ? parent : callNode;
878
+ receiver = scopedCall.childForFieldName('scope');
879
+ if (receiver?.type === 'relative_scope') {
880
+ receiver = receiver.firstChild;
881
+ }
882
+ }
883
+ if (!receiver && parent.type === 'member_binding_expression') {
884
+ const condAccess = parent.parent;
885
+ if (condAccess?.type === 'conditional_access_expression') {
886
+ receiver = condAccess.firstNamedChild;
887
+ }
888
+ }
889
+ if (!receiver && parent.type === 'navigation_suffix') {
890
+ const navExpr = parent.parent;
891
+ if (navExpr?.type === 'navigation_expression') {
892
+ for (const child of navExpr.children) {
893
+ if (child.isNamed && child !== parent) {
894
+ receiver = child;
895
+ break;
896
+ }
897
+ }
898
+ }
899
+ }
900
+ return receiver ?? undefined;
901
+ };
902
+ export const isVerboseIngestionEnabled = () => {
903
+ const raw = process.env.CODE_MAPPER_VERBOSE;
904
+ if (!raw)
905
+ return false;
906
+ const value = raw.toLowerCase();
907
+ return value === '1' || value === 'true' || value === 'yes';
908
+ };
909
+ // Chained-call extraction
910
+ // Node types representing call expressions across supported languages
911
+ export const CALL_EXPRESSION_TYPES = new Set([
912
+ 'call_expression', // TS/JS/C/C++/Go/Rust
913
+ 'method_invocation', // Java
914
+ 'member_call_expression', // PHP
915
+ 'nullsafe_member_call_expression', // PHP ?.
916
+ 'call', // Python/Ruby
917
+ 'invocation_expression', // C#
918
+ ]);
919
+ // Hard limit on chain depth to prevent runaway recursion
920
+ export const MAX_CHAIN_DEPTH = 3;
921
+ /**
922
+ * Walk a receiver call expression to accumulate a chain of intermediate method names
923
+ *
924
+ * For `svc.getUser().save()`: { chain: ['getUser'], baseReceiverName: 'svc' }
925
+ */
926
+ export function extractCallChain(receiverCallNode) {
927
+ const chain = [];
928
+ let current = receiverCallNode;
929
+ while (CALL_EXPRESSION_TYPES.has(current.type) && chain.length < MAX_CHAIN_DEPTH) {
930
+ // Extract the method name from this call node
931
+ const funcNode = current.childForFieldName?.('function')
932
+ ?? current.childForFieldName?.('name')
933
+ ?? current.childForFieldName?.('method'); // Ruby `call` node
934
+ let methodName;
935
+ let innerReceiver = null;
936
+ if (funcNode) {
937
+ // member_expression / attribute: last named child is the method identifier
938
+ methodName = funcNode.lastNamedChild?.text ?? funcNode.text;
939
+ }
940
+ // Kotlin/Swift: call_expression exposes callee as firstNamedChild, not a field
941
+ // navigation_expression: method name is in navigation_suffix -> simple_identifier
942
+ if (!funcNode && current.type === 'call_expression') {
943
+ const callee = current.firstNamedChild;
944
+ if (callee?.type === 'navigation_expression') {
945
+ const suffix = callee.lastNamedChild;
946
+ if (suffix?.type === 'navigation_suffix') {
947
+ methodName = suffix.lastNamedChild?.text;
948
+ // The receiver is the part of navigation_expression before the suffix
949
+ for (let i = 0; i < callee.namedChildCount; i++) {
950
+ const child = callee.namedChild(i);
951
+ if (child && child.type !== 'navigation_suffix') {
952
+ innerReceiver = child;
953
+ break;
954
+ }
955
+ }
956
+ }
957
+ }
958
+ }
959
+ if (!methodName)
960
+ break;
961
+ chain.unshift(methodName); // build chain outermost-last
962
+ // Walk into the receiver of this call to continue the chain
963
+ if (!innerReceiver && funcNode) {
964
+ innerReceiver = funcNode.childForFieldName?.('object')
965
+ ?? funcNode.childForFieldName?.('value')
966
+ ?? funcNode.childForFieldName?.('operand')
967
+ ?? funcNode.childForFieldName?.('expression');
968
+ }
969
+ // Java method_invocation: object field is on the call node
970
+ if (!innerReceiver && current.type === 'method_invocation') {
971
+ innerReceiver = current.childForFieldName?.('object');
972
+ }
973
+ // PHP member_call_expression
974
+ if (!innerReceiver && (current.type === 'member_call_expression' || current.type === 'nullsafe_member_call_expression')) {
975
+ innerReceiver = current.childForFieldName?.('object');
976
+ }
977
+ // Ruby `call` node: receiver field is on the call node itself
978
+ if (!innerReceiver && current.type === 'call') {
979
+ innerReceiver = current.childForFieldName?.('receiver');
980
+ }
981
+ if (!innerReceiver)
982
+ break;
983
+ if (CALL_EXPRESSION_TYPES.has(innerReceiver.type)) {
984
+ current = innerReceiver; // continue walking
985
+ }
986
+ else {
987
+ // Reached a simple identifier — the base receiver
988
+ return { chain, baseReceiverName: innerReceiver.text || undefined };
989
+ }
990
+ }
991
+ return chain.length > 0 ? { chain, baseReceiverName: undefined } : undefined;
992
+ }