@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,739 @@
1
+ // Stage C — monorepo workspace/package boundary detection (decides
2
+ // manifest-first, Louvain fallback).
3
+ //
4
+ // Declared package boundaries are the ONE structural signal in a repo that is
5
+ // authored, not inferred: a workspace glob in package.json / pnpm-workspace.yaml
6
+ // / lerna.json is the developer SAYING "this is a unit". Stage C makes those
7
+ // boundaries first-class in clustering — Louvain runs only WITHIN a package
8
+ // (small graphs, stable partitions), and the boundary between packages is
9
+ // deterministic from manifests rather than statistical. This module is the
10
+ // detection half: fs-reading, pure-output, NEVER throws (a malformed manifest
11
+ // must degrade to "fewer boundaries", never fail the ingest — worst case is a
12
+ // single-package layout, i.e. exactly the pre-Stage-C behavior).
13
+ //
14
+ // Detection rules (manifest-first, ):
15
+ // DECLARED — root package.json `workspaces` (array or `{packages}` object
16
+ // form), pnpm-workspace.yaml `packages:`, lerna.json `packages`.
17
+ // Globs are expanded against the package.json dirs found by the
18
+ // fs walk with a minimal matcher (literal, `dir/*`, `dir/**`);
19
+ // `!`-negations are IGNORED (rare in the wild, and ignoring a
20
+ // negation only yields an extra boundary — the override map's
21
+ // `drop` rules handle any noise it causes).
22
+ // INFERRED — any non-root package.json with a `"name"` field counts as a
23
+ // package even when not declared. This covers undeclared
24
+ // multi-package repos (this very repo's `worker/` is exactly
25
+ // that). Tradeoff: a test fixture containing a named
26
+ // package.json creates a noise boundary — accepted, because the
27
+ // override map's `drop` handles it and missing a real boundary
28
+ // is worse (it lets Louvain blend two packages).
29
+ //
30
+ // Membership is by LONGEST matching package root (a file under
31
+ // `worker/container/` belongs to that package, not `worker/`), with the repo
32
+ // root ('' scope) as the universal fallback.
33
+ //
34
+ // (monorepo-aware subsystem partition) extends Stage C with three
35
+ // PURE, DETERMINISTIC, ZERO-LLM signals computed off the same manifests:
36
+ // - turbo.json / nx.json recognition. Turborepo reuses the package manager's
37
+ // workspaces, so turbo.json adds no globs (named packages are already found
38
+ // INFERRED) — it's recognized only as a layout-trigger manifest. Nx declares
39
+ // its package dirs via `workspaceLayout` (appsDir/libsDir, default apps/libs),
40
+ // so nx.json contributes `<appsDir>/*` + `<libsDir>/*` DECLARED globs.
41
+ // - ROLE per package (app / lib / tooling) — descriptive metadata threaded onto
42
+ // the package's subsystem so the canvas can label the top-level box. NOT a
43
+ // Module kind (the kind enum stays locked).
44
+ // - declaredDeps: the internal package→package dependency graph, from each
45
+ // package.json's deps that NAME a workspace sibling (a workspace dep shadows
46
+ // the registry, so a name match IS the dep). The cluster layer turns these
47
+ // into cross-package `calls` edges (filling gaps the import graph alone
48
+ // missed). A tsconfig `references` / path-alias dep source is a possible
49
+ // follow-up — deliberately OUT of this slice.
50
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
51
+ import { join } from 'node:path';
52
+ import { parse as parseYaml } from 'yaml';
53
+ import { parse as parseToml } from 'smol-toml';
54
+ import { compileMatchers, slugify } from './overrides.js';
55
+ import { EXCLUDE_DIRS, PYTHON_EXCLUDE_DIRS } from '../graph/file-graph.js';
56
+ import { normalizePyName, requirementName } from '../graph/python-manifest.js';
57
+ // The manifests that can DEFINE workspace boundaries. A diff touching any of
58
+ // these is the container's trigger to recompute the layout (and invalidateAll()
59
+ // the partition cache) — everything else leaves the layout memoizable across
60
+ // checkpoints. pnpm only reads `pnpm-workspace.yaml` (no `.yml` variant).
61
+ // adds turbo.json / nx.json (monorepo layout signals).
62
+ const WORKSPACE_MANIFEST_BASENAMES = new Set([
63
+ 'package.json',
64
+ 'pnpm-workspace.yaml',
65
+ 'lerna.json',
66
+ 'turbo.json',
67
+ 'nx.json',
68
+ // Python package/workspace manifests. A diff touching any of these
69
+ // moves Python package boundaries → recompute the layout, exactly like the JS
70
+ // manifests above.
71
+ 'pyproject.toml',
72
+ 'setup.py',
73
+ 'setup.cfg',
74
+ ]);
75
+ /** Does a repo-relative path name a workspace-layout-defining manifest? */
76
+ export function isWorkspaceManifestPath(path) {
77
+ const base = path.split('/').pop() ?? path;
78
+ return WORKSPACE_MANIFEST_BASENAMES.has(base);
79
+ }
80
+ // Walk depth cap — pathological repos (vendored trees, generated dirs that
81
+ // dodge EXCLUDE_DIRS) must not turn detection into a full-disk crawl. Real
82
+ // workspace packages live 1-3 levels deep; 8 is generous.
83
+ const MAX_WALK_DEPTH = 8;
84
+ const EXCLUDE_SET = new Set(EXCLUDE_DIRS);
85
+ // `.js` entry points (typically a build artifact path like `dist/index.js`)
86
+ // almost never exist in the SOURCE graph the extractor builds — the TS sibling
87
+ // next to them often does. Map each transpiled extension to the source
88
+ // extensions it could have come from.
89
+ const TS_VARIANTS = [
90
+ ['.js', ['.ts', '.tsx']],
91
+ ['.mjs', ['.mts']],
92
+ ['.cjs', ['.cts']],
93
+ ];
94
+ /**
95
+ * Detect the workspace/package layout of a checked-out repo. fs-reading;
96
+ * NEVER throws — any unreadable/malformed manifest source is skipped, and the
97
+ * worst case is a single-package (root-only) layout, which downstream treats
98
+ * as "no Stage-C behavior" (status quo ante).
99
+ */
100
+ export function detectWorkspaceLayout(rootDir) {
101
+ // 1. Candidate package dirs: every non-root dir holding a package.json,
102
+ // pruning EXCLUDE_DIRS + dot-directories — MUST mirror file-graph.ts's
103
+ // isSourceFilePath policy, or a package boundary could claim files the
104
+ // graph will never contain (or vice versa). Sorted for determinism.
105
+ const candidates = findPackageJsonDirs(rootDir);
106
+ // 2. Declared workspace globs, from package.json/pnpm/lerna + nx.json's
107
+ // workspaceLayout. nxLayout also carries the apps/libs dir
108
+ // convention used for role classification below.
109
+ const rootManifest = readJsonObject(join(rootDir, 'package.json'));
110
+ const nxLayout = readNxLayout(rootDir);
111
+ const matchesDeclared = compileMatchers([
112
+ ...collectDeclaredGlobs(rootDir, rootManifest, nxLayout),
113
+ // uv workspace member globs (`[tool.uv.workspace].members`) are the
114
+ // Python analogue of package.json `workspaces`.
115
+ ...collectPythonDeclaredGlobs(rootDir),
116
+ ]);
117
+ // 3. Materialize packages. The root scope is ALWAYS present (and always
118
+ // declared — the repo root is a boundary by definition). Slugs dedup
119
+ // through a used-set in root-sorted order so they're stable across runs.
120
+ const usedSlugs = new Set();
121
+ const reserveSlug = (base) => {
122
+ let slug = base;
123
+ let i = 2;
124
+ while (usedSlugs.has(slug))
125
+ slug = `${base}-${i++}`;
126
+ usedSlugs.add(slug);
127
+ return slug;
128
+ };
129
+ const packages = [];
130
+ // keep each package's parsed package.json for the declaredDeps
131
+ // second pass (it needs the full name→package map, so it can't run inline).
132
+ const manifestByRoot = new Map();
133
+ const rootName = nameOf(rootManifest);
134
+ manifestByRoot.set('', rootManifest);
135
+ packages.push({
136
+ root: '',
137
+ name: rootName,
138
+ slug: reserveSlug(slugFor(rootName, 'root')),
139
+ entryFileIds: entryCandidates('', rootManifest),
140
+ declared: true,
141
+ role: roleOf('', rootManifest, nxLayout),
142
+ declaredDeps: [], // root scope groups by directory, not as a package — no edges
143
+ });
144
+ for (const dir of candidates) {
145
+ const manifest = readJsonObject(join(rootDir, dir, 'package.json'));
146
+ const name = nameOf(manifest);
147
+ const declared = matchesDeclared(dir);
148
+ // A boundary needs a reason to exist: either someone DECLARED it, or the
149
+ // package names itself (inferred). An unnamed, undeclared package.json
150
+ // (e.g. `{"type":"module"}` markers, fixtures) is not a boundary.
151
+ if (!declared && name === null)
152
+ continue;
153
+ manifestByRoot.set(dir, manifest);
154
+ packages.push({
155
+ root: dir,
156
+ name,
157
+ slug: reserveSlug(slugFor(name, dir.split('/').pop() ?? dir)),
158
+ entryFileIds: entryCandidates(dir, manifest),
159
+ declared,
160
+ role: roleOf(dir, manifest, nxLayout),
161
+ declaredDeps: [], // filled by the second pass once nameToPackage exists
162
+ });
163
+ }
164
+ // 3b. Python packages. Every non-root dir with a pyproject.toml /
165
+ // setup.py / setup.cfg that either NAMES a package (PEP 621 `[project].name`
166
+ // or `[tool.poetry].name`) or is a DECLARED uv workspace member. Merged into
167
+ // the SAME packages list so packageOf partitions Python + polyglot repos
168
+ // uniformly. A dir that is ALREADY a JS package (a polyglot single package
169
+ // with both manifests) keeps its one boundary (skip — first wins).
170
+ const seenRoots = new Set(packages.map((p) => p.root));
171
+ const pyManifestByRoot = new Map();
172
+ for (const dir of findPythonPackageDirs(rootDir)) {
173
+ if (seenRoots.has(dir))
174
+ continue;
175
+ const py = readPyproject(join(rootDir, dir));
176
+ const name = py ? pyPackageName(py) : null;
177
+ const declared = matchesDeclared(dir);
178
+ if (!declared && name === null)
179
+ continue;
180
+ seenRoots.add(dir);
181
+ if (py)
182
+ pyManifestByRoot.set(dir, py);
183
+ packages.push({
184
+ root: dir,
185
+ name,
186
+ slug: reserveSlug(slugFor(name, dir.split('/').pop() ?? dir)),
187
+ entryFileIds: pythonEntryCandidates(dir, name),
188
+ declared,
189
+ role: pythonRoleOf(dir, py),
190
+ declaredDeps: [], // filled by the second pass once nameToPackage exists
191
+ });
192
+ }
193
+ // 4. Longest-prefix membership. Walking the file's own dir chain upward
194
+ // visits prefixes longest-first, so the first hit IS the longest match;
195
+ // nested packages (worker/container under worker) resolve correctly.
196
+ const byRoot = new Map(packages.map((p) => [p.root, p]));
197
+ const rootScope = packages[0];
198
+ const packageOf = (fileId) => {
199
+ let prefix = fileId;
200
+ for (;;) {
201
+ const cut = prefix.lastIndexOf('/');
202
+ if (cut < 0)
203
+ return rootScope;
204
+ prefix = prefix.slice(0, cut);
205
+ const hit = byRoot.get(prefix);
206
+ if (hit)
207
+ return hit;
208
+ }
209
+ };
210
+ const nameToPackage = new Map();
211
+ for (const p of packages) {
212
+ if (p.root === '' || p.name === null)
213
+ continue;
214
+ if (!nameToPackage.has(p.name))
215
+ nameToPackage.set(p.name, p);
216
+ }
217
+ // 5. cross-package dependency graph (second pass — needs the full
218
+ // nameToPackage map). Each non-root package's declaredDeps are the sibling
219
+ // roots it depends on, from its package.json deps (by sibling name).
220
+ const knownRoots = new Set(packages.map((p) => p.root));
221
+ // Roots that came from a PYTHON manifest — used to scope Python name-matching to
222
+ // Python siblings, so a same-named npm package can't shadow a Python dep.
223
+ const pyRoots = new Set(pyManifestByRoot.keys());
224
+ for (const pkg of packages) {
225
+ if (pkg.root === '')
226
+ continue;
227
+ const js = computeDeclaredDeps(pkg.root, manifestByRoot.get(pkg.root) ?? null, nameToPackage);
228
+ // a Python package's sibling deps — a dep NAMING a sibling (PEP 621 /
229
+ // poetry, normalized) or a poetry `{path=…}` dep resolving to a sibling root.
230
+ const py = computePythonDeclaredDeps(pkg.root, pyManifestByRoot.get(pkg.root) ?? null, nameToPackage, knownRoots, pyRoots);
231
+ pkg.declaredDeps = [...new Set([...js, ...py])].sort();
232
+ }
233
+ // 6. Observability (no silent caps): for a real multi-package layout, log the
234
+ // packages found, their roles, and any cross-package deps — so a monorepo's
235
+ // partition is never silently mis-derived. Every package gets a role
236
+ // deterministically (lib is the fallback), so there is no "unclassified"
237
+ // bucket; the full breakdown is logged instead.
238
+ if (packages.length > 1) {
239
+ const summary = packages
240
+ .filter((p) => p.root !== '')
241
+ .map((p) => `${p.root}=${p.role}${p.declaredDeps.length ? ` →[${p.declaredDeps.join(', ')}]` : ''}`)
242
+ .join(' ');
243
+ console.log(` [workspaces] ${packages.length - 1} package(s): ${summary}`);
244
+ }
245
+ return { packages, packageOf, nameToPackage };
246
+ }
247
+ // ---------------------------------------------------------------------------
248
+ // fs walk
249
+ /** Sorted repo-relative posix dirs (non-root) that contain a package.json. */
250
+ function findPackageJsonDirs(rootDir) {
251
+ const out = [];
252
+ const walk = (rel, depth) => {
253
+ if (depth > MAX_WALK_DEPTH)
254
+ return;
255
+ let entries;
256
+ try {
257
+ entries = readdirSync(rel === '' ? rootDir : join(rootDir, rel), { withFileTypes: true });
258
+ }
259
+ catch {
260
+ return; // unreadable dir → skip subtree, never throw
261
+ }
262
+ // Sort for cross-platform determinism (readdir order is fs-dependent).
263
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
264
+ for (const ent of entries) {
265
+ if (ent.isDirectory()) {
266
+ // Mirror isSourceFilePath: dot-dirs + EXCLUDE_DIRS segments are never
267
+ // part of the graph, so a package.json inside them is not a boundary.
268
+ // (Symlinked dirs fail isDirectory() and are skipped — no cycles.)
269
+ if (ent.name.startsWith('.') || EXCLUDE_SET.has(ent.name))
270
+ continue;
271
+ walk(rel === '' ? ent.name : `${rel}/${ent.name}`, depth + 1);
272
+ }
273
+ else if (rel !== '' && ent.isFile() && ent.name === 'package.json') {
274
+ out.push(rel);
275
+ }
276
+ }
277
+ };
278
+ walk('', 0);
279
+ return out.sort();
280
+ }
281
+ // ---------------------------------------------------------------------------
282
+ // Declared-glob collection
283
+ /**
284
+ * Normalized declared workspace globs from root package.json `workspaces`
285
+ * (array or `{packages}`), pnpm-workspace.yaml `packages:`, lerna.json
286
+ * `packages`, and nx.json `workspaceLayout` (apps/libs dirs). Each
287
+ * parse failure skips that SOURCE only.
288
+ */
289
+ function collectDeclaredGlobs(rootDir, rootManifest, nx) {
290
+ const globs = [];
291
+ const pushAll = (v) => {
292
+ if (!Array.isArray(v))
293
+ return;
294
+ for (const raw of v) {
295
+ if (typeof raw !== 'string')
296
+ continue;
297
+ let g = raw.trim().replace(/\\/g, '/');
298
+ if (g.startsWith('!'))
299
+ continue; // negations ignored (see header)
300
+ if (g.startsWith('./'))
301
+ g = g.slice(2);
302
+ g = g.replace(/\/+$/, '');
303
+ if (g)
304
+ globs.push(g);
305
+ }
306
+ };
307
+ // package.json: `"workspaces": [...]` or the yarn object form
308
+ // `"workspaces": { "packages": [...] }`.
309
+ const ws = rootManifest?.workspaces;
310
+ if (Array.isArray(ws))
311
+ pushAll(ws);
312
+ else if (ws && typeof ws === 'object')
313
+ pushAll(ws.packages);
314
+ // pnpm-workspace.yaml — parsed with the `yaml` package (already a root dep,
315
+ // same parser the infra adapters use).
316
+ try {
317
+ const doc = parseYaml(readFileSync(join(rootDir, 'pnpm-workspace.yaml'), 'utf8'));
318
+ if (doc && typeof doc === 'object')
319
+ pushAll(doc.packages);
320
+ }
321
+ catch {
322
+ // absent or malformed → skip this source
323
+ }
324
+ // lerna.json
325
+ const lerna = readJsonObject(join(rootDir, 'lerna.json'));
326
+ if (lerna)
327
+ pushAll(lerna.packages);
328
+ // nx.json workspaceLayout — Nx declares its package homes via
329
+ // appsDir/libsDir (default apps/libs), not a glob list, so synthesize the
330
+ // conventional `<appsDir>/*` + `<libsDir>/*` boundaries. Turbo adds none
331
+ // (Turborepo reuses the package-manager workspaces handled above; named
332
+ // packages are still found via the INFERRED path).
333
+ if (nx.present) {
334
+ if (nx.appsDir)
335
+ globs.push(`${nx.appsDir}/*`);
336
+ if (nx.libsDir)
337
+ globs.push(`${nx.libsDir}/*`);
338
+ }
339
+ return globs;
340
+ }
341
+ // ---------------------------------------------------------------------------
342
+ // Entry-point candidates
343
+ /**
344
+ * Candidate entry files for a package, best-first. Drawn from package.json
345
+ * `main` / `module` / `exports` ('.' entry — string, `{".": …}` subpath form,
346
+ * or top-level `{import|default}` conditions form), normalized repo-relative;
347
+ * each transpiled (.js/.mjs/.cjs) candidate also yields its TS sibling(s);
348
+ * the conventional index locations are always appended as fallbacks.
349
+ * Existence is NOT checked — the cluster layer resolves the first candidate
350
+ * present in its file→module map (fail-soft: none present ⇒ no remap).
351
+ */
352
+ function entryCandidates(pkgRoot, manifest) {
353
+ const out = [];
354
+ const push = (p) => {
355
+ if (p && !out.includes(p))
356
+ out.push(p);
357
+ };
358
+ const pushWithVariants = (raw) => {
359
+ if (typeof raw !== 'string' || !raw.trim())
360
+ return;
361
+ const norm = normalizeEntry(pkgRoot, raw);
362
+ if (!norm)
363
+ return;
364
+ push(norm);
365
+ for (const [ext, variants] of TS_VARIANTS) {
366
+ if (!norm.endsWith(ext))
367
+ continue;
368
+ for (const v of variants)
369
+ push(norm.slice(0, -ext.length) + v);
370
+ }
371
+ };
372
+ pushWithVariants(manifest?.main);
373
+ pushWithVariants(manifest?.module);
374
+ pushWithVariants(dotExport(manifest?.exports));
375
+ // Conventional fallbacks — these resolve install-free packages with no
376
+ // explicit entry fields (and dist-pointing manifests whose source lives at
377
+ // the conventional spots).
378
+ for (const f of ['index.ts', 'index.tsx', 'src/index.ts', 'src/index.tsx']) {
379
+ push(pkgRoot ? `${pkgRoot}/${f}` : f);
380
+ }
381
+ return out;
382
+ }
383
+ /** Extract the '.' export target as a string, or undefined. */
384
+ function dotExport(exp) {
385
+ let dot = exp;
386
+ if (dot && typeof dot === 'object' && !Array.isArray(dot)) {
387
+ const e = dot;
388
+ // Subpath-map form has a '.' key; bare-conditions form doesn't (the object
389
+ // itself is the '.' entry's conditions).
390
+ dot = '.' in e ? e['.'] : e;
391
+ }
392
+ if (dot && typeof dot === 'object' && !Array.isArray(dot)) {
393
+ const d = dot;
394
+ dot = typeof d.import === 'string' ? d.import : d.default;
395
+ }
396
+ return typeof dot === 'string' ? dot : undefined;
397
+ }
398
+ /** `./dist/index.js` (package-relative) → `worker/dist/index.js` (repo-relative). */
399
+ function normalizeEntry(pkgRoot, raw) {
400
+ let p = raw.trim().replace(/\\/g, '/');
401
+ if (p.startsWith('./'))
402
+ p = p.slice(2);
403
+ // Refuse anything that escapes the package (or the repo): an entry above the
404
+ // package root can't be a meaningful module-id anchor.
405
+ if (!p || p.startsWith('/') || p === '..' || p.startsWith('../') || p.includes('/../'))
406
+ return null;
407
+ return pkgRoot ? `${pkgRoot}/${p}` : p;
408
+ }
409
+ // ---------------------------------------------------------------------------
410
+ // Small helpers
411
+ /** Read + parse a JSON file into an object; null on ANY failure (never throws). */
412
+ function readJsonObject(absPath) {
413
+ try {
414
+ const parsed = JSON.parse(readFileSync(absPath, 'utf8'));
415
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
416
+ ? parsed
417
+ : null;
418
+ }
419
+ catch {
420
+ return null;
421
+ }
422
+ }
423
+ function nameOf(manifest) {
424
+ const n = manifest?.name;
425
+ return typeof n === 'string' && n.trim() ? n.trim() : null;
426
+ }
427
+ /** Slug from a package name's last segment (`@scope/pkg` → `pkg`), else the dir basename. */
428
+ function slugFor(name, fallback) {
429
+ const fromName = name ? slugify(name.split('/').pop() ?? name) : '';
430
+ return fromName || slugify(fallback) || 'pkg';
431
+ }
432
+ function readNxLayout(rootDir) {
433
+ const nx = readJsonObject(join(rootDir, 'nx.json'));
434
+ if (!nx)
435
+ return { appsDir: 'apps', libsDir: 'libs', present: false };
436
+ let appsDir = 'apps';
437
+ let libsDir = 'libs';
438
+ const wl = nx.workspaceLayout;
439
+ if (wl && typeof wl === 'object' && !Array.isArray(wl)) {
440
+ const o = wl;
441
+ if (typeof o.appsDir === 'string' && o.appsDir.trim())
442
+ appsDir = cleanDir(o.appsDir);
443
+ if (typeof o.libsDir === 'string' && o.libsDir.trim())
444
+ libsDir = cleanDir(o.libsDir);
445
+ }
446
+ return { appsDir, libsDir, present: true };
447
+ }
448
+ /** Normalize a config dir value to a clean repo-relative posix segment chain. */
449
+ function cleanDir(s) {
450
+ return s.trim().replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/, '');
451
+ }
452
+ // Conventional tooling DIR names + package-name patterns. A package that is
453
+ // build/lint/config tooling is labeled `tooling` (most specific signal — checked
454
+ // before app/lib so a config package with a build script isn't mislabeled app).
455
+ const TOOLING_DIRS = new Set(['tools', 'tooling', 'config', 'configs']);
456
+ function isToolingName(name) {
457
+ const last = (name.split('/').pop() ?? name).toLowerCase();
458
+ return (last.includes('eslint-config') ||
459
+ last.includes('eslint-plugin') ||
460
+ last.includes('prettier-config') ||
461
+ last.includes('stylelint-config') ||
462
+ last === 'tsconfig' ||
463
+ last.startsWith('tsconfig-') ||
464
+ last.endsWith('-tsconfig') ||
465
+ last === 'config' ||
466
+ last.endsWith('-config') ||
467
+ last.endsWith('-preset'));
468
+ }
469
+ function hasAnyScript(manifest, names) {
470
+ const scripts = manifest?.scripts;
471
+ if (!scripts || typeof scripts !== 'object' || Array.isArray(scripts))
472
+ return false;
473
+ const s = scripts;
474
+ return names.some((n) => typeof s[n] === 'string' && s[n].trim() !== '');
475
+ }
476
+ /**
477
+ * Classify a package app / lib / tooling. Deterministic, pure:
478
+ * 1. tooling — conventional tooling dir (tools/config) or a config/eslint/
479
+ * tsconfig package name (most specific, checked first).
480
+ * 2. directory convention dominates the script heuristic: `<appsDir>/*` → app;
481
+ * `<libsDir>/*` (or the generic `packages`/`libs`) → lib. This keeps a
482
+ * `packages/ui` library a lib even when it has a build script.
483
+ * 3. otherwise (a non-conventional location) a runnable program — a
484
+ * start/dev/build/serve script, or a `bin` — is an app; everything else lib.
485
+ */
486
+ function roleOf(pkgRoot, manifest, nx) {
487
+ const seg0 = pkgRoot.split('/')[0]; // '' for the root scope
488
+ const name = nameOf(manifest);
489
+ if (seg0 !== '' && TOOLING_DIRS.has(seg0))
490
+ return 'tooling';
491
+ if (name && isToolingName(name))
492
+ return 'tooling';
493
+ if (seg0 !== '' && seg0 === nx.appsDir)
494
+ return 'app';
495
+ if (seg0 === nx.libsDir || seg0 === 'packages' || seg0 === 'libs')
496
+ return 'lib';
497
+ if (hasAnyScript(manifest, ['start', 'dev', 'build', 'serve']) || manifest?.bin != null) {
498
+ return 'app';
499
+ }
500
+ return 'lib';
501
+ }
502
+ const DEP_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
503
+ /**
504
+ * Sibling roots a package depends on — sorted, deduped, self/root
505
+ * excluded. Source: this package's package.json deps (all four dependency
506
+ * fields) whose KEY names a workspace sibling (a workspace dep shadows the
507
+ * registry, so a name match IS the dep). A tsconfig `references` / path-alias
508
+ * dep source is a possible follow-up, deliberately OUT of this slice.
509
+ */
510
+ function computeDeclaredDeps(pkgRoot, manifest, nameToPackage) {
511
+ const deps = new Set();
512
+ for (const field of DEP_FIELDS) {
513
+ const d = manifest?.[field];
514
+ if (!d || typeof d !== 'object' || Array.isArray(d))
515
+ continue;
516
+ for (const depName of Object.keys(d)) {
517
+ const sib = nameToPackage.get(depName);
518
+ if (sib && sib.root !== '' && sib.root !== pkgRoot)
519
+ deps.add(sib.root);
520
+ }
521
+ }
522
+ return [...deps].sort();
523
+ }
524
+ // ---------------------------------------------------------------------------
525
+ // Python package/workspace detection (the Python analogue of the JS
526
+ // machinery above). All pure + fs-reading + NEVER-throws (a malformed manifest
527
+ // degrades to fewer boundaries). Merged into the SAME WorkspacePackage list, so
528
+ // the cluster layer partitions Python + polyglot repos with no change.
529
+ const PY_MANIFEST_FILES = new Set(['pyproject.toml', 'setup.py', 'setup.cfg']);
530
+ // A Python package dir must skip BOTH the JS excludes and the Python ones (venvs,
531
+ // caches) — a vendored `.venv/**/pyproject.toml` is never a first-party boundary.
532
+ const PY_EXCLUDE_SET = new Set([...EXCLUDE_DIRS, ...PYTHON_EXCLUDE_DIRS]);
533
+ /** Sorted non-root dirs containing a pyproject.toml / setup.py / setup.cfg. */
534
+ function findPythonPackageDirs(rootDir) {
535
+ const out = new Set();
536
+ const walk = (rel, depth) => {
537
+ if (depth > MAX_WALK_DEPTH)
538
+ return;
539
+ let entries;
540
+ try {
541
+ entries = readdirSync(rel === '' ? rootDir : join(rootDir, rel), { withFileTypes: true });
542
+ }
543
+ catch {
544
+ return;
545
+ }
546
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
547
+ for (const ent of entries) {
548
+ if (ent.isDirectory()) {
549
+ if (ent.name.startsWith('.') || PY_EXCLUDE_SET.has(ent.name))
550
+ continue;
551
+ walk(rel === '' ? ent.name : `${rel}/${ent.name}`, depth + 1);
552
+ }
553
+ else if (rel !== '' && ent.isFile() && PY_MANIFEST_FILES.has(ent.name)) {
554
+ out.add(rel);
555
+ }
556
+ }
557
+ };
558
+ walk('', 0);
559
+ return [...out].sort();
560
+ }
561
+ /** Parse a dir's pyproject.toml into an object; null on absence/any failure. */
562
+ function readPyproject(absDir) {
563
+ try {
564
+ const p = join(absDir, 'pyproject.toml');
565
+ if (!existsSync(p))
566
+ return null;
567
+ const parsed = parseToml(readFileSync(p, 'utf8'));
568
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
569
+ ? parsed
570
+ : null;
571
+ }
572
+ catch {
573
+ return null;
574
+ }
575
+ }
576
+ function asObject(v) {
577
+ return v && typeof v === 'object' && !Array.isArray(v) ? v : undefined;
578
+ }
579
+ /** `[project].name` or `[tool.poetry].name`, or null. */
580
+ function pyPackageName(py) {
581
+ const project = asObject(py.project);
582
+ if (typeof project?.name === 'string' && project.name.trim())
583
+ return project.name.trim();
584
+ const poetry = asObject(asObject(py.tool)?.poetry);
585
+ if (typeof poetry?.name === 'string' && poetry.name.trim())
586
+ return poetry.name.trim();
587
+ return null;
588
+ }
589
+ /** Root pyproject `[tool.uv.workspace].members` globs (normalized). */
590
+ function collectPythonDeclaredGlobs(rootDir) {
591
+ const py = readPyproject(rootDir);
592
+ if (!py)
593
+ return [];
594
+ const uv = asObject(asObject(asObject(py.tool)?.uv)?.workspace);
595
+ const members = uv?.members;
596
+ if (!Array.isArray(members))
597
+ return [];
598
+ const globs = [];
599
+ for (const m of members) {
600
+ if (typeof m !== 'string')
601
+ continue;
602
+ let g = m.trim().replace(/\\/g, '/');
603
+ if (g.startsWith('!'))
604
+ continue;
605
+ if (g.startsWith('./'))
606
+ g = g.slice(2);
607
+ g = g.replace(/\/+$/, '');
608
+ if (g)
609
+ globs.push(g);
610
+ }
611
+ return globs;
612
+ }
613
+ /** Best-first candidate entry files for a Python package (its top-level
614
+ * `__init__.py`), from the dist name's import module + the dir basename. */
615
+ function pythonEntryCandidates(pkgRoot, name) {
616
+ const out = [];
617
+ const push = (p) => {
618
+ if (!out.includes(p))
619
+ out.push(p);
620
+ };
621
+ const rel = (f) => (pkgRoot ? `${pkgRoot}/${f}` : f);
622
+ const bases = [];
623
+ if (name)
624
+ bases.push(name.replace(/[-.]/g, '_').toLowerCase()); // dist name → import module
625
+ const dirBase = pkgRoot.split('/').pop() ?? '';
626
+ if (dirBase)
627
+ bases.push(dirBase);
628
+ for (const base of bases) {
629
+ push(rel(`src/${base}/__init__.py`));
630
+ push(rel(`${base}/__init__.py`));
631
+ }
632
+ push(rel('__init__.py'));
633
+ push(rel('main.py'));
634
+ return out;
635
+ }
636
+ /**
637
+ * Classify a Python package app / lib / tooling. Directory convention first
638
+ * (apps/ → app; packages|libs → lib; tools|config → tooling), then a runnable
639
+ * entry point (`[project.scripts]` / `[tool.poetry.scripts]`) → app, else lib.
640
+ */
641
+ function pythonRoleOf(pkgRoot, py) {
642
+ const seg0 = pkgRoot.split('/')[0];
643
+ if (seg0 !== '' && TOOLING_DIRS.has(seg0))
644
+ return 'tooling';
645
+ if (seg0 === 'apps')
646
+ return 'app';
647
+ if (seg0 === 'packages' || seg0 === 'libs')
648
+ return 'lib';
649
+ const scripts = asObject(asObject(py?.project)?.scripts) ?? asObject(asObject(asObject(py?.tool)?.poetry)?.scripts);
650
+ if (scripts && Object.keys(scripts).length > 0)
651
+ return 'app';
652
+ return 'lib';
653
+ }
654
+ /** Distribution names this pyproject depends on (PEP 621 + poetry deps). */
655
+ function pyprojectDepNames(py) {
656
+ const names = [];
657
+ const project = asObject(py.project);
658
+ if (Array.isArray(project?.dependencies)) {
659
+ for (const d of project.dependencies) {
660
+ if (typeof d !== 'string')
661
+ continue;
662
+ const n = requirementName(d);
663
+ if (n)
664
+ names.push(n);
665
+ }
666
+ }
667
+ const poetryDeps = asObject(asObject(asObject(py.tool)?.poetry)?.dependencies);
668
+ if (poetryDeps) {
669
+ for (const k of Object.keys(poetryDeps))
670
+ if (k.toLowerCase() !== 'python')
671
+ names.push(k);
672
+ }
673
+ return names;
674
+ }
675
+ /** Local `{path=…}` sibling deps (poetry deps + uv sources). */
676
+ function pythonPathDeps(py) {
677
+ const paths = [];
678
+ const collect = (table) => {
679
+ if (!table)
680
+ return;
681
+ for (const v of Object.values(table)) {
682
+ const o = asObject(v);
683
+ if (typeof o?.path === 'string' && o.path.trim())
684
+ paths.push(o.path.trim());
685
+ }
686
+ };
687
+ collect(asObject(asObject(asObject(py.tool)?.poetry)?.dependencies));
688
+ collect(asObject(asObject(asObject(py.tool)?.uv)?.sources));
689
+ return paths;
690
+ }
691
+ /** Resolve a package-relative dir (with `..`) to a repo-relative root, or null
692
+ * if it escapes the repo. */
693
+ function resolveRelDir(pkgRoot, rel) {
694
+ const p = rel.trim().replace(/\\/g, '/').replace(/^\.\//, '');
695
+ const segs = pkgRoot ? pkgRoot.split('/') : [];
696
+ for (const s of p.split('/')) {
697
+ if (s === '' || s === '.')
698
+ continue;
699
+ if (s === '..') {
700
+ if (segs.length === 0)
701
+ return null;
702
+ segs.pop();
703
+ }
704
+ else {
705
+ segs.push(s);
706
+ }
707
+ }
708
+ return segs.join('/');
709
+ }
710
+ /**
711
+ * A Python package's sibling deps — sibling roots it depends on, from a
712
+ * dep NAMING a sibling (PEP 621 / poetry, PEP-503 normalized) or a `{path=…}` dep
713
+ * resolving to a known sibling root. Sorted, deduped, self/root excluded.
714
+ */
715
+ function computePythonDeclaredDeps(pkgRoot, py, nameToPackage, knownRoots, pyRoots) {
716
+ if (!py)
717
+ return [];
718
+ const deps = new Set();
719
+ // Only PYTHON siblings are name-matchable (a shared-name npm package must not
720
+ // shadow a Python dep — the two ecosystems share the `nameToPackage` namespace).
721
+ const normToRoot = new Map();
722
+ for (const [name, pkg] of nameToPackage) {
723
+ if (pkg.root !== '' && pkg.root !== pkgRoot && pyRoots.has(pkg.root)) {
724
+ normToRoot.set(normalizePyName(name), pkg.root);
725
+ }
726
+ }
727
+ for (const depName of pyprojectDepNames(py)) {
728
+ const root = normToRoot.get(normalizePyName(depName));
729
+ if (root)
730
+ deps.add(root);
731
+ }
732
+ for (const rel of pythonPathDeps(py)) {
733
+ const root = resolveRelDir(pkgRoot, rel);
734
+ if (root !== null && root !== pkgRoot && knownRoots.has(root))
735
+ deps.add(root);
736
+ }
737
+ return [...deps].sort();
738
+ }
739
+ //# sourceMappingURL=workspaces.js.map