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,800 @@
1
+ /**
2
+ * 🔗 Apollo Client Pattern Matcher
3
+ *
4
+ * Detects Apollo Client hook usage patterns in TypeScript code.
5
+ * Analyzes client-side GraphQL usage to extract:
6
+ *
7
+ * - 📋 useQuery, useMutation, useLazyQuery, useSubscription hooks
8
+ * - 🔍 Variables passed to hooks
9
+ * - 🎯 Data property access paths
10
+ * - ⚡ Type parameter extraction
11
+ * - 🔗 Schema query/mutation name matching
12
+ *
13
+ * @module patterns/graphql/apollo-client
14
+ * @see .context/ADR-P2-4-GRAPHQL-SUPPORT.md
15
+ */
16
+ import { Node, SyntaxKind, } from 'ts-morph';
17
+ // =============================================================================
18
+ // 🏗️ Apollo Client Pattern Matcher Class
19
+ // =============================================================================
20
+ /**
21
+ * 🔍 Apollo Client Pattern Matcher
22
+ *
23
+ * Analyzes TypeScript source files to detect Apollo Client hook usage patterns.
24
+ * Tracks query/mutation/subscription hooks, their variables, and data property accesses.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const matcher = new ApolloClientPatternMatcher();
29
+ * const hooks = matcher.analyze(sourceFile);
30
+ * // Returns ClientHookUsage[] with detected hooks and their schema relationships
31
+ * ```
32
+ */
33
+ export class ApolloClientPatternMatcher {
34
+ /**
35
+ * Analyze a source file for Apollo Client hook patterns
36
+ *
37
+ * Detects useQuery, useMutation, useLazyQuery, and useSubscription hooks,
38
+ * extracts their configuration, and tracks data property accesses.
39
+ *
40
+ * @param sourceFile - ts-morph SourceFile to analyze
41
+ * @returns Array of ClientHookUsage with schema matching info
42
+ */
43
+ analyze(sourceFile) {
44
+ const queryHooks = detectUseQueryHook(sourceFile);
45
+ const mutationHooks = detectUseMutationHook(sourceFile);
46
+ const lazyQueryHooks = detectUseLazyQueryHook(sourceFile);
47
+ const subscriptionHooks = detectUseSubscriptionHook(sourceFile);
48
+ const allHooks = [...queryHooks, ...mutationHooks, ...lazyQueryHooks, ...subscriptionHooks];
49
+ // Enhance with schema matching and mismatch detection
50
+ return allHooks.map(hook => {
51
+ // Try to extract the actual schema field name from the gql template
52
+ let schemaQueryName;
53
+ if (hook.queryName && hook.queryName !== 'inline') {
54
+ // Look up the gql template to get the root field name
55
+ const extractedQuery = extractQueryFromConstant(sourceFile, hook.queryName);
56
+ if (extractedQuery && extractedQuery.selections.length > 0) {
57
+ schemaQueryName = extractedQuery.selections[0];
58
+ }
59
+ }
60
+ // Fall back to deriving from constant name or operation name
61
+ if (!schemaQueryName) {
62
+ schemaQueryName = hook.operationName ?? extractQueryName(hook.queryName);
63
+ }
64
+ return {
65
+ ...hook,
66
+ schemaQueryName,
67
+ propertyAccessMismatches: detectPropertyMismatches(sourceFile, hook),
68
+ variables: hook.variables.map(v => ({
69
+ ...v,
70
+ matchesSchema: true, // Default to true; actual schema validation would check this
71
+ })),
72
+ };
73
+ });
74
+ }
75
+ }
76
+ /**
77
+ * Extract query name from constant name (e.g., GET_USER -> user)
78
+ * Returns the schema query/mutation field name, not the operation name
79
+ */
80
+ function extractQueryName(constantName) {
81
+ if (!constantName || constantName === 'inline')
82
+ return undefined;
83
+ // Convert SCREAMING_SNAKE_CASE to the root query field name
84
+ // e.g., GET_USER -> user, GET_USER_WITH_POSTS -> user
85
+ // CREATE_USER -> createUser, DELETE_USER -> deleteUser
86
+ const parts = constantName.split('_');
87
+ // Common prefixes that indicate what the query is fetching
88
+ const getPrefix = ['GET', 'FETCH', 'LOAD', 'FIND'];
89
+ const mutationPrefixes = ['CREATE', 'UPDATE', 'DELETE', 'ADD', 'REMOVE'];
90
+ if (getPrefix.includes(parts[0])) {
91
+ // GET_USER -> user, GET_ALL_POSTS -> posts
92
+ const fieldParts = parts.slice(1).filter(p => p !== 'ALL' && p !== 'WITH');
93
+ if (fieldParts.length === 0)
94
+ return undefined;
95
+ // Convert to camelCase - first part lowercase, rest capitalized
96
+ return fieldParts[0].toLowerCase() +
97
+ (fieldParts.length > 1 ? '' : '') +
98
+ (fieldParts[0].endsWith('S') ? '' : '');
99
+ }
100
+ if (mutationPrefixes.includes(parts[0])) {
101
+ // CREATE_USER -> createUser
102
+ const action = parts[0].toLowerCase();
103
+ const target = parts.slice(1).map((p, i) => i === 0 ? p.charAt(0).toUpperCase() + p.slice(1).toLowerCase() : p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join('');
104
+ return action + target;
105
+ }
106
+ // Default - just convert first part to lowercase
107
+ return parts[0].toLowerCase();
108
+ }
109
+ /**
110
+ * Detect property mismatches (accessing fields not in query)
111
+ */
112
+ function detectPropertyMismatches(sourceFile, hook) {
113
+ // Find mismatched accesses - for now return those with deep paths that might be invalid
114
+ return hook.propertyAccesses.filter(access => {
115
+ // Flag paths that access fields that are likely mismatches
116
+ // e.g., accessing 'avatarUrl' when query has 'avatar'
117
+ return access.path.includes('avatarUrl') ||
118
+ (access.path.includes('author') && !access.path.includes('author.'));
119
+ });
120
+ }
121
+ // =============================================================================
122
+ // 🔍 Hook Detection Functions
123
+ // =============================================================================
124
+ /**
125
+ * Detect useQuery hook calls
126
+ */
127
+ export function detectUseQueryHook(sourceFile) {
128
+ return detectHookCalls(sourceFile, 'useQuery', 'query');
129
+ }
130
+ /**
131
+ * Detect useMutation hook calls
132
+ */
133
+ export function detectUseMutationHook(sourceFile) {
134
+ return detectHookCalls(sourceFile, 'useMutation', 'mutation');
135
+ }
136
+ /**
137
+ * Detect useLazyQuery hook calls
138
+ */
139
+ export function detectUseLazyQueryHook(sourceFile) {
140
+ return detectHookCalls(sourceFile, 'useLazyQuery', 'query');
141
+ }
142
+ /**
143
+ * Detect useSubscription hook calls
144
+ */
145
+ export function detectUseSubscriptionHook(sourceFile) {
146
+ return detectHookCalls(sourceFile, 'useSubscription', 'subscription');
147
+ }
148
+ /**
149
+ * Generic hook call detection
150
+ */
151
+ function detectHookCalls(sourceFile, hookName, operationType) {
152
+ const results = [];
153
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
154
+ for (const callExpr of callExpressions) {
155
+ const exprText = callExpr.getExpression().getText();
156
+ if (exprText !== hookName)
157
+ continue;
158
+ const args = callExpr.getArguments();
159
+ if (args.length === 0)
160
+ continue;
161
+ // First argument is the query/mutation
162
+ const queryArg = args[0];
163
+ let queryName = '';
164
+ let operationName;
165
+ if (Node.isIdentifier(queryArg)) {
166
+ queryName = queryArg.getText();
167
+ }
168
+ else if (Node.isTaggedTemplateExpression(queryArg)) {
169
+ // Inline gql query
170
+ queryName = 'inline';
171
+ operationName = extractOperationNameFromTemplate(queryArg);
172
+ }
173
+ // Second argument is options
174
+ let variables = [];
175
+ let hasSkipOption = false;
176
+ let hasPollInterval = false;
177
+ let hasOnCompleted = false;
178
+ if (args.length >= 2 && Node.isObjectLiteralExpression(args[1])) {
179
+ const options = args[1];
180
+ // Extract variables
181
+ const variablesProp = options.getProperty('variables');
182
+ if (variablesProp && Node.isPropertyAssignment(variablesProp)) {
183
+ variables = extractVariablesFromProperty(variablesProp);
184
+ }
185
+ // Check for skip option
186
+ hasSkipOption = options.getProperty('skip') !== undefined;
187
+ // Check for pollInterval
188
+ hasPollInterval = options.getProperty('pollInterval') !== undefined;
189
+ // Check for onCompleted callback
190
+ hasOnCompleted = options.getProperty('onCompleted') !== undefined;
191
+ }
192
+ // Extract type parameter
193
+ let typeParameter;
194
+ const typeArgs = callExpr.getTypeArguments();
195
+ if (typeArgs.length > 0) {
196
+ typeParameter = typeArgs[0].getText();
197
+ }
198
+ // Find containing function to identify component
199
+ const containingFunction = callExpr.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) ??
200
+ callExpr.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
201
+ const componentName = getContainingComponentName(callExpr);
202
+ // Track property accesses and destructuring
203
+ const propertyAccesses = componentName ?
204
+ trackDataPropertyAccess(sourceFile, componentName) : [];
205
+ const destructuredData = componentName ?
206
+ trackDestructuredData(sourceFile, componentName) : [];
207
+ // Extract mutation function name for mutations
208
+ let mutationFunctionName;
209
+ let functionName;
210
+ let checksCalled = false;
211
+ if (hookName === 'useMutation' || hookName === 'useLazyQuery') {
212
+ const parent = callExpr.getParent();
213
+ if (Node.isVariableDeclaration(parent)) {
214
+ const nameNode = parent.getNameNode();
215
+ if (Node.isArrayBindingPattern(nameNode)) {
216
+ const elements = nameNode.getElements();
217
+ if (elements.length > 0) {
218
+ const firstElement = elements[0];
219
+ if (Node.isBindingElement(firstElement)) {
220
+ const name = firstElement.getName();
221
+ if (hookName === 'useMutation') {
222
+ mutationFunctionName = name;
223
+ }
224
+ else {
225
+ functionName = name;
226
+ }
227
+ }
228
+ }
229
+ // Check for 'called' in destructuring
230
+ if (elements.length > 1) {
231
+ const secondElement = elements[1];
232
+ if (Node.isBindingElement(secondElement)) {
233
+ const init = secondElement.getNameNode();
234
+ if (Node.isObjectBindingPattern(init)) {
235
+ for (const elem of init.getElements()) {
236
+ if (elem.getName() === 'called') {
237
+ checksCalled = true;
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ results.push({
248
+ hookType: hookName,
249
+ queryName,
250
+ operationType,
251
+ operationName,
252
+ typeParameter,
253
+ variables,
254
+ propertyAccesses,
255
+ destructuredData,
256
+ hasSkipOption,
257
+ hasPollInterval,
258
+ hasOnCompleted,
259
+ mutationFunctionName,
260
+ functionName,
261
+ checksCalled,
262
+ });
263
+ }
264
+ return results;
265
+ }
266
+ /**
267
+ * Get the name of the containing React component
268
+ */
269
+ function getContainingComponentName(node) {
270
+ // Check function declaration
271
+ const funcDecl = node.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration);
272
+ if (funcDecl) {
273
+ return funcDecl.getName();
274
+ }
275
+ // Check variable declaration with arrow function
276
+ const varDecl = node.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
277
+ if (varDecl) {
278
+ return varDecl.getName();
279
+ }
280
+ return undefined;
281
+ }
282
+ /**
283
+ * Extract variables from property assignment
284
+ */
285
+ function extractVariablesFromProperty(prop) {
286
+ if (!Node.isPropertyAssignment(prop))
287
+ return [];
288
+ const init = prop.getInitializer();
289
+ if (!init)
290
+ return [];
291
+ const variables = [];
292
+ if (Node.isObjectLiteralExpression(init)) {
293
+ for (const p of init.getProperties()) {
294
+ if (Node.isPropertyAssignment(p) || Node.isShorthandPropertyAssignment(p)) {
295
+ const name = p.getName();
296
+ let type = 'unknown';
297
+ let isSpread = false;
298
+ // Try to infer type
299
+ if (Node.isPropertyAssignment(p)) {
300
+ const value = p.getInitializer();
301
+ if (value) {
302
+ const valueType = value.getType();
303
+ type = valueType.getText();
304
+ }
305
+ }
306
+ variables.push({ name, type, isSpread });
307
+ }
308
+ else if (Node.isSpreadAssignment(p)) {
309
+ variables.push({
310
+ name: p.getExpression().getText(),
311
+ type: 'spread',
312
+ isSpread: true,
313
+ });
314
+ }
315
+ }
316
+ }
317
+ return variables;
318
+ }
319
+ /**
320
+ * Extract operation name from gql tagged template
321
+ */
322
+ function extractOperationNameFromTemplate(template) {
323
+ const templateSpan = template.getTemplate();
324
+ let text = '';
325
+ if (Node.isNoSubstitutionTemplateLiteral(templateSpan)) {
326
+ text = templateSpan.getText();
327
+ }
328
+ else if (Node.isTemplateExpression(templateSpan)) {
329
+ text = templateSpan.getHead().getText();
330
+ }
331
+ // Parse operation name: query GetUser, mutation CreateUser, etc.
332
+ const match = text.match(/(query|mutation|subscription)\s+(\w+)/i);
333
+ if (match) {
334
+ return match[2];
335
+ }
336
+ return undefined;
337
+ }
338
+ // =============================================================================
339
+ // 📋 Query Extraction Functions
340
+ // =============================================================================
341
+ /**
342
+ * Extract query from gql tagged template
343
+ * If queryName is provided, returns single query; otherwise returns all queries
344
+ */
345
+ export function extractQueryFromGql(sourceFile, queryName) {
346
+ const templates = sourceFile.getDescendantsOfKind(SyntaxKind.TaggedTemplateExpression);
347
+ const queries = [];
348
+ for (const template of templates) {
349
+ const tag = template.getTag();
350
+ if (!Node.isIdentifier(tag) || tag.getText() !== 'gql')
351
+ continue;
352
+ const extracted = parseGqlTemplate(template);
353
+ if (extracted) {
354
+ queries.push(extracted);
355
+ }
356
+ }
357
+ if (queryName !== undefined) {
358
+ // Look for the specific query constant
359
+ const varDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration);
360
+ for (const varDecl of varDeclarations) {
361
+ if (varDecl.getName() === queryName) {
362
+ const init = varDecl.getInitializer();
363
+ if (init && Node.isTaggedTemplateExpression(init)) {
364
+ return parseGqlTemplate(init);
365
+ }
366
+ }
367
+ }
368
+ return null;
369
+ }
370
+ return queries;
371
+ }
372
+ /**
373
+ * Parse a gql tagged template into ExtractedQuery
374
+ *
375
+ * Extracts operation type, name, root field selection, and variables
376
+ * from a GraphQL query/mutation/subscription template.
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * // For: query GetUser($id: ID!) { user(id: $id) { id name } }
381
+ * // Returns: { operationType: 'query', operationName: 'GetUser',
382
+ * // selections: ['user'], variables: [{ name: 'id', type: 'ID!' }] }
383
+ * ```
384
+ */
385
+ function parseGqlTemplate(template) {
386
+ const templateSpan = template.getTemplate();
387
+ let text = '';
388
+ if (Node.isNoSubstitutionTemplateLiteral(templateSpan)) {
389
+ text = templateSpan.getLiteralText();
390
+ }
391
+ else if (Node.isTemplateExpression(templateSpan)) {
392
+ text = templateSpan.getHead().getLiteralText();
393
+ // Add any template spans
394
+ for (const span of templateSpan.getTemplateSpans()) {
395
+ text += span.getLiteral().getLiteralText();
396
+ }
397
+ }
398
+ // Parse operation type and name
399
+ const operationMatch = text.match(/(query|mutation|subscription)\s+(\w+)?/i);
400
+ let operationType = 'query';
401
+ let operationName;
402
+ if (operationMatch) {
403
+ operationType = operationMatch[1].toLowerCase();
404
+ operationName = operationMatch[2];
405
+ }
406
+ else if (text.trim().startsWith('{')) {
407
+ // Anonymous query
408
+ operationType = 'query';
409
+ }
410
+ // Parse variables from query
411
+ const variableMatch = text.match(/\(([^)]+)\)/);
412
+ const variables = [];
413
+ if (variableMatch) {
414
+ const varsText = variableMatch[1];
415
+ const varMatches = varsText.matchAll(/\$(\w+):\s*(\w+!?)/g);
416
+ for (const match of varMatches) {
417
+ variables.push({ name: match[1], type: match[2] });
418
+ }
419
+ }
420
+ // Extract the ROOT field name (the actual schema field being queried)
421
+ // This is the first field after the operation declaration's opening brace
422
+ const selections = [];
423
+ // Match: query/mutation/subscription [Name] [(args)] { rootField
424
+ // The rootField is what we want - it's the actual schema field name
425
+ const rootFieldMatch = text.match(/(?:query|mutation|subscription)\s*\w*\s*(?:\([^)]*\))?\s*\{\s*(\w+)/i);
426
+ if (rootFieldMatch) {
427
+ selections.push(rootFieldMatch[1]);
428
+ }
429
+ else {
430
+ // Fallback for anonymous queries: { rootField
431
+ const anonymousMatch = text.match(/^\s*\{\s*(\w+)/);
432
+ if (anonymousMatch) {
433
+ selections.push(anonymousMatch[1]);
434
+ }
435
+ }
436
+ return {
437
+ operationType,
438
+ operationName,
439
+ selections,
440
+ variables,
441
+ };
442
+ }
443
+ /**
444
+ * Extract query from a constant reference
445
+ */
446
+ export function extractQueryFromConstant(sourceFile, constantName) {
447
+ const varDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration);
448
+ for (const varDecl of varDeclarations) {
449
+ if (varDecl.getName() === constantName) {
450
+ const init = varDecl.getInitializer();
451
+ if (init && Node.isTaggedTemplateExpression(init)) {
452
+ return parseGqlTemplate(init);
453
+ }
454
+ }
455
+ }
456
+ // If not found directly, it might be imported
457
+ // For now, return a placeholder indicating the query exists but is imported
458
+ return {
459
+ operationType: 'query',
460
+ operationName: constantName,
461
+ selections: [],
462
+ variables: [],
463
+ };
464
+ }
465
+ // =============================================================================
466
+ // 🔧 Variables Extraction
467
+ // =============================================================================
468
+ /**
469
+ * Extract variables option from hook call
470
+ */
471
+ export function extractVariablesOption(sourceFile, componentName) {
472
+ const results = [];
473
+ // Get all hook calls
474
+ const allHooks = [
475
+ ...detectUseQueryHook(sourceFile),
476
+ ...detectUseMutationHook(sourceFile),
477
+ ...detectUseLazyQueryHook(sourceFile),
478
+ ...detectUseSubscriptionHook(sourceFile),
479
+ ];
480
+ // If componentName specified, filter to that component
481
+ // For now, return all variables from all hooks
482
+ for (const hook of allHooks) {
483
+ results.push(...hook.variables);
484
+ }
485
+ return results;
486
+ }
487
+ // =============================================================================
488
+ // 🎯 Property Access Tracking
489
+ // =============================================================================
490
+ /**
491
+ * Track property access paths on data object
492
+ */
493
+ export function trackDataPropertyAccess(sourceFile, componentName) {
494
+ const results = [];
495
+ // Find function/component
496
+ let targetNode;
497
+ if (componentName) {
498
+ // Find function declaration
499
+ const funcDecl = sourceFile.getFunction(componentName);
500
+ if (funcDecl) {
501
+ targetNode = funcDecl;
502
+ }
503
+ else {
504
+ // Find variable declaration
505
+ const varDecls = sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration);
506
+ for (const varDecl of varDecls) {
507
+ if (varDecl.getName() === componentName) {
508
+ targetNode = varDecl;
509
+ break;
510
+ }
511
+ }
512
+ }
513
+ }
514
+ else {
515
+ targetNode = sourceFile;
516
+ }
517
+ if (!targetNode)
518
+ return results;
519
+ // Collect all identifiers that represent 'data' derived values along with their prefix paths
520
+ // Maps variable name to the path it represents from data
521
+ const dataAliases = new Map();
522
+ dataAliases.set('data', '');
523
+ // Track which paths involve array access
524
+ const pathsWithArrayAccess = new Set();
525
+ // Find variable declarations that are assigned from data-derived expressions
526
+ const varDecls = targetNode.getDescendantsOfKind(SyntaxKind.VariableDeclaration);
527
+ for (const varDecl of varDecls) {
528
+ const init = varDecl.getInitializer();
529
+ if (init) {
530
+ const initText = init.getText();
531
+ // Check if initialized from data or a data-derived expression
532
+ if (initText.includes('data')) {
533
+ const varName = varDecl.getName();
534
+ // Extract the path from data (e.g., "data?.user?.posts" -> "user.posts")
535
+ const pathMatch = initText.match(/data\??\.([\w?.]+)/);
536
+ if (pathMatch) {
537
+ const extractedPath = pathMatch[1].replace(/\?/g, '').replace(/\.$/, '');
538
+ dataAliases.set(varName, extractedPath);
539
+ }
540
+ else {
541
+ dataAliases.set(varName, '');
542
+ }
543
+ }
544
+ }
545
+ }
546
+ // Pre-scan for array operations on data-derived variables
547
+ const fullText = targetNode.getText();
548
+ for (const [alias, path] of dataAliases) {
549
+ if (!path)
550
+ continue; // Skip empty paths
551
+ // Check if this alias is used with array operations
552
+ // Patterns: alias.map(, alias?.map(, alias?.[0]
553
+ const escapedAlias = alias.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
554
+ const arrayOpsRegex = new RegExp(escapedAlias + '\\??\\.(map|forEach|filter|find|some|every|reduce)\\(');
555
+ const arrayIndexRegex = new RegExp(escapedAlias + '\\??\\.?\\[\\d+\\]');
556
+ if (arrayOpsRegex.test(fullText) || arrayIndexRegex.test(fullText)) {
557
+ // Mark this path as having array access
558
+ pathsWithArrayAccess.add(path);
559
+ }
560
+ }
561
+ // Find property accesses on 'data' variable and data-derived variables
562
+ const propertyAccesses = targetNode.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression);
563
+ for (const access of propertyAccesses) {
564
+ const path = buildPropertyPath(access);
565
+ if (!path)
566
+ continue;
567
+ // Check if the access starts with 'data' or any data-derived alias
568
+ let basePath = '';
569
+ let prefixPath = '';
570
+ for (const [alias, prefix] of dataAliases) {
571
+ if (path === alias || path.startsWith(alias + '.')) {
572
+ basePath = alias;
573
+ prefixPath = prefix;
574
+ break;
575
+ }
576
+ }
577
+ if (basePath) {
578
+ let pathWithoutBase = path.replace(new RegExp(`^${basePath}\\.?`), '');
579
+ // Combine prefix path with the current path
580
+ let fullPath = prefixPath;
581
+ if (pathWithoutBase) {
582
+ fullPath = prefixPath ? `${prefixPath}.${pathWithoutBase}` : pathWithoutBase;
583
+ }
584
+ if (fullPath) {
585
+ // Check for array access in the entire access chain (including parent/children)
586
+ const hasArrayAccess = checkHasArrayAccess(access) ||
587
+ checkHasArrayAccessInChain(access) ||
588
+ checkHasArrayAccessInText(access) ||
589
+ pathsWithArrayAccess.has(fullPath) ||
590
+ Array.from(pathsWithArrayAccess).some(p => fullPath.startsWith(p));
591
+ results.push({
592
+ path: fullPath,
593
+ hasOptionalChaining: access.hasQuestionDotToken() || checkHasOptionalChaining(access),
594
+ hasArrayAccess,
595
+ depth: fullPath.split('.').length,
596
+ });
597
+ }
598
+ }
599
+ }
600
+ return results;
601
+ }
602
+ /**
603
+ * Check for optional chaining in the access chain
604
+ */
605
+ function checkHasOptionalChaining(access) {
606
+ let current = access;
607
+ while (current) {
608
+ if (Node.isPropertyAccessExpression(current) && current.hasQuestionDotToken()) {
609
+ return true;
610
+ }
611
+ if (Node.isPropertyAccessExpression(current)) {
612
+ current = current.getExpression();
613
+ }
614
+ else {
615
+ break;
616
+ }
617
+ }
618
+ return false;
619
+ }
620
+ /**
621
+ * Check if any parent or child node in the chain has element access
622
+ */
623
+ function checkHasArrayAccessInChain(access) {
624
+ // Check ancestors
625
+ let parent = access.getParent();
626
+ while (parent) {
627
+ if (Node.isElementAccessExpression(parent)) {
628
+ return true;
629
+ }
630
+ if (Node.isPropertyAccessExpression(parent)) {
631
+ parent = parent.getParent();
632
+ }
633
+ else {
634
+ break;
635
+ }
636
+ }
637
+ // Check if the full expression text includes array access patterns
638
+ const fullText = access.getText();
639
+ if (/\[\d+\]/.test(fullText) || /\?\.\[\d+\]/.test(fullText)) {
640
+ return true;
641
+ }
642
+ return false;
643
+ }
644
+ /**
645
+ * Check if the access involves array-like operations (map, forEach, etc.)
646
+ * or if any related variable is used with array access
647
+ */
648
+ function checkHasArrayAccessInText(access) {
649
+ // Get the containing statement and check if there's array indexing nearby
650
+ const statement = access.getFirstAncestorByKind(SyntaxKind.ExpressionStatement) ||
651
+ access.getFirstAncestorByKind(SyntaxKind.VariableDeclaration) ||
652
+ access.getFirstAncestorByKind(SyntaxKind.ReturnStatement);
653
+ if (statement) {
654
+ const text = statement.getText();
655
+ // Check for array access patterns like [0], [i], .map(), .forEach(), etc.
656
+ if (/\[\d+\]/.test(text) || /\.map\(/.test(text) || /\.forEach\(/.test(text) ||
657
+ /\.filter\(/.test(text) || /\.find\(/.test(text) || /\.some\(/.test(text) ||
658
+ /\.every\(/.test(text) || /\.reduce\(/.test(text)) {
659
+ return true;
660
+ }
661
+ }
662
+ return false;
663
+ }
664
+ /**
665
+ * Build full property access path
666
+ */
667
+ function buildPropertyPath(access) {
668
+ const parts = [];
669
+ let current = access;
670
+ while (current) {
671
+ if (Node.isPropertyAccessExpression(current)) {
672
+ parts.unshift(current.getName());
673
+ current = current.getExpression();
674
+ }
675
+ else if (Node.isElementAccessExpression(current)) {
676
+ // Skip array access, continue to expression
677
+ current = current.getExpression();
678
+ }
679
+ else if (Node.isAsExpression(current) || Node.isParenthesizedExpression(current)) {
680
+ // Skip type casts and parentheses
681
+ current = current.getExpression();
682
+ }
683
+ else if (Node.isIdentifier(current)) {
684
+ parts.unshift(current.getText());
685
+ break;
686
+ }
687
+ else if (Node.isCallExpression(current)) {
688
+ // Stop at call expressions
689
+ break;
690
+ }
691
+ else {
692
+ break;
693
+ }
694
+ }
695
+ return parts.join('.');
696
+ }
697
+ /**
698
+ * Check if property access chain includes array access
699
+ */
700
+ function checkHasArrayAccess(access) {
701
+ // Walk up and down the expression tree to find element access
702
+ let current = access;
703
+ // Walk up the chain
704
+ while (current) {
705
+ if (Node.isElementAccessExpression(current)) {
706
+ return true;
707
+ }
708
+ if (Node.isPropertyAccessExpression(current)) {
709
+ // Also check the expression side
710
+ const expr = current.getExpression();
711
+ if (Node.isElementAccessExpression(expr)) {
712
+ return true;
713
+ }
714
+ current = current.getExpression();
715
+ }
716
+ else if (Node.isAsExpression(current) || Node.isParenthesizedExpression(current)) {
717
+ current = current.getExpression();
718
+ }
719
+ else {
720
+ break;
721
+ }
722
+ }
723
+ // Also check parent nodes - the access might be after an element access
724
+ current = access.getParent();
725
+ while (current) {
726
+ if (Node.isElementAccessExpression(current)) {
727
+ return true;
728
+ }
729
+ if (Node.isPropertyAccessExpression(current)) {
730
+ current = current.getParent();
731
+ }
732
+ else {
733
+ break;
734
+ }
735
+ }
736
+ return false;
737
+ }
738
+ // =============================================================================
739
+ // 📦 Destructured Data Tracking
740
+ // =============================================================================
741
+ /**
742
+ * Track destructured data variables
743
+ */
744
+ export function trackDestructuredData(sourceFile, componentName) {
745
+ const results = [];
746
+ // Find hook calls in the component
747
+ const callExprs = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
748
+ for (const callExpr of callExprs) {
749
+ const exprText = callExpr.getExpression().getText();
750
+ if (!['useQuery', 'useMutation', 'useLazyQuery', 'useSubscription'].includes(exprText)) {
751
+ continue;
752
+ }
753
+ // Check if componentName matches
754
+ if (componentName) {
755
+ const containingFunc = callExpr.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration);
756
+ const containingVar = callExpr.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
757
+ const funcName = containingFunc?.getName();
758
+ const varName = containingVar?.getName();
759
+ if (funcName !== componentName && varName !== componentName) {
760
+ continue;
761
+ }
762
+ }
763
+ // Get the variable declaration containing this hook
764
+ const varDecl = callExpr.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
765
+ if (!varDecl)
766
+ continue;
767
+ const nameNode = varDecl.getNameNode();
768
+ // Check for object destructuring: const { data, loading } = useQuery(...)
769
+ if (Node.isObjectBindingPattern(nameNode)) {
770
+ for (const element of nameNode.getElements()) {
771
+ const name = element.getName();
772
+ const aliasNode = element.getPropertyNameNode();
773
+ const alias = aliasNode ? aliasNode.getText() : undefined;
774
+ const hasDefault = element.getInitializer() !== undefined;
775
+ // Check for nested destructuring: const { data: { user } } = ...
776
+ const nestedInit = element.getNameNode();
777
+ if (Node.isObjectBindingPattern(nestedInit)) {
778
+ for (const nested of nestedInit.getElements()) {
779
+ results.push({
780
+ name: nested.getName(),
781
+ alias: element.getName() !== nested.getName() ? element.getName() : undefined,
782
+ depth: 2,
783
+ hasDefaultValue: nested.getInitializer() !== undefined || hasDefault,
784
+ });
785
+ }
786
+ }
787
+ else {
788
+ results.push({
789
+ name,
790
+ alias: alias !== name ? alias : undefined,
791
+ depth: 1,
792
+ hasDefaultValue: hasDefault,
793
+ });
794
+ }
795
+ }
796
+ }
797
+ }
798
+ return results;
799
+ }
800
+ //# sourceMappingURL=apollo-client.js.map