codeguardian-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 (335) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -0
  3. package/dist/agent/agentTools.d.ts +26 -0
  4. package/dist/agent/agentTools.d.ts.map +1 -0
  5. package/dist/agent/agentTools.js +699 -0
  6. package/dist/agent/agentTools.js.map +1 -0
  7. package/dist/agent/autoValidator.d.ts +110 -0
  8. package/dist/agent/autoValidator.d.ts.map +1 -0
  9. package/dist/agent/autoValidator.js +964 -0
  10. package/dist/agent/autoValidator.js.map +1 -0
  11. package/dist/agent/fileWatcher.d.ts +28 -0
  12. package/dist/agent/fileWatcher.d.ts.map +1 -0
  13. package/dist/agent/fileWatcher.js +88 -0
  14. package/dist/agent/fileWatcher.js.map +1 -0
  15. package/dist/agent/guardianPersistence.d.ts +98 -0
  16. package/dist/agent/guardianPersistence.d.ts.map +1 -0
  17. package/dist/agent/guardianPersistence.js +296 -0
  18. package/dist/agent/guardianPersistence.js.map +1 -0
  19. package/dist/agent/mcpNotifications.d.ts +38 -0
  20. package/dist/agent/mcpNotifications.d.ts.map +1 -0
  21. package/dist/agent/mcpNotifications.js +81 -0
  22. package/dist/agent/mcpNotifications.js.map +1 -0
  23. package/dist/analyzers/aiPatterns.d.ts +16 -0
  24. package/dist/analyzers/aiPatterns.d.ts.map +1 -0
  25. package/dist/analyzers/aiPatterns.js +103 -0
  26. package/dist/analyzers/aiPatterns.js.map +1 -0
  27. package/dist/analyzers/antiPatterns.d.ts +60 -0
  28. package/dist/analyzers/antiPatterns.d.ts.map +1 -0
  29. package/dist/analyzers/antiPatterns.js +198 -0
  30. package/dist/analyzers/antiPatterns.js.map +1 -0
  31. package/dist/analyzers/builtinTypes.d.ts +18 -0
  32. package/dist/analyzers/builtinTypes.d.ts.map +1 -0
  33. package/dist/analyzers/builtinTypes.js +1275 -0
  34. package/dist/analyzers/builtinTypes.js.map +1 -0
  35. package/dist/analyzers/complexity.d.ts +14 -0
  36. package/dist/analyzers/complexity.d.ts.map +1 -0
  37. package/dist/analyzers/complexity.js +610 -0
  38. package/dist/analyzers/complexity.js.map +1 -0
  39. package/dist/analyzers/findingVerifier.d.ts +59 -0
  40. package/dist/analyzers/findingVerifier.d.ts.map +1 -0
  41. package/dist/analyzers/findingVerifier.js +1169 -0
  42. package/dist/analyzers/findingVerifier.js.map +1 -0
  43. package/dist/analyzers/impactAnalyzer.d.ts +53 -0
  44. package/dist/analyzers/impactAnalyzer.d.ts.map +1 -0
  45. package/dist/analyzers/impactAnalyzer.js +152 -0
  46. package/dist/analyzers/impactAnalyzer.js.map +1 -0
  47. package/dist/analyzers/languageDetector.d.ts +48 -0
  48. package/dist/analyzers/languageDetector.d.ts.map +1 -0
  49. package/dist/analyzers/languageDetector.js +404 -0
  50. package/dist/analyzers/languageDetector.js.map +1 -0
  51. package/dist/analyzers/parsers/incrementalParser.d.ts +53 -0
  52. package/dist/analyzers/parsers/incrementalParser.d.ts.map +1 -0
  53. package/dist/analyzers/parsers/incrementalParser.js +193 -0
  54. package/dist/analyzers/parsers/incrementalParser.js.map +1 -0
  55. package/dist/analyzers/parsers/scopeResolver.d.ts +92 -0
  56. package/dist/analyzers/parsers/scopeResolver.d.ts.map +1 -0
  57. package/dist/analyzers/parsers/scopeResolver.js +324 -0
  58. package/dist/analyzers/parsers/scopeResolver.js.map +1 -0
  59. package/dist/analyzers/parsers/semanticIndex.d.ts +127 -0
  60. package/dist/analyzers/parsers/semanticIndex.d.ts.map +1 -0
  61. package/dist/analyzers/parsers/semanticIndex.js +429 -0
  62. package/dist/analyzers/parsers/semanticIndex.js.map +1 -0
  63. package/dist/analyzers/parsers/sessionDiffAnalyzer.d.ts +42 -0
  64. package/dist/analyzers/parsers/sessionDiffAnalyzer.d.ts.map +1 -0
  65. package/dist/analyzers/parsers/sessionDiffAnalyzer.js +233 -0
  66. package/dist/analyzers/parsers/sessionDiffAnalyzer.js.map +1 -0
  67. package/dist/analyzers/parsers/treeSitterParser.d.ts +76 -0
  68. package/dist/analyzers/parsers/treeSitterParser.d.ts.map +1 -0
  69. package/dist/analyzers/parsers/treeSitterParser.js +709 -0
  70. package/dist/analyzers/parsers/treeSitterParser.js.map +1 -0
  71. package/dist/analyzers/relevanceScorer.d.ts +43 -0
  72. package/dist/analyzers/relevanceScorer.d.ts.map +1 -0
  73. package/dist/analyzers/relevanceScorer.js +200 -0
  74. package/dist/analyzers/relevanceScorer.js.map +1 -0
  75. package/dist/analyzers/standardLibrary.d.ts +22 -0
  76. package/dist/analyzers/standardLibrary.d.ts.map +1 -0
  77. package/dist/analyzers/standardLibrary.js +211 -0
  78. package/dist/analyzers/standardLibrary.js.map +1 -0
  79. package/dist/analyzers/symbolGraph.d.ts +30 -0
  80. package/dist/analyzers/symbolGraph.d.ts.map +1 -0
  81. package/dist/analyzers/symbolGraph.js +380 -0
  82. package/dist/analyzers/symbolGraph.js.map +1 -0
  83. package/dist/analyzers/symbolTable.d.ts +18 -0
  84. package/dist/analyzers/symbolTable.d.ts.map +1 -0
  85. package/dist/analyzers/symbolTable.js +176 -0
  86. package/dist/analyzers/symbolTable.js.map +1 -0
  87. package/dist/analyzers/typeChecker.d.ts +13 -0
  88. package/dist/analyzers/typeChecker.d.ts.map +1 -0
  89. package/dist/analyzers/typeChecker.js +580 -0
  90. package/dist/analyzers/typeChecker.js.map +1 -0
  91. package/dist/analyzers/usagePatterns.d.ts +42 -0
  92. package/dist/analyzers/usagePatterns.d.ts.map +1 -0
  93. package/dist/analyzers/usagePatterns.js +75 -0
  94. package/dist/analyzers/usagePatterns.js.map +1 -0
  95. package/dist/api-contract/context/backend.d.ts +19 -0
  96. package/dist/api-contract/context/backend.d.ts.map +1 -0
  97. package/dist/api-contract/context/backend.js +64 -0
  98. package/dist/api-contract/context/backend.js.map +1 -0
  99. package/dist/api-contract/context/contract.d.ts +34 -0
  100. package/dist/api-contract/context/contract.d.ts.map +1 -0
  101. package/dist/api-contract/context/contract.js +306 -0
  102. package/dist/api-contract/context/contract.js.map +1 -0
  103. package/dist/api-contract/context/frontend.d.ts +19 -0
  104. package/dist/api-contract/context/frontend.d.ts.map +1 -0
  105. package/dist/api-contract/context/frontend.js +64 -0
  106. package/dist/api-contract/context/frontend.js.map +1 -0
  107. package/dist/api-contract/detector.d.ts +28 -0
  108. package/dist/api-contract/detector.d.ts.map +1 -0
  109. package/dist/api-contract/detector.js +393 -0
  110. package/dist/api-contract/detector.js.map +1 -0
  111. package/dist/api-contract/extractors/python.d.ts +32 -0
  112. package/dist/api-contract/extractors/python.d.ts.map +1 -0
  113. package/dist/api-contract/extractors/python.js +521 -0
  114. package/dist/api-contract/extractors/python.js.map +1 -0
  115. package/dist/api-contract/extractors/pythonAstUtils.d.ts +44 -0
  116. package/dist/api-contract/extractors/pythonAstUtils.d.ts.map +1 -0
  117. package/dist/api-contract/extractors/pythonAstUtils.js +489 -0
  118. package/dist/api-contract/extractors/pythonAstUtils.js.map +1 -0
  119. package/dist/api-contract/extractors/tsAstUtils.d.ts +47 -0
  120. package/dist/api-contract/extractors/tsAstUtils.d.ts.map +1 -0
  121. package/dist/api-contract/extractors/tsAstUtils.js +173 -0
  122. package/dist/api-contract/extractors/tsAstUtils.js.map +1 -0
  123. package/dist/api-contract/extractors/typescript.d.ts +32 -0
  124. package/dist/api-contract/extractors/typescript.d.ts.map +1 -0
  125. package/dist/api-contract/extractors/typescript.js +666 -0
  126. package/dist/api-contract/extractors/typescript.js.map +1 -0
  127. package/dist/api-contract/index.d.ts +104 -0
  128. package/dist/api-contract/index.d.ts.map +1 -0
  129. package/dist/api-contract/index.js +232 -0
  130. package/dist/api-contract/index.js.map +1 -0
  131. package/dist/api-contract/types.d.ts +151 -0
  132. package/dist/api-contract/types.d.ts.map +1 -0
  133. package/dist/api-contract/types.js +19 -0
  134. package/dist/api-contract/types.js.map +1 -0
  135. package/dist/api-contract/validators/endpoint.d.ts +21 -0
  136. package/dist/api-contract/validators/endpoint.d.ts.map +1 -0
  137. package/dist/api-contract/validators/endpoint.js +224 -0
  138. package/dist/api-contract/validators/endpoint.js.map +1 -0
  139. package/dist/api-contract/validators/index.d.ts +40 -0
  140. package/dist/api-contract/validators/index.d.ts.map +1 -0
  141. package/dist/api-contract/validators/index.js +875 -0
  142. package/dist/api-contract/validators/index.js.map +1 -0
  143. package/dist/api-contract/validators/parameter.d.ts +17 -0
  144. package/dist/api-contract/validators/parameter.d.ts.map +1 -0
  145. package/dist/api-contract/validators/parameter.js +250 -0
  146. package/dist/api-contract/validators/parameter.js.map +1 -0
  147. package/dist/api-contract/validators/type.d.ts +38 -0
  148. package/dist/api-contract/validators/type.d.ts.map +1 -0
  149. package/dist/api-contract/validators/type.js +244 -0
  150. package/dist/api-contract/validators/type.js.map +1 -0
  151. package/dist/context/apiContract/complexTypeSupport.d.ts +83 -0
  152. package/dist/context/apiContract/complexTypeSupport.d.ts.map +1 -0
  153. package/dist/context/apiContract/complexTypeSupport.js +665 -0
  154. package/dist/context/apiContract/complexTypeSupport.js.map +1 -0
  155. package/dist/context/apiContract/graphqlSupport.d.ts +105 -0
  156. package/dist/context/apiContract/graphqlSupport.d.ts.map +1 -0
  157. package/dist/context/apiContract/graphqlSupport.js +671 -0
  158. package/dist/context/apiContract/graphqlSupport.js.map +1 -0
  159. package/dist/context/apiContract/index.d.ts +14 -0
  160. package/dist/context/apiContract/index.d.ts.map +1 -0
  161. package/dist/context/apiContract/index.js +17 -0
  162. package/dist/context/apiContract/index.js.map +1 -0
  163. package/dist/context/apiContract/webSocketSupport.d.ts +104 -0
  164. package/dist/context/apiContract/webSocketSupport.d.ts.map +1 -0
  165. package/dist/context/apiContract/webSocketSupport.js +465 -0
  166. package/dist/context/apiContract/webSocketSupport.js.map +1 -0
  167. package/dist/context/apiContractContext.d.ts +15 -0
  168. package/dist/context/apiContractContext.d.ts.map +1 -0
  169. package/dist/context/apiContractContext.js +979 -0
  170. package/dist/context/apiContractContext.js.map +1 -0
  171. package/dist/context/apiContractExtraction.d.ts +52 -0
  172. package/dist/context/apiContractExtraction.d.ts.map +1 -0
  173. package/dist/context/apiContractExtraction.js +438 -0
  174. package/dist/context/apiContractExtraction.js.map +1 -0
  175. package/dist/context/contextLineage.d.ts +79 -0
  176. package/dist/context/contextLineage.d.ts.map +1 -0
  177. package/dist/context/contextLineage.js +259 -0
  178. package/dist/context/contextLineage.js.map +1 -0
  179. package/dist/context/contextOrchestrator.d.ts +57 -0
  180. package/dist/context/contextOrchestrator.d.ts.map +1 -0
  181. package/dist/context/contextOrchestrator.js +162 -0
  182. package/dist/context/contextOrchestrator.js.map +1 -0
  183. package/dist/context/intentTracker.d.ts +73 -0
  184. package/dist/context/intentTracker.d.ts.map +1 -0
  185. package/dist/context/intentTracker.js +168 -0
  186. package/dist/context/intentTracker.js.map +1 -0
  187. package/dist/context/projectContext.d.ts +219 -0
  188. package/dist/context/projectContext.d.ts.map +1 -0
  189. package/dist/context/projectContext.js +1984 -0
  190. package/dist/context/projectContext.js.map +1 -0
  191. package/dist/prompts/index.d.ts +17 -0
  192. package/dist/prompts/index.d.ts.map +1 -0
  193. package/dist/prompts/index.js +260 -0
  194. package/dist/prompts/index.js.map +1 -0
  195. package/dist/prompts/library.d.ts +51 -0
  196. package/dist/prompts/library.d.ts.map +1 -0
  197. package/dist/prompts/library.js +65 -0
  198. package/dist/prompts/library.js.map +1 -0
  199. package/dist/prompts/templates.d.ts +44 -0
  200. package/dist/prompts/templates.d.ts.map +1 -0
  201. package/dist/prompts/templates.js +97 -0
  202. package/dist/prompts/templates.js.map +1 -0
  203. package/dist/queue/jobPersistence.d.ts +46 -0
  204. package/dist/queue/jobPersistence.d.ts.map +1 -0
  205. package/dist/queue/jobPersistence.js +158 -0
  206. package/dist/queue/jobPersistence.js.map +1 -0
  207. package/dist/queue/jobQueue.d.ts +116 -0
  208. package/dist/queue/jobQueue.d.ts.map +1 -0
  209. package/dist/queue/jobQueue.js +275 -0
  210. package/dist/queue/jobQueue.js.map +1 -0
  211. package/dist/queue/validationJob.d.ts +69 -0
  212. package/dist/queue/validationJob.d.ts.map +1 -0
  213. package/dist/queue/validationJob.js +435 -0
  214. package/dist/queue/validationJob.js.map +1 -0
  215. package/dist/resources/index.d.ts +15 -0
  216. package/dist/resources/index.d.ts.map +1 -0
  217. package/dist/resources/index.js +328 -0
  218. package/dist/resources/index.js.map +1 -0
  219. package/dist/resources/validationReportStore.d.ts +170 -0
  220. package/dist/resources/validationReportStore.d.ts.map +1 -0
  221. package/dist/resources/validationReportStore.js +515 -0
  222. package/dist/resources/validationReportStore.js.map +1 -0
  223. package/dist/server.d.ts +12 -0
  224. package/dist/server.d.ts.map +1 -0
  225. package/dist/server.js +102 -0
  226. package/dist/server.js.map +1 -0
  227. package/dist/tools/asyncValidation.d.ts +19 -0
  228. package/dist/tools/asyncValidation.d.ts.map +1 -0
  229. package/dist/tools/asyncValidation.js +346 -0
  230. package/dist/tools/asyncValidation.js.map +1 -0
  231. package/dist/tools/buildContext.d.ts +17 -0
  232. package/dist/tools/buildContext.d.ts.map +1 -0
  233. package/dist/tools/buildContext.js +188 -0
  234. package/dist/tools/buildContext.js.map +1 -0
  235. package/dist/tools/getDependencyGraph.d.ts +16 -0
  236. package/dist/tools/getDependencyGraph.d.ts.map +1 -0
  237. package/dist/tools/getDependencyGraph.js +436 -0
  238. package/dist/tools/getDependencyGraph.js.map +1 -0
  239. package/dist/tools/incrementalValidation.d.ts +71 -0
  240. package/dist/tools/incrementalValidation.d.ts.map +1 -0
  241. package/dist/tools/incrementalValidation.js +203 -0
  242. package/dist/tools/incrementalValidation.js.map +1 -0
  243. package/dist/tools/index.d.ts +24 -0
  244. package/dist/tools/index.d.ts.map +1 -0
  245. package/dist/tools/index.js +106 -0
  246. package/dist/tools/index.js.map +1 -0
  247. package/dist/tools/validateCode.d.ts +17 -0
  248. package/dist/tools/validateCode.d.ts.map +1 -0
  249. package/dist/tools/validateCode.js +368 -0
  250. package/dist/tools/validateCode.js.map +1 -0
  251. package/dist/tools/validateCodeLite.d.ts +2 -0
  252. package/dist/tools/validateCodeLite.d.ts.map +1 -0
  253. package/dist/tools/validateCodeLite.js +2 -0
  254. package/dist/tools/validateCodeLite.js.map +1 -0
  255. package/dist/tools/validation/builtins.d.ts +92 -0
  256. package/dist/tools/validation/builtins.d.ts.map +1 -0
  257. package/dist/tools/validation/builtins.js +2184 -0
  258. package/dist/tools/validation/builtins.js.map +1 -0
  259. package/dist/tools/validation/contextualNaming.d.ts +99 -0
  260. package/dist/tools/validation/contextualNaming.d.ts.map +1 -0
  261. package/dist/tools/validation/contextualNaming.js +959 -0
  262. package/dist/tools/validation/contextualNaming.js.map +1 -0
  263. package/dist/tools/validation/deadCode.d.ts +115 -0
  264. package/dist/tools/validation/deadCode.d.ts.map +1 -0
  265. package/dist/tools/validation/deadCode.js +861 -0
  266. package/dist/tools/validation/deadCode.js.map +1 -0
  267. package/dist/tools/validation/extractors/index.d.ts +131 -0
  268. package/dist/tools/validation/extractors/index.d.ts.map +1 -0
  269. package/dist/tools/validation/extractors/index.js +233 -0
  270. package/dist/tools/validation/extractors/index.js.map +1 -0
  271. package/dist/tools/validation/extractors/javascript.d.ts +73 -0
  272. package/dist/tools/validation/extractors/javascript.d.ts.map +1 -0
  273. package/dist/tools/validation/extractors/javascript.js +1841 -0
  274. package/dist/tools/validation/extractors/javascript.js.map +1 -0
  275. package/dist/tools/validation/extractors/python.d.ts +93 -0
  276. package/dist/tools/validation/extractors/python.d.ts.map +1 -0
  277. package/dist/tools/validation/extractors/python.js +799 -0
  278. package/dist/tools/validation/extractors/python.js.map +1 -0
  279. package/dist/tools/validation/manifest.d.ts +45 -0
  280. package/dist/tools/validation/manifest.d.ts.map +1 -0
  281. package/dist/tools/validation/manifest.js +719 -0
  282. package/dist/tools/validation/manifest.js.map +1 -0
  283. package/dist/tools/validation/parser.d.ts +58 -0
  284. package/dist/tools/validation/parser.d.ts.map +1 -0
  285. package/dist/tools/validation/parser.js +232 -0
  286. package/dist/tools/validation/parser.js.map +1 -0
  287. package/dist/tools/validation/registry.d.ts +15 -0
  288. package/dist/tools/validation/registry.d.ts.map +1 -0
  289. package/dist/tools/validation/registry.js +169 -0
  290. package/dist/tools/validation/registry.js.map +1 -0
  291. package/dist/tools/validation/scoring.d.ts +54 -0
  292. package/dist/tools/validation/scoring.d.ts.map +1 -0
  293. package/dist/tools/validation/scoring.js +242 -0
  294. package/dist/tools/validation/scoring.js.map +1 -0
  295. package/dist/tools/validation/types.d.ts +120 -0
  296. package/dist/tools/validation/types.d.ts.map +1 -0
  297. package/dist/tools/validation/types.js +11 -0
  298. package/dist/tools/validation/types.js.map +1 -0
  299. package/dist/tools/validation/unusedLocals.d.ts +36 -0
  300. package/dist/tools/validation/unusedLocals.d.ts.map +1 -0
  301. package/dist/tools/validation/unusedLocals.js +333 -0
  302. package/dist/tools/validation/unusedLocals.js.map +1 -0
  303. package/dist/tools/validation/validation.d.ts +98 -0
  304. package/dist/tools/validation/validation.d.ts.map +1 -0
  305. package/dist/tools/validation/validation.js +1837 -0
  306. package/dist/tools/validation/validation.js.map +1 -0
  307. package/dist/types/codeGraph.d.ts +163 -0
  308. package/dist/types/codeGraph.d.ts.map +1 -0
  309. package/dist/types/codeGraph.js +9 -0
  310. package/dist/types/codeGraph.js.map +1 -0
  311. package/dist/types/symbolGraph.d.ts +68 -0
  312. package/dist/types/symbolGraph.d.ts.map +1 -0
  313. package/dist/types/symbolGraph.js +10 -0
  314. package/dist/types/symbolGraph.js.map +1 -0
  315. package/dist/types/tools.d.ts +43 -0
  316. package/dist/types/tools.d.ts.map +1 -0
  317. package/dist/types/tools.js +7 -0
  318. package/dist/types/tools.js.map +1 -0
  319. package/dist/utils/fileFilter.d.ts +37 -0
  320. package/dist/utils/fileFilter.d.ts.map +1 -0
  321. package/dist/utils/fileFilter.js +91 -0
  322. package/dist/utils/fileFilter.js.map +1 -0
  323. package/dist/utils/gitUtils.d.ts +28 -0
  324. package/dist/utils/gitUtils.d.ts.map +1 -0
  325. package/dist/utils/gitUtils.js +81 -0
  326. package/dist/utils/gitUtils.js.map +1 -0
  327. package/dist/utils/logger.d.ts +15 -0
  328. package/dist/utils/logger.d.ts.map +1 -0
  329. package/dist/utils/logger.js +38 -0
  330. package/dist/utils/logger.js.map +1 -0
  331. package/dist/utils/serialization.d.ts +25 -0
  332. package/dist/utils/serialization.d.ts.map +1 -0
  333. package/dist/utils/serialization.js +53 -0
  334. package/dist/utils/serialization.js.map +1 -0
  335. package/package.json +90 -0
@@ -0,0 +1,671 @@
1
+ /**
2
+ * API Contract Guardian - GraphQL Support
3
+ *
4
+ * Extracts and validates GraphQL schemas, queries, and mutations.
5
+ * Detects mismatches between frontend GraphQL operations and backend schema.
6
+ *
7
+ * @format
8
+ */
9
+ import * as fs from "fs/promises";
10
+ import * as path from "path";
11
+ import { glob } from "glob";
12
+ import { logger } from "../../utils/logger.js";
13
+ import { getParser } from "../../tools/validation/parser.js";
14
+ // ============================================================================
15
+ // Schema Extraction (Backend)
16
+ // ============================================================================
17
+ /**
18
+ * Extract GraphQL schema from .graphql files or schema definitions
19
+ */
20
+ export async function extractGraphQLSchema(filePath) {
21
+ try {
22
+ const content = await fs.readFile(filePath, "utf-8");
23
+ // Check if it's a GraphQL schema file
24
+ if (filePath.endsWith(".graphql") || filePath.endsWith(".gql")) {
25
+ return parseGraphQLSchema(content, filePath);
26
+ }
27
+ // Check for GraphQL schema in TypeScript/JavaScript files
28
+ if (filePath.endsWith(".ts") || filePath.endsWith(".js") || filePath.endsWith(".tsx") || filePath.endsWith(".jsx")) {
29
+ return extractSchemaFromCode(content, filePath);
30
+ }
31
+ return null;
32
+ }
33
+ catch (err) {
34
+ logger.debug(`Failed to extract GraphQL schema from ${filePath}: ${err}`);
35
+ return null;
36
+ }
37
+ }
38
+ export function parseGraphQLSchema(content, filePath) {
39
+ const schema = {
40
+ types: [],
41
+ queries: [],
42
+ mutations: [],
43
+ subscriptions: [],
44
+ file: filePath,
45
+ line: 1,
46
+ };
47
+ const lines = content.split("\n");
48
+ let currentType = null;
49
+ let lineNum = 0;
50
+ for (let i = 0; i < lines.length; i++) {
51
+ lineNum = i + 1;
52
+ const line = lines[i].trim();
53
+ // Skip comments and empty lines
54
+ if (!line || line.startsWith("#"))
55
+ continue;
56
+ // Type definition: type User { ... }
57
+ const typeMatch = line.match(/^type\s+(\w+)\s*\{/);
58
+ if (typeMatch) {
59
+ const typeName = typeMatch[1];
60
+ // Handle Query, Mutation, Subscription specially
61
+ if (typeName === "Query" || typeName === "Mutation" || typeName === "Subscription") {
62
+ if (currentType) {
63
+ schema.types.push(currentType);
64
+ currentType = null;
65
+ }
66
+ // Parse the entire Query/Mutation/Subscription block
67
+ const { operations, endLine } = parseQueryTypeBlock(content, i, typeName.toLowerCase());
68
+ if (typeName === "Query") {
69
+ schema.queries.push(...operations);
70
+ }
71
+ else if (typeName === "Mutation") {
72
+ schema.mutations.push(...operations);
73
+ }
74
+ else if (typeName === "Subscription") {
75
+ schema.subscriptions.push(...operations);
76
+ }
77
+ // Skip to the end of this type block
78
+ i = endLine;
79
+ continue;
80
+ }
81
+ if (currentType) {
82
+ schema.types.push(currentType);
83
+ }
84
+ currentType = {
85
+ name: typeName,
86
+ kind: "object",
87
+ fields: [],
88
+ file: filePath,
89
+ line: lineNum,
90
+ };
91
+ continue;
92
+ }
93
+ // Input type: input CreateUserInput { ... }
94
+ const inputMatch = line.match(/^input\s+(\w+)\s*\{/);
95
+ if (inputMatch) {
96
+ if (currentType) {
97
+ schema.types.push(currentType);
98
+ }
99
+ currentType = {
100
+ name: inputMatch[1],
101
+ kind: "input",
102
+ fields: [],
103
+ file: filePath,
104
+ line: lineNum,
105
+ };
106
+ continue;
107
+ }
108
+ // Enum type: enum Status { ... }
109
+ const enumMatch = line.match(/^enum\s+(\w+)\s*\{/);
110
+ if (enumMatch) {
111
+ if (currentType) {
112
+ schema.types.push(currentType);
113
+ }
114
+ currentType = {
115
+ name: enumMatch[1],
116
+ kind: "enum",
117
+ fields: [],
118
+ file: filePath,
119
+ line: lineNum,
120
+ };
121
+ continue;
122
+ }
123
+ // Interface type: interface Node { ... }
124
+ const interfaceMatch = line.match(/^interface\s+(\w+)\s*\{/);
125
+ if (interfaceMatch) {
126
+ if (currentType) {
127
+ schema.types.push(currentType);
128
+ }
129
+ currentType = {
130
+ name: interfaceMatch[1],
131
+ kind: "interface",
132
+ fields: [],
133
+ file: filePath,
134
+ line: lineNum,
135
+ };
136
+ continue;
137
+ }
138
+ // Type closing
139
+ if (line === "}" && currentType) {
140
+ schema.types.push(currentType);
141
+ currentType = null;
142
+ continue;
143
+ }
144
+ // Field definition within type
145
+ if (currentType && line.includes(":")) {
146
+ const field = parseGraphQLField(line);
147
+ if (field) {
148
+ currentType.fields.push(field);
149
+ }
150
+ }
151
+ }
152
+ // Don't forget the last type
153
+ if (currentType) {
154
+ schema.types.push(currentType);
155
+ }
156
+ return schema;
157
+ }
158
+ function parseGraphQLField(line) {
159
+ // Parse: fieldName: Type! or fieldName(arg: Type!): ReturnType!
160
+ const fieldMatch = line.match(/^(\w+)(?:\(([^)]+)\))?\s*:\s*(.+)$/);
161
+ if (!fieldMatch)
162
+ return null;
163
+ const name = fieldMatch[1];
164
+ const argsString = fieldMatch[2];
165
+ const typeString = fieldMatch[3].trim();
166
+ const { type, required } = parseGraphQLType(typeString);
167
+ const field = {
168
+ name,
169
+ type,
170
+ required,
171
+ };
172
+ if (argsString) {
173
+ field.arguments = parseGraphQLArguments(argsString);
174
+ }
175
+ return field;
176
+ }
177
+ function parseGraphQLType(typeString) {
178
+ const required = typeString.endsWith("!");
179
+ const type = typeString.replace(/!$/, "").trim();
180
+ return { type, required };
181
+ }
182
+ function parseGraphQLArguments(argsString) {
183
+ const args = [];
184
+ const argMatches = argsString.matchAll(/(\w+)\s*:\s*([^,]+)/g);
185
+ for (const match of argMatches) {
186
+ const name = match[1];
187
+ const typeStr = match[2].trim();
188
+ const { type, required } = parseGraphQLType(typeStr);
189
+ args.push({
190
+ name,
191
+ type,
192
+ required,
193
+ });
194
+ }
195
+ return args;
196
+ }
197
+ function parseQueryTypeBlock(content, startLine, type) {
198
+ const operations = [];
199
+ const lines = content.split("\n");
200
+ let braceCount = 1; // We start inside the type definition (the opening { on startLine)
201
+ let i = startLine + 1; // Start from the line AFTER the type declaration
202
+ while (i < lines.length && braceCount > 0) {
203
+ const line = lines[i];
204
+ const trimmedLine = line.trim();
205
+ // Count braces more carefully
206
+ for (const char of line) {
207
+ if (char === "{")
208
+ braceCount++;
209
+ if (char === "}")
210
+ braceCount--;
211
+ }
212
+ // If we've closed the type, stop
213
+ if (braceCount <= 0)
214
+ break;
215
+ // Parse operation field - must be at braceCount == 1 (inside the type but not nested)
216
+ // and the line should contain a field definition
217
+ if (braceCount === 1 && trimmedLine.includes(":")) {
218
+ // Match field name, optional args, colon, and return type (including arrays and non-null)
219
+ const opMatch = trimmedLine.match(/^(\w+)(?:\(([^)]+)\))?\s*:\s*([\w\[\]!]+)/);
220
+ if (opMatch) {
221
+ const name = opMatch[1];
222
+ const argsString = opMatch[2];
223
+ const returnType = opMatch[3];
224
+ operations.push({
225
+ name,
226
+ type,
227
+ returnType,
228
+ arguments: argsString ? parseGraphQLArguments(argsString) : [],
229
+ file: "schema.graphql",
230
+ line: i + 1,
231
+ });
232
+ }
233
+ }
234
+ i++;
235
+ }
236
+ return { operations, endLine: i };
237
+ }
238
+ function extractSchemaFromCode(content, filePath) {
239
+ // Look for schema definitions in code (e.g., gql`...` or buildSchema(`...`))
240
+ const gqlMatch = content.match(/gql`([\s\S]*?)`/);
241
+ const buildSchemaMatch = content.match(/buildSchema\(`([\s\S]*?)`\)/);
242
+ const schemaMatch = content.match(/typeDefs\s*=\s*`([\s\S]*?)`/);
243
+ const schemaContent = gqlMatch?.[1] || buildSchemaMatch?.[1] || schemaMatch?.[1];
244
+ if (schemaContent) {
245
+ return parseGraphQLSchema(schemaContent, filePath);
246
+ }
247
+ return null;
248
+ }
249
+ // ============================================================================
250
+ // Frontend Operation Extraction
251
+ // ============================================================================
252
+ /**
253
+ * Extract GraphQL operations from frontend code
254
+ */
255
+ export async function extractGraphQLOperations(filePath) {
256
+ const operations = [];
257
+ try {
258
+ const content = await fs.readFile(filePath, "utf-8");
259
+ // Detect framework
260
+ const framework = detectGraphQLFramework(content);
261
+ // Extract operations based on framework patterns
262
+ if (framework === "apollo") {
263
+ operations.push(...extractApolloOperations(content, filePath));
264
+ }
265
+ else if (framework === "relay") {
266
+ operations.push(...extractRelayOperations(content, filePath));
267
+ }
268
+ else if (framework === "urql") {
269
+ operations.push(...extractUrqlOperations(content, filePath));
270
+ }
271
+ else {
272
+ // Generic extraction
273
+ operations.push(...extractGenericOperations(content, filePath));
274
+ }
275
+ // Set framework on all operations
276
+ operations.forEach(op => op.framework = framework);
277
+ }
278
+ catch (err) {
279
+ logger.debug(`Failed to extract GraphQL operations from ${filePath}: ${err}`);
280
+ }
281
+ return operations;
282
+ }
283
+ function detectGraphQLFramework(content) {
284
+ if (content.includes("@apollo/client") || content.includes("useQuery") || content.includes("useMutation")) {
285
+ return "apollo";
286
+ }
287
+ if (content.includes("react-relay") || content.includes("graphql") && content.includes("@argumentDefinitions")) {
288
+ return "relay";
289
+ }
290
+ if (content.includes("urql") || content.includes("useQuery") && content.includes("urql")) {
291
+ return "urql";
292
+ }
293
+ if (content.includes("graphql-request")) {
294
+ return "graphql-request";
295
+ }
296
+ return "raw";
297
+ }
298
+ function extractApolloOperations(content, filePath) {
299
+ const operations = [];
300
+ try {
301
+ const parser = getParser("typescript");
302
+ const tree = parser.parse(content);
303
+ // Traverse AST to find gql`...` (Tree-sitter TSX parses this as a call_expression
304
+ // with a template_string argument) and, in some grammars, tagged_template_expression.
305
+ function traverse(node) {
306
+ if (!node)
307
+ return;
308
+ const addOperationFromTemplate = (templateNode) => {
309
+ if (!templateNode)
310
+ return;
311
+ const raw = content.slice(templateNode.startIndex, templateNode.endIndex);
312
+ const graphqlString = raw.replace(/^`/, "").replace(/`$/, "").trim();
313
+ const line = node.startPosition.row + 1;
314
+ const operation = parseGraphQLOperationString(graphqlString, filePath, line);
315
+ if (operation)
316
+ operations.push(operation);
317
+ };
318
+ // TSX grammar: gql`...` => call_expression(function: identifier, arguments: template_string)
319
+ if (node.type === "call_expression") {
320
+ const functionNode = node.childForFieldName("function");
321
+ const argsNode = node.childForFieldName("arguments");
322
+ if (functionNode && argsNode) {
323
+ const funcText = content.slice(functionNode.startIndex, functionNode.endIndex).trim();
324
+ if (funcText === "gql") {
325
+ const templateNode = argsNode.type === "template_string"
326
+ ? argsNode
327
+ : (argsNode.children || []).find((c) => c?.type === "template_string");
328
+ addOperationFromTemplate(templateNode);
329
+ }
330
+ }
331
+ }
332
+ // Other grammars: tagged_template_expression(tag: gql, template: template_string)
333
+ if (node.type === "tagged_template_expression") {
334
+ const tagNode = node.childForFieldName("tag");
335
+ const templateNode = node.childForFieldName("template");
336
+ if (tagNode && templateNode) {
337
+ const tagName = content.slice(tagNode.startIndex, tagNode.endIndex).trim();
338
+ if (tagName === "gql") {
339
+ addOperationFromTemplate(templateNode);
340
+ }
341
+ }
342
+ }
343
+ // Recursively traverse children
344
+ for (const child of node.children || []) {
345
+ traverse(child);
346
+ }
347
+ }
348
+ traverse(tree.rootNode);
349
+ }
350
+ catch (err) {
351
+ logger.debug(`AST parsing failed for ${filePath}: ${err}`);
352
+ }
353
+ return operations;
354
+ }
355
+ function extractRelayOperations(content, filePath) {
356
+ // Similar to Apollo but with Relay-specific syntax
357
+ return extractApolloOperations(content, filePath);
358
+ }
359
+ function extractUrqlOperations(content, filePath) {
360
+ // Similar pattern
361
+ return extractApolloOperations(content, filePath);
362
+ }
363
+ function extractGenericOperations(content, filePath) {
364
+ // Look for any GraphQL-like strings
365
+ const operations = [];
366
+ // Match query/mutation/subscription strings
367
+ const queryRegex = /(?:query|mutation|subscription)\s+(\w+)/g;
368
+ let match;
369
+ while ((match = queryRegex.exec(content)) !== null) {
370
+ const operationName = match[1];
371
+ const line = content.substring(0, match.index).split("\n").length;
372
+ // Try to extract the full operation
373
+ const startIdx = match.index;
374
+ let endIdx = startIdx;
375
+ let braceCount = 0;
376
+ let inString = false;
377
+ for (let i = startIdx; i < content.length; i++) {
378
+ const char = content[i];
379
+ if (char === '"' || char === "'" || char === "`") {
380
+ inString = !inString;
381
+ }
382
+ if (!inString) {
383
+ if (char === "{")
384
+ braceCount++;
385
+ if (char === "}")
386
+ braceCount--;
387
+ if (braceCount === 0 && char === "}") {
388
+ endIdx = i + 1;
389
+ break;
390
+ }
391
+ }
392
+ }
393
+ const operationString = content.substring(startIdx, endIdx);
394
+ const operation = parseGraphQLOperationString(operationString, filePath, line);
395
+ if (operation) {
396
+ operations.push(operation);
397
+ }
398
+ }
399
+ return operations;
400
+ }
401
+ function parseGraphQLOperationString(operationString, filePath, line) {
402
+ const trimmed = operationString.trim();
403
+ if (!trimmed)
404
+ return null;
405
+ // Find the first operation keyword (allow leading whitespace/comments/newlines)
406
+ const opMatch = /\b(query|mutation|subscription)\b\s*(\w+)?/i.exec(trimmed);
407
+ if (!opMatch) {
408
+ return null;
409
+ }
410
+ const type = opMatch[1].toLowerCase();
411
+ const name = opMatch[2] || "anonymous";
412
+ const headerEnd = trimmed.indexOf("{", opMatch.index);
413
+ const header = headerEnd >= 0 ? trimmed.slice(opMatch.index, headerEnd) : trimmed.slice(opMatch.index);
414
+ // Extract variables
415
+ const variables = [];
416
+ const varMatch = header.match(/\(([^)]+)\)/);
417
+ if (varMatch) {
418
+ const varDefs = varMatch[1].split(",");
419
+ for (const varDef of varDefs) {
420
+ const parts = varDef.trim().match(/\$(\w+)\s*:\s*(\w+!?)/);
421
+ if (parts) {
422
+ variables.push({
423
+ name: parts[1],
424
+ type: parts[2].replace("!", ""),
425
+ required: parts[2].endsWith("!"),
426
+ });
427
+ }
428
+ }
429
+ }
430
+ // Extract selections (fields being requested)
431
+ const selections = parseSelections(trimmed);
432
+ return {
433
+ name,
434
+ type,
435
+ operationString,
436
+ variables,
437
+ selections,
438
+ file: filePath,
439
+ line,
440
+ };
441
+ }
442
+ function parseSelections(operationString) {
443
+ const selections = [];
444
+ // Extract content between outer braces
445
+ const braceMatch = operationString.match(/\{([\s\S]*)\}$/);
446
+ if (!braceMatch)
447
+ return selections;
448
+ const content = braceMatch[1];
449
+ const lines = content.split("\n");
450
+ let currentSelection = null;
451
+ let braceCount = 0;
452
+ for (const line of lines) {
453
+ const trimmed = line.trim();
454
+ if (!trimmed)
455
+ continue;
456
+ if (trimmed.includes("{")) {
457
+ braceCount++;
458
+ if (currentSelection) {
459
+ // This is a nested selection
460
+ if (!currentSelection.subSelections) {
461
+ currentSelection.subSelections = [];
462
+ }
463
+ }
464
+ }
465
+ if (trimmed.includes("}")) {
466
+ braceCount--;
467
+ if (braceCount === 0 && currentSelection) {
468
+ selections.push(currentSelection);
469
+ currentSelection = null;
470
+ }
471
+ continue;
472
+ }
473
+ // Parse field with optional alias: fieldName or alias: fieldName
474
+ const fieldMatch = trimmed.match(/^(?:(\w+)\s*:\s*)?(\w+)/);
475
+ if (fieldMatch && braceCount === 0) {
476
+ const alias = fieldMatch[1];
477
+ const name = fieldMatch[2];
478
+ if (currentSelection) {
479
+ selections.push(currentSelection);
480
+ }
481
+ currentSelection = {
482
+ name,
483
+ ...(alias && { alias }),
484
+ };
485
+ }
486
+ }
487
+ if (currentSelection) {
488
+ selections.push(currentSelection);
489
+ }
490
+ return selections;
491
+ }
492
+ // ============================================================================
493
+ // Validation
494
+ // ============================================================================
495
+ /**
496
+ * Validate frontend GraphQL operations against backend schema
497
+ */
498
+ export function validateGraphQLOperations(operations, schemas) {
499
+ const results = [];
500
+ // Combine all schema operations
501
+ const allSchemaOperations = [
502
+ ...schemas.flatMap(s => s.queries),
503
+ ...schemas.flatMap(s => s.mutations),
504
+ ...schemas.flatMap(s => s.subscriptions),
505
+ ];
506
+ // Build type map
507
+ const typeMap = new Map();
508
+ for (const schema of schemas) {
509
+ for (const type of schema.types) {
510
+ typeMap.set(type.name, type);
511
+ }
512
+ }
513
+ for (const operation of operations) {
514
+ const result = validateSingleOperation(operation, allSchemaOperations, typeMap);
515
+ results.push(result);
516
+ }
517
+ return results;
518
+ }
519
+ function validateSingleOperation(operation, schemaOperations, typeMap) {
520
+ const issues = [];
521
+ let score = 100;
522
+ const schemaOpsOfType = schemaOperations.filter(op => op.type === operation.type);
523
+ const opByName = schemaOpsOfType.find(op => op.name === operation.name);
524
+ const rootSelections = operation.selections || [];
525
+ const matchedRoot = rootSelections
526
+ .map(sel => ({
527
+ selection: sel,
528
+ schemaOp: schemaOpsOfType.find(op => op.name === sel.name),
529
+ }))
530
+ .filter((m) => Boolean(m.schemaOp));
531
+ // If none of the root selections match and the operation name doesn't match a schema field, treat as missing.
532
+ if (matchedRoot.length === 0 && !opByName) {
533
+ issues.push({
534
+ severity: "error",
535
+ type: "missing_operation",
536
+ message: `Operation '${operation.name}' of type '${operation.type}' not found in schema`,
537
+ });
538
+ return { operation, issues, score: 0 };
539
+ }
540
+ const validateFieldsOnType = (selections, typeName) => {
541
+ const t = typeMap.get(typeName);
542
+ if (!t)
543
+ return;
544
+ for (const selection of selections) {
545
+ const field = t.fields.find(f => f.name === selection.name);
546
+ if (!field) {
547
+ issues.push({
548
+ severity: "error",
549
+ type: "missing_field",
550
+ message: `Field '${selection.name}' does not exist on type '${t.name}'`,
551
+ field: selection.name,
552
+ });
553
+ score -= 20;
554
+ continue;
555
+ }
556
+ if (selection.subSelections && selection.subSelections.length > 0) {
557
+ validateFieldsOnType(selection.subSelections, field.type.replace(/[\[\]!]/g, ""));
558
+ }
559
+ }
560
+ };
561
+ // Strategy A: validate using schema root fields (Query/Mutation/Subscription fields)
562
+ // This is the primary strategy when we can match root selections.
563
+ if (matchedRoot.length > 0) {
564
+ for (const { selection, schemaOp } of matchedRoot) {
565
+ if (selection.subSelections && selection.subSelections.length > 0) {
566
+ validateFieldsOnType(selection.subSelections, schemaOp.returnType);
567
+ }
568
+ }
569
+ }
570
+ else if (opByName) {
571
+ // Strategy B (fallback): match by operation name and validate selections directly on return type.
572
+ const returnType = typeMap.get(opByName.returnType);
573
+ if (returnType) {
574
+ const anyDirectMatch = rootSelections.some(sel => returnType.fields.some(f => f.name === sel.name));
575
+ const selectionsToValidate = !anyDirectMatch && rootSelections.length === 1 && rootSelections[0]?.subSelections
576
+ ? rootSelections[0].subSelections
577
+ : rootSelections;
578
+ validateFieldsOnType(selectionsToValidate || [], opByName.returnType);
579
+ }
580
+ }
581
+ // Variables: validate against the first matched root schema field if present, else operation-name fallback.
582
+ const schemaForArgs = matchedRoot[0]?.schemaOp ?? opByName;
583
+ if (schemaForArgs) {
584
+ for (const variable of operation.variables) {
585
+ const schemaArg = schemaForArgs.arguments?.find(a => a.name === variable.name);
586
+ if (!schemaArg) {
587
+ issues.push({
588
+ severity: "warning",
589
+ type: "unused_variable",
590
+ message: `Variable '$${variable.name}' is not used by operation '${operation.name}'`,
591
+ field: variable.name,
592
+ });
593
+ score -= 10;
594
+ }
595
+ else if (schemaArg.type !== variable.type) {
596
+ issues.push({
597
+ severity: "error",
598
+ type: "type_mismatch",
599
+ message: `Variable '$${variable.name}' type mismatch: expected '${schemaArg.type}', got '${variable.type}'`,
600
+ field: variable.name,
601
+ expectedType: schemaArg.type,
602
+ actualType: variable.type,
603
+ });
604
+ score -= 15;
605
+ }
606
+ }
607
+ for (const arg of schemaForArgs.arguments || []) {
608
+ if (!arg.required)
609
+ continue;
610
+ const provided = operation.variables.find(v => v.name === arg.name);
611
+ if (!provided) {
612
+ issues.push({
613
+ severity: "error",
614
+ type: "missing_field",
615
+ message: `Required argument '${arg.name}' is missing`,
616
+ field: arg.name,
617
+ });
618
+ score -= 20;
619
+ }
620
+ }
621
+ }
622
+ return {
623
+ operation,
624
+ schemaOperation: schemaForArgs,
625
+ issues,
626
+ score: Math.max(0, score),
627
+ };
628
+ }
629
+ // ============================================================================
630
+ // Main Entry Point
631
+ // ============================================================================
632
+ /**
633
+ * Extract complete GraphQL context from project
634
+ */
635
+ export async function extractGraphQLContext(projectPath) {
636
+ const context = {
637
+ schemas: [],
638
+ frontendOperations: [],
639
+ validationResults: [],
640
+ unmatchedOperations: [],
641
+ };
642
+ // Find schema files
643
+ const schemaFiles = await glob("**/*.{graphql,gql}", { cwd: projectPath });
644
+ for (const file of schemaFiles) {
645
+ const fullPath = path.join(projectPath, file);
646
+ const schema = await extractGraphQLSchema(fullPath);
647
+ if (schema) {
648
+ context.schemas.push(schema);
649
+ }
650
+ }
651
+ // Find frontend files with GraphQL operations
652
+ const frontendFiles = await glob("**/*.{ts,tsx,js,jsx}", { cwd: projectPath });
653
+ for (const file of frontendFiles) {
654
+ const fullPath = path.join(projectPath, file);
655
+ const operations = await extractGraphQLOperations(fullPath);
656
+ context.frontendOperations.push(...operations);
657
+ }
658
+ // Validate operations against schemas
659
+ if (context.schemas.length > 0 && context.frontendOperations.length > 0) {
660
+ context.validationResults = validateGraphQLOperations(context.frontendOperations, context.schemas);
661
+ // Find unmatched operations
662
+ context.unmatchedOperations = context.validationResults
663
+ .filter(r => r.issues.some(i => i.type === "missing_operation"))
664
+ .map(r => r.operation);
665
+ }
666
+ logger.info(`GraphQL context extracted: ${context.schemas.length} schemas, ` +
667
+ `${context.frontendOperations.length} operations, ` +
668
+ `${context.validationResults.length} validation results`);
669
+ return context;
670
+ }
671
+ //# sourceMappingURL=graphqlSupport.js.map