@backthread/extractor 0.1.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 (379) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/dist/cluster/domain-grouping.d.ts +113 -0
  4. package/dist/cluster/domain-grouping.d.ts.map +1 -0
  5. package/dist/cluster/domain-grouping.js +277 -0
  6. package/dist/cluster/domain-grouping.js.map +1 -0
  7. package/dist/cluster/grouping-gate.d.ts +54 -0
  8. package/dist/cluster/grouping-gate.d.ts.map +1 -0
  9. package/dist/cluster/grouping-gate.js +0 -0
  10. package/dist/cluster/grouping-gate.js.map +1 -0
  11. package/dist/cluster/louvain.d.ts +88 -0
  12. package/dist/cluster/louvain.d.ts.map +1 -0
  13. package/dist/cluster/louvain.js +674 -0
  14. package/dist/cluster/louvain.js.map +1 -0
  15. package/dist/cluster/overrides.d.ts +19 -0
  16. package/dist/cluster/overrides.d.ts.map +1 -0
  17. package/dist/cluster/overrides.js +32 -0
  18. package/dist/cluster/overrides.js.map +1 -0
  19. package/dist/cluster/subsystem.d.ts +45 -0
  20. package/dist/cluster/subsystem.d.ts.map +1 -0
  21. package/dist/cluster/subsystem.js +299 -0
  22. package/dist/cluster/subsystem.js.map +1 -0
  23. package/dist/cluster/workspaces.d.ts +64 -0
  24. package/dist/cluster/workspaces.d.ts.map +1 -0
  25. package/dist/cluster/workspaces.js +739 -0
  26. package/dist/cluster/workspaces.js.map +1 -0
  27. package/dist/extract.d.ts +52 -0
  28. package/dist/extract.d.ts.map +1 -0
  29. package/dist/extract.js +48 -0
  30. package/dist/extract.js.map +1 -0
  31. package/dist/framework/celery/celery.d.ts +17 -0
  32. package/dist/framework/celery/celery.d.ts.map +1 -0
  33. package/dist/framework/celery/celery.js +524 -0
  34. package/dist/framework/celery/celery.js.map +1 -0
  35. package/dist/framework/contribute-step.d.ts +38 -0
  36. package/dist/framework/contribute-step.d.ts.map +1 -0
  37. package/dist/framework/contribute-step.js +403 -0
  38. package/dist/framework/contribute-step.js.map +1 -0
  39. package/dist/framework/detect-step.d.ts +8 -0
  40. package/dist/framework/detect-step.d.ts.map +1 -0
  41. package/dist/framework/detect-step.js +54 -0
  42. package/dist/framework/detect-step.js.map +1 -0
  43. package/dist/framework/detect-util.d.ts +24 -0
  44. package/dist/framework/detect-util.d.ts.map +1 -0
  45. package/dist/framework/detect-util.js +59 -0
  46. package/dist/framework/detect-util.js.map +1 -0
  47. package/dist/framework/django/django.d.ts +22 -0
  48. package/dist/framework/django/django.d.ts.map +1 -0
  49. package/dist/framework/django/django.js +940 -0
  50. package/dist/framework/django/django.js.map +1 -0
  51. package/dist/framework/fastapi/fastapi.d.ts +27 -0
  52. package/dist/framework/fastapi/fastapi.d.ts.map +1 -0
  53. package/dist/framework/fastapi/fastapi.js +477 -0
  54. package/dist/framework/fastapi/fastapi.js.map +1 -0
  55. package/dist/framework/faststream/faststream.d.ts +19 -0
  56. package/dist/framework/faststream/faststream.d.ts.map +1 -0
  57. package/dist/framework/faststream/faststream.js +765 -0
  58. package/dist/framework/faststream/faststream.js.map +1 -0
  59. package/dist/framework/flask/flask.d.ts +17 -0
  60. package/dist/framework/flask/flask.d.ts.map +1 -0
  61. package/dist/framework/flask/flask.js +507 -0
  62. package/dist/framework/flask/flask.js.map +1 -0
  63. package/dist/framework/graphql/graphql.d.ts +17 -0
  64. package/dist/framework/graphql/graphql.d.ts.map +1 -0
  65. package/dist/framework/graphql/graphql.js +708 -0
  66. package/dist/framework/graphql/graphql.js.map +1 -0
  67. package/dist/framework/grpc/grpc.d.ts +22 -0
  68. package/dist/framework/grpc/grpc.d.ts.map +1 -0
  69. package/dist/framework/grpc/grpc.js +618 -0
  70. package/dist/framework/grpc/grpc.js.map +1 -0
  71. package/dist/framework/litestar/litestar.d.ts +17 -0
  72. package/dist/framework/litestar/litestar.d.ts.map +1 -0
  73. package/dist/framework/litestar/litestar.js +536 -0
  74. package/dist/framework/litestar/litestar.js.map +1 -0
  75. package/dist/framework/nest/nest.d.ts +19 -0
  76. package/dist/framework/nest/nest.d.ts.map +1 -0
  77. package/dist/framework/nest/nest.js +613 -0
  78. package/dist/framework/nest/nest.js.map +1 -0
  79. package/dist/framework/next/next.d.ts +22 -0
  80. package/dist/framework/next/next.d.ts.map +1 -0
  81. package/dist/framework/next/next.js +449 -0
  82. package/dist/framework/next/next.js.map +1 -0
  83. package/dist/framework/node/node.d.ts +24 -0
  84. package/dist/framework/node/node.d.ts.map +1 -0
  85. package/dist/framework/node/node.js +1009 -0
  86. package/dist/framework/node/node.js.map +1 -0
  87. package/dist/framework/orchestrator/orchestrator.d.ts +34 -0
  88. package/dist/framework/orchestrator/orchestrator.d.ts.map +1 -0
  89. package/dist/framework/orchestrator/orchestrator.js +691 -0
  90. package/dist/framework/orchestrator/orchestrator.js.map +1 -0
  91. package/dist/framework/orm/orm.d.ts +61 -0
  92. package/dist/framework/orm/orm.d.ts.map +1 -0
  93. package/dist/framework/orm/orm.js +620 -0
  94. package/dist/framework/orm/orm.js.map +1 -0
  95. package/dist/framework/python/analyze.d.ts +38 -0
  96. package/dist/framework/python/analyze.d.ts.map +1 -0
  97. package/dist/framework/python/analyze.js +98 -0
  98. package/dist/framework/python/analyze.js.map +1 -0
  99. package/dist/framework/python/py-ast.d.ts +130 -0
  100. package/dist/framework/python/py-ast.d.ts.map +1 -0
  101. package/dist/framework/python/py-ast.js +344 -0
  102. package/dist/framework/python/py-ast.js.map +1 -0
  103. package/dist/framework/python-orm/python-orm.d.ts +19 -0
  104. package/dist/framework/python-orm/python-orm.d.ts.map +1 -0
  105. package/dist/framework/python-orm/python-orm.js +516 -0
  106. package/dist/framework/python-orm/python-orm.js.map +1 -0
  107. package/dist/framework/react-native/react-native.d.ts +35 -0
  108. package/dist/framework/react-native/react-native.d.ts.map +1 -0
  109. package/dist/framework/react-native/react-native.js +1181 -0
  110. package/dist/framework/react-native/react-native.js.map +1 -0
  111. package/dist/framework/register.d.ts +2 -0
  112. package/dist/framework/register.d.ts.map +1 -0
  113. package/dist/framework/register.js +59 -0
  114. package/dist/framework/register.js.map +1 -0
  115. package/dist/framework/registry.d.ts +31 -0
  116. package/dist/framework/registry.d.ts.map +1 -0
  117. package/dist/framework/registry.js +70 -0
  118. package/dist/framework/registry.js.map +1 -0
  119. package/dist/framework/routing/expo-router.d.ts +31 -0
  120. package/dist/framework/routing/expo-router.d.ts.map +1 -0
  121. package/dist/framework/routing/expo-router.js +356 -0
  122. package/dist/framework/routing/expo-router.js.map +1 -0
  123. package/dist/framework/routing/index.d.ts +4 -0
  124. package/dist/framework/routing/index.d.ts.map +1 -0
  125. package/dist/framework/routing/index.js +16 -0
  126. package/dist/framework/routing/index.js.map +1 -0
  127. package/dist/framework/routing/next-router.d.ts +45 -0
  128. package/dist/framework/routing/next-router.d.ts.map +1 -0
  129. package/dist/framework/routing/next-router.js +378 -0
  130. package/dist/framework/routing/next-router.js.map +1 -0
  131. package/dist/framework/routing/route-tree.d.ts +73 -0
  132. package/dist/framework/routing/route-tree.d.ts.map +1 -0
  133. package/dist/framework/routing/route-tree.js +120 -0
  134. package/dist/framework/routing/route-tree.js.map +1 -0
  135. package/dist/framework/types.d.ts +230 -0
  136. package/dist/framework/types.d.ts.map +1 -0
  137. package/dist/framework/types.js +25 -0
  138. package/dist/framework/types.js.map +1 -0
  139. package/dist/graph/cross-language.d.ts +11 -0
  140. package/dist/graph/cross-language.d.ts.map +1 -0
  141. package/dist/graph/cross-language.js +123 -0
  142. package/dist/graph/cross-language.js.map +1 -0
  143. package/dist/graph/extract.d.ts +13 -0
  144. package/dist/graph/extract.d.ts.map +1 -0
  145. package/dist/graph/extract.js +79 -0
  146. package/dist/graph/extract.js.map +1 -0
  147. package/dist/graph/file-graph.d.ts +186 -0
  148. package/dist/graph/file-graph.d.ts.map +1 -0
  149. package/dist/graph/file-graph.js +427 -0
  150. package/dist/graph/file-graph.js.map +1 -0
  151. package/dist/graph/http-callsites.d.ts +14 -0
  152. package/dist/graph/http-callsites.d.ts.map +1 -0
  153. package/dist/graph/http-callsites.js +107 -0
  154. package/dist/graph/http-callsites.js.map +1 -0
  155. package/dist/graph/incremental.d.ts +122 -0
  156. package/dist/graph/incremental.d.ts.map +1 -0
  157. package/dist/graph/incremental.js +448 -0
  158. package/dist/graph/incremental.js.map +1 -0
  159. package/dist/graph/language.d.ts +31 -0
  160. package/dist/graph/language.d.ts.map +1 -0
  161. package/dist/graph/language.js +146 -0
  162. package/dist/graph/language.js.map +1 -0
  163. package/dist/graph/noise-filter.d.ts +46 -0
  164. package/dist/graph/noise-filter.d.ts.map +1 -0
  165. package/dist/graph/noise-filter.js +199 -0
  166. package/dist/graph/noise-filter.js.map +1 -0
  167. package/dist/graph/python-adapter.d.ts +36 -0
  168. package/dist/graph/python-adapter.d.ts.map +1 -0
  169. package/dist/graph/python-adapter.js +402 -0
  170. package/dist/graph/python-adapter.js.map +1 -0
  171. package/dist/graph/python-manifest.d.ts +22 -0
  172. package/dist/graph/python-manifest.d.ts.map +1 -0
  173. package/dist/graph/python-manifest.js +137 -0
  174. package/dist/graph/python-manifest.js.map +1 -0
  175. package/dist/graph/python-stdlib.d.ts +2 -0
  176. package/dist/graph/python-stdlib.d.ts.map +1 -0
  177. package/dist/graph/python-stdlib.js +53 -0
  178. package/dist/graph/python-stdlib.js.map +1 -0
  179. package/dist/graph/ts-morph-adapter.d.ts +65 -0
  180. package/dist/graph/ts-morph-adapter.d.ts.map +1 -0
  181. package/dist/graph/ts-morph-adapter.js +265 -0
  182. package/dist/graph/ts-morph-adapter.js.map +1 -0
  183. package/dist/graph/types.d.ts +37 -0
  184. package/dist/graph/types.d.ts.map +1 -0
  185. package/dist/graph/types.js +50 -0
  186. package/dist/graph/types.js.map +1 -0
  187. package/dist/index.d.ts +45 -0
  188. package/dist/index.d.ts.map +1 -0
  189. package/dist/index.js +51 -0
  190. package/dist/index.js.map +1 -0
  191. package/dist/infra/aws/aws-parse.d.ts +39 -0
  192. package/dist/infra/aws/aws-parse.d.ts.map +1 -0
  193. package/dist/infra/aws/aws-parse.js +149 -0
  194. package/dist/infra/aws/aws-parse.js.map +1 -0
  195. package/dist/infra/aws/aws.d.ts +6 -0
  196. package/dist/infra/aws/aws.d.ts.map +1 -0
  197. package/dist/infra/aws/aws.js +425 -0
  198. package/dist/infra/aws/aws.js.map +1 -0
  199. package/dist/infra/azure/azure-parse.d.ts +73 -0
  200. package/dist/infra/azure/azure-parse.d.ts.map +1 -0
  201. package/dist/infra/azure/azure-parse.js +178 -0
  202. package/dist/infra/azure/azure-parse.js.map +1 -0
  203. package/dist/infra/azure/azure.d.ts +12 -0
  204. package/dist/infra/azure/azure.d.ts.map +1 -0
  205. package/dist/infra/azure/azure.js +673 -0
  206. package/dist/infra/azure/azure.js.map +1 -0
  207. package/dist/infra/cloudflare/cloudflare.d.ts +50 -0
  208. package/dist/infra/cloudflare/cloudflare.d.ts.map +1 -0
  209. package/dist/infra/cloudflare/cloudflare.js +382 -0
  210. package/dist/infra/cloudflare/cloudflare.js.map +1 -0
  211. package/dist/infra/cloudflare/wrangler-parse.d.ts +6 -0
  212. package/dist/infra/cloudflare/wrangler-parse.d.ts.map +1 -0
  213. package/dist/infra/cloudflare/wrangler-parse.js +278 -0
  214. package/dist/infra/cloudflare/wrangler-parse.js.map +1 -0
  215. package/dist/infra/compose/compose-parse.d.ts +24 -0
  216. package/dist/infra/compose/compose-parse.d.ts.map +1 -0
  217. package/dist/infra/compose/compose-parse.js +77 -0
  218. package/dist/infra/compose/compose-parse.js.map +1 -0
  219. package/dist/infra/compose/compose.d.ts +12 -0
  220. package/dist/infra/compose/compose.d.ts.map +1 -0
  221. package/dist/infra/compose/compose.js +201 -0
  222. package/dist/infra/compose/compose.js.map +1 -0
  223. package/dist/infra/convex/convex-parse.d.ts +28 -0
  224. package/dist/infra/convex/convex-parse.d.ts.map +1 -0
  225. package/dist/infra/convex/convex-parse.js +74 -0
  226. package/dist/infra/convex/convex-parse.js.map +1 -0
  227. package/dist/infra/convex/convex.d.ts +18 -0
  228. package/dist/infra/convex/convex.d.ts.map +1 -0
  229. package/dist/infra/convex/convex.js +199 -0
  230. package/dist/infra/convex/convex.js.map +1 -0
  231. package/dist/infra/digitalocean/digitalocean.d.ts +33 -0
  232. package/dist/infra/digitalocean/digitalocean.d.ts.map +1 -0
  233. package/dist/infra/digitalocean/digitalocean.js +204 -0
  234. package/dist/infra/digitalocean/digitalocean.js.map +1 -0
  235. package/dist/infra/firebase/firebase.d.ts +43 -0
  236. package/dist/infra/firebase/firebase.d.ts.map +1 -0
  237. package/dist/infra/firebase/firebase.js +271 -0
  238. package/dist/infra/firebase/firebase.js.map +1 -0
  239. package/dist/infra/fly/fly-parse.d.ts +52 -0
  240. package/dist/infra/fly/fly-parse.d.ts.map +1 -0
  241. package/dist/infra/fly/fly-parse.js +114 -0
  242. package/dist/infra/fly/fly-parse.js.map +1 -0
  243. package/dist/infra/fly/fly.d.ts +15 -0
  244. package/dist/infra/fly/fly.d.ts.map +1 -0
  245. package/dist/infra/fly/fly.js +265 -0
  246. package/dist/infra/fly/fly.js.map +1 -0
  247. package/dist/infra/gcp/gcp-parse.d.ts +24 -0
  248. package/dist/infra/gcp/gcp-parse.d.ts.map +1 -0
  249. package/dist/infra/gcp/gcp-parse.js +123 -0
  250. package/dist/infra/gcp/gcp-parse.js.map +1 -0
  251. package/dist/infra/gcp/gcp.d.ts +10 -0
  252. package/dist/infra/gcp/gcp.d.ts.map +1 -0
  253. package/dist/infra/gcp/gcp.js +502 -0
  254. package/dist/infra/gcp/gcp.js.map +1 -0
  255. package/dist/infra/heroku/heroku-parse.d.ts +33 -0
  256. package/dist/infra/heroku/heroku-parse.d.ts.map +1 -0
  257. package/dist/infra/heroku/heroku-parse.js +63 -0
  258. package/dist/infra/heroku/heroku-parse.js.map +1 -0
  259. package/dist/infra/heroku/heroku.d.ts +24 -0
  260. package/dist/infra/heroku/heroku.d.ts.map +1 -0
  261. package/dist/infra/heroku/heroku.js +253 -0
  262. package/dist/infra/heroku/heroku.js.map +1 -0
  263. package/dist/infra/image-resolve.d.ts +56 -0
  264. package/dist/infra/image-resolve.d.ts.map +1 -0
  265. package/dist/infra/image-resolve.js +229 -0
  266. package/dist/infra/image-resolve.js.map +1 -0
  267. package/dist/infra/infra-step.d.ts +39 -0
  268. package/dist/infra/infra-step.d.ts.map +1 -0
  269. package/dist/infra/infra-step.js +57 -0
  270. package/dist/infra/infra-step.js.map +1 -0
  271. package/dist/infra/k8s/index.d.ts +78 -0
  272. package/dist/infra/k8s/index.d.ts.map +1 -0
  273. package/dist/infra/k8s/index.js +251 -0
  274. package/dist/infra/k8s/index.js.map +1 -0
  275. package/dist/infra/kamal/kamal-parse.d.ts +23 -0
  276. package/dist/infra/kamal/kamal-parse.d.ts.map +1 -0
  277. package/dist/infra/kamal/kamal-parse.js +66 -0
  278. package/dist/infra/kamal/kamal-parse.js.map +1 -0
  279. package/dist/infra/kamal/kamal.d.ts +17 -0
  280. package/dist/infra/kamal/kamal.d.ts.map +1 -0
  281. package/dist/infra/kamal/kamal.js +162 -0
  282. package/dist/infra/kamal/kamal.js.map +1 -0
  283. package/dist/infra/netlify/netlify.d.ts +33 -0
  284. package/dist/infra/netlify/netlify.d.ts.map +1 -0
  285. package/dist/infra/netlify/netlify.js +247 -0
  286. package/dist/infra/netlify/netlify.js.map +1 -0
  287. package/dist/infra/opentofu/opentofu.d.ts +3 -0
  288. package/dist/infra/opentofu/opentofu.d.ts.map +1 -0
  289. package/dist/infra/opentofu/opentofu.js +157 -0
  290. package/dist/infra/opentofu/opentofu.js.map +1 -0
  291. package/dist/infra/pulumi/pulumi-parse.d.ts +60 -0
  292. package/dist/infra/pulumi/pulumi-parse.d.ts.map +1 -0
  293. package/dist/infra/pulumi/pulumi-parse.js +155 -0
  294. package/dist/infra/pulumi/pulumi-parse.js.map +1 -0
  295. package/dist/infra/pulumi/pulumi.d.ts +23 -0
  296. package/dist/infra/pulumi/pulumi.d.ts.map +1 -0
  297. package/dist/infra/pulumi/pulumi.js +260 -0
  298. package/dist/infra/pulumi/pulumi.js.map +1 -0
  299. package/dist/infra/railway/railway-parse.d.ts +54 -0
  300. package/dist/infra/railway/railway-parse.d.ts.map +1 -0
  301. package/dist/infra/railway/railway-parse.js +200 -0
  302. package/dist/infra/railway/railway-parse.js.map +1 -0
  303. package/dist/infra/railway/railway.d.ts +19 -0
  304. package/dist/infra/railway/railway.d.ts.map +1 -0
  305. package/dist/infra/railway/railway.js +397 -0
  306. package/dist/infra/railway/railway.js.map +1 -0
  307. package/dist/infra/register.d.ts +2 -0
  308. package/dist/infra/register.d.ts.map +1 -0
  309. package/dist/infra/register.js +81 -0
  310. package/dist/infra/register.js.map +1 -0
  311. package/dist/infra/registry.d.ts +64 -0
  312. package/dist/infra/registry.d.ts.map +1 -0
  313. package/dist/infra/registry.js +205 -0
  314. package/dist/infra/registry.js.map +1 -0
  315. package/dist/infra/relevance.d.ts +16 -0
  316. package/dist/infra/relevance.d.ts.map +1 -0
  317. package/dist/infra/relevance.js +132 -0
  318. package/dist/infra/relevance.js.map +1 -0
  319. package/dist/infra/render/render-parse.d.ts +76 -0
  320. package/dist/infra/render/render-parse.d.ts.map +1 -0
  321. package/dist/infra/render/render-parse.js +155 -0
  322. package/dist/infra/render/render-parse.js.map +1 -0
  323. package/dist/infra/render/render.d.ts +11 -0
  324. package/dist/infra/render/render.d.ts.map +1 -0
  325. package/dist/infra/render/render.js +303 -0
  326. package/dist/infra/render/render.js.map +1 -0
  327. package/dist/infra/sst/sst-parse.d.ts +25 -0
  328. package/dist/infra/sst/sst-parse.d.ts.map +1 -0
  329. package/dist/infra/sst/sst-parse.js +45 -0
  330. package/dist/infra/sst/sst-parse.js.map +1 -0
  331. package/dist/infra/sst/sst.d.ts +19 -0
  332. package/dist/infra/sst/sst.d.ts.map +1 -0
  333. package/dist/infra/sst/sst.js +236 -0
  334. package/dist/infra/sst/sst.js.map +1 -0
  335. package/dist/infra/supabase/supabase-parse.d.ts +34 -0
  336. package/dist/infra/supabase/supabase-parse.d.ts.map +1 -0
  337. package/dist/infra/supabase/supabase-parse.js +113 -0
  338. package/dist/infra/supabase/supabase-parse.js.map +1 -0
  339. package/dist/infra/supabase/supabase.d.ts +15 -0
  340. package/dist/infra/supabase/supabase.d.ts.map +1 -0
  341. package/dist/infra/supabase/supabase.js +198 -0
  342. package/dist/infra/supabase/supabase.js.map +1 -0
  343. package/dist/infra/terraform/hcl-parse.d.ts +34 -0
  344. package/dist/infra/terraform/hcl-parse.d.ts.map +1 -0
  345. package/dist/infra/terraform/hcl-parse.js +203 -0
  346. package/dist/infra/terraform/hcl-parse.js.map +1 -0
  347. package/dist/infra/terraform/terraform.d.ts +14 -0
  348. package/dist/infra/terraform/terraform.d.ts.map +1 -0
  349. package/dist/infra/terraform/terraform.js +264 -0
  350. package/dist/infra/terraform/terraform.js.map +1 -0
  351. package/dist/infra/types.d.ts +151 -0
  352. package/dist/infra/types.d.ts.map +1 -0
  353. package/dist/infra/types.js +14 -0
  354. package/dist/infra/types.js.map +1 -0
  355. package/dist/infra/vercel/vercel-parse.d.ts +70 -0
  356. package/dist/infra/vercel/vercel-parse.d.ts.map +1 -0
  357. package/dist/infra/vercel/vercel-parse.js +194 -0
  358. package/dist/infra/vercel/vercel-parse.js.map +1 -0
  359. package/dist/infra/vercel/vercel.d.ts +30 -0
  360. package/dist/infra/vercel/vercel.d.ts.map +1 -0
  361. package/dist/infra/vercel/vercel.js +523 -0
  362. package/dist/infra/vercel/vercel.js.map +1 -0
  363. package/dist/infra/walk.d.ts +36 -0
  364. package/dist/infra/walk.d.ts.map +1 -0
  365. package/dist/infra/walk.js +85 -0
  366. package/dist/infra/walk.js.map +1 -0
  367. package/dist/safety.d.ts +13 -0
  368. package/dist/safety.d.ts.map +1 -0
  369. package/dist/safety.js +67 -0
  370. package/dist/safety.js.map +1 -0
  371. package/dist/types.d.ts +47 -0
  372. package/dist/types.d.ts.map +1 -0
  373. package/dist/types.js +111 -0
  374. package/dist/types.js.map +1 -0
  375. package/dist/version.d.ts +2 -0
  376. package/dist/version.d.ts.map +1 -0
  377. package/dist/version.js +35 -0
  378. package/dist/version.js.map +1 -0
  379. package/package.json +65 -0
@@ -0,0 +1,708 @@
1
+ // the Strawberry + Graphene GraphQL FrameworkAdapter. A Python
2
+ // framework adapter following the FastAPI/Flask template (/997), reusing
3
+ // the shared Python core (py-ast + parsePythonScope). Strawberry is the
4
+ // modern default and the priority here; Graphene is a solid best-effort.
5
+ //
6
+ // GraphQL declares its request surface as CLASSES — a `@strawberry.type` /
7
+ // `graphene.ObjectType` root (Query / Mutation / Subscription) whose resolver
8
+ // methods are the actual query/mutation entry points, plus plain object types
9
+ // (User / Post / …). We read this STATICALLY (install-free, never-store-source —
10
+ // a pure syntactic Pyright parse; never executes repo code) and persist only the
11
+ // derived groups/edges/roles:
12
+ //
13
+ // * detect() — the `strawberry-graphql` and/or `graphene` dependency;
14
+ // shallow nested scan for a `backend/`|`server/`|`api/`
15
+ // package (mirrors FastAPI's ).
16
+ // * roleTags — a Query/Mutation/Subscription ROOT (a `@strawberry.type`
17
+ // or `graphene.ObjectType` used as a schema root, or named
18
+ // by the GraphQL convention) + its field-resolver methods →
19
+ // `gateway` (the GraphQL request entry); every other
20
+ // object/input/interface type → `service` (role
21
+ // 'graphql-type'). METADATA onto the LOCKED MODULE_KINDS
22
+ // enum; never a new kind (only `role` renders).
23
+ // * syntheticEdges — the wiring the import graph doesn't name as verbs
24
+ // (kind 'calls' throughout): a resolver → the type it
25
+ // returns ('resolver-returns'); a graphene field decl → its
26
+ // type ('field-type'); a root field → its cross-file
27
+ // resolver function via `resolver=` ('field-resolver'); a
28
+ // `Schema(query=…, mutation=…)` construction → each root
29
+ // type file ('schema-root').
30
+ // * groupingPrior — files under a dedicated GraphQL directory
31
+ // (`graphql/`|`gql/`|`schema/`) that carry a GraphQL role
32
+ // → one subsystem (the schema layer), authoritative over
33
+ // the directory heuristic. Deterministic → no
34
+ // classificationsNeeded. Skipped when there is no dedicated
35
+ // GraphQL dir (degrades to directory grouping).
36
+ //
37
+ // Unresolvable schema roots / return types / cross-file resolvers DEGRADE + LOG —
38
+ // no silent caps.
39
+ import { existsSync, readdirSync } from 'node:fs';
40
+ import { join } from 'node:path';
41
+ import { clampConfidence, resolveBase } from '../detect-util.js';
42
+ import { readPythonDeps } from '../../graph/python-manifest.js';
43
+ import { parsePythonScope } from '../python/analyze.js';
44
+ import { callCallee, classBaseChains, className, classDirectAssignments, classDirectFunctions, keywordArg, memberChain, positionalArgs, stringValue, PN, } from '../python/py-ast.js';
45
+ /** Gather the signal set for a single root dir (reads manifests only). */
46
+ export function gatherGraphqlSignals(baseDir) {
47
+ const deps = readPythonDeps(baseDir);
48
+ return {
49
+ hasStrawberry: deps.has('strawberry-graphql'),
50
+ // `graphene`, or a `graphene-django` / `graphene-sqlalchemy` integration
51
+ // (which pulls graphene in): either means a Graphene schema is present.
52
+ hasGraphene: deps.has('graphene') || [...deps].some((d) => d.startsWith('graphene-')),
53
+ };
54
+ }
55
+ // Non-source dirs the nested scan skips (cheap + can't hold a first-party manifest).
56
+ const NESTED_SKIP_DIRS = new Set([
57
+ 'node_modules',
58
+ 'dist',
59
+ 'build',
60
+ 'out',
61
+ 'coverage',
62
+ 'venv',
63
+ '.venv',
64
+ '__pycache__',
65
+ 'site-packages',
66
+ ]);
67
+ /** True if `dir` holds a Python manifest worth scanning for deps. */
68
+ function hasPythonManifest(dir) {
69
+ return (existsSync(join(dir, 'pyproject.toml')) ||
70
+ existsSync(join(dir, 'setup.py')) ||
71
+ existsSync(join(dir, 'setup.cfg')) ||
72
+ existsSync(join(dir, 'requirements.txt')));
73
+ }
74
+ /**
75
+ * Immediate subdirs (depth 1) that contain a Python manifest — the shallow search
76
+ * for a nested GraphQL backend (`backend/` | `server/` | `api/`). Sorted, so the
77
+ * first-match pick is deterministic; skips dot-dirs + non-source dirs to stay cheap.
78
+ */
79
+ function shallowManifestSubdirs(base) {
80
+ let entries;
81
+ try {
82
+ entries = readdirSync(base, { withFileTypes: true });
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ const out = [];
88
+ for (const e of entries) {
89
+ if (!e.isDirectory() || e.name.startsWith('.') || NESTED_SKIP_DIRS.has(e.name))
90
+ continue;
91
+ if (hasPythonManifest(join(base, e.name)))
92
+ out.push(e.name);
93
+ }
94
+ return out.sort();
95
+ }
96
+ /**
97
+ * Decide a GraphQL stack from the signal set. Either `strawberry-graphql` or
98
+ * `graphene` is sufficient; strawberry (the modern default) scores marginally
99
+ * higher. Returns null → generic-Python fallthrough, byte-for-byte unchanged.
100
+ */
101
+ export function scoreGraphql(s, rootPath = '') {
102
+ if (!s.hasStrawberry && !s.hasGraphene)
103
+ return null;
104
+ const confidence = s.hasStrawberry ? 0.85 : 0.8;
105
+ return {
106
+ adapter: 'graphql',
107
+ confidence: clampConfidence(confidence),
108
+ rootPath,
109
+ metadata: {
110
+ strawberry: s.hasStrawberry,
111
+ graphene: s.hasGraphene,
112
+ signals: { strawberry: s.hasStrawberry, graphene: s.hasGraphene },
113
+ },
114
+ };
115
+ }
116
+ // Collapse priority when one FILE carries several roles, AND downstream when
117
+ // several files of different roles land in one module. A query root outranks a
118
+ // mutation outranks a subscription (all root request entries) outranks a
119
+ // standalone resolver-function file outranks a schema-assembly file outranks a
120
+ // plain data type. All but 'graphql-type' are GraphQL request entries → gateway.
121
+ const ROLE_PRIORITY = {
122
+ 'graphql-query': 8,
123
+ 'graphql-mutation': 7,
124
+ 'graphql-subscription': 6,
125
+ 'graphql-resolver': 5,
126
+ 'graphql-schema': 4,
127
+ 'graphql-type': 3,
128
+ };
129
+ const ROLE_KIND = {
130
+ 'graphql-query': 'gateway',
131
+ 'graphql-mutation': 'gateway',
132
+ 'graphql-subscription': 'gateway',
133
+ 'graphql-resolver': 'gateway',
134
+ 'graphql-schema': 'gateway',
135
+ 'graphql-type': 'service',
136
+ };
137
+ const SLOT_ROLE = {
138
+ query: 'graphql-query',
139
+ mutation: 'graphql-mutation',
140
+ subscription: 'graphql-subscription',
141
+ };
142
+ // The GraphQL root type names, by convention (a schema requires exactly these
143
+ // three operation roots; strawberry/graphene root classes are near-universally
144
+ // named this) — the fallback signal when a Schema(...) construction can't be
145
+ // resolved to the class.
146
+ const ROOT_NAMES = new Map([
147
+ ['Query', 'query'],
148
+ ['Mutation', 'mutation'],
149
+ ['Subscription', 'subscription'],
150
+ ]);
151
+ // The strawberry module roots recognized as first-party GraphQL decorators. The
152
+ // official Django integration `strawberry_django` re-exports `type`/`field`/
153
+ // `mutation` (a large fraction of real strawberry apps use it), so treat its
154
+ // aliases the same as `strawberry`'s.
155
+ const STRAWBERRY_MODULE_ROOTS = ['strawberry', 'strawberry_django'];
156
+ // Strawberry class decorators: `@strawberry.type` (rootable object type) vs
157
+ // `@strawberry.input` / `@strawberry.interface` (always a plain type).
158
+ const STRAWBERRY_TYPE_DECOS = new Set(['type']);
159
+ const STRAWBERRY_PLAIN_DECOS = new Set(['input', 'interface']);
160
+ // Strawberry field-resolver method decorators.
161
+ const STRAWBERRY_FIELD_DECOS = new Set(['field', 'mutation', 'subscription']);
162
+ // Graphene base classes (by leaf name; gated on graphene being detected). The
163
+ // integration ObjectType variants (graphene-django's `DjangoObjectType`,
164
+ // graphene-sqlalchemy's `SQLAlchemyObjectType`, graphene-mongo's
165
+ // `MongoengineObjectType`) are the norm in real graphene apps, so treat them as
166
+ // object types too.
167
+ const GRAPHENE_OBJECT_BASES = new Set([
168
+ 'ObjectType',
169
+ 'DjangoObjectType',
170
+ 'SQLAlchemyObjectType',
171
+ 'MongoengineObjectType',
172
+ ]);
173
+ const GRAPHENE_ROOT_BASE_SLOT = new Map([
174
+ // A `graphene.Mutation` / `graphene.Subscription` subclass IS an individual
175
+ // request entry (a mutation/subscription field resolver) → a gateway root.
176
+ ['Mutation', 'mutation'],
177
+ ['Subscription', 'subscription'],
178
+ ]);
179
+ const GRAPHENE_PLAIN_BASES = new Set(['InputObjectType', 'Interface']);
180
+ // Graphene field constructors whose FIRST positional arg is the field's type.
181
+ const GRAPHENE_FIELD_CTORS = new Set(['Field', 'List', 'NonNull']);
182
+ // Graphene resolver method: `resolve_<field>` or a Mutation's `mutate`.
183
+ function isGrapheneResolverName(name) {
184
+ return name.startsWith('resolve_') || name === 'mutate';
185
+ }
186
+ // GraphQL-dedicated directory segments that anchor the schema subsystem.
187
+ const GRAPHQL_DIR_SEGMENTS = new Set(['graphql', 'gql', 'schema']);
188
+ // Memoized on the FrameworkContext OBJECT so groupingPrior + syntheticEdges +
189
+ // roleTags share ONE parse, while the merge walk's per-checkpoint ctx gets a fresh
190
+ // analysis — no cross-tree staleness. Mirrors fastapi / flask / nest.
191
+ const ANALYSIS_CACHE = new WeakMap();
192
+ function slugify(s) {
193
+ return s
194
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
195
+ .toLowerCase()
196
+ .replace(/[^a-z0-9]+/g, '-')
197
+ .replace(/^-+|-+$/g, '');
198
+ }
199
+ function humanize(s) {
200
+ const words = s
201
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
202
+ .replace(/[_\-.]+/g, ' ')
203
+ .trim()
204
+ .split(/\s+/)
205
+ .filter(Boolean);
206
+ if (words.length === 0)
207
+ return s;
208
+ return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
209
+ }
210
+ function dirSegment(fileId) {
211
+ const slash = fileId.indexOf('/');
212
+ return slash > 0 ? slugify(fileId.slice(0, slash)) : 'root';
213
+ }
214
+ // Keep the highest-priority candidate role per file (deterministic, lexical
215
+ // tiebreak) — mirrors the fastapi/flask/nest addRole + the contribute-step collapse.
216
+ function addRole(map, fileId, role) {
217
+ const cur = map.get(fileId);
218
+ if (cur === undefined ||
219
+ ROLE_PRIORITY[role] > ROLE_PRIORITY[cur] ||
220
+ (ROLE_PRIORITY[role] === ROLE_PRIORITY[cur] && role < cur)) {
221
+ map.set(fileId, role);
222
+ }
223
+ }
224
+ function addEdge(edges, from, to, relation) {
225
+ if (from === to)
226
+ return; // intra-file wiring collapses; the step drops self-edges too
227
+ const key = `${from}→${to}:${relation}`;
228
+ if (!edges.has(key)) {
229
+ edges.set(key, { source: from, target: to, kind: 'calls', metadata: { framework: 'graphql', relation } });
230
+ }
231
+ }
232
+ // A decorator's callee chain (`@strawberry.type(...)` → root 'strawberry', path
233
+ // ['type']; `@strawberry.field` → root 'strawberry', path ['field']). The expr is
234
+ // either a call (with args) or a bare name/attribute.
235
+ function decoratorChain(deco) {
236
+ const expr = deco.d.expr;
237
+ if (expr.nodeType === PN.Call)
238
+ return callCallee(expr);
239
+ return memberChain(expr);
240
+ }
241
+ // The framework import bindings for one module root in a file (both `import X` and
242
+ // `from X import a, b` forms). Deep imports (`import strawberry.federation`) still
243
+ // bind the top-level alias.
244
+ function frameworkImports(file, moduleRoot) {
245
+ const aliases = new Set();
246
+ const symbols = new Map();
247
+ for (const imp of file.nodes.imports) {
248
+ if (imp.nodeType === PN.Import) {
249
+ for (const entry of imp.d.list) {
250
+ const parts = entry.d.module.d.nameParts.map((p) => p.d.value);
251
+ if (parts.length === 0 || parts[0] !== moduleRoot)
252
+ continue;
253
+ aliases.add(entry.d.alias ? entry.d.alias.d.value : parts[0]);
254
+ }
255
+ }
256
+ else if (imp.nodeType === PN.ImportFrom) {
257
+ const from = imp;
258
+ if (from.d.module.d.leadingDots !== 0)
259
+ continue; // relative import — not the framework
260
+ const parts = from.d.module.d.nameParts.map((p) => p.d.value);
261
+ if (parts.length === 0 || parts[0] !== moduleRoot)
262
+ continue;
263
+ for (const spec of from.d.imports) {
264
+ const local = spec.d.alias ? spec.d.alias.d.value : spec.d.name.d.value;
265
+ symbols.set(local, spec.d.name.d.value);
266
+ }
267
+ }
268
+ }
269
+ return { aliases, symbols };
270
+ }
271
+ // Classify a strawberry class decorator: 'type' (rootable object type), 'plain'
272
+ // (input/interface), or undefined (not a strawberry type class).
273
+ function strawberryClassKind(cls, imp) {
274
+ for (const deco of cls.d.decorators) {
275
+ const chain = decoratorChain(deco);
276
+ if (!chain)
277
+ continue;
278
+ // `@strawberry.type` / `@<alias>.type(...)` / `@strawberry.federation.type`.
279
+ if (imp.aliases.has(chain.root) && chain.path.length >= 1) {
280
+ const leaf = chain.path[chain.path.length - 1];
281
+ if (STRAWBERRY_TYPE_DECOS.has(leaf))
282
+ return 'type';
283
+ if (STRAWBERRY_PLAIN_DECOS.has(leaf))
284
+ return 'plain';
285
+ }
286
+ // Bare `@type` / `@input` via `from strawberry import type`.
287
+ if (chain.path.length === 0) {
288
+ const canon = imp.symbols.get(chain.root);
289
+ if (canon && STRAWBERRY_TYPE_DECOS.has(canon))
290
+ return 'type';
291
+ if (canon && STRAWBERRY_PLAIN_DECOS.has(canon))
292
+ return 'plain';
293
+ }
294
+ }
295
+ return undefined;
296
+ }
297
+ // Classify a graphene class from its base classes (gated on graphene detected):
298
+ // a rootable ObjectType, a Mutation/Subscription root resolver, a plain
299
+ // input/interface, or undefined.
300
+ function grapheneClassKind(cls) {
301
+ for (const chain of classBaseChains(cls)) {
302
+ const leaf = chain.path.length ? chain.path[chain.path.length - 1] : chain.root;
303
+ if (GRAPHENE_OBJECT_BASES.has(leaf))
304
+ return { rootable: true };
305
+ const slot = GRAPHENE_ROOT_BASE_SLOT.get(leaf);
306
+ if (slot)
307
+ return { rootable: true, slotBase: slot };
308
+ if (GRAPHENE_PLAIN_BASES.has(leaf))
309
+ return { rootable: false };
310
+ }
311
+ return undefined;
312
+ }
313
+ // Is a strawberry method a field resolver (decorated with @strawberry.field /
314
+ // .mutation / .subscription, in either module-attr or from-import form)?
315
+ function isStrawberryResolver(fn, imp) {
316
+ for (const deco of fn.d.decorators) {
317
+ const chain = decoratorChain(deco);
318
+ if (!chain)
319
+ continue;
320
+ if (imp.aliases.has(chain.root) && chain.path.length >= 1) {
321
+ if (STRAWBERRY_FIELD_DECOS.has(chain.path[chain.path.length - 1]))
322
+ return true;
323
+ }
324
+ if (chain.path.length === 0) {
325
+ const canon = imp.symbols.get(chain.root);
326
+ if (canon && STRAWBERRY_FIELD_DECOS.has(canon))
327
+ return true;
328
+ }
329
+ }
330
+ return false;
331
+ }
332
+ // Collect the internal-resolvable TYPE NAMES from a return/type annotation,
333
+ // unwrapping generics (`List[User]`), unions (`User | None`), subscripts, and
334
+ // forward-ref strings (`"User"`). Container names (List/Optional) and primitives
335
+ // simply don't resolve to an internal file downstream, so they're harmless.
336
+ function collectAnnotationTypeNames(expr, out) {
337
+ if (!expr)
338
+ return;
339
+ const nt = expr.nodeType;
340
+ if (nt === PN.Name || nt === PN.MemberAccess) {
341
+ const chain = memberChain(expr);
342
+ if (chain)
343
+ out.add(chain.path.length ? chain.path[chain.path.length - 1] : chain.root);
344
+ return;
345
+ }
346
+ if (nt === PN.Index) {
347
+ const idx = expr;
348
+ collectAnnotationTypeNames(idx.d.leftExpr, out); // e.g. a relay `Connection[User]`
349
+ for (const item of idx.d.items)
350
+ collectAnnotationTypeNames(item.d.valueExpr, out);
351
+ return;
352
+ }
353
+ if (nt === PN.BinaryOperation) {
354
+ const bin = expr;
355
+ collectAnnotationTypeNames(bin.d.leftExpr, out);
356
+ collectAnnotationTypeNames(bin.d.rightExpr, out);
357
+ return;
358
+ }
359
+ if (nt === PN.StringList) {
360
+ const s = stringValue(expr);
361
+ if (s && /^[A-Za-z_][A-Za-z0-9_]*$/.test(s))
362
+ out.add(s); // a plain forward-ref name
363
+ }
364
+ }
365
+ function analyzeGraphql(ctx) {
366
+ const strawberryEnabled = ctx.match.metadata?.strawberry === true;
367
+ const grapheneEnabled = ctx.match.metadata?.graphene === true;
368
+ const { parsed } = parsePythonScope(ctx);
369
+ // Pre-pass: per-file class-name set (so a return type / schema ref resolves to a
370
+ // co-located class file), plus per-file framework import bindings.
371
+ const localClassNames = new Map();
372
+ const strawberryImp = new Map();
373
+ const grapheneImp = new Map();
374
+ for (const [id, file] of parsed) {
375
+ const names = new Set();
376
+ for (const cls of file.nodes.classes) {
377
+ const n = className(cls);
378
+ if (n)
379
+ names.add(n);
380
+ }
381
+ localClassNames.set(id, names);
382
+ if (strawberryEnabled) {
383
+ // Merge strawberry + strawberry_django import bindings into one alias/symbol set.
384
+ const merged = { aliases: new Set(), symbols: new Map() };
385
+ for (const root of STRAWBERRY_MODULE_ROOTS) {
386
+ const fi = frameworkImports(file, root);
387
+ for (const a of fi.aliases)
388
+ merged.aliases.add(a);
389
+ for (const [k, v] of fi.symbols)
390
+ merged.symbols.set(k, v);
391
+ }
392
+ strawberryImp.set(id, merged);
393
+ }
394
+ if (grapheneEnabled)
395
+ grapheneImp.set(id, frameworkImports(file, 'graphene'));
396
+ }
397
+ // Resolve a bare TYPE/CLASS name to a file: an internal import, else a class
398
+ // co-located in the SAME file. Mirrors nest's resolveClassRef.
399
+ function resolveName(name, fromFile) {
400
+ const binds = parsed.get(fromFile)?.bindings;
401
+ return binds?.get(name) ?? (localClassNames.get(fromFile)?.has(name) ? fromFile : undefined);
402
+ }
403
+ const diag = {
404
+ unresolvedSchemaRoots: new Set(),
405
+ unresolvedReturns: new Set(),
406
+ unresolvedResolvers: new Set(),
407
+ };
408
+ const edges = new Map();
409
+ // Pass 1 — discover every GraphQL class + collect Schema(...) root wiring.
410
+ const classes = [];
411
+ // `${rootFile}#${refName}` → slot, from a resolved Schema(query=…) kwarg.
412
+ const schemaRootSlot = new Map();
413
+ const schemaFiles = new Set(); // files that construct a Schema(...)
414
+ function isSchemaCall(callee, id) {
415
+ const leaf = callee.path.length ? callee.path[callee.path.length - 1] : callee.root;
416
+ if (leaf !== 'Schema')
417
+ return false;
418
+ if (callee.path.length >= 1) {
419
+ // `strawberry.Schema(...)` / `graphene.Schema(...)`.
420
+ const s = strawberryEnabled && strawberryImp.get(id)?.aliases.has(callee.root);
421
+ const g = grapheneEnabled && grapheneImp.get(id)?.aliases.has(callee.root);
422
+ return Boolean(s || g);
423
+ }
424
+ // Bare `Schema(...)` via `from strawberry import Schema`.
425
+ const s = strawberryEnabled && strawberryImp.get(id)?.symbols.get(callee.root) === 'Schema';
426
+ const g = grapheneEnabled && grapheneImp.get(id)?.symbols.get(callee.root) === 'Schema';
427
+ return Boolean(s || g);
428
+ }
429
+ for (const [id, file] of parsed) {
430
+ const sImp = strawberryImp.get(id);
431
+ const gImp = grapheneImp.get(id);
432
+ for (const cls of file.nodes.classes) {
433
+ const name = className(cls);
434
+ if (!name)
435
+ continue;
436
+ if (strawberryEnabled && sImp) {
437
+ const kind = strawberryClassKind(cls, sImp);
438
+ if (kind) {
439
+ classes.push({ fileId: id, name, framework: 'strawberry', rootable: kind === 'type', node: cls });
440
+ continue;
441
+ }
442
+ }
443
+ if (grapheneEnabled && gImp) {
444
+ const gk = grapheneClassKind(cls);
445
+ if (gk) {
446
+ classes.push({
447
+ fileId: id,
448
+ name,
449
+ framework: 'graphene',
450
+ rootable: gk.rootable,
451
+ slotBase: gk.slotBase,
452
+ node: cls,
453
+ });
454
+ }
455
+ }
456
+ }
457
+ // Schema(...) constructions → resolve each operation-root kwarg to a file.
458
+ for (const call of file.nodes.calls) {
459
+ const callee = callCallee(call);
460
+ if (!callee || !isSchemaCall(callee, id))
461
+ continue;
462
+ schemaFiles.add(id); // the GraphQL schema-assembly entry point
463
+ for (const slot of ['query', 'mutation', 'subscription']) {
464
+ const arg = keywordArg(call, slot);
465
+ if (!arg)
466
+ continue;
467
+ const chain = memberChain(arg);
468
+ if (!chain) {
469
+ diag.unresolvedSchemaRoots.add(`${id}: Schema(${slot}=…)`);
470
+ continue;
471
+ }
472
+ const refName = chain.path.length ? chain.path[chain.path.length - 1] : chain.root;
473
+ const targetFile = resolveName(chain.root, id);
474
+ if (targetFile) {
475
+ schemaRootSlot.set(`${targetFile}#${refName}`, slot);
476
+ addEdge(edges, id, targetFile, 'schema-root');
477
+ }
478
+ else {
479
+ diag.unresolvedSchemaRoots.add(`${id}: Schema(${slot}=${refName})`);
480
+ }
481
+ }
482
+ }
483
+ }
484
+ // Pass 2 — roles + resolver/field edges, now that schema roots are known.
485
+ const roleByFile = new Map();
486
+ const roleFiles = new Set(); // every file carrying any GraphQL role (for grouping)
487
+ for (const gc of classes) {
488
+ // Decide the class's slot (root) or plain-type role.
489
+ let slot = gc.slotBase;
490
+ if (!slot && gc.rootable) {
491
+ slot = schemaRootSlot.get(`${gc.fileId}#${gc.name}`) ?? ROOT_NAMES.get(gc.name);
492
+ }
493
+ const role = slot ? SLOT_ROLE[slot] : 'graphql-type';
494
+ addRole(roleByFile, gc.fileId, role);
495
+ roleFiles.add(gc.fileId);
496
+ // Resolver return-type + field edges from the class body.
497
+ for (const fn of classDirectFunctions(gc.node)) {
498
+ const fnName = fn.d.name ? fn.d.name.d.value : undefined;
499
+ const isResolver = (gc.framework === 'strawberry' && isStrawberryResolver(fn, strawberryImp.get(gc.fileId))) ||
500
+ (gc.framework === 'graphene' && fnName !== undefined && isGrapheneResolverName(fnName));
501
+ if (!isResolver)
502
+ continue;
503
+ const typeNames = new Set();
504
+ collectAnnotationTypeNames(fn.d.returnAnnotation, typeNames);
505
+ let resolvedAny = false;
506
+ for (const tn of typeNames) {
507
+ const target = resolveName(tn, gc.fileId);
508
+ if (target) {
509
+ addEdge(edges, gc.fileId, target, 'resolver-returns');
510
+ resolvedAny = true;
511
+ }
512
+ }
513
+ if (typeNames.size > 0 && !resolvedAny) {
514
+ diag.unresolvedReturns.add(`${gc.fileId}: ${gc.name}.${fnName ?? '<fn>'} → ${[...typeNames].sort().join('|')}`);
515
+ }
516
+ }
517
+ // Field-declaration edges: graphene `x = graphene.Field(User)` type target +
518
+ // any `resolver=` cross-file resolver function (strawberry & graphene).
519
+ for (const a of classDirectAssignments(gc.node)) {
520
+ const rhs = a.d.rightExpr;
521
+ if (rhs.nodeType !== PN.Call)
522
+ continue;
523
+ const call = rhs;
524
+ const callee = callCallee(call);
525
+ // graphene field type: `Field(User)` / `graphene.List(User)` → first arg type.
526
+ if (gc.framework === 'graphene' && callee) {
527
+ const leaf = callee.path.length ? callee.path[callee.path.length - 1] : callee.root;
528
+ if (GRAPHENE_FIELD_CTORS.has(leaf)) {
529
+ const typeArg = positionalArgs(call)[0];
530
+ const names = new Set();
531
+ collectAnnotationTypeNames(typeArg, names);
532
+ for (const tn of names) {
533
+ const target = resolveName(tn, gc.fileId);
534
+ if (target)
535
+ addEdge(edges, gc.fileId, target, 'field-type');
536
+ }
537
+ }
538
+ }
539
+ // `resolver=fn` cross-file resolver function (both frameworks).
540
+ const resolverArg = keywordArg(call, 'resolver');
541
+ if (resolverArg) {
542
+ const chain = memberChain(resolverArg);
543
+ const target = chain ? resolveName(chain.root, gc.fileId) : undefined;
544
+ if (target) {
545
+ addEdge(edges, gc.fileId, target, 'field-resolver');
546
+ // A standalone resolver-function file is itself a GraphQL request entry.
547
+ addRole(roleByFile, target, 'graphql-resolver');
548
+ roleFiles.add(target);
549
+ }
550
+ else if (chain) {
551
+ diag.unresolvedResolvers.add(`${gc.fileId}: resolver=${chain.root}`);
552
+ }
553
+ }
554
+ }
555
+ }
556
+ // The schema-assembly files (a `Schema(...)` construction) are GraphQL entries
557
+ // too — tag them (a root class in the same file still wins by priority).
558
+ for (const f of schemaFiles) {
559
+ addRole(roleByFile, f, 'graphql-schema');
560
+ roleFiles.add(f);
561
+ }
562
+ // Grouping — files carrying a GraphQL role, anchored on a dedicated GraphQL dir.
563
+ const groups = buildGraphqlGroups(roleFiles);
564
+ const roles = new Map();
565
+ for (const [fileId, role] of roleByFile) {
566
+ roles.set(fileId, {
567
+ role,
568
+ kind: ROLE_KIND[role],
569
+ priority: ROLE_PRIORITY[role],
570
+ metadata: { framework: 'graphql' },
571
+ });
572
+ }
573
+ const sortedEdges = [...edges.values()].sort((a, b) => a.source < b.source
574
+ ? -1
575
+ : a.source > b.source
576
+ ? 1
577
+ : a.target < b.target
578
+ ? -1
579
+ : a.target > b.target
580
+ ? 1
581
+ : a.metadata?.relation < b.metadata?.relation
582
+ ? -1
583
+ : a.metadata?.relation > b.metadata?.relation
584
+ ? 1
585
+ : 0);
586
+ // Positive signal for validation (mirrors fastapi/flask/nest's log line).
587
+ if (groups.length > 0 || roleByFile.size > 0 || sortedEdges.length > 0) {
588
+ console.log(` [graphql] ${classes.length} type(s) → ${roleByFile.size} role(s) · ${groups.length} schema group(s) · ${sortedEdges.length} edge(s)`);
589
+ }
590
+ // No silent caps (locked): log everything that degraded.
591
+ if (diag.unresolvedSchemaRoots.size > 0 ||
592
+ diag.unresolvedReturns.size > 0 ||
593
+ diag.unresolvedResolvers.size > 0) {
594
+ const parts = [];
595
+ if (diag.unresolvedSchemaRoots.size > 0) {
596
+ parts.push(`${diag.unresolvedSchemaRoots.size} unresolvable schema root(s): ${[...diag.unresolvedSchemaRoots].sort().slice(0, 10).join(' · ')}`);
597
+ }
598
+ if (diag.unresolvedReturns.size > 0) {
599
+ parts.push(`${diag.unresolvedReturns.size} unresolvable resolver return type(s): ${[...diag.unresolvedReturns].sort().slice(0, 10).join(' · ')}`);
600
+ }
601
+ if (diag.unresolvedResolvers.size > 0) {
602
+ parts.push(`${diag.unresolvedResolvers.size} unresolvable resolver ref(s): ${[...diag.unresolvedResolvers].sort().slice(0, 10).join(' · ')}`);
603
+ }
604
+ console.log(` [graphql] degraded: ${parts.join(' · ')} (logged, not silently dropped)`);
605
+ }
606
+ return { groups, edges: sortedEdges, roles };
607
+ }
608
+ // The nearest ancestor directory of `fileId` whose basename is a dedicated GraphQL
609
+ // dir (`graphql`/`gql`/`schema`), shallowest-first; undefined when there is none.
610
+ function graphqlAnchor(fileId) {
611
+ const parts = fileId.split('/');
612
+ for (let i = 0; i < parts.length - 1; i++) {
613
+ if (GRAPHQL_DIR_SEGMENTS.has(parts[i].toLowerCase())) {
614
+ return { anchor: parts.slice(0, i + 1).join('/'), segment: parts[i] };
615
+ }
616
+ }
617
+ return undefined;
618
+ }
619
+ // One subsystem per dedicated GraphQL directory, gathering the GraphQL-role files
620
+ // under it. Deterministic, collision-free ids (the assignGroupIds discipline):
621
+ // process anchors by ascending path so the SMALLEST wins the bare slug; collisions
622
+ // take a `-<dirSegment>` then `-<n>` suffix. Files with no GraphQL dir are left
623
+ // ungrouped (directory grouping handles them).
624
+ function buildGraphqlGroups(roleFiles) {
625
+ const byAnchor = new Map();
626
+ for (const fileId of roleFiles) {
627
+ const a = graphqlAnchor(fileId);
628
+ if (!a)
629
+ continue;
630
+ const seed = byAnchor.get(a.anchor) ?? { anchor: a.anchor, segment: a.segment, fileIds: new Set() };
631
+ seed.fileIds.add(fileId);
632
+ byAnchor.set(a.anchor, seed);
633
+ }
634
+ const taken = new Set();
635
+ const ordered = [...byAnchor.values()].sort((x, y) => (x.anchor < y.anchor ? -1 : x.anchor > y.anchor ? 1 : 0));
636
+ const groups = [];
637
+ for (const seed of ordered) {
638
+ const seg = seed.segment.toLowerCase();
639
+ const label = seg === 'graphql' || seg === 'gql' ? 'GraphQL' : humanize(seed.segment);
640
+ const baseSlug = slugify(seed.segment) || 'graphql';
641
+ let id = baseSlug;
642
+ if (taken.has(id))
643
+ id = `${baseSlug}-${dirSegment(seed.anchor)}`;
644
+ let n = 2;
645
+ while (taken.has(id))
646
+ id = `${baseSlug}-${n++}`;
647
+ taken.add(id);
648
+ groups.push({ id, label, fileIds: [...seed.fileIds].sort() });
649
+ }
650
+ return groups.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
651
+ }
652
+ function getAnalysis(ctx) {
653
+ let a = ANALYSIS_CACHE.get(ctx);
654
+ if (!a) {
655
+ a = analyzeGraphql(ctx);
656
+ ANALYSIS_CACHE.set(ctx, a);
657
+ }
658
+ return a;
659
+ }
660
+ // ---------------------------------------------------------------------------
661
+ // The adapter.
662
+ export const graphqlAdapter = {
663
+ name: 'graphql',
664
+ async detect(ctx) {
665
+ const { base, rootPath } = resolveBase(ctx);
666
+ // Root scan first (a repo whose manifest is at root reports rootPath '').
667
+ const rootMatch = scoreGraphql(gatherGraphqlSignals(base), rootPath);
668
+ if (rootMatch)
669
+ return rootMatch;
670
+ // A GraphQL backend often lives one dir down (a `backend/` | `server/` | `api/`
671
+ // package in a frontend+backend monorepo). Shallow-scan immediate subdirs for a
672
+ // strawberry/graphene manifest and scope to it. Only when NOT already scoped to
673
+ // a workspace package (the path).
674
+ if (!ctx.packageDir) {
675
+ for (const sub of shallowManifestSubdirs(base)) {
676
+ const m = scoreGraphql(gatherGraphqlSignals(join(base, sub)), sub);
677
+ if (m)
678
+ return m;
679
+ }
680
+ }
681
+ return null;
682
+ },
683
+ // One grouping prior per dedicated GraphQL directory → its own subsystem,
684
+ // authoritative over directory grouping (the fastapi/flask/nest mechanism).
685
+ // Fully deterministic (dir-derived) → no classificationsNeeded.
686
+ async groupingPrior(ctx) {
687
+ return { groups: getAnalysis(ctx).groups };
688
+ },
689
+ // resolver → returned type, graphene field → type, root field → cross-file
690
+ // resolver, Schema(...) → root type files (all kind 'calls'). File-id endpoints;
691
+ // the step resolves to modules, drops self-edges, dedupes, 8-verb-validates.
692
+ async syntheticEdges(ctx) {
693
+ return getAnalysis(ctx).edges;
694
+ },
695
+ // Query/Mutation/Subscription roots + their resolvers → gateway; every other
696
+ // object/input/interface type → service. METADATA; the module's `kind` is
697
+ // unchanged.
698
+ async roleTags(ctx) {
699
+ return getAnalysis(ctx).roles;
700
+ },
701
+ // The hooks READ SOURCE (Python). Declare the paths the diff-driven hosted walk
702
+ // must treat as framework-relevant. Never-store-source holds: parse server-side,
703
+ // persist only the derived groups/edges/roles.
704
+ scansSourcePath(path) {
705
+ return path.endsWith('.py') || path.endsWith('.pyi');
706
+ },
707
+ };
708
+ //# sourceMappingURL=graphql.js.map