@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,162 @@
1
+ // the Kamal InfraAdapter (net-new infra coverage, child of ).
2
+ //
3
+ // Kamal (Basecamp/DHH) builds a Docker image from your repo and deploys it to
4
+ // VPS hosts (the Rails-world Heroku-replacement). It's image-referencing, so it
5
+ // reuses the image→source resolver, and self-hosted-container-shaped, so
6
+ // it reuses the docker-compose image-role token map for its accessories.
7
+ //
8
+ // Kind mapping (locked 8-kind InfraModuleKind enum — map onto it, never weaken):
9
+ // the app `service` (built + deployed) → container (your code, an image)
10
+ // accessories, by image role (compose map):
11
+ // postgres/mysql/mongo/redis/… → datastore
12
+ // kafka/rabbitmq/nats/… → queue
13
+ // anything else → container
14
+ //
15
+ // sourceRoots, in precedence order (deterministic, no LLM):
16
+ // 1. `builder.context` — the explicit build-context dir (direct source).
17
+ // 2. `builder.dockerfile` — its dir (direct source).
18
+ // 3. else `image:` → the resolver (image → in-repo Dockerfile build
19
+ // context). A repo-root build context ('' — the common root-Dockerfile
20
+ // monolith) is dropped: per the locked rule we NEVER emit a bare repo-root
21
+ // (it'd swallow siblings), so that code honestly stays "Other (not deployed)."
22
+ // Accessories pull prebuilt images → they run no code of yours → no source root.
23
+ //
24
+ // Edges: app → accessory, verb by accessory kind (datastore→stores-in,
25
+ // queue→publishes, else calls) — the docker-compose precedent.
26
+ //
27
+ // Zone label: "Kamal" (PROVIDER_ZONE_LABEL['kamal'] in assemble/zones.ts).
28
+ // Config-only (reads config/deploy.yml + the repo's Dockerfiles, not app source);
29
+ // the relevance gate catches config/deploy*.yml. Deterministic, no LLM.
30
+ import { existsSync, readFileSync } from 'node:fs';
31
+ import { join, relative } from 'node:path';
32
+ import { findFiles } from '../walk.js';
33
+ import { classifyImage } from '../compose/compose.js';
34
+ import { buildDockerfileIndex, resolveImageToSourceRoots } from '../image-resolve.js';
35
+ import { parseKamalConfig } from './kamal-parse.js';
36
+ // `config/deploy.yml` + env-destination variants (`config/deploy.production.yml`),
37
+ // path-scoped (a bare `deploy.yml` basename is too generic — CI files use it).
38
+ const KAMAL_CONFIG_RE = /(^|\/)config\/deploy(\.[A-Za-z0-9_-]+)?\.ya?ml$/;
39
+ /** The base config (no env suffix) — preferred when several variants exist. */
40
+ const KAMAL_BASE_RE = /(^|\/)config\/deploy\.ya?ml$/;
41
+ // ---------------------------------------------------------------------------
42
+ // Path helpers (repo-relative, normalized) — same idiom as compose.ts/sst.ts.
43
+ function normalizeRoot(p) {
44
+ return p.replace(/\\/g, '/').replace(/^\.(?:\/|$)/, '').replace(/\/+$/, '');
45
+ }
46
+ function dirOfPath(relPath) {
47
+ const n = normalizeRoot(relPath);
48
+ const i = n.lastIndexOf('/');
49
+ return i === -1 ? '' : n.slice(0, i);
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // Node-id helpers (adapter-local; registry prefixes `kamal:` at merge time).
53
+ const appId = (service) => `app:${service}`;
54
+ const accessoryId = (name) => `accessory:${name}`;
55
+ // ---------------------------------------------------------------------------
56
+ // the app's deployment-target source roots.
57
+ /**
58
+ * The repo-relative source roots the Kamal app deploys. builder.context /
59
+ * builder.dockerfile are DIRECT signals (preferred); else the image ref is run
60
+ * through the resolver. Repo-root resolutions are dropped (never a
61
+ * catch-all). Pure (the resolver index is injected). Deterministic + sorted.
62
+ */
63
+ export function kamalAppSourceRoots(config, dockerfileIndex) {
64
+ const roots = new Set();
65
+ // 1 + 2 — direct builder signals (a `.`/repo-root context normalizes to '' and
66
+ // is dropped, falling through to the resolver).
67
+ const context = config.builder?.context ? normalizeRoot(config.builder.context) : '';
68
+ if (context)
69
+ roots.add(context);
70
+ const dfDir = config.builder?.dockerfile ? dirOfPath(config.builder.dockerfile) : '';
71
+ if (dfDir)
72
+ roots.add(dfDir);
73
+ // 3 — no direct signal → resolve the image to an in-repo Dockerfile context.
74
+ if (roots.size === 0 && config.image && dockerfileIndex) {
75
+ for (const r of resolveImageToSourceRoots(config.image, dockerfileIndex))
76
+ roots.add(r);
77
+ }
78
+ return [...roots].sort();
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Pure graph builder. Parsed config + the Dockerfile index injected so it's
82
+ // unit-testable with no real repo (the compose/render split).
83
+ /** Edge verb for an app→accessory dependency, by the accessory's kind. */
84
+ function accessoryVerb(kind) {
85
+ if (kind === 'datastore')
86
+ return 'stores-in';
87
+ if (kind === 'queue')
88
+ return 'publishes';
89
+ return 'calls';
90
+ }
91
+ export function buildKamalGraph(args, root) {
92
+ const { config, configFile, dockerfileIndex } = args;
93
+ const nodes = [];
94
+ const edges = [];
95
+ // The app service → a container (it runs YOUR code, built from a Dockerfile).
96
+ const appName = config.service ?? 'app';
97
+ const id = appId(appName);
98
+ const roots = kamalAppSourceRoots(config, dockerfileIndex);
99
+ nodes.push({
100
+ id,
101
+ label: appName,
102
+ kind: 'container',
103
+ provenance: 'declared',
104
+ metadata: { provider: 'kamal', config: configFile, ...(config.image ? { image: config.image } : {}) },
105
+ ...(roots.length ? { sourceRoots: roots } : {}),
106
+ });
107
+ // Accessories → classified by image role; pulled images run no code → no source.
108
+ for (const acc of config.accessories) {
109
+ const kind = acc.image ? classifyImage(acc.image) : 'container';
110
+ const accId = accessoryId(acc.name);
111
+ nodes.push({
112
+ id: accId,
113
+ label: acc.name,
114
+ kind,
115
+ provenance: 'declared',
116
+ metadata: { provider: 'kamal', config: configFile, ...(acc.image ? { image: acc.image } : {}) },
117
+ });
118
+ edges.push({ source: id, target: accId, kind: accessoryVerb(kind), metadata: { via: 'kamal-accessory' } });
119
+ }
120
+ return { root, adapter: 'kamal', nodes, edges, classificationsNeeded: [] };
121
+ }
122
+ // ---------------------------------------------------------------------------
123
+ // Adapter.
124
+ function findKamalConfigs(repoDir) {
125
+ // Match the REPO-RELATIVE path (not the absolute one) so the `config/deploy…`
126
+ // path-scoping can't be skewed by the repo's checkout location — consistent
127
+ // with extract()'s KAMAL_BASE_RE.test(rel(f)).
128
+ return findFiles(repoDir, (abs) => KAMAL_CONFIG_RE.test((relative(repoDir, abs) || abs).split('\\').join('/')), { maxDepth: 5 });
129
+ }
130
+ export const kamalAdapter = {
131
+ name: 'kamal',
132
+ async detect(repoDir) {
133
+ if (existsSync(join(repoDir, 'config', 'deploy.yml')) || existsSync(join(repoDir, 'config', 'deploy.yaml'))) {
134
+ return true;
135
+ }
136
+ if (findKamalConfigs(repoDir).length > 0)
137
+ return true;
138
+ // Secondary: the `.kamal/` dir (secrets/hooks) — present in a Kamal repo.
139
+ return existsSync(join(repoDir, '.kamal'));
140
+ },
141
+ async extract(repoDir) {
142
+ const rel = (abs) => (relative(repoDir, abs) || abs).split('\\').join('/');
143
+ const files = findKamalConfigs(repoDir);
144
+ if (files.length === 0) {
145
+ // detect() may have fired on `.kamal/` alone — nothing to parse.
146
+ return { root: repoDir, adapter: 'kamal', nodes: [], edges: [], classificationsNeeded: [] };
147
+ }
148
+ // Prefer the base config/deploy.yml; env-destination variants override only
149
+ // servers/env (not the app/accessory topology) — merging them is a v0 deferral.
150
+ const base = files.find((f) => KAMAL_BASE_RE.test(rel(f))) ?? files[0];
151
+ let config;
152
+ try {
153
+ config = parseKamalConfig(readFileSync(base, 'utf8'));
154
+ }
155
+ catch (err) {
156
+ console.warn(` [kamal] skipping unparseable ${rel(base)}: ${err.message}`);
157
+ return { root: repoDir, adapter: 'kamal', nodes: [], edges: [], classificationsNeeded: [] };
158
+ }
159
+ return buildKamalGraph({ config, configFile: rel(base), dockerfileIndex: buildDockerfileIndex(repoDir) }, repoDir);
160
+ },
161
+ };
162
+ //# sourceMappingURL=kamal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kamal.js","sourceRoot":"","sources":["../../../src/infra/kamal/kamal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { InfraAdapter, InfraGraph } from '../types.js';
2
+ import { type WranglerTree } from '../cloudflare/wrangler-parse.js';
3
+ export interface NetlifyConfig {
4
+ /** `[build].base` — where the build runs (the frontend source root). */
5
+ baseDir?: string;
6
+ /** `[build].publish` — the build output dir (metadata only). */
7
+ publishDir?: string;
8
+ /** `[functions].directory` or `[build].functions` — the Functions source dir. */
9
+ functionsDir?: string;
10
+ /** True iff `[[edge_functions]]` blocks are declared. */
11
+ hasEdgeFunctionsDeclared: boolean;
12
+ }
13
+ /** Pure: parsed netlify.toml tree → the typed NetlifyConfig. */
14
+ export declare function parseNetlifyConfig(tree: WranglerTree): NetlifyConfig;
15
+ export interface BuildNetlifyOpts {
16
+ /** Repo-relative dir of the netlify.toml ('' = repo root). */
17
+ configDir: string;
18
+ /** Site node label/id name (package.json name or repo dir). */
19
+ siteName: string;
20
+ /** Does `<configDir>/src` exist? (the site source-root fallback when no base) */
21
+ srcExists: boolean;
22
+ /** Does `<configDir>/netlify/functions` exist? (default Functions dir) */
23
+ defaultFunctionsDirExists: boolean;
24
+ /** Does `<configDir>/netlify/edge-functions` exist? (default Edge Functions dir) */
25
+ edgeDirExists: boolean;
26
+ }
27
+ export declare function buildNetlifyGraph(configs: Array<{
28
+ config: NetlifyConfig;
29
+ opts: BuildNetlifyOpts;
30
+ configFile: string;
31
+ }>, root: string): InfraGraph;
32
+ export declare const netlifyAdapter: InfraAdapter;
33
+ //# sourceMappingURL=netlify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netlify.d.ts","sourceRoot":"","sources":["../../../src/infra/netlify/netlify.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,YAAY,EAAa,UAAU,EAAa,MAAM,aAAa,CAAC;AAClF,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAqCrF,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,wBAAwB,EAAE,OAAO,CAAC;CACnC;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,CAapE;AAgBD,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,0EAA0E;IAC1E,yBAAyB,EAAE,OAAO,CAAC;IACnC,oFAAoF;IACpF,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EACrF,IAAI,EAAE,MAAM,GACX,UAAU,CA2EZ;AA6BD,eAAO,MAAM,cAAc,EAAE,YA2D5B,CAAC"}
@@ -0,0 +1,247 @@
1
+ // the Netlify InfraAdapter.
2
+ //
3
+ // Netlify is the only one of the three AI-builder default frontends (Vercel /
4
+ // Netlify / Cloudflare) we didn't cover — a top-3 frontend host + the default
5
+ // export target for Lovable / Bolt / v0. This surfaces a Netlify deployment from
6
+ // `netlify.toml` (+ the `netlify/functions` / `netlify/edge-functions` conventions):
7
+ // the static site, its Functions, and its Edge Functions.
8
+ //
9
+ // Entirely `declared`/`inferred` provenance — netlify.toml names the build base +
10
+ // functions dir; the default function dirs are a convention (inferred). No LLM
11
+ // (classificationsNeeded: []).
12
+ //
13
+ // Kind mapping (locked 8-kind InfraModuleKind enum — map onto it, never weaken):
14
+ // the site → static-site
15
+ // Netlify Functions (dir) → worker (serverless functions)
16
+ // Netlify Edge Functions (dir) → worker (Deno edge runtime)
17
+ //
18
+ // sourceRoots:
19
+ // * site → `[build].base` (where the build runs = the frontend source). When
20
+ // absent, fall back to `src/` IF it exists (the SPA convention, mirroring the
21
+ // Cloudflare Vite-SPA rule) — never the repo root (no catch-all that swallows
22
+ // the functions). A base that resolves to the repo root yields no source root.
23
+ // * functions / edge functions → their directory (clean per-unit attribution).
24
+ //
25
+ // Zone label: "Netlify" (PROVIDER_ZONE_LABEL['netlify'] in zones.ts).
26
+ //
27
+ // Disambiguation ( precedence seam): the Vercel adapter detects bare
28
+ // `next.config.*`, and Next.js deploys to Netlify too — so a Next-on-Netlify repo
29
+ // trips BOTH adapters. That's fine: netlify.toml is the explicit, stronger Netlify
30
+ // signal and it emits `sourceRoots`, whereas the Vercel `next.config` path emits
31
+ // none today — so code attributes to Netlify by 's source-root rule, and
32
+ // `next.config` alone never outranks `netlify.toml`. (Both still render their zone.)
33
+ import { existsSync, readFileSync } from 'node:fs';
34
+ import { join, relative } from 'node:path';
35
+ import { parseTomlSubset } from '../cloudflare/wrangler-parse.js';
36
+ import { findFiles } from '../walk.js';
37
+ const CONFIG_NAME = 'netlify.toml';
38
+ const DEFAULT_FUNCTIONS_DIR = 'netlify/functions';
39
+ const DEFAULT_EDGE_FUNCTIONS_DIR = 'netlify/edge-functions';
40
+ // ---------------------------------------------------------------------------
41
+ // Path helpers (repo-relative, normalized).
42
+ function dirOf(relPath) {
43
+ const i = relPath.lastIndexOf('/');
44
+ return i === -1 ? '' : relPath.slice(0, i);
45
+ }
46
+ function resolveRel(baseDir, rel) {
47
+ const parts = (baseDir ? baseDir.split('/') : []).concat(rel.split('/'));
48
+ const out = [];
49
+ for (const p of parts) {
50
+ if (p === '' || p === '.')
51
+ continue;
52
+ if (p === '..')
53
+ out.pop();
54
+ else
55
+ out.push(p);
56
+ }
57
+ return out.join('/');
58
+ }
59
+ function str(v) {
60
+ return typeof v === 'string' && v ? v : undefined;
61
+ }
62
+ function rec(v) {
63
+ return v && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
64
+ }
65
+ /** Pure: parsed netlify.toml tree → the typed NetlifyConfig. */
66
+ export function parseNetlifyConfig(tree) {
67
+ const build = rec(tree.build);
68
+ const functions = rec(tree.functions);
69
+ const cfg = {
70
+ hasEdgeFunctionsDeclared: Array.isArray(tree.edge_functions) && tree.edge_functions.length > 0,
71
+ };
72
+ const baseDir = str(build?.base);
73
+ if (baseDir)
74
+ cfg.baseDir = baseDir;
75
+ const publishDir = str(build?.publish);
76
+ if (publishDir)
77
+ cfg.publishDir = publishDir;
78
+ const functionsDir = str(functions?.directory) ?? str(build?.functions);
79
+ if (functionsDir)
80
+ cfg.functionsDir = functionsDir;
81
+ return cfg;
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Node-id helpers (adapter-local; registry prefixes `netlify:`).
85
+ const siteId = (name) => `site:${name}`;
86
+ // Scoped by site name so a monorepo with multiple netlify.toml files (apps/web +
87
+ // apps/admin) doesn't collide its function units onto one shared id (which would
88
+ // silently drop the second site's functions at the first-wins dedup).
89
+ const functionsId = (site) => `function:${site}:functions`;
90
+ const edgeId = (site) => `function:${site}:edge`;
91
+ export function buildNetlifyGraph(configs, root) {
92
+ const nodes = new Map();
93
+ const edges = [];
94
+ const addNode = (n) => {
95
+ if (!nodes.has(n.id))
96
+ nodes.set(n.id, n);
97
+ };
98
+ for (const { config, opts, configFile } of configs) {
99
+ const { configDir, siteName } = opts;
100
+ // --- the static site -----------------------------------------------------
101
+ // sourceRoots: base if set; else src/ if it exists; else none (never repo root).
102
+ let siteRoots = [];
103
+ if (config.baseDir) {
104
+ const r = resolveRel(configDir, config.baseDir);
105
+ if (r)
106
+ siteRoots = [r];
107
+ }
108
+ else if (opts.srcExists) {
109
+ const r = resolveRel(configDir, 'src');
110
+ if (r)
111
+ siteRoots = [r];
112
+ }
113
+ const site = {
114
+ id: siteId(siteName),
115
+ label: siteName,
116
+ kind: 'static-site',
117
+ provenance: 'declared',
118
+ metadata: {
119
+ provider: 'netlify',
120
+ config: configFile,
121
+ ...(config.publishDir ? { publish: config.publishDir } : {}),
122
+ },
123
+ ...(siteRoots.length ? { sourceRoots: siteRoots } : {}),
124
+ };
125
+ addNode(site);
126
+ // --- Netlify Functions ---------------------------------------------------
127
+ // Explicit `[functions].directory` (declared) or the default dir (inferred).
128
+ const funcDir = config.functionsDir ?? (opts.defaultFunctionsDirExists ? DEFAULT_FUNCTIONS_DIR : undefined);
129
+ if (funcDir) {
130
+ const r = resolveRel(configDir, funcDir);
131
+ const id = functionsId(siteName);
132
+ addNode({
133
+ id,
134
+ label: 'Netlify Functions',
135
+ kind: 'worker',
136
+ provenance: config.functionsDir ? 'declared' : 'inferred',
137
+ metadata: { provider: 'netlify', config: configFile, directory: funcDir },
138
+ ...(r ? { sourceRoots: [r] } : {}),
139
+ });
140
+ edges.push({ source: site.id, target: id, kind: 'calls', metadata: { config: configFile } });
141
+ }
142
+ // --- Netlify Edge Functions ----------------------------------------------
143
+ // Declared via `[[edge_functions]]` or the default `netlify/edge-functions` dir.
144
+ if (config.hasEdgeFunctionsDeclared || opts.edgeDirExists) {
145
+ const r = resolveRel(configDir, DEFAULT_EDGE_FUNCTIONS_DIR);
146
+ const id = edgeId(siteName);
147
+ addNode({
148
+ id,
149
+ label: 'Netlify Edge Functions',
150
+ kind: 'worker',
151
+ provenance: config.hasEdgeFunctionsDeclared ? 'declared' : 'inferred',
152
+ metadata: { provider: 'netlify', config: configFile, directory: DEFAULT_EDGE_FUNCTIONS_DIR },
153
+ ...(r ? { sourceRoots: [r] } : {}),
154
+ });
155
+ edges.push({ source: site.id, target: id, kind: 'calls', metadata: { config: configFile } });
156
+ }
157
+ }
158
+ return {
159
+ root,
160
+ adapter: 'netlify',
161
+ nodes: [...nodes.values()],
162
+ edges,
163
+ classificationsNeeded: [],
164
+ };
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // fs helpers for extract().
168
+ function findNetlifyConfigs(repoDir) {
169
+ return findFiles(repoDir, (_abs, e) => e.name === CONFIG_NAME, { maxDepth: 5 });
170
+ }
171
+ /** Derive a site name from the configDir's package.json, else its basename, else repo. */
172
+ function deriveSiteName(repoDir, configDir) {
173
+ const dir = configDir ? join(repoDir, configDir) : repoDir;
174
+ try {
175
+ const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
176
+ if (pkg.name)
177
+ return pkg.name.replace(/^@[^/]+\//, '');
178
+ }
179
+ catch {
180
+ /* no/invalid package.json — fall through */
181
+ }
182
+ const base = (configDir ? configDir.split('/').pop() : repoDir.split('/').filter(Boolean).pop()) ?? 'site';
183
+ return base;
184
+ }
185
+ function dirExists(repoDir, configDir, sub) {
186
+ return existsSync(join(repoDir, configDir, sub));
187
+ }
188
+ // ---------------------------------------------------------------------------
189
+ // Adapter.
190
+ export const netlifyAdapter = {
191
+ name: 'netlify',
192
+ async detect(repoDir) {
193
+ if (findNetlifyConfigs(repoDir).length > 0)
194
+ return true;
195
+ // Secondary: the unambiguous Netlify function-dir conventions (a repo can
196
+ // ship functions without a netlify.toml). `_redirects` is NOT used — it's
197
+ // shared with Cloudflare Pages, so it's not a Netlify-specific signal.
198
+ return (existsSync(join(repoDir, DEFAULT_FUNCTIONS_DIR)) ||
199
+ existsSync(join(repoDir, DEFAULT_EDGE_FUNCTIONS_DIR)));
200
+ },
201
+ async extract(repoDir) {
202
+ const files = findNetlifyConfigs(repoDir);
203
+ const configs = [];
204
+ if (files.length > 0) {
205
+ for (const file of files) {
206
+ const configFile = (relative(repoDir, file) || file).split('\\').join('/');
207
+ const configDir = dirOf(configFile);
208
+ let config;
209
+ try {
210
+ config = parseNetlifyConfig(parseTomlSubset(readFileSync(file, 'utf8')));
211
+ }
212
+ catch (err) {
213
+ console.warn(` [netlify] skipping unparseable ${file}: ${err.message}`);
214
+ continue;
215
+ }
216
+ configs.push({
217
+ config,
218
+ configFile,
219
+ opts: {
220
+ configDir,
221
+ siteName: deriveSiteName(repoDir, configDir),
222
+ srcExists: dirExists(repoDir, configDir, 'src'),
223
+ defaultFunctionsDirExists: dirExists(repoDir, configDir, DEFAULT_FUNCTIONS_DIR),
224
+ edgeDirExists: dirExists(repoDir, configDir, DEFAULT_EDGE_FUNCTIONS_DIR),
225
+ },
226
+ });
227
+ }
228
+ }
229
+ else {
230
+ // No netlify.toml, but a function-dir convention tripped detect() — model a
231
+ // root site + the functions it found.
232
+ configs.push({
233
+ config: { hasEdgeFunctionsDeclared: false },
234
+ configFile: '(netlify convention)',
235
+ opts: {
236
+ configDir: '',
237
+ siteName: deriveSiteName(repoDir, ''),
238
+ srcExists: dirExists(repoDir, '', 'src'),
239
+ defaultFunctionsDirExists: dirExists(repoDir, '', DEFAULT_FUNCTIONS_DIR),
240
+ edgeDirExists: dirExists(repoDir, '', DEFAULT_EDGE_FUNCTIONS_DIR),
241
+ },
242
+ });
243
+ }
244
+ return buildNetlifyGraph(configs, repoDir);
245
+ },
246
+ };
247
+ //# sourceMappingURL=netlify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netlify.js","sourceRoot":"","sources":["../../../src/infra/netlify/netlify.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,iFAAiF;AACjF,qFAAqF;AACrF,0DAA0D;AAC1D,EAAE;AACF,kFAAkF;AAClF,+EAA+E;AAC/E,+BAA+B;AAC/B,EAAE;AACF,iFAAiF;AACjF,iDAAiD;AACjD,oEAAoE;AACpE,iEAAiE;AACjE,EAAE;AACF,eAAe;AACf,gFAAgF;AAChF,kFAAkF;AAClF,kFAAkF;AAClF,mFAAmF;AACnF,iFAAiF;AACjF,EAAE;AACF,sEAAsE;AACtE,EAAE;AACF,qEAAqE;AACrE,kFAAkF;AAClF,mFAAmF;AACnF,iFAAiF;AACjF,yEAAyE;AACzE,qFAAqF;AAErF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAqB,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,GAAG,cAAc,CAAC;AACnC,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAClD,MAAM,0BAA0B,GAAG,wBAAwB,CAAC;AAE5D,8EAA8E;AAC9E,4CAA4C;AAE5C,SAAS,KAAK,CAAC,OAAe;IAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,GAAW;IAC9C,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACzE,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG;YAAE,SAAS;QACpC,IAAI,CAAC,KAAK,IAAI;YAAE,GAAG,CAAC,GAAG,EAAE,CAAC;;YACrB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,GAAG,CAAC,CAAU;IACrB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;AAED,SAAS,GAAG,CAAC,CAAU;IACrB,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAkB,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3F,CAAC;AAgBD,gEAAgE;AAChE,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,GAAG,GAAkB;QACzB,wBAAwB,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;KAC/F,CAAC;IACF,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,IAAI,OAAO;QAAE,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;IACnC,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACvC,IAAI,UAAU;QAAE,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC;IAC5C,MAAM,YAAY,GAAG,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACxE,IAAI,YAAY;QAAE,GAAG,CAAC,YAAY,GAAG,YAAY,CAAC;IAClD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,iEAAiE;AAEjE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC;AAChD,iFAAiF;AACjF,iFAAiF;AACjF,sEAAsE;AACtE,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,YAAY,IAAI,YAAY,CAAC;AACnE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,YAAY,IAAI,OAAO,CAAC;AAmBzD,MAAM,UAAU,iBAAiB,CAC/B,OAAqF,EACrF,IAAY;IAEZ,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC3C,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,CAAC,CAAY,EAAE,EAAE;QAC/B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,OAAO,EAAE,CAAC;QACnD,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;QAErC,4EAA4E;QAC5E,iFAAiF;QACjF,IAAI,SAAS,GAAa,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC;gBAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YACvC,IAAI,CAAC;gBAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;QACD,MAAM,IAAI,GAAc;YACtB,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC;YACpB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,aAAa;YACnB,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE;gBACR,QAAQ,EAAE,SAAS;gBACnB,MAAM,EAAE,UAAU;gBAClB,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7D;YACD,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxD,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC;QAEd,4EAA4E;QAC5E,6EAA6E;QAC7E,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC5G,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACzC,MAAM,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;YACjC,OAAO,CAAC;gBACN,EAAE;gBACF,KAAK,EAAE,mBAAmB;gBAC1B,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;gBACzD,QAAQ,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE;gBACzE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnC,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;QAC/F,CAAC;QAED,4EAA4E;QAC5E,iFAAiF;QACjF,IAAI,MAAM,CAAC,wBAAwB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC1D,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC;YAC5D,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC5B,OAAO,CAAC;gBACN,EAAE;gBACF,KAAK,EAAE,wBAAwB;gBAC/B,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;gBACrE,QAAQ,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,0BAA0B,EAAE;gBAC5F,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnC,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,SAAS;QAClB,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAC1B,KAAK;QACL,qBAAqB,EAAE,EAAE;KAC1B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,4BAA4B;AAE5B,SAAS,kBAAkB,CAAC,OAAe;IACzC,OAAO,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;AAClF,CAAC;AAED,0FAA0F;AAC1F,SAAS,cAAc,CAAC,OAAe,EAAE,SAAiB;IACxD,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAsB,CAAC;QAC7F,IAAI,GAAG,CAAC,IAAI;YAAE,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,4CAA4C;IAC9C,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC;IAC3G,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,OAAe,EAAE,SAAiB,EAAE,GAAW;IAChE,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,WAAW;AAEX,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,IAAI,EAAE,SAAS;IAEf,KAAK,CAAC,MAAM,CAAC,OAAe;QAC1B,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACxD,0EAA0E;QAC1E,0EAA0E;QAC1E,uEAAuE;QACvE,OAAO,CACL,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;YAChD,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC,CACtD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAiF,EAAE,CAAC;QAEjG,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,UAAU,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC3E,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;gBACpC,IAAI,MAAqB,CAAC;gBAC1B,IAAI,CAAC;oBACH,MAAM,GAAG,kBAAkB,CAAC,eAAe,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC3E,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,IAAI,CAAC,oCAAoC,IAAI,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;oBACpF,SAAS;gBACX,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM;oBACN,UAAU;oBACV,IAAI,EAAE;wBACJ,SAAS;wBACT,QAAQ,EAAE,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC;wBAC5C,SAAS,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC;wBAC/C,yBAAyB,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,qBAAqB,CAAC;wBAC/E,aAAa,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,0BAA0B,CAAC;qBACzE;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;aAAM,CAAC;YACN,4EAA4E;YAC5E,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC;gBACX,MAAM,EAAE,EAAE,wBAAwB,EAAE,KAAK,EAAE;gBAC3C,UAAU,EAAE,sBAAsB;gBAClC,IAAI,EAAE;oBACJ,SAAS,EAAE,EAAE;oBACb,QAAQ,EAAE,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;oBACrC,SAAS,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC;oBACxC,yBAAyB,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,EAAE,qBAAqB,CAAC;oBACxE,aAAa,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,EAAE,0BAA0B,CAAC;iBAClE;aACF,CAAC,CAAC;QACL,CAAC;QAED,OAAO,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { InfraAdapter } from '../types.js';
2
+ export declare const opentofuAdapter: InfraAdapter;
3
+ //# sourceMappingURL=opentofu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opentofu.d.ts","sourceRoot":"","sources":["../../../src/infra/opentofu/opentofu.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AAuE5D,eAAO,MAAM,eAAe,EAAE,YA0E7B,CAAC"}
@@ -0,0 +1,157 @@
1
+ // the OpenTofu InfraAdapter.
2
+ //
3
+ // OpenTofu is a Terraform fork with identical HCL syntax and the same provider
4
+ // model (registry.terraform.io namespace is shared; `tofu` CLI is a drop-in
5
+ // replacement). This adapter REUSES buildTerraformGraph and parseHcl entirely —
6
+ // no new taxonomy, no new classify provider strings. The LLM cache entries
7
+ // created by the Terraform adapter are reused verbatim here (the
8
+ // `resource_type_classifications` row for `provider = 'terraform/aws'` applies
9
+ // to both `*.tf` and `*.tofu` files).
10
+ //
11
+ // DETECT GUARD — the critical contract:
12
+ // detect() returns TRUE only when an OpenTofu-SPECIFIC signal exists: a `*.tofu`
13
+ // file, a `*.tofu.json` file, or a `tofu.tfvars` file. A repo with ONLY `*.tf`
14
+ // files falls through to the Terraform adapter — this adapter NEVER fires on
15
+ // plain Terraform repos, preventing double-counting of nodes.
16
+ //
17
+ // FILE SCOPE — extract() collects BOTH `*.tofu` AND `*.tf` files, because an
18
+ // OpenTofu repo may mix both extensions (providers declared in `providers.tf`,
19
+ // resources in `main.tofu` is a common migration pattern). The Terraform adapter
20
+ // is NOT run additionally on OpenTofu repos — the registry registers the more
21
+ // specific adapter first, and a repo matched by OpenTofu is NOT re-matched by
22
+ // Terraform.
23
+ //
24
+ // DEFERRED: `*.tofu.json` parsing. parseHcl is an HCL text parser; JSON-HCL is
25
+ // a structurally different encoding (same semantics, different surface syntax).
26
+ // detect() recognises `*.tofu.json` as an OpenTofu signal so the repo is
27
+ // claimed by this adapter, but the JSON bodies are skipped in extract() with a
28
+ // console.warn. JSON-HCL support is tracked under .
29
+ import { readFileSync } from 'node:fs';
30
+ import { relative } from 'node:path';
31
+ import { walkRepo } from '../walk.js';
32
+ import { buildDockerfileIndex } from '../image-resolve.js';
33
+ import { parseHcl } from '../terraform/hcl-parse.js';
34
+ import { buildTerraformGraph } from '../terraform/terraform.js';
35
+ /** Repo-relative dir of a file path ('' = repo root). */
36
+ function dirOfRel(repoDir, file) {
37
+ const rel = (relative(repoDir, file) || file).split('\\').join('/');
38
+ const i = rel.lastIndexOf('/');
39
+ return i === -1 ? '' : rel.slice(0, i);
40
+ }
41
+ // Same skip-set as the Terraform adapter (OpenTofu shares its `.terraform` noise dir).
42
+ const TOFU_SKIP_DIRS = ['node_modules', '.git', 'dist', '.wrangler', '.terraform', '.next', 'build'];
43
+ /**
44
+ * Walk the repo collecting OpenTofu-relevant files (bounded, skips noise dirs).
45
+ * Returns both the files to parse (hclFiles) and the OpenTofu-specific signals
46
+ * (tofuJsonFiles, tofuTfvarsFiles) separately so detect() can check the latter
47
+ * without needing any HCL content.
48
+ */
49
+ function scanTofuFiles(repoDir) {
50
+ const hclFiles = [];
51
+ const tofuJsonFiles = [];
52
+ const tofuTfvarsFiles = [];
53
+ walkRepo(repoDir, {
54
+ skipDirs: TOFU_SKIP_DIRS,
55
+ onFile: (abs, e) => {
56
+ // ORDER IS LOAD-BEARING — compound suffixes (.tofu.json, .tofu.tfvars) must
57
+ // be checked BEFORE the bare-suffix arms (.tofu, .tf). A name like
58
+ // "override.tofu.json" ends with both ".tofu.json" AND ".tofu"; testing
59
+ // the bare arm first would mis-classify it as HCL. Do NOT reorder.
60
+ if (e.name.endsWith('.tofu.json')) {
61
+ // JSON-HCL: OpenTofu-specific signal; parsing deferred (see module comment).
62
+ tofuJsonFiles.push(abs);
63
+ }
64
+ else if (e.name === 'tofu.tfvars' || e.name.endsWith('.tofu.tfvars')) {
65
+ // tofu.tfvars / *.tofu.tfvars: OpenTofu-native variables file.
66
+ tofuTfvarsFiles.push(abs);
67
+ }
68
+ else if (e.name.endsWith('.tofu')) {
69
+ // `.tofu` = OpenTofu-native HCL file; also counts as HCL for parsing.
70
+ // Checked AFTER the compound-suffix arms above (see ORDER note).
71
+ hclFiles.push(abs);
72
+ }
73
+ else if (e.name.endsWith('.tf')) {
74
+ // Plain Terraform HCL: collect for parsing when the repo has other
75
+ // OpenTofu signals (so we get the full topology, not just *.tofu files).
76
+ hclFiles.push(abs);
77
+ }
78
+ },
79
+ });
80
+ hclFiles.sort();
81
+ return { hclFiles, tofuJsonFiles, tofuTfvarsFiles };
82
+ }
83
+ /** True iff the repo has at least one OpenTofu-SPECIFIC file. */
84
+ function hasOpentofuSignal(scan) {
85
+ const tofuHcl = scan.hclFiles.some((f) => f.endsWith('.tofu'));
86
+ return tofuHcl || scan.tofuJsonFiles.length > 0 || scan.tofuTfvarsFiles.length > 0;
87
+ }
88
+ export const opentofuAdapter = {
89
+ name: 'opentofu',
90
+ /**
91
+ * Detect an OpenTofu repo by the presence of at least one OpenTofu-specific
92
+ * file: `*.tofu`, `*.tofu.json`, or `tofu.tfvars` / `*.tofu.tfvars`.
93
+ *
94
+ * A repo with ONLY `*.tf` files is NOT detected here — that is the Terraform
95
+ * adapter's jurisdiction. This is the primary regression guard: registering
96
+ * both adapters and having only `*.tf` files will fire Terraform, never
97
+ * OpenTofu.
98
+ */
99
+ async detect(repoDir) {
100
+ const scan = scanTofuFiles(repoDir);
101
+ return hasOpentofuSignal(scan);
102
+ },
103
+ /**
104
+ * Extract the infrastructure graph from an OpenTofu repo.
105
+ *
106
+ * Parses all `*.tofu` + `*.tf` files (which may coexist in the same OpenTofu
107
+ * project) via parseHcl, then delegates to buildTerraformGraph for the
108
+ * identical resource-type taxonomy, heuristic kind assignment, and cross-
109
+ * resource edge detection. Returns the graph with `adapter: 'opentofu'` so
110
+ * the registry prefixes nodes as `opentofu:*` (distinct from `terraform:*`).
111
+ *
112
+ * `*.tofu.json` files are noted but skipped — JSON-HCL parsing is deferred.
113
+ */
114
+ async extract(repoDir) {
115
+ const scan = scanTofuFiles(repoDir);
116
+ // Warn about deferred JSON-HCL files so operators know topology may be
117
+ // incomplete (not an error — the HCL files still yield a useful graph).
118
+ // JSON-HCL is a structurally different encoding that needs its own parser;
119
+ // support is tracked under .
120
+ if (scan.tofuJsonFiles.length > 0) {
121
+ const names = scan.tofuJsonFiles.map((f) => relative(repoDir, f)).join(', ');
122
+ console.warn(` [opentofu] skipping ${scan.tofuJsonFiles.length} .tofu.json file(s) — ` +
123
+ `JSON-HCL parsing is not yet supported (tracked: ). ` +
124
+ `Skipped: ${names}`);
125
+ }
126
+ const blocks = [];
127
+ for (const file of scan.hclFiles) {
128
+ const dir = dirOfRel(repoDir, file);
129
+ try {
130
+ for (const b of parseHcl(readFileSync(file, 'utf8'))) {
131
+ b.dir = dir; // module dir for source-path resolution
132
+ blocks.push(b);
133
+ }
134
+ }
135
+ catch (err) {
136
+ console.warn(` [opentofu] skipping unparseable ${relative(repoDir, file)}: ${err.message}`);
137
+ }
138
+ }
139
+ // buildTerraformGraph is pure: it takes blocks + root and returns an
140
+ // InfraGraph. We override `adapter` so downstream namespacing is correct.
141
+ //
142
+ // MIXED-REPO NAMESPACING (intended behaviour): a repo that contains both
143
+ // `*.tf` and `*.tofu` files will be claimed by this adapter (detect-positive)
144
+ // AND also by the Terraform adapter if both are registered and run
145
+ // independently. In that scenario, shared `*.tf` resources appear under
146
+ // BOTH `opentofu:resource:*` and `terraform:resource:*` ids — they are NOT
147
+ // deduplicated at the adapter level. This is intentional: namespacing by
148
+ // adapter is the design (pure-TF repos get `terraform:*` ids; this adapter
149
+ // owns the full graph for OpenTofu repos including any co-located `.tf`
150
+ // files). Mixed repos are rare in practice; deduplication is the
151
+ // orchestrator's concern if it ever runs both adapters on the same repo.
152
+ // same Dockerfile-index resolver path as Terraform.
153
+ const graph = buildTerraformGraph(blocks, repoDir, buildDockerfileIndex(repoDir));
154
+ return { ...graph, adapter: 'opentofu' };
155
+ },
156
+ };
157
+ //# sourceMappingURL=opentofu.js.map