@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,691 @@
1
+ // the Workflow-orchestrator FrameworkAdapter. ONE adapter for the
2
+ // four Python workflow/DAG orchestrators, built on a single shared
3
+ // "decorated flow/task/asset" heuristic + per-tool refinement. Follows the
4
+ // FastAPI/Flask template (/997) and reuses the shared Python core
5
+ // (py-ast + parsePythonScope). Install-free + deterministic (a pure
6
+ // syntactic Pyright parse; never executes repo code); persist only the derived
7
+ // groups/edges/roles.
8
+ //
9
+ // The unifying idea: in every one of these tools the UNIT OF WORK is a DECORATED
10
+ // function — @asset (Dagster) / @flow,@task (Prefect) / @dag,@task (Airflow
11
+ // TaskFlow) / @activity.defn,@workflow.run (Temporal). One heuristic tags all of
12
+ // them; a small per-tool table refines the role name + the dependency DSL.
13
+ //
14
+ // * detect() — any of `dagster` / `prefect` / `apache-airflow` /
15
+ // `temporalio`. The metadata carries WHICH tools are
16
+ // present, so the analysis only runs the vocab/edge passes
17
+ // for the tools actually in the repo (mirrors FastAPI's
18
+ // `celery` flag).
19
+ // * roleTags — a decorated flow/task/asset/op/dag/activity/workflow →
20
+ // `job` (the role: own-code triggered by a
21
+ // schedule/queue/event, not a request). METADATA onto the
22
+ // LOCKED MODULE_KINDS enum; never a new kind.
23
+ // * groupingPrior — a `dags/` directory → one subsystem; and every
24
+ // orchestration CONTAINER (Airflow `@dag`, Dagster `@job`,
25
+ // Prefect `@flow`) → a subsystem grouping the definer +
26
+ // the task/asset files it references. Same authoritative-
27
+ // over-directory mechanism the FastAPI router / Nest
28
+ // @Module priors use.
29
+ // * syntheticEdges — the task-dependency wiring the import graph doesn't name
30
+ // as verbs, resolved STATICALLY to `calls` where possible:
31
+ // · Dagster asset deps — an @asset fn PARAMETER named
32
+ // after another asset, or `@asset(deps=[…])`.
33
+ // · Prefect/Dagster/Airflow — a @task/@op called from a
34
+ // @flow/@job/@dag (cross-file, via import bindings).
35
+ // · Airflow `a >> b` / `a << b` / `a.set_downstream(b)`.
36
+ // · Temporal `workflow.execute_activity(fn, …)`.
37
+ // Endpoints in FILE-id space; the contribute-step resolves
38
+ // to modules, drops self-edges (same-file deps collapse),
39
+ // dedupes, 8-verb-validates. Dynamic/unresolvable deps
40
+ // DEGRADE + LOG — no silent caps.
41
+ //
42
+ // Dagster + Prefect + Airflow-TaskFlow are covered solidly; Temporal is
43
+ // best-effort (its workflow is a decorated CLASS — caught via the always-present
44
+ // `@workflow.run` method rather than class-decorator collection).
45
+ import { existsSync, readdirSync } from 'node:fs';
46
+ import { join } from 'node:path';
47
+ import { clampConfidence, resolveBase } from '../detect-util.js';
48
+ import { readPythonDeps } from '../../graph/python-manifest.js';
49
+ import { parsePythonScope } from '../python/analyze.js';
50
+ import { callCallee, keywordArg, listItems, memberChain, nameValue, positionalArgs, stringValue, PN, } from '../python/py-ast.js';
51
+ /** Gather the signal set for a single root dir (reads manifests only). */
52
+ export function gatherOrchestratorSignals(baseDir) {
53
+ const deps = readPythonDeps(baseDir);
54
+ return {
55
+ tools: {
56
+ dagster: deps.has('dagster'),
57
+ prefect: deps.has('prefect'),
58
+ // The PyPI distribution is `apache-airflow`; accept a bare `airflow` too.
59
+ airflow: deps.has('apache-airflow') || deps.has('airflow'),
60
+ temporal: deps.has('temporalio'),
61
+ },
62
+ };
63
+ }
64
+ function toolCount(t) {
65
+ return (t.dagster ? 1 : 0) + (t.prefect ? 1 : 0) + (t.airflow ? 1 : 0) + (t.temporal ? 1 : 0);
66
+ }
67
+ // Non-source dirs the nested scan skips (cheap + can't hold a first-party manifest).
68
+ const NESTED_SKIP_DIRS = new Set([
69
+ 'node_modules',
70
+ 'dist',
71
+ 'build',
72
+ 'out',
73
+ 'coverage',
74
+ 'venv',
75
+ '.venv',
76
+ '__pycache__',
77
+ 'site-packages',
78
+ ]);
79
+ /** True if `dir` holds a Python manifest worth scanning for deps. */
80
+ function hasPythonManifest(dir) {
81
+ return (existsSync(join(dir, 'pyproject.toml')) ||
82
+ existsSync(join(dir, 'setup.py')) ||
83
+ existsSync(join(dir, 'setup.cfg')) ||
84
+ existsSync(join(dir, 'requirements.txt')));
85
+ }
86
+ /**
87
+ * Immediate subdirs (depth 1) that contain a Python manifest — the shallow search
88
+ * for a nested orchestration package. Sorted, so the first-match pick is
89
+ * deterministic; skips dot-dirs + non-source dirs to stay cheap.
90
+ */
91
+ function shallowManifestSubdirs(base) {
92
+ let entries;
93
+ try {
94
+ entries = readdirSync(base, { withFileTypes: true });
95
+ }
96
+ catch {
97
+ return [];
98
+ }
99
+ const out = [];
100
+ for (const e of entries) {
101
+ if (!e.isDirectory() || e.name.startsWith('.') || NESTED_SKIP_DIRS.has(e.name))
102
+ continue;
103
+ if (hasPythonManifest(join(base, e.name)))
104
+ out.push(e.name);
105
+ }
106
+ return out.sort();
107
+ }
108
+ /**
109
+ * Decide the orchestrator match from the signal set. At least one of the four
110
+ * deps is REQUIRED; a second raises confidence (a repo wiring two orchestrators
111
+ * is an even stronger signal). Returns null → generic-Python fallthrough,
112
+ * byte-for-byte unchanged.
113
+ */
114
+ export function scoreOrchestrator(s, rootPath = '') {
115
+ const n = toolCount(s.tools);
116
+ if (n === 0)
117
+ return null;
118
+ let confidence = 0.8;
119
+ if (n > 1)
120
+ confidence += 0.1;
121
+ return {
122
+ adapter: 'orchestrator',
123
+ confidence: clampConfidence(confidence),
124
+ rootPath,
125
+ metadata: { tools: s.tools },
126
+ };
127
+ }
128
+ // Collapse priority when one FILE carries several roles (a @dag file that also
129
+ // declares @task functions reads as `dag` — the orchestration headline; a
130
+ // Prefect @flow+@task file reads as `flow`). Higher wins; lexical tiebreak.
131
+ const ROLE_PRIORITY = {
132
+ workflow: 11,
133
+ dag: 10,
134
+ job: 9,
135
+ flow: 8,
136
+ asset: 7,
137
+ op: 6,
138
+ activity: 5,
139
+ task: 4,
140
+ sensor: 3,
141
+ schedule: 2,
142
+ 'task-group': 1,
143
+ };
144
+ // Every role maps onto the single locked kind `job` (never a new Module-kind).
145
+ const ROLE_KIND = 'job';
146
+ // Roles that are orchestration CONTAINERS — each becomes its own subsystem
147
+ // grouping the tasks/assets it references (Airflow @dag, Dagster @job, Prefect
148
+ // @flow). Temporal's @workflow is a unit, not a container of other files.
149
+ const CONTAINER_ROLES = new Set(['dag', 'job', 'flow']);
150
+ // Roles that are a "unit" a container can reference cross-file (a call from a
151
+ // @flow/@job/@dag to one of these files becomes a `calls` edge + group member).
152
+ const UNIT_ROLES = new Set(['asset', 'op', 'task', 'flow', 'activity', 'workflow']);
153
+ // Temporal orchestration-call methods whose first positional arg is the
154
+ // activity/child-workflow reference (best-effort).
155
+ const TEMPORAL_CALL_METHODS = new Set([
156
+ 'execute_activity',
157
+ 'execute_local_activity',
158
+ 'execute_activity_method',
159
+ 'execute_child_workflow',
160
+ 'start_activity',
161
+ 'start_child_workflow',
162
+ 'start_local_activity',
163
+ ]);
164
+ // Airflow imperative dependency setters (a.set_downstream(b) / b.set_upstream(a)).
165
+ const DOWNSTREAM_METHODS = new Set(['set_downstream', 'set_upstream']);
166
+ // Pinned OperatorType values (parser/tokenizerTypes) for the Airflow `>>` / `<<`
167
+ // task-dependency DSL. The dep is pinned EXACTLY (same rationale as py-ast's PN).
168
+ const OP_RSHIFT = 31; // `a >> b` — a upstream of b
169
+ const OP_LSHIFT = 17; // `a << b` — b upstream of a
170
+ /**
171
+ * Classify a decorator's `{root, path}` chain to an orchestrator role, GATED by
172
+ * which tools the repo declares (so a bare `@task` is only claimed when a tool
173
+ * that defines it is present). Matches on the chain LEAF (`@dg.asset` and bare
174
+ * `@asset` both read as leaf `asset`), so it's import-alias-independent.
175
+ */
176
+ export function classifyDecorator(chain, tools) {
177
+ const leaf = chain.path.length ? chain.path[chain.path.length - 1] : chain.root;
178
+ if (tools.dagster) {
179
+ if (leaf === 'asset' || leaf === 'multi_asset' || leaf === 'graph_asset' || leaf === 'graph_multi_asset' || leaf === 'asset_check')
180
+ return 'asset';
181
+ if (leaf === 'op')
182
+ return 'op';
183
+ if (leaf === 'job' || leaf === 'graph')
184
+ return 'job';
185
+ if (leaf === 'sensor' || leaf === 'asset_sensor' || leaf === 'multi_asset_sensor' || leaf === 'run_status_sensor' || leaf === 'run_failure_sensor')
186
+ return 'sensor';
187
+ if (leaf === 'schedule')
188
+ return 'schedule';
189
+ }
190
+ if (tools.airflow) {
191
+ // The TaskFlow `task` namespace covers `@task` and `@task.<variant>`
192
+ // (virtualenv/docker/kubernetes/branch/short_circuit/…): match the ROOT.
193
+ if (chain.root === 'task')
194
+ return 'task';
195
+ if (leaf === 'dag')
196
+ return 'dag';
197
+ if (leaf === 'task_group')
198
+ return 'task-group';
199
+ }
200
+ if (tools.prefect) {
201
+ if (leaf === 'flow')
202
+ return 'flow';
203
+ if (leaf === 'task')
204
+ return 'task';
205
+ }
206
+ if (tools.temporal) {
207
+ // @workflow.defn (class) / @workflow.run|signal|query|update (method).
208
+ if (chain.root === 'workflow')
209
+ return 'workflow';
210
+ // @activity.defn (function).
211
+ if (chain.root === 'activity' && (chain.path.length === 0 || chain.path[0] === 'defn'))
212
+ return 'activity';
213
+ }
214
+ return undefined;
215
+ }
216
+ // Memoized on the FrameworkContext OBJECT (not repoDir) so groupingPrior +
217
+ // syntheticEdges + roleTags share ONE parse, while the merge walk's per-checkpoint
218
+ // ctx gets a fresh analysis. Mirrors fastapi / flask / nest.
219
+ const ANALYSIS_CACHE = new WeakMap();
220
+ function slugify(s) {
221
+ return s
222
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
223
+ .toLowerCase()
224
+ .replace(/[^a-z0-9]+/g, '-')
225
+ .replace(/^-+|-+$/g, '');
226
+ }
227
+ function humanize(s) {
228
+ const words = s
229
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
230
+ .replace(/[_\-.]+/g, ' ')
231
+ .trim()
232
+ .split(/\s+/)
233
+ .filter(Boolean);
234
+ if (words.length === 0)
235
+ return s;
236
+ return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
237
+ }
238
+ function baseName(fileId) {
239
+ const last = fileId.split('/').pop() ?? fileId;
240
+ return last.replace(/\.[^.]+$/, '');
241
+ }
242
+ function dirSegment(fileId) {
243
+ const slash = fileId.indexOf('/');
244
+ return slash > 0 ? slugify(fileId.slice(0, slash)) : 'root';
245
+ }
246
+ // Keep the highest-priority candidate role per file (deterministic, lexical
247
+ // tiebreak) — mirrors the fastapi/flask addRole + the contribute-step's collapse.
248
+ function addRole(map, fileId, role) {
249
+ const cur = map.get(fileId);
250
+ if (cur === undefined ||
251
+ ROLE_PRIORITY[role] > ROLE_PRIORITY[cur] ||
252
+ (ROLE_PRIORITY[role] === ROLE_PRIORITY[cur] && role < cur)) {
253
+ map.set(fileId, role);
254
+ }
255
+ }
256
+ function addEdge(edges, from, to, relation) {
257
+ if (from === to)
258
+ return; // intra-file deps collapse; the step drops self-edges too
259
+ const key = `${from}→${to}:calls`;
260
+ if (!edges.has(key)) {
261
+ edges.set(key, { source: from, target: to, kind: 'calls', metadata: { framework: 'orchestrator', relation } });
262
+ }
263
+ }
264
+ // The constructor name of an `x = Ctor(...)` assignment RHS (last chain segment,
265
+ // so both `DAG(...)` and `airflow.DAG(...)` read as 'DAG'), or undefined.
266
+ function assignedCtorName(rhs) {
267
+ if (rhs.nodeType !== PN.Call)
268
+ return undefined;
269
+ const callee = callCallee(rhs);
270
+ if (!callee)
271
+ return undefined;
272
+ return callee.path.length ? callee.path[callee.path.length - 1] : callee.root;
273
+ }
274
+ // A decorator's callee chain (`@bp.route(...)` → root 'bp', path ['route'];
275
+ // `@shared_task` → root 'shared_task', path []; `@workflow.defn` → root
276
+ // 'workflow', path ['defn']). The decorator expr is either a call or a bare
277
+ // name/attribute. Mirrors the fastapi/flask local helper (per-adapter pattern).
278
+ function decoratorChain(deco) {
279
+ const expr = deco.d.expr;
280
+ if (expr.nodeType === PN.Call)
281
+ return callCallee(expr);
282
+ return memberChain(expr);
283
+ }
284
+ // A container decorator's human label: its `dag_id=`/`name=` string arg when the
285
+ // decorator is called, else the decorated function's name.
286
+ function containerLabel(deco, fnName) {
287
+ const expr = deco.d.expr;
288
+ if (expr.nodeType === PN.Call) {
289
+ const call = expr;
290
+ const s = stringValue(keywordArg(call, 'dag_id')) ?? stringValue(keywordArg(call, 'name'));
291
+ if (s)
292
+ return s;
293
+ }
294
+ return fnName;
295
+ }
296
+ // Resolve a symbol name used at a task-dependency site to a file id: an imported
297
+ // symbol resolves to its module file; anything else (a local task variable, an
298
+ // unresolved name) resolves to THIS file → a self-edge the step drops.
299
+ function resolveName(root, binds, thisFile) {
300
+ return binds.get(root) ?? thisFile;
301
+ }
302
+ // Resolve a task-dependency operand EXPRESSION (a task variable `a`, an
303
+ // attribute `mod.a`, or a task call `extract()`) to a file id, or undefined when
304
+ // it isn't a dotted-name/call we can anchor (a dynamic expression → logged).
305
+ function operandFile(expr, binds, thisFile) {
306
+ const mc = memberChain(expr);
307
+ if (mc)
308
+ return resolveName(mc.root, binds, thisFile);
309
+ if (expr.nodeType === PN.Call) {
310
+ const callee = callCallee(expr);
311
+ if (callee)
312
+ return resolveName(callee.root, binds, thisFile);
313
+ }
314
+ return undefined;
315
+ }
316
+ // The upstream-most (`head`) or downstream-most (`tail`) task operands of a
317
+ // `>>`/`<<` chain, expanding list operands (`[a, b] >> c`) and recursing through
318
+ // nested shifts. For `a >> b`: head = a's head, tail = b's tail; `<<` mirrors.
319
+ function shiftEnds(expr, side) {
320
+ const t = expr.nodeType;
321
+ if (t === PN.List)
322
+ return listItems(expr);
323
+ if (t === PN.BinaryOperation) {
324
+ const b = expr;
325
+ if (b.d.operator === OP_RSHIFT)
326
+ return shiftEnds(side === 'head' ? b.d.leftExpr : b.d.rightExpr, side);
327
+ if (b.d.operator === OP_LSHIFT)
328
+ return shiftEnds(side === 'head' ? b.d.rightExpr : b.d.leftExpr, side);
329
+ return [expr]; // a non-shift operator is an opaque endpoint
330
+ }
331
+ return [expr]; // a Name / MemberAccess / Call leaf
332
+ }
333
+ function assignGroups(seeds) {
334
+ const taken = new Set();
335
+ const ordered = [...seeds].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
336
+ const groups = [];
337
+ for (const seed of ordered) {
338
+ let id = seed.baseSlug;
339
+ if (taken.has(id))
340
+ id = `${seed.baseSlug}-${dirSegment(seed.key)}`;
341
+ let n = 2;
342
+ while (taken.has(id))
343
+ id = `${seed.baseSlug}-${n++}`;
344
+ taken.add(id);
345
+ groups.push({ id, label: seed.label, fileIds: [...seed.fileIds].sort() });
346
+ }
347
+ return groups.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
348
+ }
349
+ // The `dags/` directory path prefix a file lives under (up to & including the
350
+ // `dags` segment), or undefined. `svc/dags/etl.py` → `svc/dags`.
351
+ function dagsDirOf(fileId) {
352
+ const parts = fileId.split('/');
353
+ const idx = parts.findIndex((p) => p.toLowerCase() === 'dags');
354
+ if (idx < 0 || idx === parts.length - 1)
355
+ return undefined; // not under a dags/ dir
356
+ return parts.slice(0, idx + 1).join('/');
357
+ }
358
+ function analyzeOrchestrator(ctx) {
359
+ const { parsed } = parsePythonScope(ctx);
360
+ const tools = ctx.match.metadata?.tools ?? {
361
+ dagster: false,
362
+ prefect: false,
363
+ airflow: false,
364
+ temporal: false,
365
+ };
366
+ const roleByFile = new Map();
367
+ const diag = {
368
+ ambiguousAssets: new Set(),
369
+ unresolvedShift: new Set(),
370
+ unresolvedTemporal: new Set(),
371
+ };
372
+ // Pass 1 — roles + indexes (asset-name → file(s); the set of unit files; the
373
+ // container files; each asset def's params/deps for the Dagster dependency pass).
374
+ const assetFilesByName = new Map(); // asset fn name → defining file(s)
375
+ const unitFiles = new Set(); // files that define any orchestrator unit
376
+ const containerByFile = new Map(); // container file → its label/role
377
+ const assetDefs = [];
378
+ for (const [id, file] of parsed) {
379
+ // Classic Airflow `dag = DAG(...)` (the non-decorator, context-manager-adjacent
380
+ // form) — its file is a DAG orchestration unit + container.
381
+ if (tools.airflow) {
382
+ for (const a of file.nodes.assignments) {
383
+ if (assignedCtorName(a.d.rightExpr) !== 'DAG')
384
+ continue;
385
+ addRole(roleByFile, id, 'dag');
386
+ if (!containerByFile.has(id)) {
387
+ const call = a.d.rightExpr;
388
+ const label = stringValue(keywordArg(call, 'dag_id')) ??
389
+ stringValue(positionalArgs(call)[0]) ??
390
+ nameValue(a.d.leftExpr) ??
391
+ baseName(id);
392
+ containerByFile.set(id, { role: 'dag', label });
393
+ }
394
+ }
395
+ }
396
+ for (const fn of file.nodes.functions) {
397
+ const fnName = fn.d.name.d.value;
398
+ for (const deco of fn.d.decorators) {
399
+ const chain = decoratorChain(deco);
400
+ if (!chain)
401
+ continue;
402
+ const role = classifyDecorator(chain, tools);
403
+ if (!role)
404
+ continue;
405
+ addRole(roleByFile, id, role);
406
+ if (UNIT_ROLES.has(role))
407
+ unitFiles.add(id);
408
+ if (role === 'asset') {
409
+ let set = assetFilesByName.get(fnName);
410
+ if (!set) {
411
+ set = new Set();
412
+ assetFilesByName.set(fnName, set);
413
+ }
414
+ set.add(id);
415
+ const call = deco.d.expr.nodeType === PN.Call ? deco.d.expr : undefined;
416
+ const params = fn.d.params.map((p) => p.d.name?.d.value).filter((n) => !!n);
417
+ const depsItems = call ? listItems(keywordArg(call, 'deps')) : [];
418
+ assetDefs.push({ fileId: id, params, depsItems });
419
+ }
420
+ if (CONTAINER_ROLES.has(role) && !containerByFile.has(id)) {
421
+ containerByFile.set(id, { role, label: containerLabel(deco, fnName) });
422
+ }
423
+ }
424
+ }
425
+ }
426
+ // Resolve an asset KEY (a param name, a `deps=` string key) to its defining
427
+ // file via the global asset-name index — Dagster wires assets by key, not by
428
+ // import. Ambiguous (same name in >1 file) → undefined + logged.
429
+ function assetByName(name) {
430
+ const set = assetFilesByName.get(name);
431
+ if (!set || set.size === 0)
432
+ return undefined;
433
+ if (set.size > 1) {
434
+ diag.ambiguousAssets.add(`${name} → {${[...set].sort().join(', ')}}`);
435
+ return undefined;
436
+ }
437
+ return [...set][0];
438
+ }
439
+ // Pass 2 — edges + container group membership.
440
+ const edges = new Map();
441
+ const containerMembers = new Map(); // container file → member files
442
+ for (const id of containerByFile.keys())
443
+ containerMembers.set(id, new Set([id]));
444
+ // Dagster asset deps — an @asset PARAMETER named after an asset (resolved by
445
+ // asset key), or an `@asset(deps=[…])` reference (an imported asset fn → its
446
+ // import binding; a string key → the asset index). Direction: upstream → this.
447
+ for (const def of assetDefs) {
448
+ const binds = parsed.get(def.fileId).bindings;
449
+ for (const paramName of def.params) {
450
+ const upstream = assetByName(paramName);
451
+ if (upstream)
452
+ addEdge(edges, upstream, def.fileId, 'asset-dep-param');
453
+ }
454
+ for (const item of def.depsItems) {
455
+ let upstream;
456
+ const mc = memberChain(item);
457
+ if (mc)
458
+ upstream = binds.get(mc.root) ?? assetByName(mc.root);
459
+ else {
460
+ const key = stringValue(item);
461
+ if (key)
462
+ upstream = assetByName(key.split('/').pop() ?? key);
463
+ }
464
+ if (upstream)
465
+ addEdge(edges, upstream, def.fileId, 'asset-dep');
466
+ }
467
+ }
468
+ for (const [id, file] of parsed) {
469
+ const binds = file.bindings;
470
+ const isContainer = containerByFile.has(id);
471
+ // Container → imported-unit calls (Prefect @flow → @task, Dagster @job → @op,
472
+ // Airflow @dag → an imported @task). Same-file tasks aren't imported → no
473
+ // binding → no edge (they collapse anyway). Each target also joins the group.
474
+ if (isContainer) {
475
+ for (const call of file.nodes.calls) {
476
+ const callee = callCallee(call);
477
+ if (!callee)
478
+ continue;
479
+ const target = binds.get(callee.root);
480
+ if (target && target !== id && unitFiles.has(target)) {
481
+ addEdge(edges, id, target, 'contains-task');
482
+ containerMembers.get(id).add(target);
483
+ }
484
+ }
485
+ }
486
+ // Temporal orchestration calls (best-effort): the first positional arg of
487
+ // `workflow.execute_activity(fn, …)` / `execute_child_workflow(Wf, …)` is the
488
+ // activity/child-workflow reference.
489
+ if (tools.temporal) {
490
+ for (const call of file.nodes.calls) {
491
+ const callee = callCallee(call);
492
+ if (!callee || callee.path.length === 0)
493
+ continue;
494
+ if (!TEMPORAL_CALL_METHODS.has(callee.path[callee.path.length - 1]))
495
+ continue;
496
+ const arg = positionalArgs(call)[0];
497
+ if (!arg)
498
+ continue;
499
+ const target = operandFile(arg, binds, id);
500
+ if (target && target !== id)
501
+ addEdge(edges, id, target, 'temporal-execute');
502
+ else if (!target)
503
+ diag.unresolvedTemporal.add(`${id}: ${callee.path.join('.')}(…)`);
504
+ }
505
+ }
506
+ // Airflow imperative dependency setters + the `>>`/`<<` DSL.
507
+ if (tools.airflow) {
508
+ for (const call of file.nodes.calls) {
509
+ const callee = callCallee(call);
510
+ if (!callee || callee.path.length !== 1 || !DOWNSTREAM_METHODS.has(callee.path[0]))
511
+ continue;
512
+ const arg = positionalArgs(call)[0];
513
+ if (!arg)
514
+ continue;
515
+ const obj = resolveName(callee.root, binds, id);
516
+ const other = operandFile(arg, binds, id);
517
+ if (!other) {
518
+ diag.unresolvedShift.add(`${id}: ${callee.root}.${callee.path[0]}(…)`);
519
+ continue;
520
+ }
521
+ // set_downstream: obj → other; set_upstream: other → obj.
522
+ if (callee.path[0] === 'set_downstream')
523
+ addEdge(edges, obj, other, 'set-downstream');
524
+ else
525
+ addEdge(edges, other, obj, 'set-upstream');
526
+ }
527
+ for (const b of file.nodes.binaryOps) {
528
+ const rs = b.d.operator === OP_RSHIFT;
529
+ const ls = b.d.operator === OP_LSHIFT;
530
+ if (!rs && !ls)
531
+ continue;
532
+ // Upstream set → downstream set. RS `L >> R`: tails(L) → heads(R);
533
+ // LS `L << R` (R upstream of L): tails(R) → heads(L).
534
+ const ups = rs ? shiftEnds(b.d.leftExpr, 'tail') : shiftEnds(b.d.rightExpr, 'tail');
535
+ const downs = rs ? shiftEnds(b.d.rightExpr, 'head') : shiftEnds(b.d.leftExpr, 'head');
536
+ for (const u of ups) {
537
+ const uf = operandFile(u, binds, id);
538
+ if (!uf) {
539
+ diag.unresolvedShift.add(`${id}: dynamic shift operand`);
540
+ continue;
541
+ }
542
+ for (const d of downs) {
543
+ const df = operandFile(d, binds, id);
544
+ if (!df) {
545
+ diag.unresolvedShift.add(`${id}: dynamic shift operand`);
546
+ continue;
547
+ }
548
+ addEdge(edges, uf, df, 'shift-dep');
549
+ }
550
+ }
551
+ }
552
+ }
553
+ }
554
+ // Grouping — a `dags/` dir group + a per-container group. A `@dag`/`@job`/
555
+ // `@flow` DEFINER is excluded from the coarse dags-dir group so its finer,
556
+ // semantic per-container subsystem always wins; the dags-dir group then catches
557
+ // the rest (classic `with DAG(...)` files, shared helpers, configs) that have
558
+ // no container of their own.
559
+ const seeds = [];
560
+ const dagsDirs = new Map();
561
+ for (const id of parsed.keys()) {
562
+ if (containerByFile.has(id))
563
+ continue; // its own per-container group wins
564
+ const dir = dagsDirOf(id);
565
+ if (!dir)
566
+ continue;
567
+ let set = dagsDirs.get(dir);
568
+ if (!set) {
569
+ set = new Set();
570
+ dagsDirs.set(dir, set);
571
+ }
572
+ set.add(id);
573
+ }
574
+ for (const [dir, fileIds] of dagsDirs) {
575
+ // Label the group by its parent dir when nested (`svc/dags` → "Svc DAGs"),
576
+ // else just "DAGs". Slug from the full dir path so multiple dags/ dirs never
577
+ // collide.
578
+ const parts = dir.split('/');
579
+ const parent = parts.length >= 2 ? humanize(parts[parts.length - 2]) : '';
580
+ seeds.push({
581
+ key: `dagsdir:${dir}`,
582
+ baseSlug: slugify(dir) || 'dags',
583
+ label: parent ? `${parent} DAGs` : 'DAGs',
584
+ fileIds,
585
+ });
586
+ }
587
+ for (const [id, container] of containerByFile) {
588
+ seeds.push({
589
+ key: `container:${id}`,
590
+ baseSlug: slugify(container.label) || 'workflow',
591
+ label: humanize(container.label) || container.label,
592
+ fileIds: containerMembers.get(id),
593
+ });
594
+ }
595
+ const groups = assignGroups(seeds);
596
+ // Materialize the role tags.
597
+ const roles = new Map();
598
+ for (const [fileId, role] of roleByFile) {
599
+ roles.set(fileId, {
600
+ role,
601
+ kind: ROLE_KIND,
602
+ priority: ROLE_PRIORITY[role],
603
+ metadata: { framework: 'orchestrator' },
604
+ });
605
+ }
606
+ const sortedEdges = [...edges.values()].sort((a, b) => a.source < b.source
607
+ ? -1
608
+ : a.source > b.source
609
+ ? 1
610
+ : a.target < b.target
611
+ ? -1
612
+ : a.target > b.target
613
+ ? 1
614
+ : 0);
615
+ // Positive signal for validation (mirrors fastapi/flask's log line).
616
+ if (groups.length > 0 || roleByFile.size > 0 || sortedEdges.length > 0) {
617
+ const present = Object.keys(tools).filter((k) => tools[k]);
618
+ console.log(` [orchestrator] ${present.join('+') || 'none'}: ${roleByFile.size} role(s) · ${groups.length} group(s) · ${sortedEdges.length} edge(s)`);
619
+ }
620
+ // No silent caps (locked): log everything that degraded.
621
+ const degraded = [];
622
+ if (diag.ambiguousAssets.size > 0) {
623
+ degraded.push(`${diag.ambiguousAssets.size} ambiguous asset name(s): ${[...diag.ambiguousAssets].sort().slice(0, 10).join(' · ')}`);
624
+ }
625
+ if (diag.unresolvedShift.size > 0) {
626
+ degraded.push(`${diag.unresolvedShift.size} dynamic/unresolved task dependency(ies): ${[...diag.unresolvedShift].sort().slice(0, 10).join(' · ')}`);
627
+ }
628
+ if (diag.unresolvedTemporal.size > 0) {
629
+ degraded.push(`${diag.unresolvedTemporal.size} unresolved Temporal call(s): ${[...diag.unresolvedTemporal].sort().slice(0, 10).join(' · ')}`);
630
+ }
631
+ if (degraded.length > 0) {
632
+ console.log(` [orchestrator] degraded: ${degraded.join(' · ')} (logged, not silently dropped)`);
633
+ }
634
+ return { groups, edges: sortedEdges, roles };
635
+ }
636
+ function getAnalysis(ctx) {
637
+ let a = ANALYSIS_CACHE.get(ctx);
638
+ if (!a) {
639
+ a = analyzeOrchestrator(ctx);
640
+ ANALYSIS_CACHE.set(ctx, a);
641
+ }
642
+ return a;
643
+ }
644
+ // ---------------------------------------------------------------------------
645
+ // The adapter.
646
+ export const orchestratorAdapter = {
647
+ name: 'orchestrator',
648
+ async detect(ctx) {
649
+ const { base, rootPath } = resolveBase(ctx);
650
+ // Root scan first (a repo whose manifest is at root reports rootPath '').
651
+ const rootMatch = scoreOrchestrator(gatherOrchestratorSignals(base), rootPath);
652
+ if (rootMatch)
653
+ return rootMatch;
654
+ // The orchestration package often lives one dir down (a `pipelines/` |
655
+ // `dagster_project/` | `airflow/` package in a larger repo). Shallow-scan
656
+ // immediate subdirs for a manifest and scope to it. Only when NOT already
657
+ // scoped to a workspace package (the path).
658
+ if (!ctx.packageDir) {
659
+ for (const sub of shallowManifestSubdirs(base)) {
660
+ const m = scoreOrchestrator(gatherOrchestratorSignals(join(base, sub)), sub);
661
+ if (m)
662
+ return m;
663
+ }
664
+ }
665
+ return null;
666
+ },
667
+ // A `dags/` dir + each orchestration container (@dag/@job/@flow) → its own
668
+ // subsystem, authoritative over directory grouping. Fully deterministic
669
+ // (name/path-derived) → no classificationsNeeded.
670
+ async groupingPrior(ctx) {
671
+ return { groups: getAnalysis(ctx).groups };
672
+ },
673
+ // Task-dependency wiring → `calls` (statically resolvable ones; dynamic ones
674
+ // degrade + log). File-id endpoints; the step resolves to modules, drops
675
+ // self-edges, dedupes, 8-verb-validates.
676
+ async syntheticEdges(ctx) {
677
+ return getAnalysis(ctx).edges;
678
+ },
679
+ // Every decorated flow/task/asset/op/dag/activity/workflow → `job`. METADATA;
680
+ // the module's `kind` is unchanged.
681
+ async roleTags(ctx) {
682
+ return getAnalysis(ctx).roles;
683
+ },
684
+ // The hooks READ SOURCE (Python). Declare the paths the diff-driven hosted walk
685
+ // must treat as framework-relevant. Never-store-source holds: parse server-side,
686
+ // persist only the derived groups/edges/roles.
687
+ scansSourcePath(path) {
688
+ return path.endsWith('.py') || path.endsWith('.pyi');
689
+ },
690
+ };
691
+ //# sourceMappingURL=orchestrator.js.map