mnehmos.trace.mcp 1.0.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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1662 -0
  3. package/dist/adapters/bootstrap.d.ts +29 -0
  4. package/dist/adapters/bootstrap.d.ts.map +1 -0
  5. package/dist/adapters/bootstrap.js +46 -0
  6. package/dist/adapters/bootstrap.js.map +1 -0
  7. package/dist/adapters/errors.d.ts +94 -0
  8. package/dist/adapters/errors.d.ts.map +1 -0
  9. package/dist/adapters/errors.js +107 -0
  10. package/dist/adapters/errors.js.map +1 -0
  11. package/dist/adapters/graphql/index.d.ts +9 -0
  12. package/dist/adapters/graphql/index.d.ts.map +1 -0
  13. package/dist/adapters/graphql/index.js +9 -0
  14. package/dist/adapters/graphql/index.js.map +1 -0
  15. package/dist/adapters/graphql/sdl-parser.d.ts +74 -0
  16. package/dist/adapters/graphql/sdl-parser.d.ts.map +1 -0
  17. package/dist/adapters/graphql/sdl-parser.js +559 -0
  18. package/dist/adapters/graphql/sdl-parser.js.map +1 -0
  19. package/dist/adapters/grpc/adapter.d.ts +76 -0
  20. package/dist/adapters/grpc/adapter.d.ts.map +1 -0
  21. package/dist/adapters/grpc/adapter.js +362 -0
  22. package/dist/adapters/grpc/adapter.js.map +1 -0
  23. package/dist/adapters/grpc/index.d.ts +10 -0
  24. package/dist/adapters/grpc/index.d.ts.map +1 -0
  25. package/dist/adapters/grpc/index.js +12 -0
  26. package/dist/adapters/grpc/index.js.map +1 -0
  27. package/dist/adapters/grpc/proto-parser.d.ts +76 -0
  28. package/dist/adapters/grpc/proto-parser.d.ts.map +1 -0
  29. package/dist/adapters/grpc/proto-parser.js +523 -0
  30. package/dist/adapters/grpc/proto-parser.js.map +1 -0
  31. package/dist/adapters/grpc/type-converter.d.ts +43 -0
  32. package/dist/adapters/grpc/type-converter.d.ts.map +1 -0
  33. package/dist/adapters/grpc/type-converter.js +270 -0
  34. package/dist/adapters/grpc/type-converter.js.map +1 -0
  35. package/dist/adapters/grpc/types.d.ts +85 -0
  36. package/dist/adapters/grpc/types.d.ts.map +1 -0
  37. package/dist/adapters/grpc/types.js +7 -0
  38. package/dist/adapters/grpc/types.js.map +1 -0
  39. package/dist/adapters/index.d.ts +39 -0
  40. package/dist/adapters/index.d.ts.map +1 -0
  41. package/dist/adapters/index.js +50 -0
  42. package/dist/adapters/index.js.map +1 -0
  43. package/dist/adapters/mcp.d.ts +23 -0
  44. package/dist/adapters/mcp.d.ts.map +1 -0
  45. package/dist/adapters/mcp.js +293 -0
  46. package/dist/adapters/mcp.js.map +1 -0
  47. package/dist/adapters/openapi/adapter.d.ts +213 -0
  48. package/dist/adapters/openapi/adapter.d.ts.map +1 -0
  49. package/dist/adapters/openapi/adapter.js +557 -0
  50. package/dist/adapters/openapi/adapter.js.map +1 -0
  51. package/dist/adapters/openapi/convert.d.ts +120 -0
  52. package/dist/adapters/openapi/convert.d.ts.map +1 -0
  53. package/dist/adapters/openapi/convert.js +363 -0
  54. package/dist/adapters/openapi/convert.js.map +1 -0
  55. package/dist/adapters/openapi/index.d.ts +39 -0
  56. package/dist/adapters/openapi/index.d.ts.map +1 -0
  57. package/dist/adapters/openapi/index.js +48 -0
  58. package/dist/adapters/openapi/index.js.map +1 -0
  59. package/dist/adapters/openapi/parser.d.ts +95 -0
  60. package/dist/adapters/openapi/parser.d.ts.map +1 -0
  61. package/dist/adapters/openapi/parser.js +171 -0
  62. package/dist/adapters/openapi/parser.js.map +1 -0
  63. package/dist/adapters/registry.d.ts +116 -0
  64. package/dist/adapters/registry.d.ts.map +1 -0
  65. package/dist/adapters/registry.js +246 -0
  66. package/dist/adapters/registry.js.map +1 -0
  67. package/dist/adapters/trpc/adapter.d.ts +159 -0
  68. package/dist/adapters/trpc/adapter.d.ts.map +1 -0
  69. package/dist/adapters/trpc/adapter.js +223 -0
  70. package/dist/adapters/trpc/adapter.js.map +1 -0
  71. package/dist/adapters/trpc/extractor.d.ts +218 -0
  72. package/dist/adapters/trpc/extractor.d.ts.map +1 -0
  73. package/dist/adapters/trpc/extractor.js +708 -0
  74. package/dist/adapters/trpc/extractor.js.map +1 -0
  75. package/dist/adapters/trpc/index.d.ts +31 -0
  76. package/dist/adapters/trpc/index.d.ts.map +1 -0
  77. package/dist/adapters/trpc/index.js +40 -0
  78. package/dist/adapters/trpc/index.js.map +1 -0
  79. package/dist/adapters/trpc/parser.d.ts +119 -0
  80. package/dist/adapters/trpc/parser.d.ts.map +1 -0
  81. package/dist/adapters/trpc/parser.js +128 -0
  82. package/dist/adapters/trpc/parser.js.map +1 -0
  83. package/dist/compare/index.d.ts +33 -0
  84. package/dist/compare/index.d.ts.map +1 -0
  85. package/dist/compare/index.js +261 -0
  86. package/dist/compare/index.js.map +1 -0
  87. package/dist/core/types.d.ts +188 -0
  88. package/dist/core/types.d.ts.map +1 -0
  89. package/dist/core/types.js +9 -0
  90. package/dist/core/types.js.map +1 -0
  91. package/dist/extract/index.d.ts +26 -0
  92. package/dist/extract/index.d.ts.map +1 -0
  93. package/dist/extract/index.js +44 -0
  94. package/dist/extract/index.js.map +1 -0
  95. package/dist/index.d.ts +9 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +674 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/languages/base.d.ts +57 -0
  100. package/dist/languages/base.d.ts.map +1 -0
  101. package/dist/languages/base.js +6 -0
  102. package/dist/languages/base.js.map +1 -0
  103. package/dist/languages/bootstrap.d.ts +10 -0
  104. package/dist/languages/bootstrap.d.ts.map +1 -0
  105. package/dist/languages/bootstrap.js +25 -0
  106. package/dist/languages/bootstrap.js.map +1 -0
  107. package/dist/languages/go/handlers/chi.d.ts +24 -0
  108. package/dist/languages/go/handlers/chi.d.ts.map +1 -0
  109. package/dist/languages/go/handlers/chi.js +205 -0
  110. package/dist/languages/go/handlers/chi.js.map +1 -0
  111. package/dist/languages/go/handlers/gin.d.ts +24 -0
  112. package/dist/languages/go/handlers/gin.d.ts.map +1 -0
  113. package/dist/languages/go/handlers/gin.js +156 -0
  114. package/dist/languages/go/handlers/gin.js.map +1 -0
  115. package/dist/languages/go/handlers/stdlib.d.ts +19 -0
  116. package/dist/languages/go/handlers/stdlib.d.ts.map +1 -0
  117. package/dist/languages/go/handlers/stdlib.js +112 -0
  118. package/dist/languages/go/handlers/stdlib.js.map +1 -0
  119. package/dist/languages/go/index.d.ts +18 -0
  120. package/dist/languages/go/index.d.ts.map +1 -0
  121. package/dist/languages/go/index.js +20 -0
  122. package/dist/languages/go/index.js.map +1 -0
  123. package/dist/languages/go/parser.d.ts +33 -0
  124. package/dist/languages/go/parser.d.ts.map +1 -0
  125. package/dist/languages/go/parser.js +95 -0
  126. package/dist/languages/go/parser.js.map +1 -0
  127. package/dist/languages/go/struct-extractor.d.ts +59 -0
  128. package/dist/languages/go/struct-extractor.d.ts.map +1 -0
  129. package/dist/languages/go/struct-extractor.js +483 -0
  130. package/dist/languages/go/struct-extractor.js.map +1 -0
  131. package/dist/languages/go/tag-parser.d.ts +62 -0
  132. package/dist/languages/go/tag-parser.d.ts.map +1 -0
  133. package/dist/languages/go/tag-parser.js +108 -0
  134. package/dist/languages/go/tag-parser.js.map +1 -0
  135. package/dist/languages/go/type-converter.d.ts +32 -0
  136. package/dist/languages/go/type-converter.d.ts.map +1 -0
  137. package/dist/languages/go/type-converter.js +226 -0
  138. package/dist/languages/go/type-converter.js.map +1 -0
  139. package/dist/languages/go/types.d.ts +153 -0
  140. package/dist/languages/go/types.d.ts.map +1 -0
  141. package/dist/languages/go/types.js +6 -0
  142. package/dist/languages/go/types.js.map +1 -0
  143. package/dist/languages/import-resolver.d.ts +645 -0
  144. package/dist/languages/import-resolver.d.ts.map +1 -0
  145. package/dist/languages/import-resolver.js +1278 -0
  146. package/dist/languages/import-resolver.js.map +1 -0
  147. package/dist/languages/index.d.ts +34 -0
  148. package/dist/languages/index.d.ts.map +1 -0
  149. package/dist/languages/index.js +93 -0
  150. package/dist/languages/index.js.map +1 -0
  151. package/dist/languages/json-schema.d.ts +40 -0
  152. package/dist/languages/json-schema.d.ts.map +1 -0
  153. package/dist/languages/json-schema.js +188 -0
  154. package/dist/languages/json-schema.js.map +1 -0
  155. package/dist/languages/python-ast/index.d.ts +8 -0
  156. package/dist/languages/python-ast/index.d.ts.map +1 -0
  157. package/dist/languages/python-ast/index.js +7 -0
  158. package/dist/languages/python-ast/index.js.map +1 -0
  159. package/dist/languages/python-ast/parser.d.ts +174 -0
  160. package/dist/languages/python-ast/parser.d.ts.map +1 -0
  161. package/dist/languages/python-ast/parser.js +1205 -0
  162. package/dist/languages/python-ast/parser.js.map +1 -0
  163. package/dist/languages/python-ast/type-resolver.d.ts +75 -0
  164. package/dist/languages/python-ast/type-resolver.d.ts.map +1 -0
  165. package/dist/languages/python-ast/type-resolver.js +421 -0
  166. package/dist/languages/python-ast/type-resolver.js.map +1 -0
  167. package/dist/languages/python-ast/types.d.ts +216 -0
  168. package/dist/languages/python-ast/types.d.ts.map +1 -0
  169. package/dist/languages/python-ast/types.js +6 -0
  170. package/dist/languages/python-ast/types.js.map +1 -0
  171. package/dist/languages/python.d.ts +55 -0
  172. package/dist/languages/python.d.ts.map +1 -0
  173. package/dist/languages/python.js +311 -0
  174. package/dist/languages/python.js.map +1 -0
  175. package/dist/languages/typescript.d.ts +272 -0
  176. package/dist/languages/typescript.d.ts.map +1 -0
  177. package/dist/languages/typescript.js +1381 -0
  178. package/dist/languages/typescript.js.map +1 -0
  179. package/dist/patterns/base.d.ts +146 -0
  180. package/dist/patterns/base.d.ts.map +1 -0
  181. package/dist/patterns/base.js +89 -0
  182. package/dist/patterns/base.js.map +1 -0
  183. package/dist/patterns/errors.d.ts +172 -0
  184. package/dist/patterns/errors.d.ts.map +1 -0
  185. package/dist/patterns/errors.js +185 -0
  186. package/dist/patterns/errors.js.map +1 -0
  187. package/dist/patterns/extractors.d.ts +170 -0
  188. package/dist/patterns/extractors.d.ts.map +1 -0
  189. package/dist/patterns/extractors.js +305 -0
  190. package/dist/patterns/extractors.js.map +1 -0
  191. package/dist/patterns/graphql/apollo-client.d.ts +80 -0
  192. package/dist/patterns/graphql/apollo-client.d.ts.map +1 -0
  193. package/dist/patterns/graphql/apollo-client.js +800 -0
  194. package/dist/patterns/graphql/apollo-client.js.map +1 -0
  195. package/dist/patterns/graphql/apollo-server.d.ts +55 -0
  196. package/dist/patterns/graphql/apollo-server.d.ts.map +1 -0
  197. package/dist/patterns/graphql/apollo-server.js +523 -0
  198. package/dist/patterns/graphql/apollo-server.js.map +1 -0
  199. package/dist/patterns/graphql/index.d.ts +11 -0
  200. package/dist/patterns/graphql/index.d.ts.map +1 -0
  201. package/dist/patterns/graphql/index.js +12 -0
  202. package/dist/patterns/graphql/index.js.map +1 -0
  203. package/dist/patterns/graphql/types.d.ts +213 -0
  204. package/dist/patterns/graphql/types.d.ts.map +1 -0
  205. package/dist/patterns/graphql/types.js +16 -0
  206. package/dist/patterns/graphql/types.js.map +1 -0
  207. package/dist/patterns/http-clients/axios.d.ts +148 -0
  208. package/dist/patterns/http-clients/axios.d.ts.map +1 -0
  209. package/dist/patterns/http-clients/axios.js +652 -0
  210. package/dist/patterns/http-clients/axios.js.map +1 -0
  211. package/dist/patterns/http-clients/fetch.d.ts +88 -0
  212. package/dist/patterns/http-clients/fetch.d.ts.map +1 -0
  213. package/dist/patterns/http-clients/fetch.js +364 -0
  214. package/dist/patterns/http-clients/fetch.js.map +1 -0
  215. package/dist/patterns/http-clients/index.d.ts +36 -0
  216. package/dist/patterns/http-clients/index.d.ts.map +1 -0
  217. package/dist/patterns/http-clients/index.js +50 -0
  218. package/dist/patterns/http-clients/index.js.map +1 -0
  219. package/dist/patterns/http-clients/property-access.d.ts +46 -0
  220. package/dist/patterns/http-clients/property-access.d.ts.map +1 -0
  221. package/dist/patterns/http-clients/property-access.js +818 -0
  222. package/dist/patterns/http-clients/property-access.js.map +1 -0
  223. package/dist/patterns/http-clients/type-inference.d.ts +48 -0
  224. package/dist/patterns/http-clients/type-inference.d.ts.map +1 -0
  225. package/dist/patterns/http-clients/type-inference.js +293 -0
  226. package/dist/patterns/http-clients/type-inference.js.map +1 -0
  227. package/dist/patterns/http-clients/types.d.ts +168 -0
  228. package/dist/patterns/http-clients/types.d.ts.map +1 -0
  229. package/dist/patterns/http-clients/types.js +10 -0
  230. package/dist/patterns/http-clients/types.js.map +1 -0
  231. package/dist/patterns/http-clients/url-extractor.d.ts +53 -0
  232. package/dist/patterns/http-clients/url-extractor.d.ts.map +1 -0
  233. package/dist/patterns/http-clients/url-extractor.js +338 -0
  234. package/dist/patterns/http-clients/url-extractor.js.map +1 -0
  235. package/dist/patterns/index.d.ts +44 -0
  236. package/dist/patterns/index.d.ts.map +1 -0
  237. package/dist/patterns/index.js +49 -0
  238. package/dist/patterns/index.js.map +1 -0
  239. package/dist/patterns/python/aiohttp.d.ts +21 -0
  240. package/dist/patterns/python/aiohttp.d.ts.map +1 -0
  241. package/dist/patterns/python/aiohttp.js +188 -0
  242. package/dist/patterns/python/aiohttp.js.map +1 -0
  243. package/dist/patterns/python/httpx.d.ts +20 -0
  244. package/dist/patterns/python/httpx.d.ts.map +1 -0
  245. package/dist/patterns/python/httpx.js +183 -0
  246. package/dist/patterns/python/httpx.js.map +1 -0
  247. package/dist/patterns/python/index.d.ts +32 -0
  248. package/dist/patterns/python/index.d.ts.map +1 -0
  249. package/dist/patterns/python/index.js +63 -0
  250. package/dist/patterns/python/index.js.map +1 -0
  251. package/dist/patterns/python/property-access.d.ts +27 -0
  252. package/dist/patterns/python/property-access.d.ts.map +1 -0
  253. package/dist/patterns/python/property-access.js +132 -0
  254. package/dist/patterns/python/property-access.js.map +1 -0
  255. package/dist/patterns/python/requests.d.ts +19 -0
  256. package/dist/patterns/python/requests.d.ts.map +1 -0
  257. package/dist/patterns/python/requests.js +239 -0
  258. package/dist/patterns/python/requests.js.map +1 -0
  259. package/dist/patterns/python/types.d.ts +95 -0
  260. package/dist/patterns/python/types.d.ts.map +1 -0
  261. package/dist/patterns/python/types.js +43 -0
  262. package/dist/patterns/python/types.js.map +1 -0
  263. package/dist/patterns/registry.d.ts +181 -0
  264. package/dist/patterns/registry.d.ts.map +1 -0
  265. package/dist/patterns/registry.js +304 -0
  266. package/dist/patterns/registry.js.map +1 -0
  267. package/dist/patterns/rest/express.d.ts +78 -0
  268. package/dist/patterns/rest/express.d.ts.map +1 -0
  269. package/dist/patterns/rest/express.js +289 -0
  270. package/dist/patterns/rest/express.js.map +1 -0
  271. package/dist/patterns/rest/fastify.d.ts +93 -0
  272. package/dist/patterns/rest/fastify.d.ts.map +1 -0
  273. package/dist/patterns/rest/fastify.js +420 -0
  274. package/dist/patterns/rest/fastify.js.map +1 -0
  275. package/dist/patterns/rest/index.d.ts +31 -0
  276. package/dist/patterns/rest/index.d.ts.map +1 -0
  277. package/dist/patterns/rest/index.js +45 -0
  278. package/dist/patterns/rest/index.js.map +1 -0
  279. package/dist/patterns/rest/middleware.d.ts +25 -0
  280. package/dist/patterns/rest/middleware.d.ts.map +1 -0
  281. package/dist/patterns/rest/middleware.js +219 -0
  282. package/dist/patterns/rest/middleware.js.map +1 -0
  283. package/dist/patterns/rest/path-parser.d.ts +50 -0
  284. package/dist/patterns/rest/path-parser.d.ts.map +1 -0
  285. package/dist/patterns/rest/path-parser.js +137 -0
  286. package/dist/patterns/rest/path-parser.js.map +1 -0
  287. package/dist/patterns/rest/response-inference.d.ts +44 -0
  288. package/dist/patterns/rest/response-inference.d.ts.map +1 -0
  289. package/dist/patterns/rest/response-inference.js +218 -0
  290. package/dist/patterns/rest/response-inference.js.map +1 -0
  291. package/dist/patterns/rest/types.d.ts +102 -0
  292. package/dist/patterns/rest/types.d.ts.map +1 -0
  293. package/dist/patterns/rest/types.js +10 -0
  294. package/dist/patterns/rest/types.js.map +1 -0
  295. package/dist/patterns/types.d.ts +105 -0
  296. package/dist/patterns/types.d.ts.map +1 -0
  297. package/dist/patterns/types.js +11 -0
  298. package/dist/patterns/types.js.map +1 -0
  299. package/dist/report/index.d.ts +11 -0
  300. package/dist/report/index.d.ts.map +1 -0
  301. package/dist/report/index.js +55 -0
  302. package/dist/report/index.js.map +1 -0
  303. package/dist/tools/contract-comments.d.ts +48 -0
  304. package/dist/tools/contract-comments.d.ts.map +1 -0
  305. package/dist/tools/contract-comments.js +130 -0
  306. package/dist/tools/contract-comments.js.map +1 -0
  307. package/dist/tools/index.d.ts +6 -0
  308. package/dist/tools/index.d.ts.map +1 -0
  309. package/dist/tools/index.js +6 -0
  310. package/dist/tools/index.js.map +1 -0
  311. package/dist/tools/scaffold.d.ts +38 -0
  312. package/dist/tools/scaffold.d.ts.map +1 -0
  313. package/dist/tools/scaffold.js +373 -0
  314. package/dist/tools/scaffold.js.map +1 -0
  315. package/dist/trace/index.d.ts +28 -0
  316. package/dist/trace/index.d.ts.map +1 -0
  317. package/dist/trace/index.js +45 -0
  318. package/dist/trace/index.js.map +1 -0
  319. package/dist/types.d.ts +135 -0
  320. package/dist/types.d.ts.map +1 -0
  321. package/dist/types.js +22 -0
  322. package/dist/types.js.map +1 -0
  323. package/dist/watch/cache.d.ts +41 -0
  324. package/dist/watch/cache.d.ts.map +1 -0
  325. package/dist/watch/cache.js +230 -0
  326. package/dist/watch/cache.js.map +1 -0
  327. package/dist/watch/index.d.ts +9 -0
  328. package/dist/watch/index.d.ts.map +1 -0
  329. package/dist/watch/index.js +7 -0
  330. package/dist/watch/index.js.map +1 -0
  331. package/dist/watch/project.d.ts +128 -0
  332. package/dist/watch/project.d.ts.map +1 -0
  333. package/dist/watch/project.js +152 -0
  334. package/dist/watch/project.js.map +1 -0
  335. package/dist/watch/watcher.d.ts +76 -0
  336. package/dist/watch/watcher.d.ts.map +1 -0
  337. package/dist/watch/watcher.js +235 -0
  338. package/dist/watch/watcher.js.map +1 -0
  339. package/package.json +70 -0
@@ -0,0 +1,818 @@
1
+ /**
2
+ * Property Access Tracking
3
+ *
4
+ * Track property accesses on HTTP client response data to infer types.
5
+ * This enables consumer schema inference from how response data is used.
6
+ *
7
+ * @module patterns/http-clients/property-access
8
+ * @see .context/ADR-P2-3-HTTP-CLIENT-TRACING.md
9
+ */
10
+ import { Node, SyntaxKind } from 'ts-morph';
11
+ /* ═══════════════════════════════════════════════════════════════════════════
12
+ * 🔍 Public API
13
+ * ═══════════════════════════════════════════════════════════════════════════ */
14
+ /**
15
+ * Track all property accesses on a variable assigned from a call expression.
16
+ *
17
+ * Handles:
18
+ * - Direct access: `response.data`
19
+ * - Chained access: `response.data.user.name`
20
+ * - Destructuring: `const { data } = response`
21
+ * - Optional chaining: `response?.data?.user`
22
+ *
23
+ * @param callNode - The call expression node
24
+ * @param variableName - Optional variable name to track (derived if not provided)
25
+ * @returns Array of property accesses found
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const accesses = trackPropertyAccesses(fetchCall);
30
+ * // [{ path: 'data.user.name', segments: ['data', 'user', 'name'] }]
31
+ * ```
32
+ */
33
+ export function trackPropertyAccesses(callNode, variableName) {
34
+ const accesses = [];
35
+ if (!Node.isCallExpression(callNode)) {
36
+ return accesses;
37
+ }
38
+ // First, find the innermost fetch/axios call in the chain
39
+ const rootCall = findRootCallInChain(callNode);
40
+ // Find the variable name if not provided
41
+ const varName = variableName || findVariableName(rootCall);
42
+ // Check for destructuring patterns first
43
+ const destructuringAccesses = findDestructuringFromCall(rootCall);
44
+ for (const access of destructuringAccesses) {
45
+ addUniqueAccess(accesses, access);
46
+ }
47
+ // If we have a variable name, track direct accesses on it
48
+ if (varName) {
49
+ // Get the source file to search for usages
50
+ const sourceFile = rootCall.getSourceFile();
51
+ // Find all identifiers with this name in the file
52
+ const identifiers = sourceFile.getDescendantsOfKind(SyntaxKind.Identifier);
53
+ for (const id of identifiers) {
54
+ if (id.getText() !== varName)
55
+ continue;
56
+ // Skip the declaration itself
57
+ const parent = id.getParent();
58
+ if (Node.isVariableDeclaration(parent))
59
+ continue;
60
+ // Check if this is a property access
61
+ const propertyAccess = findFullPropertyAccessChain(id, varName);
62
+ if (propertyAccess) {
63
+ addUniqueAccess(accesses, propertyAccess);
64
+ }
65
+ // Check for element access (array index)
66
+ const elementAccess = findElementAccessChain(id, varName);
67
+ if (elementAccess) {
68
+ addUniqueAccess(accesses, elementAccess);
69
+ }
70
+ }
71
+ }
72
+ // Track accesses in chained .then() callbacks starting from root
73
+ const chainedAccesses = trackChainedAccesses(rootCall);
74
+ for (const access of chainedAccesses) {
75
+ addUniqueAccess(accesses, access);
76
+ }
77
+ // Find ALL .then() callbacks in the entire call tree and extract accesses
78
+ const allThenAccesses = findAllThenCallbackAccesses(callNode);
79
+ for (const access of allThenAccesses) {
80
+ addUniqueAccess(accesses, access);
81
+ }
82
+ return accesses;
83
+ }
84
+ /**
85
+ * Build an inferred type structure from property accesses.
86
+ *
87
+ * @param accesses - Property accesses observed
88
+ * @returns Nested object representing the inferred type structure
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const accesses = [{ path: 'user.name' }, { path: 'user.email' }];
93
+ * const type = buildTypeFromAccesses(accesses);
94
+ * // { user: { name: 'unknown', email: 'unknown' } }
95
+ * ```
96
+ */
97
+ export function buildTypeFromAccesses(accesses) {
98
+ const result = {};
99
+ for (const access of accesses) {
100
+ let current = result;
101
+ const segments = access.segments;
102
+ for (let i = 0; i < segments.length; i++) {
103
+ const segment = segments[i];
104
+ // Handle array access segments
105
+ if (segment.startsWith('[') && segment.endsWith(']')) {
106
+ // Mark current level as array
107
+ if (!current._isArray) {
108
+ current._isArray = true;
109
+ current._element = {};
110
+ }
111
+ // Continue building inside element
112
+ if (i < segments.length - 1) {
113
+ current = current._element;
114
+ }
115
+ continue;
116
+ }
117
+ if (i === segments.length - 1) {
118
+ // Leaf node - mark as unknown type
119
+ if (!(segment in current)) {
120
+ current[segment] = 'unknown';
121
+ }
122
+ }
123
+ else {
124
+ // Intermediate node - ensure it's an object
125
+ if (!(segment in current) || typeof current[segment] !== 'object') {
126
+ current[segment] = {};
127
+ }
128
+ current = current[segment];
129
+ }
130
+ }
131
+ }
132
+ return result;
133
+ }
134
+ /* ═══════════════════════════════════════════════════════════════════════════
135
+ * 🔗 Chain Navigation
136
+ * ═══════════════════════════════════════════════════════════════════════════ */
137
+ /**
138
+ * Find the root call expression in a method chain.
139
+ *
140
+ * For `fetch().then().then()`, returns `fetch()`.
141
+ *
142
+ * @internal
143
+ */
144
+ function findRootCallInChain(callNode) {
145
+ if (!Node.isCallExpression(callNode)) {
146
+ return callNode;
147
+ }
148
+ const expr = callNode.getExpression();
149
+ // If it's a property access like .then(), walk down
150
+ if (Node.isPropertyAccessExpression(expr)) {
151
+ const innerExpr = expr.getExpression();
152
+ if (Node.isCallExpression(innerExpr)) {
153
+ return findRootCallInChain(innerExpr);
154
+ }
155
+ }
156
+ return callNode;
157
+ }
158
+ /**
159
+ * Collect all call expressions in a method chain.
160
+ *
161
+ * @internal
162
+ */
163
+ function collectChainedCalls(startNode) {
164
+ const calls = [];
165
+ // Walk up through the chain collecting all call expressions
166
+ let current = startNode;
167
+ while (current) {
168
+ const parent = current.getParent();
169
+ if (!parent)
170
+ break;
171
+ if (Node.isPropertyAccessExpression(parent)) {
172
+ const grandparent = parent.getParent();
173
+ if (grandparent && Node.isCallExpression(grandparent)) {
174
+ calls.push(grandparent);
175
+ current = grandparent;
176
+ continue;
177
+ }
178
+ }
179
+ break;
180
+ }
181
+ return calls;
182
+ }
183
+ /* ═══════════════════════════════════════════════════════════════════════════
184
+ * 🎯 .then() Callback Tracking
185
+ * ═══════════════════════════════════════════════════════════════════════════ */
186
+ /**
187
+ * Find all .then() callbacks in a call tree and extract property accesses from them.
188
+ *
189
+ * @internal
190
+ */
191
+ function findAllThenCallbackAccesses(callNode) {
192
+ const accesses = [];
193
+ // Recursively find all .then() calls in the entire tree
194
+ findThenCallsRecursive(callNode, accesses);
195
+ // Also check the source file for any .then() calls we might have missed
196
+ const sourceFile = callNode.getSourceFile();
197
+ sourceFile.forEachDescendant((node) => {
198
+ if (Node.isCallExpression(node)) {
199
+ findThenCallsRecursive(node, accesses);
200
+ }
201
+ });
202
+ return accesses;
203
+ }
204
+ /**
205
+ * Recursively find .then() callbacks and extract property accesses.
206
+ *
207
+ * @internal
208
+ */
209
+ function findThenCallsRecursive(node, accesses) {
210
+ if (!Node.isCallExpression(node))
211
+ return;
212
+ const expr = node.getExpression();
213
+ // Check if this is a .then() call
214
+ if (Node.isPropertyAccessExpression(expr)) {
215
+ const methodName = expr.getName();
216
+ if (methodName === 'then' || methodName === 'catch' || methodName === 'finally') {
217
+ // Get the callback argument
218
+ const args = node.getArguments();
219
+ if (args.length > 0) {
220
+ const callback = args[0];
221
+ if (Node.isArrowFunction(callback) || Node.isFunctionExpression(callback)) {
222
+ const params = callback.getParameters();
223
+ if (params.length > 0) {
224
+ const paramName = params[0].getName();
225
+ // Extract property accesses from the callback
226
+ extractPropertyAccessesFromCallback(callback, paramName, accesses);
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ /**
234
+ * Extract property accesses from a callback function.
235
+ *
236
+ * @internal
237
+ */
238
+ function extractPropertyAccessesFromCallback(callback, paramName, accesses) {
239
+ // Get the body of the callback
240
+ let body;
241
+ if (Node.isArrowFunction(callback)) {
242
+ body = callback.getBody();
243
+ }
244
+ else if (Node.isFunctionExpression(callback)) {
245
+ body = callback.getBody();
246
+ }
247
+ if (!body)
248
+ return;
249
+ // For expression bodies like `data => data.name`, body IS the expression
250
+ if (Node.isPropertyAccessExpression(body)) {
251
+ // Check if root is the param
252
+ let root = body.getExpression();
253
+ while (Node.isPropertyAccessExpression(root)) {
254
+ root = root.getExpression();
255
+ }
256
+ if (Node.isIdentifier(root) && root.getText() === paramName) {
257
+ const chain = extractPropertyChain(body);
258
+ if (chain.length > 0) {
259
+ addUniqueAccess(accesses, {
260
+ path: chain.join('.'),
261
+ segments: chain,
262
+ location: getLocation(body),
263
+ });
264
+ }
265
+ }
266
+ }
267
+ // For block bodies like `data => { console.log(data.name); }`
268
+ // Find all property access expressions
269
+ body.forEachDescendant((descendant) => {
270
+ if (Node.isPropertyAccessExpression(descendant)) {
271
+ // Check if the root expression is the param identifier
272
+ let root = descendant.getExpression();
273
+ while (Node.isPropertyAccessExpression(root)) {
274
+ root = root.getExpression();
275
+ }
276
+ if (Node.isIdentifier(root) && root.getText() === paramName) {
277
+ const chain = extractPropertyChain(descendant);
278
+ if (chain.length > 0) {
279
+ addUniqueAccess(accesses, {
280
+ path: chain.join('.'),
281
+ segments: chain,
282
+ location: getLocation(descendant),
283
+ });
284
+ }
285
+ }
286
+ }
287
+ });
288
+ }
289
+ /**
290
+ * Extract property chain from a property access expression.
291
+ *
292
+ * For `data.user.name`, returns `['user', 'name']`.
293
+ *
294
+ * @internal
295
+ */
296
+ function extractPropertyChain(propAccess) {
297
+ const chain = [];
298
+ let current = propAccess;
299
+ // Walk up through chained property accesses
300
+ while (current && Node.isPropertyAccessExpression(current)) {
301
+ const parent = current.getParent();
302
+ if (Node.isPropertyAccessExpression(parent) && parent.getExpression() === current) {
303
+ // This property access is the base of another - continue up
304
+ current = parent;
305
+ }
306
+ else {
307
+ break;
308
+ }
309
+ }
310
+ // Now walk down collecting names
311
+ while (current && Node.isPropertyAccessExpression(current)) {
312
+ chain.push(current.getName());
313
+ const expr = current.getExpression();
314
+ if (Node.isPropertyAccessExpression(expr)) {
315
+ current = expr;
316
+ }
317
+ else {
318
+ break;
319
+ }
320
+ }
321
+ return chain.reverse();
322
+ }
323
+ /**
324
+ * Track accesses in all .then() callbacks in the chain.
325
+ *
326
+ * @internal
327
+ */
328
+ function trackChainedAccesses(callNode) {
329
+ const accesses = [];
330
+ // Collect all .then() calls in the chain starting from this node and going up
331
+ const chainedCalls = collectChainedCalls(callNode);
332
+ for (const chainCall of chainedCalls) {
333
+ if (!Node.isCallExpression(chainCall))
334
+ continue;
335
+ const expr = chainCall.getExpression();
336
+ if (!Node.isPropertyAccessExpression(expr))
337
+ continue;
338
+ const methodName = expr.getName();
339
+ if (methodName === 'then' || methodName === 'catch' || methodName === 'finally') {
340
+ // Get the callback argument
341
+ const args = chainCall.getArguments();
342
+ if (args.length > 0) {
343
+ const callback = args[0];
344
+ // Track accesses inside the callback
345
+ if (Node.isArrowFunction(callback) || Node.isFunctionExpression(callback)) {
346
+ const params = callback.getParameters();
347
+ if (params.length > 0) {
348
+ const paramName = params[0].getName();
349
+ const body = callback.getBody();
350
+ if (body) {
351
+ const callbackAccesses = trackAccessesInCallbackBody(body, paramName);
352
+ for (const access of callbackAccesses) {
353
+ addUniqueAccess(accesses, access);
354
+ }
355
+ }
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+ // Also check if the callNode itself is a .then() call and process it
362
+ if (Node.isCallExpression(callNode)) {
363
+ const expr = callNode.getExpression();
364
+ if (Node.isPropertyAccessExpression(expr)) {
365
+ const methodName = expr.getName();
366
+ if (methodName === 'then' || methodName === 'catch' || methodName === 'finally') {
367
+ const args = callNode.getArguments();
368
+ if (args.length > 0) {
369
+ const callback = args[0];
370
+ if (Node.isArrowFunction(callback) || Node.isFunctionExpression(callback)) {
371
+ const params = callback.getParameters();
372
+ if (params.length > 0) {
373
+ const paramName = params[0].getName();
374
+ const body = callback.getBody();
375
+ if (body) {
376
+ const callbackAccesses = trackAccessesInCallbackBody(body, paramName);
377
+ for (const access of callbackAccesses) {
378
+ addUniqueAccess(accesses, access);
379
+ }
380
+ }
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
387
+ return accesses;
388
+ }
389
+ /**
390
+ * Track property accesses in a callback body for a parameter.
391
+ *
392
+ * @internal
393
+ */
394
+ function trackAccessesInCallbackBody(body, paramName) {
395
+ const accesses = [];
396
+ if (!body)
397
+ return accesses;
398
+ // Also check if body itself is a property access (expression body)
399
+ if (Node.isPropertyAccessExpression(body)) {
400
+ const rootIdentifier = getRootIdentifier(body);
401
+ if (rootIdentifier && rootIdentifier.getText() === paramName) {
402
+ const chain = buildFullChainFromRoot(body, paramName);
403
+ if (chain.length > 0) {
404
+ const path = chain.join('.');
405
+ addUniqueAccess(accesses, {
406
+ path,
407
+ segments: chain,
408
+ location: getLocation(body),
409
+ });
410
+ }
411
+ }
412
+ }
413
+ // Use body.getDescendantsOfKind directly
414
+ const bodyPropAccesses = body.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression);
415
+ for (const node of bodyPropAccesses) {
416
+ // Check if the root is the parameter
417
+ const rootIdentifier = getRootIdentifier(node);
418
+ if (rootIdentifier && rootIdentifier.getText() === paramName) {
419
+ const chain = buildFullChainFromRoot(node, paramName);
420
+ if (chain.length > 0) {
421
+ const path = chain.join('.');
422
+ addUniqueAccess(accesses, {
423
+ path,
424
+ segments: chain,
425
+ location: getLocation(node),
426
+ });
427
+ }
428
+ }
429
+ }
430
+ return accesses;
431
+ }
432
+ /**
433
+ * Track property accesses if the current call is a .then() callback.
434
+ *
435
+ * @internal
436
+ */
437
+ function trackAccessesInCurrentCall(callNode) {
438
+ const accesses = [];
439
+ if (!Node.isCallExpression(callNode)) {
440
+ return accesses;
441
+ }
442
+ const expr = callNode.getExpression();
443
+ if (!Node.isPropertyAccessExpression(expr)) {
444
+ return accesses;
445
+ }
446
+ const methodName = expr.getName();
447
+ if (methodName !== 'then' && methodName !== 'catch' && methodName !== 'finally') {
448
+ return accesses;
449
+ }
450
+ // Get the callback argument
451
+ const args = callNode.getArguments();
452
+ if (args.length > 0) {
453
+ const callback = args[0];
454
+ if (Node.isArrowFunction(callback) || Node.isFunctionExpression(callback)) {
455
+ const params = callback.getParameters();
456
+ if (params.length > 0) {
457
+ const paramName = params[0].getName();
458
+ // Find property accesses directly by searching the source file
459
+ // within the callback's text range
460
+ const sourceFile = callNode.getSourceFile();
461
+ const callbackStart = callback.getStart();
462
+ const callbackEnd = callback.getEnd();
463
+ // Get all property access expressions in the source file
464
+ const allPropertyAccesses = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression);
465
+ for (const propAccess of allPropertyAccesses) {
466
+ // Check if this property access is within the callback
467
+ const propStart = propAccess.getStart();
468
+ if (propStart < callbackStart || propStart > callbackEnd) {
469
+ continue;
470
+ }
471
+ // Check if root is the parameter
472
+ const rootId = getRootIdentifier(propAccess);
473
+ if (rootId && rootId.getText() === paramName) {
474
+ const chain = buildFullChainFromRoot(propAccess, paramName);
475
+ if (chain.length > 0) {
476
+ const path = chain.join('.');
477
+ addUniqueAccess(accesses, {
478
+ path,
479
+ segments: chain,
480
+ location: getLocation(propAccess),
481
+ });
482
+ }
483
+ }
484
+ }
485
+ }
486
+ }
487
+ }
488
+ return accesses;
489
+ }
490
+ /* ═══════════════════════════════════════════════════════════════════════════
491
+ * 📦 Destructuring Support
492
+ * ═══════════════════════════════════════════════════════════════════════════ */
493
+ /**
494
+ * Find destructuring patterns from a call expression.
495
+ *
496
+ * Handles:
497
+ * - `const { name, email } = await fetch()`
498
+ * - `const { user: { name } } = await fetch()` (nested)
499
+ *
500
+ * @internal
501
+ */
502
+ function findDestructuringFromCall(callNode) {
503
+ const accesses = [];
504
+ // Walk up to find variable declaration
505
+ let current = callNode;
506
+ while (current) {
507
+ const parent = current.getParent();
508
+ if (!parent)
509
+ break;
510
+ if (Node.isVariableDeclaration(parent)) {
511
+ const nameNode = parent.getNameNode();
512
+ if (Node.isObjectBindingPattern(nameNode)) {
513
+ // Extract all bound properties
514
+ extractBindingPatternAccesses(nameNode, [], accesses);
515
+ }
516
+ break;
517
+ }
518
+ // Skip await and parentheses
519
+ if (Node.isAwaitExpression(parent) || Node.isParenthesizedExpression(parent)) {
520
+ current = parent;
521
+ continue;
522
+ }
523
+ // Handle chained calls
524
+ if (Node.isCallExpression(parent) || Node.isPropertyAccessExpression(parent)) {
525
+ current = parent;
526
+ continue;
527
+ }
528
+ break;
529
+ }
530
+ return accesses;
531
+ }
532
+ /**
533
+ * Extract property accesses from an object binding pattern.
534
+ *
535
+ * @internal
536
+ */
537
+ function extractBindingPatternAccesses(pattern, pathPrefix, accesses) {
538
+ if (!Node.isObjectBindingPattern(pattern))
539
+ return;
540
+ const elements = pattern.getElements();
541
+ for (const elem of elements) {
542
+ const nameNode = elem.getNameNode();
543
+ const propertyName = elem.getPropertyNameNode();
544
+ // Get the property name being accessed
545
+ let propName;
546
+ if (propertyName && Node.isIdentifier(propertyName)) {
547
+ propName = propertyName.getText();
548
+ }
549
+ else if (Node.isIdentifier(nameNode)) {
550
+ propName = nameNode.getText();
551
+ }
552
+ else {
553
+ continue;
554
+ }
555
+ const fullPath = [...pathPrefix, propName];
556
+ // Check if the name node is another binding pattern (nested destructuring)
557
+ if (Node.isObjectBindingPattern(nameNode)) {
558
+ // Recursively extract nested patterns
559
+ extractBindingPatternAccesses(nameNode, fullPath, accesses);
560
+ }
561
+ else {
562
+ // Leaf node - add the access
563
+ accesses.push({
564
+ path: fullPath.join('.'),
565
+ segments: fullPath,
566
+ location: getLocation(elem),
567
+ });
568
+ }
569
+ }
570
+ }
571
+ /* ═══════════════════════════════════════════════════════════════════════════
572
+ * 🔗 Property Chain Building
573
+ * ═══════════════════════════════════════════════════════════════════════════ */
574
+ /**
575
+ * Find the variable name from a call expression's context.
576
+ *
577
+ * @internal
578
+ */
579
+ function findVariableName(callNode) {
580
+ let current = callNode;
581
+ while (current) {
582
+ const parent = current.getParent();
583
+ if (!parent)
584
+ break;
585
+ // Found variable declaration
586
+ if (Node.isVariableDeclaration(parent)) {
587
+ const nameNode = parent.getNameNode();
588
+ if (Node.isIdentifier(nameNode)) {
589
+ return nameNode.getText();
590
+ }
591
+ // Handle destructuring pattern - we need to track the original object
592
+ if (Node.isObjectBindingPattern(nameNode)) {
593
+ // Return undefined to trigger destructuring tracking
594
+ return undefined;
595
+ }
596
+ break;
597
+ }
598
+ // Skip await and parentheses
599
+ if (Node.isAwaitExpression(parent) || Node.isParenthesizedExpression(parent)) {
600
+ current = parent;
601
+ continue;
602
+ }
603
+ // Handle chained calls
604
+ if (Node.isCallExpression(parent) || Node.isPropertyAccessExpression(parent)) {
605
+ current = parent;
606
+ continue;
607
+ }
608
+ break;
609
+ }
610
+ return undefined;
611
+ }
612
+ /**
613
+ * Get the root identifier from a property access chain.
614
+ *
615
+ * @internal
616
+ */
617
+ function getRootIdentifier(node) {
618
+ let current = node;
619
+ while (Node.isPropertyAccessExpression(current)) {
620
+ current = current.getExpression();
621
+ }
622
+ if (Node.isIdentifier(current)) {
623
+ return current;
624
+ }
625
+ return undefined;
626
+ }
627
+ /**
628
+ * Build the full chain from root, excluding root name.
629
+ *
630
+ * @internal
631
+ */
632
+ function buildFullChainFromRoot(node, rootName) {
633
+ if (!Node.isPropertyAccessExpression(node))
634
+ return [];
635
+ // Collect the entire chain from innermost to outermost
636
+ const chain = [];
637
+ let current = node;
638
+ // Walk up to find the outermost property access
639
+ while (current) {
640
+ const parent = current.getParent();
641
+ if (parent && Node.isPropertyAccessExpression(parent) && parent.getExpression() === current) {
642
+ current = parent;
643
+ }
644
+ else {
645
+ break;
646
+ }
647
+ }
648
+ // Now walk down collecting property names
649
+ let walkNode = current;
650
+ while (walkNode && Node.isPropertyAccessExpression(walkNode)) {
651
+ chain.push(walkNode.getName());
652
+ const expr = walkNode.getExpression();
653
+ if (Node.isPropertyAccessExpression(expr)) {
654
+ walkNode = expr;
655
+ }
656
+ else {
657
+ break;
658
+ }
659
+ }
660
+ return chain.reverse();
661
+ }
662
+ /**
663
+ * Find the full property access chain starting from an identifier.
664
+ *
665
+ * For `data.user.email`, returns `{ path: 'user.email', segments: ['user', 'email'] }`.
666
+ *
667
+ * @internal
668
+ */
669
+ function findFullPropertyAccessChain(identifier, rootName) {
670
+ // The identifier must be the start of a property access chain
671
+ const parent = identifier.getParent();
672
+ if (!parent)
673
+ return undefined;
674
+ // Check for property access expression where identifier is the object
675
+ if (Node.isPropertyAccessExpression(parent) && parent.getExpression() === identifier) {
676
+ // Walk up to find the outermost property access
677
+ let outermost = parent;
678
+ let current = parent;
679
+ while (current) {
680
+ const currentParent = current.getParent();
681
+ if (currentParent && Node.isPropertyAccessExpression(currentParent) &&
682
+ currentParent.getExpression() === current) {
683
+ outermost = currentParent;
684
+ current = currentParent;
685
+ }
686
+ else {
687
+ break;
688
+ }
689
+ }
690
+ // Build the chain from identifier to outermost
691
+ const chain = buildChainFromIdentifier(outermost, rootName);
692
+ if (chain.length > 0) {
693
+ return {
694
+ path: chain.join('.'),
695
+ segments: chain,
696
+ location: getLocation(outermost),
697
+ };
698
+ }
699
+ }
700
+ return undefined;
701
+ }
702
+ /**
703
+ * Build chain from a property access expression, excluding the root identifier.
704
+ *
705
+ * @internal
706
+ */
707
+ function buildChainFromIdentifier(node, rootName) {
708
+ const chain = [];
709
+ let current = node;
710
+ while (current && Node.isPropertyAccessExpression(current)) {
711
+ chain.unshift(current.getName());
712
+ const expr = current.getExpression();
713
+ if (Node.isIdentifier(expr)) {
714
+ if (expr.getText() === rootName) {
715
+ break;
716
+ }
717
+ }
718
+ if (Node.isPropertyAccessExpression(expr)) {
719
+ current = expr;
720
+ }
721
+ else {
722
+ break;
723
+ }
724
+ }
725
+ return chain;
726
+ }
727
+ /* ═══════════════════════════════════════════════════════════════════════════
728
+ * 📊 Element Access (Array Indexing)
729
+ * ═══════════════════════════════════════════════════════════════════════════ */
730
+ /**
731
+ * Find element access chain (array indexing).
732
+ *
733
+ * For `data[0].name`, returns appropriate access.
734
+ *
735
+ * @internal
736
+ */
737
+ function findElementAccessChain(identifier, rootName) {
738
+ const parent = identifier.getParent();
739
+ if (!parent)
740
+ return undefined;
741
+ // Check for element access expression (data[0])
742
+ if (Node.isElementAccessExpression(parent) && parent.getExpression() === identifier) {
743
+ const argExpr = parent.getArgumentExpression();
744
+ if (!argExpr)
745
+ return undefined;
746
+ const indexText = argExpr.getText();
747
+ // Check if there's property access after the element access
748
+ const grandparent = parent.getParent();
749
+ if (grandparent && Node.isPropertyAccessExpression(grandparent) &&
750
+ grandparent.getExpression() === parent) {
751
+ // data[0].name
752
+ const propChain = buildChainFromElementAccess(grandparent, parent);
753
+ const chain = [`[${indexText}]`, ...propChain];
754
+ return {
755
+ path: chain.join('.'),
756
+ segments: chain,
757
+ location: getLocation(grandparent),
758
+ };
759
+ }
760
+ // Just data[0]
761
+ return {
762
+ path: `[${indexText}]`,
763
+ segments: [`[${indexText}]`],
764
+ location: getLocation(parent),
765
+ };
766
+ }
767
+ return undefined;
768
+ }
769
+ /**
770
+ * Build chain from property access after element access.
771
+ *
772
+ * @internal
773
+ */
774
+ function buildChainFromElementAccess(node, elementAccess) {
775
+ const chain = [];
776
+ let current = node;
777
+ while (current && Node.isPropertyAccessExpression(current)) {
778
+ chain.unshift(current.getName());
779
+ const expr = current.getExpression();
780
+ if (expr === elementAccess) {
781
+ break;
782
+ }
783
+ if (Node.isPropertyAccessExpression(expr)) {
784
+ current = expr;
785
+ }
786
+ else {
787
+ break;
788
+ }
789
+ }
790
+ return chain.reverse();
791
+ }
792
+ /* ═══════════════════════════════════════════════════════════════════════════
793
+ * 🛠️ Utility Functions
794
+ * ═══════════════════════════════════════════════════════════════════════════ */
795
+ /**
796
+ * Add access if not already present (deduplication).
797
+ *
798
+ * @internal
799
+ */
800
+ function addUniqueAccess(accesses, access) {
801
+ const existing = accesses.find(a => a.path === access.path);
802
+ if (!existing) {
803
+ accesses.push(access);
804
+ }
805
+ }
806
+ /**
807
+ * Get source location for a node.
808
+ *
809
+ * @internal
810
+ */
811
+ function getLocation(node) {
812
+ const sourceFile = node.getSourceFile();
813
+ return {
814
+ file: sourceFile.getFilePath(),
815
+ line: node.getStartLineNumber(),
816
+ };
817
+ }
818
+ //# sourceMappingURL=property-access.js.map