alchemy 2.0.0-beta.62 → 2.0.0-beta.63

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 (489) hide show
  1. package/README.md +12 -12
  2. package/bin/exec.js +1959 -70
  3. package/bin/exec.js.map +1 -1
  4. package/lib/AWS/AuthProvider.js +1 -1
  5. package/lib/AWS/AuthProvider.js.map +1 -1
  6. package/lib/AWS/EC2/hosted.d.ts.map +1 -1
  7. package/lib/AWS/EC2/hosted.js +2 -1
  8. package/lib/AWS/EC2/hosted.js.map +1 -1
  9. package/lib/AWS/ECS/Task.d.ts.map +1 -1
  10. package/lib/AWS/ECS/Task.js +2 -1
  11. package/lib/AWS/ECS/Task.js.map +1 -1
  12. package/lib/AWS/Lambda/Function.d.ts.map +1 -1
  13. package/lib/AWS/Lambda/Function.js +13 -40
  14. package/lib/AWS/Lambda/Function.js.map +1 -1
  15. package/lib/AWS/Lambda/MicrovmRuntimeContext.d.ts.map +1 -1
  16. package/lib/AWS/Lambda/MicrovmRuntimeContext.js +9 -21
  17. package/lib/AWS/Lambda/MicrovmRuntimeContext.js.map +1 -1
  18. package/lib/Action.d.ts +8 -0
  19. package/lib/Action.d.ts.map +1 -1
  20. package/lib/Action.js +16 -5
  21. package/lib/Action.js.map +1 -1
  22. package/lib/ActionRuntimeContext.d.ts +14 -0
  23. package/lib/ActionRuntimeContext.d.ts.map +1 -0
  24. package/lib/ActionRuntimeContext.js +56 -0
  25. package/lib/ActionRuntimeContext.js.map +1 -0
  26. package/lib/Apply.d.ts.map +1 -1
  27. package/lib/Apply.js +40 -6
  28. package/lib/Apply.js.map +1 -1
  29. package/lib/Auth/AuthProvider.d.ts +3 -3
  30. package/lib/Auth/AuthProvider.d.ts.map +1 -1
  31. package/lib/Auth/AuthProvider.js +1 -1
  32. package/lib/Auth/AuthProvider.js.map +1 -1
  33. package/lib/Auth/Lock.d.ts.map +1 -1
  34. package/lib/Auth/Lock.js +5 -2
  35. package/lib/Auth/Lock.js.map +1 -1
  36. package/lib/Bundle/PurePlugin.d.ts.map +1 -1
  37. package/lib/Bundle/PurePlugin.js +26 -1
  38. package/lib/Bundle/PurePlugin.js.map +1 -1
  39. package/lib/Bundle/Vite.d.ts +2 -0
  40. package/lib/Bundle/Vite.d.ts.map +1 -1
  41. package/lib/Bundle/Vite.js +24 -1
  42. package/lib/Bundle/Vite.js.map +1 -1
  43. package/lib/Cli/LoggingCli.d.ts.map +1 -1
  44. package/lib/Cli/LoggingCli.js +7 -4
  45. package/lib/Cli/LoggingCli.js.map +1 -1
  46. package/lib/Cli/commands/_shared.d.ts +19 -0
  47. package/lib/Cli/commands/_shared.d.ts.map +1 -1
  48. package/lib/Cli/commands/_shared.js +22 -0
  49. package/lib/Cli/commands/_shared.js.map +1 -1
  50. package/lib/Cli/commands/login.d.ts +7 -3
  51. package/lib/Cli/commands/login.d.ts.map +1 -1
  52. package/lib/Cli/commands/login.js +70 -12
  53. package/lib/Cli/commands/login.js.map +1 -1
  54. package/lib/Cli/commands/profile.d.ts.map +1 -1
  55. package/lib/Cli/commands/profile.js +7 -20
  56. package/lib/Cli/commands/profile.js.map +1 -1
  57. package/lib/Cloudflare/AI/LanguageModel.js +21 -4
  58. package/lib/Cloudflare/AI/LanguageModel.js.map +1 -1
  59. package/lib/Cloudflare/AI/QuerySearchLocal.d.ts +37 -0
  60. package/lib/Cloudflare/AI/QuerySearchLocal.d.ts.map +1 -0
  61. package/lib/Cloudflare/AI/QuerySearchLocal.js +53 -0
  62. package/lib/Cloudflare/AI/QuerySearchLocal.js.map +1 -0
  63. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.d.ts +35 -0
  64. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.d.ts.map +1 -0
  65. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.js +47 -0
  66. package/lib/Cloudflare/AI/QuerySearchNamespaceLocal.js.map +1 -0
  67. package/lib/Cloudflare/AI/SearchHttpClient.d.ts +34 -0
  68. package/lib/Cloudflare/AI/SearchHttpClient.d.ts.map +1 -0
  69. package/lib/Cloudflare/AI/SearchHttpClient.js +262 -0
  70. package/lib/Cloudflare/AI/SearchHttpClient.js.map +1 -0
  71. package/lib/Cloudflare/AI/index.d.ts +2 -0
  72. package/lib/Cloudflare/AI/index.d.ts.map +1 -1
  73. package/lib/Cloudflare/AI/index.js +2 -0
  74. package/lib/Cloudflare/AI/index.js.map +1 -1
  75. package/lib/Cloudflare/Auth/AuthProvider.js +1 -1
  76. package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
  77. package/lib/Cloudflare/Containers/ContainerBundle.d.ts.map +1 -1
  78. package/lib/Cloudflare/Containers/ContainerBundle.js +11 -1
  79. package/lib/Cloudflare/Containers/ContainerBundle.js.map +1 -1
  80. package/lib/Cloudflare/Containers/ContainerPlatform.d.ts.map +1 -1
  81. package/lib/Cloudflare/Containers/ContainerPlatform.js +12 -33
  82. package/lib/Cloudflare/Containers/ContainerPlatform.js.map +1 -1
  83. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts +24 -0
  84. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts.map +1 -0
  85. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js +106 -0
  86. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js.map +1 -0
  87. package/lib/Cloudflare/D1/QueryDatabaseLocal.d.ts +38 -0
  88. package/lib/Cloudflare/D1/QueryDatabaseLocal.d.ts.map +1 -0
  89. package/lib/Cloudflare/D1/QueryDatabaseLocal.js +54 -0
  90. package/lib/Cloudflare/D1/QueryDatabaseLocal.js.map +1 -0
  91. package/lib/Cloudflare/D1/index.d.ts +1 -0
  92. package/lib/Cloudflare/D1/index.d.ts.map +1 -1
  93. package/lib/Cloudflare/D1/index.js +1 -0
  94. package/lib/Cloudflare/D1/index.js.map +1 -1
  95. package/lib/Cloudflare/DNS/DnsHttp.d.ts +16 -1
  96. package/lib/Cloudflare/DNS/DnsHttp.d.ts.map +1 -1
  97. package/lib/Cloudflare/DNS/DnsHttp.js +6 -1
  98. package/lib/Cloudflare/DNS/DnsHttp.js.map +1 -1
  99. package/lib/Cloudflare/DNS/DnsLocal.d.ts +22 -0
  100. package/lib/Cloudflare/DNS/DnsLocal.d.ts.map +1 -0
  101. package/lib/Cloudflare/DNS/DnsLocal.js +31 -0
  102. package/lib/Cloudflare/DNS/DnsLocal.js.map +1 -0
  103. package/lib/Cloudflare/DNS/ReadDnsHttp.d.ts +3 -3
  104. package/lib/Cloudflare/DNS/ReadDnsHttp.d.ts.map +1 -1
  105. package/lib/Cloudflare/DNS/ReadDnsHttp.js +3 -4
  106. package/lib/Cloudflare/DNS/ReadDnsHttp.js.map +1 -1
  107. package/lib/Cloudflare/DNS/ReadDnsLocal.d.ts +32 -0
  108. package/lib/Cloudflare/DNS/ReadDnsLocal.d.ts.map +1 -0
  109. package/lib/Cloudflare/DNS/ReadDnsLocal.js +35 -0
  110. package/lib/Cloudflare/DNS/ReadDnsLocal.js.map +1 -0
  111. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.d.ts +3 -3
  112. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.d.ts.map +1 -1
  113. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.js +4 -4
  114. package/lib/Cloudflare/DNS/ReadWriteDnsHttp.js.map +1 -1
  115. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.d.ts +40 -0
  116. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.d.ts.map +1 -0
  117. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.js +43 -0
  118. package/lib/Cloudflare/DNS/ReadWriteDnsLocal.js.map +1 -0
  119. package/lib/Cloudflare/DNS/WriteDnsHttp.d.ts +3 -3
  120. package/lib/Cloudflare/DNS/WriteDnsHttp.d.ts.map +1 -1
  121. package/lib/Cloudflare/DNS/WriteDnsHttp.js +3 -4
  122. package/lib/Cloudflare/DNS/WriteDnsHttp.js.map +1 -1
  123. package/lib/Cloudflare/DNS/WriteDnsLocal.d.ts +38 -0
  124. package/lib/Cloudflare/DNS/WriteDnsLocal.d.ts.map +1 -0
  125. package/lib/Cloudflare/DNS/WriteDnsLocal.js +41 -0
  126. package/lib/Cloudflare/DNS/WriteDnsLocal.js.map +1 -0
  127. package/lib/Cloudflare/DNS/index.d.ts +3 -0
  128. package/lib/Cloudflare/DNS/index.d.ts.map +1 -1
  129. package/lib/Cloudflare/DNS/index.js +3 -0
  130. package/lib/Cloudflare/DNS/index.js.map +1 -1
  131. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.d.ts +27 -0
  132. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.d.ts.map +1 -0
  133. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.js +86 -0
  134. package/lib/Cloudflare/Flagship/ReadFlagsHttpClient.js.map +1 -0
  135. package/lib/Cloudflare/Flagship/ReadFlagsLocal.d.ts +41 -0
  136. package/lib/Cloudflare/Flagship/ReadFlagsLocal.d.ts.map +1 -0
  137. package/lib/Cloudflare/Flagship/ReadFlagsLocal.js +57 -0
  138. package/lib/Cloudflare/Flagship/ReadFlagsLocal.js.map +1 -0
  139. package/lib/Cloudflare/Flagship/index.d.ts +1 -0
  140. package/lib/Cloudflare/Flagship/index.d.ts.map +1 -1
  141. package/lib/Cloudflare/Flagship/index.js +1 -0
  142. package/lib/Cloudflare/Flagship/index.js.map +1 -1
  143. package/lib/Cloudflare/KV/NamespaceHttp.d.ts +16 -1
  144. package/lib/Cloudflare/KV/NamespaceHttp.d.ts.map +1 -1
  145. package/lib/Cloudflare/KV/NamespaceHttp.js +8 -2
  146. package/lib/Cloudflare/KV/NamespaceHttp.js.map +1 -1
  147. package/lib/Cloudflare/KV/NamespaceLocal.d.ts +22 -0
  148. package/lib/Cloudflare/KV/NamespaceLocal.d.ts.map +1 -0
  149. package/lib/Cloudflare/KV/NamespaceLocal.js +32 -0
  150. package/lib/Cloudflare/KV/NamespaceLocal.js.map +1 -0
  151. package/lib/Cloudflare/KV/ReadNamespaceHttp.d.ts +2 -2
  152. package/lib/Cloudflare/KV/ReadNamespaceHttp.d.ts.map +1 -1
  153. package/lib/Cloudflare/KV/ReadNamespaceHttp.js +5 -6
  154. package/lib/Cloudflare/KV/ReadNamespaceHttp.js.map +1 -1
  155. package/lib/Cloudflare/KV/ReadNamespaceLocal.d.ts +27 -0
  156. package/lib/Cloudflare/KV/ReadNamespaceLocal.d.ts.map +1 -0
  157. package/lib/Cloudflare/KV/ReadNamespaceLocal.js +30 -0
  158. package/lib/Cloudflare/KV/ReadNamespaceLocal.js.map +1 -0
  159. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.d.ts +3 -3
  160. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.d.ts.map +1 -1
  161. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.js +6 -6
  162. package/lib/Cloudflare/KV/ReadWriteNamespaceHttp.js.map +1 -1
  163. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.d.ts +34 -0
  164. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.d.ts.map +1 -0
  165. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js +37 -0
  166. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js.map +1 -0
  167. package/lib/Cloudflare/KV/WriteNamespaceHttp.d.ts +2 -2
  168. package/lib/Cloudflare/KV/WriteNamespaceHttp.d.ts.map +1 -1
  169. package/lib/Cloudflare/KV/WriteNamespaceHttp.js +5 -6
  170. package/lib/Cloudflare/KV/WriteNamespaceHttp.js.map +1 -1
  171. package/lib/Cloudflare/KV/WriteNamespaceLocal.d.ts +27 -0
  172. package/lib/Cloudflare/KV/WriteNamespaceLocal.d.ts.map +1 -0
  173. package/lib/Cloudflare/KV/WriteNamespaceLocal.js +30 -0
  174. package/lib/Cloudflare/KV/WriteNamespaceLocal.js.map +1 -0
  175. package/lib/Cloudflare/KV/index.d.ts +3 -0
  176. package/lib/Cloudflare/KV/index.d.ts.map +1 -1
  177. package/lib/Cloudflare/KV/index.js +3 -0
  178. package/lib/Cloudflare/KV/index.js.map +1 -1
  179. package/lib/Cloudflare/Queues/QueueHttp.d.ts +20 -1
  180. package/lib/Cloudflare/Queues/QueueHttp.d.ts.map +1 -1
  181. package/lib/Cloudflare/Queues/QueueHttp.js +2 -2
  182. package/lib/Cloudflare/Queues/QueueHttp.js.map +1 -1
  183. package/lib/Cloudflare/Queues/WriteQueueHttp.d.ts +9 -3
  184. package/lib/Cloudflare/Queues/WriteQueueHttp.d.ts.map +1 -1
  185. package/lib/Cloudflare/Queues/WriteQueueHttp.js +11 -6
  186. package/lib/Cloudflare/Queues/WriteQueueHttp.js.map +1 -1
  187. package/lib/Cloudflare/Queues/WriteQueueLocal.d.ts +36 -0
  188. package/lib/Cloudflare/Queues/WriteQueueLocal.d.ts.map +1 -0
  189. package/lib/Cloudflare/Queues/WriteQueueLocal.js +51 -0
  190. package/lib/Cloudflare/Queues/WriteQueueLocal.js.map +1 -0
  191. package/lib/Cloudflare/Queues/index.d.ts +1 -0
  192. package/lib/Cloudflare/Queues/index.d.ts.map +1 -1
  193. package/lib/Cloudflare/Queues/index.js +1 -0
  194. package/lib/Cloudflare/Queues/index.js.map +1 -1
  195. package/lib/Cloudflare/R2/BucketHttp.d.ts +15 -1
  196. package/lib/Cloudflare/R2/BucketHttp.d.ts.map +1 -1
  197. package/lib/Cloudflare/R2/BucketHttp.js +3 -3
  198. package/lib/Cloudflare/R2/BucketHttp.js.map +1 -1
  199. package/lib/Cloudflare/R2/BucketLocal.d.ts +21 -0
  200. package/lib/Cloudflare/R2/BucketLocal.d.ts.map +1 -0
  201. package/lib/Cloudflare/R2/BucketLocal.js +32 -0
  202. package/lib/Cloudflare/R2/BucketLocal.js.map +1 -0
  203. package/lib/Cloudflare/R2/ReadBucketHttp.d.ts +2 -2
  204. package/lib/Cloudflare/R2/ReadBucketHttp.d.ts.map +1 -1
  205. package/lib/Cloudflare/R2/ReadBucketHttp.js +4 -4
  206. package/lib/Cloudflare/R2/ReadBucketHttp.js.map +1 -1
  207. package/lib/Cloudflare/R2/ReadBucketLocal.d.ts +27 -0
  208. package/lib/Cloudflare/R2/ReadBucketLocal.d.ts.map +1 -0
  209. package/lib/Cloudflare/R2/ReadBucketLocal.js +30 -0
  210. package/lib/Cloudflare/R2/ReadBucketLocal.js.map +1 -0
  211. package/lib/Cloudflare/R2/ReadWriteBucketHttp.d.ts +2 -2
  212. package/lib/Cloudflare/R2/ReadWriteBucketHttp.d.ts.map +1 -1
  213. package/lib/Cloudflare/R2/ReadWriteBucketHttp.js +5 -4
  214. package/lib/Cloudflare/R2/ReadWriteBucketHttp.js.map +1 -1
  215. package/lib/Cloudflare/R2/ReadWriteBucketLocal.d.ts +30 -0
  216. package/lib/Cloudflare/R2/ReadWriteBucketLocal.d.ts.map +1 -0
  217. package/lib/Cloudflare/R2/ReadWriteBucketLocal.js +33 -0
  218. package/lib/Cloudflare/R2/ReadWriteBucketLocal.js.map +1 -0
  219. package/lib/Cloudflare/R2/WriteBucketHttp.d.ts +2 -2
  220. package/lib/Cloudflare/R2/WriteBucketHttp.d.ts.map +1 -1
  221. package/lib/Cloudflare/R2/WriteBucketHttp.js +4 -4
  222. package/lib/Cloudflare/R2/WriteBucketHttp.js.map +1 -1
  223. package/lib/Cloudflare/R2/WriteBucketLocal.d.ts +27 -0
  224. package/lib/Cloudflare/R2/WriteBucketLocal.d.ts.map +1 -0
  225. package/lib/Cloudflare/R2/WriteBucketLocal.js +30 -0
  226. package/lib/Cloudflare/R2/WriteBucketLocal.js.map +1 -0
  227. package/lib/Cloudflare/R2/index.d.ts +3 -0
  228. package/lib/Cloudflare/R2/index.d.ts.map +1 -1
  229. package/lib/Cloudflare/R2/index.js +3 -0
  230. package/lib/Cloudflare/R2/index.js.map +1 -1
  231. package/lib/Cloudflare/StateStore/Api.d.ts +2 -0
  232. package/lib/Cloudflare/Tunnel/ReadTunnel.d.ts +3 -3
  233. package/lib/Cloudflare/Tunnel/ReadTunnel.d.ts.map +1 -1
  234. package/lib/Cloudflare/Tunnel/ReadTunnel.js +7 -8
  235. package/lib/Cloudflare/Tunnel/ReadTunnel.js.map +1 -1
  236. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.d.ts +29 -0
  237. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.d.ts.map +1 -0
  238. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.js +30 -0
  239. package/lib/Cloudflare/Tunnel/ReadTunnelLocal.js.map +1 -0
  240. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.d.ts +3 -3
  241. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.d.ts.map +1 -1
  242. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.js +4 -4
  243. package/lib/Cloudflare/Tunnel/ReadWriteTunnel.js.map +1 -1
  244. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.d.ts +35 -0
  245. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.d.ts.map +1 -0
  246. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.js +36 -0
  247. package/lib/Cloudflare/Tunnel/ReadWriteTunnelLocal.js.map +1 -0
  248. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts +17 -1
  249. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts.map +1 -1
  250. package/lib/Cloudflare/Tunnel/TunnelBinding.js +7 -1
  251. package/lib/Cloudflare/Tunnel/TunnelBinding.js.map +1 -1
  252. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.d.ts +19 -0
  253. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.d.ts.map +1 -0
  254. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.js +29 -0
  255. package/lib/Cloudflare/Tunnel/TunnelLocalBinding.js.map +1 -0
  256. package/lib/Cloudflare/Tunnel/WarpConnector.d.ts +3 -3
  257. package/lib/Cloudflare/Tunnel/WarpConnector.d.ts.map +1 -1
  258. package/lib/Cloudflare/Tunnel/WarpConnector.js +7 -4
  259. package/lib/Cloudflare/Tunnel/WarpConnector.js.map +1 -1
  260. package/lib/Cloudflare/Tunnel/WriteTunnel.d.ts +3 -3
  261. package/lib/Cloudflare/Tunnel/WriteTunnel.d.ts.map +1 -1
  262. package/lib/Cloudflare/Tunnel/WriteTunnel.js +7 -8
  263. package/lib/Cloudflare/Tunnel/WriteTunnel.js.map +1 -1
  264. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.d.ts +29 -0
  265. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.d.ts.map +1 -0
  266. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.js +30 -0
  267. package/lib/Cloudflare/Tunnel/WriteTunnelLocal.js.map +1 -0
  268. package/lib/Cloudflare/Tunnel/index.d.ts +3 -0
  269. package/lib/Cloudflare/Tunnel/index.d.ts.map +1 -1
  270. package/lib/Cloudflare/Tunnel/index.js +3 -0
  271. package/lib/Cloudflare/Tunnel/index.js.map +1 -1
  272. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.d.ts +30 -0
  273. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.d.ts.map +1 -0
  274. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.js +100 -0
  275. package/lib/Cloudflare/Vectorize/SearchIndexHttpClient.js.map +1 -0
  276. package/lib/Cloudflare/Vectorize/SearchIndexLocal.d.ts +42 -0
  277. package/lib/Cloudflare/Vectorize/SearchIndexLocal.d.ts.map +1 -0
  278. package/lib/Cloudflare/Vectorize/SearchIndexLocal.js +58 -0
  279. package/lib/Cloudflare/Vectorize/SearchIndexLocal.js.map +1 -0
  280. package/lib/Cloudflare/Vectorize/index.d.ts +1 -0
  281. package/lib/Cloudflare/Vectorize/index.d.ts.map +1 -1
  282. package/lib/Cloudflare/Vectorize/index.js +1 -0
  283. package/lib/Cloudflare/Vectorize/index.js.map +1 -1
  284. package/lib/Cloudflare/Workers/BrowserHttpClient.d.ts +32 -0
  285. package/lib/Cloudflare/Workers/BrowserHttpClient.d.ts.map +1 -0
  286. package/lib/Cloudflare/Workers/BrowserHttpClient.js +100 -0
  287. package/lib/Cloudflare/Workers/BrowserHttpClient.js.map +1 -0
  288. package/lib/Cloudflare/Workers/BrowserLocal.d.ts +37 -0
  289. package/lib/Cloudflare/Workers/BrowserLocal.d.ts.map +1 -0
  290. package/lib/Cloudflare/Workers/BrowserLocal.js +53 -0
  291. package/lib/Cloudflare/Workers/BrowserLocal.js.map +1 -0
  292. package/lib/Cloudflare/Workers/ConfigProvider.d.ts +8 -0
  293. package/lib/Cloudflare/Workers/ConfigProvider.d.ts.map +1 -1
  294. package/lib/Cloudflare/Workers/ConfigProvider.js +10 -1
  295. package/lib/Cloudflare/Workers/ConfigProvider.js.map +1 -1
  296. package/lib/Cloudflare/Workers/DurableObject.d.ts +137 -3
  297. package/lib/Cloudflare/Workers/DurableObject.d.ts.map +1 -1
  298. package/lib/Cloudflare/Workers/DurableObject.js +141 -6
  299. package/lib/Cloudflare/Workers/DurableObject.js.map +1 -1
  300. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +2 -185
  301. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  302. package/lib/Cloudflare/Workers/Vite.d.ts.map +1 -1
  303. package/lib/Cloudflare/Workers/Vite.js +81 -2
  304. package/lib/Cloudflare/Workers/Vite.js.map +1 -1
  305. package/lib/Cloudflare/Workers/Worker.d.ts +15 -1
  306. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  307. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  308. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  309. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +2 -1
  310. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  311. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +22 -1
  312. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  313. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  314. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  315. package/lib/Cloudflare/Workers/WorkerBridge.js +7 -2
  316. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  317. package/lib/Cloudflare/Workers/WorkerProvider.d.ts +81 -0
  318. package/lib/Cloudflare/Workers/WorkerProvider.d.ts.map +1 -1
  319. package/lib/Cloudflare/Workers/WorkerProvider.js +450 -61
  320. package/lib/Cloudflare/Workers/WorkerProvider.js.map +1 -1
  321. package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts.map +1 -1
  322. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js +11 -37
  323. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js.map +1 -1
  324. package/lib/Cloudflare/Workers/index.d.ts +1 -0
  325. package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
  326. package/lib/Cloudflare/Workers/index.js +1 -0
  327. package/lib/Cloudflare/Workers/index.js.map +1 -1
  328. package/lib/Diff.d.ts +16 -0
  329. package/lib/Diff.d.ts.map +1 -1
  330. package/lib/Diff.js +33 -0
  331. package/lib/Diff.js.map +1 -1
  332. package/lib/GitHub/Secrets.d.ts +1 -1
  333. package/lib/GitHub/Secrets.js +1 -1
  334. package/lib/GitHub/Variables.d.ts +1 -1
  335. package/lib/GitHub/Variables.js +1 -1
  336. package/lib/Local/RpcSerialization.d.ts.map +1 -1
  337. package/lib/Local/RpcSerialization.js +6 -2
  338. package/lib/Local/RpcSerialization.js.map +1 -1
  339. package/lib/Local/RpcSpawner.d.ts.map +1 -1
  340. package/lib/Local/RpcSpawner.js +14 -8
  341. package/lib/Local/RpcSpawner.js.map +1 -1
  342. package/lib/Neon/AuthProvider.js +1 -1
  343. package/lib/Neon/AuthProvider.js.map +1 -1
  344. package/lib/Plan.d.ts.map +1 -1
  345. package/lib/Plan.js +11 -4
  346. package/lib/Plan.js.map +1 -1
  347. package/lib/Planetscale/Database.d.ts +2 -1
  348. package/lib/Planetscale/Database.d.ts.map +1 -1
  349. package/lib/Planetscale/MySQL/MySQLClusterSize.d.ts +41 -3
  350. package/lib/Planetscale/MySQL/MySQLClusterSize.d.ts.map +1 -1
  351. package/lib/Planetscale/MySQL/MySQLClusterSize.js +88 -11
  352. package/lib/Planetscale/MySQL/MySQLClusterSize.js.map +1 -1
  353. package/lib/Planetscale/MySQL/MySQLDatabase.d.ts +14 -0
  354. package/lib/Planetscale/MySQL/MySQLDatabase.d.ts.map +1 -1
  355. package/lib/Planetscale/MySQL/MySQLDatabase.js +24 -11
  356. package/lib/Planetscale/MySQL/MySQLDatabase.js.map +1 -1
  357. package/lib/Platform.d.ts.map +1 -1
  358. package/lib/Platform.js +11 -1
  359. package/lib/Platform.js.map +1 -1
  360. package/lib/Runtime.d.ts +23 -0
  361. package/lib/Runtime.d.ts.map +1 -1
  362. package/lib/Runtime.js +83 -0
  363. package/lib/Runtime.js.map +1 -1
  364. package/lib/RuntimeContext.d.ts +46 -0
  365. package/lib/RuntimeContext.d.ts.map +1 -1
  366. package/lib/RuntimeContext.js +60 -0
  367. package/lib/RuntimeContext.js.map +1 -1
  368. package/lib/Server/Process.d.ts +1 -1
  369. package/lib/Server/Process.d.ts.map +1 -1
  370. package/lib/Server/Process.js +8 -8
  371. package/lib/Server/Process.js.map +1 -1
  372. package/lib/Stack.js +1 -1
  373. package/lib/Stack.js.map +1 -1
  374. package/lib/State/HttpStateApi.d.ts +10 -10
  375. package/lib/Test/Alchemy.d.ts +74 -0
  376. package/lib/Test/Alchemy.d.ts.map +1 -0
  377. package/lib/Test/Alchemy.js +143 -0
  378. package/lib/Test/Alchemy.js.map +1 -0
  379. package/lib/Util/cached-function.d.ts +1 -1
  380. package/lib/Util/cached-function.d.ts.map +1 -1
  381. package/lib/Util/cached-function.js +2 -2
  382. package/lib/Util/cached-function.js.map +1 -1
  383. package/lib/Util/proxy-chain.js +3 -0
  384. package/lib/Util/proxy-chain.js.map +1 -1
  385. package/package.json +10 -10
  386. package/src/AWS/AuthProvider.ts +1 -1
  387. package/src/AWS/EC2/hosted.ts +2 -1
  388. package/src/AWS/ECS/Task.ts +2 -1
  389. package/src/AWS/Lambda/Function.ts +13 -47
  390. package/src/AWS/Lambda/MicrovmRuntimeContext.ts +11 -38
  391. package/src/Action.ts +39 -6
  392. package/src/ActionRuntimeContext.ts +68 -0
  393. package/src/Apply.ts +46 -8
  394. package/src/Auth/AuthProvider.ts +1 -1
  395. package/src/Auth/Lock.ts +5 -2
  396. package/src/Bundle/PurePlugin.ts +34 -1
  397. package/src/Bundle/Vite.ts +51 -1
  398. package/src/Cli/LoggingCli.ts +7 -4
  399. package/src/Cli/commands/_shared.ts +52 -0
  400. package/src/Cli/commands/login.ts +90 -14
  401. package/src/Cli/commands/profile.ts +7 -41
  402. package/src/Cloudflare/AI/LanguageModel.ts +25 -5
  403. package/src/Cloudflare/AI/QuerySearchLocal.ts +66 -0
  404. package/src/Cloudflare/AI/QuerySearchNamespaceLocal.ts +60 -0
  405. package/src/Cloudflare/AI/SearchHttpClient.ts +423 -0
  406. package/src/Cloudflare/AI/index.ts +2 -0
  407. package/src/Cloudflare/Auth/AuthProvider.ts +1 -1
  408. package/src/Cloudflare/Containers/ContainerBundle.ts +11 -1
  409. package/src/Cloudflare/Containers/ContainerPlatform.ts +14 -52
  410. package/src/Cloudflare/D1/QueryDatabaseHttpClient.ts +176 -0
  411. package/src/Cloudflare/D1/QueryDatabaseLocal.ts +66 -0
  412. package/src/Cloudflare/D1/index.ts +1 -0
  413. package/src/Cloudflare/DNS/DnsHttp.ts +24 -2
  414. package/src/Cloudflare/DNS/DnsLocal.ts +41 -0
  415. package/src/Cloudflare/DNS/ReadDnsHttp.ts +4 -5
  416. package/src/Cloudflare/DNS/ReadDnsLocal.ts +38 -0
  417. package/src/Cloudflare/DNS/ReadWriteDnsHttp.ts +5 -5
  418. package/src/Cloudflare/DNS/ReadWriteDnsLocal.ts +46 -0
  419. package/src/Cloudflare/DNS/WriteDnsHttp.ts +4 -5
  420. package/src/Cloudflare/DNS/WriteDnsLocal.ts +44 -0
  421. package/src/Cloudflare/DNS/index.ts +3 -0
  422. package/src/Cloudflare/Flagship/ReadFlagsHttpClient.ts +183 -0
  423. package/src/Cloudflare/Flagship/ReadFlagsLocal.ts +69 -0
  424. package/src/Cloudflare/Flagship/index.ts +1 -0
  425. package/src/Cloudflare/KV/NamespaceHttp.ts +25 -2
  426. package/src/Cloudflare/KV/NamespaceLocal.ts +42 -0
  427. package/src/Cloudflare/KV/ReadNamespaceHttp.ts +7 -6
  428. package/src/Cloudflare/KV/ReadNamespaceLocal.ts +35 -0
  429. package/src/Cloudflare/KV/ReadWriteNamespaceHttp.ts +11 -6
  430. package/src/Cloudflare/KV/ReadWriteNamespaceLocal.ts +42 -0
  431. package/src/Cloudflare/KV/WriteNamespaceHttp.ts +7 -6
  432. package/src/Cloudflare/KV/WriteNamespaceLocal.ts +35 -0
  433. package/src/Cloudflare/KV/index.ts +3 -0
  434. package/src/Cloudflare/Queues/QueueHttp.ts +24 -2
  435. package/src/Cloudflare/Queues/WriteQueueHttp.ts +16 -7
  436. package/src/Cloudflare/Queues/WriteQueueLocal.ts +64 -0
  437. package/src/Cloudflare/Queues/index.ts +1 -0
  438. package/src/Cloudflare/R2/BucketHttp.ts +20 -3
  439. package/src/Cloudflare/R2/BucketLocal.ts +47 -0
  440. package/src/Cloudflare/R2/ReadBucketHttp.ts +10 -5
  441. package/src/Cloudflare/R2/ReadBucketLocal.ts +33 -0
  442. package/src/Cloudflare/R2/ReadWriteBucketHttp.ts +11 -5
  443. package/src/Cloudflare/R2/ReadWriteBucketLocal.ts +36 -0
  444. package/src/Cloudflare/R2/WriteBucketHttp.ts +10 -5
  445. package/src/Cloudflare/R2/WriteBucketLocal.ts +33 -0
  446. package/src/Cloudflare/R2/index.ts +3 -0
  447. package/src/Cloudflare/Tunnel/ReadTunnel.ts +8 -9
  448. package/src/Cloudflare/Tunnel/ReadTunnelLocal.ts +33 -0
  449. package/src/Cloudflare/Tunnel/ReadWriteTunnel.ts +5 -5
  450. package/src/Cloudflare/Tunnel/ReadWriteTunnelLocal.ts +39 -0
  451. package/src/Cloudflare/Tunnel/TunnelBinding.ts +26 -2
  452. package/src/Cloudflare/Tunnel/TunnelLocalBinding.ts +38 -0
  453. package/src/Cloudflare/Tunnel/WarpConnector.ts +8 -5
  454. package/src/Cloudflare/Tunnel/WriteTunnel.ts +8 -9
  455. package/src/Cloudflare/Tunnel/WriteTunnelLocal.ts +33 -0
  456. package/src/Cloudflare/Tunnel/index.ts +3 -0
  457. package/src/Cloudflare/Vectorize/SearchIndexHttpClient.ts +183 -0
  458. package/src/Cloudflare/Vectorize/SearchIndexLocal.ts +70 -0
  459. package/src/Cloudflare/Vectorize/index.ts +1 -0
  460. package/src/Cloudflare/Workers/BrowserHttpClient.ts +201 -0
  461. package/src/Cloudflare/Workers/BrowserLocal.ts +65 -0
  462. package/src/Cloudflare/Workers/ConfigProvider.ts +15 -1
  463. package/src/Cloudflare/Workers/DurableObject.ts +227 -6
  464. package/src/Cloudflare/Workers/Vite.ts +93 -11
  465. package/src/Cloudflare/Workers/Worker.ts +19 -1
  466. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +6 -5
  467. package/src/Cloudflare/Workers/WorkerBinding.ts +20 -1
  468. package/src/Cloudflare/Workers/WorkerBridge.ts +12 -2
  469. package/src/Cloudflare/Workers/WorkerProvider.ts +664 -117
  470. package/src/Cloudflare/Workers/WorkerRuntimeContext.ts +14 -45
  471. package/src/Cloudflare/Workers/index.ts +1 -0
  472. package/src/Diff.ts +33 -0
  473. package/src/GitHub/Secrets.ts +1 -1
  474. package/src/GitHub/Variables.ts +1 -1
  475. package/src/Local/RpcSerialization.ts +6 -2
  476. package/src/Local/RpcSpawner.ts +21 -8
  477. package/src/Neon/AuthProvider.ts +1 -1
  478. package/src/Plan.ts +17 -4
  479. package/src/Planetscale/Database.ts +2 -1
  480. package/src/Planetscale/MySQL/MySQLClusterSize.ts +120 -21
  481. package/src/Planetscale/MySQL/MySQLDatabase.ts +84 -44
  482. package/src/Platform.ts +12 -3
  483. package/src/Runtime.ts +92 -0
  484. package/src/RuntimeContext.ts +79 -0
  485. package/src/Server/Process.ts +11 -18
  486. package/src/Stack.ts +1 -1
  487. package/src/Test/Alchemy.ts +282 -0
  488. package/src/Util/cached-function.ts +5 -5
  489. package/src/Util/proxy-chain.ts +3 -0
@@ -1,3 +1,4 @@
1
+ import * as durableObjectsApi from "@distilled.cloud/cloudflare/durable-objects";
1
2
  import * as workers from "@distilled.cloud/cloudflare/workers";
2
3
  import * as wfp from "@distilled.cloud/cloudflare/workers-for-platforms";
3
4
  import * as zones from "@distilled.cloud/cloudflare/zones";
@@ -12,7 +13,7 @@ import * as crypto from "node:crypto";
12
13
  import { Unowned } from "../../AdoptPolicy.ts";
13
14
  import * as Artifacts from "../../Artifacts.ts";
14
15
  import type { ScopedPlanStatusSession } from "../../Cli/Cli.ts";
15
- import { hashDirectory } from "../../Command/Memo.ts";
16
+ import { hashDirectory, type MemoOptions } from "../../Command/Memo.ts";
16
17
  import { isResolved } from "../../Diff.ts";
17
18
  import * as ProviderLayer from "../../Local/ProviderLayer.ts";
18
19
  import * as Provider from "../../Provider.ts";
@@ -26,7 +27,12 @@ import { readAssets, uploadAssets } from "./Assets.ts";
26
27
  import { getCompatibility } from "./Compatibility.ts";
27
28
  import { isDurableObjectExport } from "./DurableObject.ts";
28
29
  import { LocalWorkerProvider } from "./LocalWorkerProvider.ts";
29
- import { Worker, type WorkerProps, type WorkerRouteConfig } from "./Worker.ts";
30
+ import {
31
+ Worker,
32
+ type ViteOptions,
33
+ type WorkerProps,
34
+ type WorkerRouteConfig,
35
+ } from "./Worker.ts";
30
36
  import { getCacheBinding, getCronBindings } from "./WorkerAsyncBindings.ts";
31
37
  import type { WorkerBinding, WorkerSettingsBinding } from "./WorkerBinding.ts";
32
38
  import { readPrebuiltWorkerBundle, WorkerBundle } from "./WorkerBundle.ts";
@@ -37,6 +43,65 @@ class MissingDurableObjects extends Data.TaggedError("MissingDurableObjects")<{
37
43
  expected: string[];
38
44
  }> {}
39
45
 
46
+ /**
47
+ * A Durable Object class is being dropped from this Worker while a binding in
48
+ * the same deploy still references it on another script — the class moved
49
+ * cross-script, but its namespace (and every stored object in it) still lives
50
+ * on this Worker. Cloudflare rejects a single upload that both deletes the
51
+ * class and ships a binding referencing it, and silently deleting would
52
+ * destroy the namespace's data irreversibly, so the deploy fails before any
53
+ * upload.
54
+ *
55
+ * Moving a Durable Object class between Workers is always declared: set
56
+ * `transferredFrom` on the Durable Object at its **new host** — naming the
57
+ * former host by Worker logical id (same stack) or physical script name — and
58
+ * Alchemy performs the data-preserving `transferred_classes` migration on the
59
+ * new host's deploy; this deploy then converges on its own. To abandon the
60
+ * data instead, remove the binding entirely in one deploy (which deletes the
61
+ * class and its data), then add the cross-script binding in a second deploy.
62
+ */
63
+ export class DurableObjectTransferRequired extends Data.TaggedError(
64
+ "DurableObjectTransferRequired",
65
+ )<{
66
+ scriptName: string;
67
+ className: string;
68
+ targetScriptName: string | undefined;
69
+ }> {
70
+ override get message() {
71
+ return (
72
+ `Durable Object class '${this.className}' still lives on Worker '${this.scriptName}' but this deploy re-binds it as a cross-script reference` +
73
+ (this.targetScriptName ? ` to '${this.targetScriptName}'` : "") +
74
+ ". Durable Object data does NOT move with the class automatically. " +
75
+ `To move the data, set transferredFrom: "${this.scriptName}" (the former host's script name, or its Worker logical id for same-stack moves) on the Durable Object declaration in its new host Worker. ` +
76
+ "To abandon the data, remove the binding entirely in one deploy before re-adding it as a cross-script reference."
77
+ );
78
+ }
79
+ }
80
+
81
+ /**
82
+ * More than one script matches the `transferredFrom` declaration of a Durable
83
+ * Object (e.g. an orphaned script left behind by a `name` prop change still
84
+ * carries the same alchemy tags, or the host history lists several scripts
85
+ * that each still hold a same-class namespace). Alchemy refuses to guess
86
+ * which namespace's data to move — narrow the declaration to the exact
87
+ * physical script name that holds the data.
88
+ */
89
+ export class AmbiguousDurableObjectTransfer extends Data.TaggedError(
90
+ "AmbiguousDurableObjectTransfer",
91
+ )<{
92
+ scriptName: string;
93
+ logicalId: string;
94
+ className: string;
95
+ sources: string[];
96
+ }> {
97
+ override get message() {
98
+ return (
99
+ `Durable Object '${this.logicalId}' (class '${this.className}') is new to Worker '${this.scriptName}' and multiple scripts match its transferredFrom declaration: ${this.sources.join(", ")}. ` +
100
+ `Narrow transferredFrom to the exact physical script name that holds the data.`
101
+ );
102
+ }
103
+ }
104
+
40
105
  /**
41
106
  * Resolve the Workers for Platforms dispatch-namespace *name* from a resolved
42
107
  * `namespace` prop or persisted attribute. The engine resolves a passed
@@ -895,6 +960,79 @@ export const LiveWorkerProvider = () =>
895
960
  return createAlchemyWorkerTags(id).every((tag) => actualTags.has(tag));
896
961
  };
897
962
 
963
+ /**
964
+ * Resolve the source script of a declared Durable Object transfer for
965
+ * a class that is new to `selfScriptName`. `sources` is the
966
+ * `transferredFrom` host history: each entry names a former host either
967
+ * by *physical script name* or by *Worker logical id* in this stack +
968
+ * stage (matched via the `alchemy:id:`/stack/stage ownership tags).
969
+ * The namespace is transferred from whichever listed host currently
970
+ * holds a same-class namespace; when none does (fresh stage, or every
971
+ * transfer already completed) this returns `undefined` and the caller
972
+ * creates the class fresh — the declaration is inert and safe to keep.
973
+ * More than one match (e.g. an orphaned script from a `name` change)
974
+ * fails with {@link AmbiguousDurableObjectTransfer} rather than
975
+ * guessing whose data to move.
976
+ */
977
+ const resolveTransferSource = Effect.fn(function* (params: {
978
+ accountId: string;
979
+ selfScriptName: string;
980
+ logicalId: string;
981
+ className: string;
982
+ sources: readonly string[];
983
+ observedNamespaces: readonly { script: string; class: string }[];
984
+ }) {
985
+ if (params.sources.length === 0) {
986
+ return undefined;
987
+ }
988
+ const candidates = Array.from(
989
+ new Set(
990
+ params.observedNamespaces.flatMap((ns) =>
991
+ ns.class === params.className &&
992
+ ns.script !== params.selfScriptName
993
+ ? [ns.script]
994
+ : [],
995
+ ),
996
+ ),
997
+ );
998
+ const matched: string[] = [];
999
+ for (const script of candidates) {
1000
+ if (params.sources.includes(script)) {
1001
+ matched.push(script);
1002
+ continue;
1003
+ }
1004
+ const settings = yield* getScriptSettings(
1005
+ params.accountId,
1006
+ script,
1007
+ undefined,
1008
+ ).pipe(
1009
+ Effect.catchTag("WorkerNotFound", () => Effect.succeed(undefined)),
1010
+ Effect.catchTag("WorkerHasNoVersions", () =>
1011
+ Effect.succeed(undefined),
1012
+ ),
1013
+ );
1014
+ const tags = new Set(settings?.tags ?? []);
1015
+ if (
1016
+ tags.has(`alchemy:stack:${stack.name}`) &&
1017
+ tags.has(`alchemy:stage:${stack.stage}`) &&
1018
+ params.sources.some((source) => tags.has(`alchemy:id:${source}`))
1019
+ ) {
1020
+ matched.push(script);
1021
+ }
1022
+ }
1023
+ if (matched.length > 1) {
1024
+ return yield* Effect.fail(
1025
+ new AmbiguousDurableObjectTransfer({
1026
+ scriptName: params.selfScriptName,
1027
+ logicalId: params.logicalId,
1028
+ className: params.className,
1029
+ sources: matched,
1030
+ }),
1031
+ );
1032
+ }
1033
+ return matched[0];
1034
+ });
1035
+
898
1036
  const getDurableObjects = (
899
1037
  bindings: readonly WorkerSettingsBinding[] | null | undefined,
900
1038
  ) => {
@@ -1031,41 +1169,42 @@ export const LiveWorkerProvider = () =>
1031
1169
  // (~0.5s), which is only needed for vite-based workers at build time —
1032
1170
  // not for every Worker definition at module-load time.
1033
1171
  const Vite = yield* Effect.promise(() => import("./Vite.ts"));
1034
- const { clientDirectory, serverBundle } = yield* Vite.viteBuild(
1035
- props.vite?.rootDir,
1036
- Object.fromEntries(
1037
- (yield* Effect.all(
1038
- Object.entries(props.env ?? {}).map(
1039
- Effect.fn(function* ([key, value]) {
1040
- return [
1041
- key,
1042
- typeof value === "string"
1043
- ? value
1044
- : Redacted.isRedacted(value) &&
1045
- typeof Redacted.value(value) === "string"
1046
- ? Redacted.value(value)
1047
- : // A `WorkerLoader` is a real Effect that also carries
1048
- // the `~alchemy/Kind` marker — it is a binding, not a
1049
- // runnable env value. Check it before `Effect.isEffect`
1050
- // so we don't execute it as an inlined env entry.
1051
- isWorkerLoader(value)
1052
- ? undefined
1053
- : Effect.isEffect(value)
1054
- ? yield* value as any as Effect.Effect<any>
1055
- : undefined,
1056
- ];
1057
- }),
1058
- ),
1059
- )).filter(([_, value]) => value !== undefined),
1060
- ),
1061
- {
1062
- main: props.vite?.main,
1063
- compatibilityDate: compatibility.date,
1064
- compatibilityFlags: compatibility.flags,
1065
- viteEnvironments: props.vite?.viteEnvironments,
1066
- },
1067
- );
1068
- const [assets, bundle] = yield* Effect.all(
1172
+ const { clientDirectory, serverBundle, externalWorkspaces } =
1173
+ yield* Vite.viteBuild(
1174
+ props.vite?.rootDir,
1175
+ Object.fromEntries(
1176
+ (yield* Effect.all(
1177
+ Object.entries(props.env ?? {}).map(
1178
+ Effect.fn(function* ([key, value]) {
1179
+ return [
1180
+ key,
1181
+ typeof value === "string"
1182
+ ? value
1183
+ : Redacted.isRedacted(value) &&
1184
+ typeof Redacted.value(value) === "string"
1185
+ ? Redacted.value(value)
1186
+ : // A `WorkerLoader` is a real Effect that also carries
1187
+ // the `~alchemy/Kind` marker — it is a binding, not a
1188
+ // runnable env value. Check it before `Effect.isEffect`
1189
+ // so we don't execute it as an inlined env entry.
1190
+ isWorkerLoader(value)
1191
+ ? undefined
1192
+ : Effect.isEffect(value)
1193
+ ? yield* value as any as Effect.Effect<any>
1194
+ : undefined,
1195
+ ];
1196
+ }),
1197
+ ),
1198
+ )).filter(([_, value]) => value !== undefined),
1199
+ ),
1200
+ {
1201
+ main: props.vite?.main,
1202
+ compatibilityDate: compatibility.date,
1203
+ compatibilityFlags: compatibility.flags,
1204
+ viteEnvironments: props.vite?.viteEnvironments,
1205
+ },
1206
+ );
1207
+ const [assets, bundle, input] = yield* Effect.all(
1069
1208
  [
1070
1209
  clientDirectory
1071
1210
  ? readAssets({
@@ -1079,6 +1218,11 @@ export const LiveWorkerProvider = () =>
1079
1218
  })
1080
1219
  : Effect.undefined,
1081
1220
  serverBundle,
1221
+ hashViteInput(
1222
+ props.vite?.rootDir,
1223
+ props.vite?.memo,
1224
+ externalWorkspaces,
1225
+ ),
1082
1226
  ],
1083
1227
  { concurrency: "unbounded" },
1084
1228
  );
@@ -1087,7 +1231,56 @@ export const LiveWorkerProvider = () =>
1087
1231
  new Error("Vite build produced neither assets nor server output"),
1088
1232
  );
1089
1233
  }
1090
- return { assets, bundle };
1234
+ return {
1235
+ assets,
1236
+ bundle,
1237
+ input: input.hash,
1238
+ additionalWorkspaces: input.workspaces,
1239
+ };
1240
+ });
1241
+
1242
+ const hashViteInput = Effect.fn(function* <E>(
1243
+ rootDir: string = process.cwd(),
1244
+ options: ViteOptions["memo"],
1245
+ additionalWorkspaces: Effect.Effect<Iterable<string>, E>,
1246
+ ) {
1247
+ const hashWorkspaceDirectory = (cwd: string, memo?: MemoOptions) =>
1248
+ hashDirectory({ cwd: path.resolve(rootDir, cwd), memo }).pipe(
1249
+ Effect.map((hash) => `${path.relative(rootDir, cwd)}:${hash}`),
1250
+ );
1251
+ const hashRoot = hashWorkspaceDirectory(rootDir, options);
1252
+ if (Array.isArray(options?.workspaces)) {
1253
+ return yield* Effect.all(
1254
+ [
1255
+ hashRoot,
1256
+ ...options.workspaces.map(({ cwd, ...options }) =>
1257
+ hashWorkspaceDirectory(cwd, options),
1258
+ ),
1259
+ ],
1260
+ { concurrency: "unbounded" },
1261
+ ).pipe(
1262
+ Effect.flatMap(([root, ...workspaces]) =>
1263
+ sha256Object([root, ...workspaces.sort()]),
1264
+ ),
1265
+ Effect.map((hash) => ({ hash, workspaces: undefined })),
1266
+ );
1267
+ }
1268
+ const [root, workspaces] = yield* Effect.all(
1269
+ [hashRoot, additionalWorkspaces],
1270
+ { concurrency: "unbounded" },
1271
+ );
1272
+ const workspaceHashes = yield* Effect.forEach(
1273
+ workspaces,
1274
+ (cwd) => hashWorkspaceDirectory(cwd),
1275
+ { concurrency: "unbounded" },
1276
+ );
1277
+ const hash = yield* sha256Object([root, ...workspaceHashes.sort()]);
1278
+ return {
1279
+ hash,
1280
+ workspaces: Array.from(workspaces).map((cwd) =>
1281
+ path.relative(rootDir, cwd),
1282
+ ),
1283
+ };
1091
1284
  });
1092
1285
 
1093
1286
  const prepareAssetsAndBundle = (
@@ -1112,27 +1305,12 @@ export const LiveWorkerProvider = () =>
1112
1305
  files: [{ path: "main.js", content: props.script }],
1113
1306
  hash: bundleHash,
1114
1307
  },
1308
+ input: undefined,
1309
+ additionalWorkspaces: undefined,
1115
1310
  };
1116
1311
  }
1117
1312
  if (props.vite) {
1118
- const [{ assets, bundle }, input] = yield* Effect.all(
1119
- [
1120
- viteBuild(props),
1121
- // hashDirectory expects `{ cwd, memo }`. The vite props
1122
- // store the project root under `rootDir`, so map it
1123
- // here. Without this, `cwd` falls back to
1124
- // `process.cwd()` and the input hash is computed over
1125
- // the wrong directory tree (often the entire monorepo
1126
- // root), making it both slow and unable to detect
1127
- // changes scoped to the actual Vite project.
1128
- hashDirectory({
1129
- cwd: props.vite.rootDir,
1130
- memo: props.vite.memo,
1131
- }),
1132
- ],
1133
- { concurrency: "unbounded" },
1134
- );
1135
- return { assets, bundle, input };
1313
+ return yield* viteBuild(props);
1136
1314
  }
1137
1315
  const [assets, bundle] = yield* Effect.all(
1138
1316
  [
@@ -1143,9 +1321,14 @@ export const LiveWorkerProvider = () =>
1143
1321
  ],
1144
1322
  { concurrency: "unbounded" },
1145
1323
  );
1146
- return { assets, bundle };
1324
+ return {
1325
+ assets,
1326
+ bundle,
1327
+ input: undefined,
1328
+ additionalWorkspaces: undefined,
1329
+ };
1147
1330
  }).pipe(
1148
- Effect.map(({ assets, bundle, input }) => ({
1331
+ Effect.map(({ assets, bundle, input, additionalWorkspaces }) => ({
1149
1332
  assets,
1150
1333
  bundle: {
1151
1334
  main: bundle?.files[0].path,
@@ -1160,6 +1343,7 @@ export const LiveWorkerProvider = () =>
1160
1343
  assets: assets?.hash,
1161
1344
  bundle: bundle?.hash,
1162
1345
  input,
1346
+ additionalWorkspaces,
1163
1347
  } satisfies Worker["Attributes"]["hash"],
1164
1348
  })),
1165
1349
  );
@@ -1222,7 +1406,22 @@ export const LiveWorkerProvider = () =>
1222
1406
  assets: prebuiltAssets?.hash ?? preparedHash.assets,
1223
1407
  metadata: metadataHash,
1224
1408
  } satisfies Worker["Attributes"]["hash"];
1225
- const metadataBindings = bindings.flatMap((b) => b.data.bindings ?? []);
1409
+ // `transferredFrom` is alchemy-only transfer metadata on
1410
+ // durable_object_namespace bindings — it drives the
1411
+ // `transferred_classes` migration below and must be stripped from the
1412
+ // wire-shape binding before upload.
1413
+ const metadataBindings = bindings.flatMap((b) =>
1414
+ (b.data.bindings ?? []).map((item): WorkerBinding => {
1415
+ if (
1416
+ item.type === "durable_object_namespace" &&
1417
+ item.transferredFrom !== undefined
1418
+ ) {
1419
+ const { transferredFrom: _, ...rest } = item;
1420
+ return rest;
1421
+ }
1422
+ return item;
1423
+ }),
1424
+ );
1226
1425
  const expectedDurableObjectClassNames =
1227
1426
  getExpectedDurableObjectClassNames(metadataBindings, name);
1228
1427
  let metadataAssets:
@@ -1355,7 +1554,8 @@ export const LiveWorkerProvider = () =>
1355
1554
  const oldTags = Array.from(new Set(oldSettings?.tags ?? []));
1356
1555
  const oldBindings = oldSettings?.bindings ?? [];
1357
1556
 
1358
- // Parse alchemy:do:{logicalId}:{className} tags
1557
+ // Parse the DO logical-id→class mapping from script tags (packed
1558
+ // `alchemy:dos:` and legacy per-DO `alchemy:do:` formats)
1359
1559
  const oldDoClassNameByLogicalId = getDurableObjectTagMap(oldTags);
1360
1560
  const currentDoBindings = getDurableObjectBindings(bindings, name);
1361
1561
  const currentDoClassNameByLogicalId = Object.fromEntries(
@@ -1373,13 +1573,15 @@ export const LiveWorkerProvider = () =>
1373
1573
  )[0];
1374
1574
  const newMigrationTag = bumpMigrationTagVersion(oldMigrationTag);
1375
1575
 
1376
- // Compute deleted classes
1377
- const deletedClasses: string[] = [];
1576
+ // Compute delete-class candidates. Candidates are validated against
1577
+ // observed namespace ownership below — a class may already have been
1578
+ // transferred to another script by its new host's deploy.
1579
+ const deletedClassCandidates: string[] = [];
1378
1580
  for (const [logicalId, className] of Object.entries(
1379
1581
  oldDoClassNameByLogicalId,
1380
1582
  )) {
1381
1583
  if (!currentDoClassNameByLogicalId[logicalId]) {
1382
- deletedClasses.push(className);
1584
+ deletedClassCandidates.push(className);
1383
1585
  }
1384
1586
  }
1385
1587
 
@@ -1401,11 +1603,107 @@ export const LiveWorkerProvider = () =>
1401
1603
  (binding) => binding.bindingName === oldBinding.name,
1402
1604
  )
1403
1605
  ) {
1404
- deletedClasses.push(oldBinding.className);
1606
+ deletedClassCandidates.push(oldBinding.className);
1405
1607
  }
1406
1608
  }
1407
1609
  }
1408
1610
 
1611
+ // Class names the current deploy references *cross-script* (mapped to
1612
+ // the foreign script). A class that both leaves the "hosted here" set
1613
+ // and shows up here has moved to another Worker — Cloudflare rejects
1614
+ // deleting a class while any binding in the upload references its
1615
+ // class name, and deleting would destroy the namespace's data.
1616
+ const crossScriptClassTargets = new Map<string, string>();
1617
+ for (const item of metadataBindings) {
1618
+ if (
1619
+ item.type === "durable_object_namespace" &&
1620
+ item.className &&
1621
+ typeof item.scriptName === "string" &&
1622
+ item.scriptName !== name
1623
+ ) {
1624
+ crossScriptClassTargets.set(item.className, item.scriptName);
1625
+ }
1626
+ }
1627
+
1628
+ // One account-level namespace listing serves both sides of a
1629
+ // transfer: the destination checks the source still hosts the class
1630
+ // before emitting `transferred_classes`, and the former host checks
1631
+ // whether a to-be-deleted class was already transferred away.
1632
+ // Dispatch-namespace user workers keep the legacy behavior — their
1633
+ // namespaces don't surface on the account-level list.
1634
+ const mayTransferIn = currentDoBindings.some(
1635
+ (binding) =>
1636
+ !oldDoClassNameByLogicalId[binding.logicalId] &&
1637
+ binding.transferredFrom !== undefined,
1638
+ );
1639
+ const observedNamespaces =
1640
+ !dispatchNamespace &&
1641
+ (deletedClassCandidates.length > 0 || mayTransferIn)
1642
+ ? yield* listDurableObjectNamespaces(accountId)
1643
+ : [];
1644
+ const hosts = (
1645
+ namespaces: readonly { script: string; class: string }[],
1646
+ scriptName: string,
1647
+ className: string,
1648
+ ) =>
1649
+ namespaces.some(
1650
+ (ns) => ns.script === scriptName && ns.class === className,
1651
+ );
1652
+ const scriptHostsClass = (scriptName: string, className: string) =>
1653
+ hosts(observedNamespaces, scriptName, className);
1654
+
1655
+ const deletedClasses: string[] = [];
1656
+ for (const className of deletedClassCandidates) {
1657
+ if (dispatchNamespace) {
1658
+ deletedClasses.push(className);
1659
+ continue;
1660
+ }
1661
+ const targetScriptName = crossScriptClassTargets.get(className);
1662
+ if (targetScriptName === undefined) {
1663
+ // Plain removal. Delete only if the namespace actually still
1664
+ // lives here — it may have been transferred to another script by
1665
+ // that script's deploy, or removed out-of-band. The stale
1666
+ // alchemy:do tag drops out either way because tags are recomputed
1667
+ // from current bindings.
1668
+ if (scriptHostsClass(name, className)) {
1669
+ deletedClasses.push(className);
1670
+ }
1671
+ continue;
1672
+ }
1673
+ // The class went local → cross-script. When the new host's deploy
1674
+ // ran a `transferred_classes` migration moments ago, the account
1675
+ // listing can briefly still attribute the namespace to this script,
1676
+ // so re-observe with a short bounded budget until the transfer
1677
+ // becomes visible (namespace off this script) or the state is
1678
+ // conclusively a conflict (still here — including the case where
1679
+ // the target created a *fresh* namespace for the same class name).
1680
+ const namespaces = yield* listDurableObjectNamespaces(accountId).pipe(
1681
+ Effect.repeat({
1682
+ schedule: Schedule.spaced("2 seconds"),
1683
+ until: (observed) =>
1684
+ !hosts(observed, name, className) ||
1685
+ hosts(observed, targetScriptName, className),
1686
+ times: 5,
1687
+ }),
1688
+ );
1689
+ if (!hosts(namespaces, name, className)) {
1690
+ // Transferred away — nothing to delete.
1691
+ continue;
1692
+ }
1693
+ // local → cross-script transition without a transfer. Fail before
1694
+ // any upload: Cloudflare would reject the combined delete + binding
1695
+ // anyway, and silently deleting would destroy the namespace's
1696
+ // data. See the error's docs for the two ways out
1697
+ // (`transferredFrom` on the new host, or a two-phase removal).
1698
+ return yield* Effect.fail(
1699
+ new DurableObjectTransferRequired({
1700
+ scriptName: name,
1701
+ className,
1702
+ targetScriptName,
1703
+ }),
1704
+ );
1705
+ }
1706
+
1409
1707
  // Collect container-backed class names so we can send container metadata
1410
1708
  const containerClassNames = new Set(
1411
1709
  bindings.flatMap((b) =>
@@ -1413,27 +1711,42 @@ export const LiveWorkerProvider = () =>
1413
1711
  ),
1414
1712
  );
1415
1713
 
1416
- // Compute new and renamed classes
1714
+ // Compute new, renamed, and transferred classes
1417
1715
  const newClasses: string[] = [];
1418
1716
  const newSqliteClasses: string[] = [];
1419
1717
  const renamedClasses: { from: string; to: string }[] = [];
1718
+ const transferredClasses: {
1719
+ from: string;
1720
+ fromScript: string;
1721
+ to: string;
1722
+ }[] = [];
1420
1723
  for (const binding of currentDoBindings) {
1421
1724
  let previousClassName: string | undefined =
1422
1725
  oldDoClassNameByLogicalId[binding.logicalId];
1423
1726
  if (!previousClassName) {
1424
- // No `alchemy:do:` tag maps this logical id to a class — the
1727
+ // No DO metadata tag maps this logical id to a class — the
1425
1728
  // worker was created outside Alchemy (raw API / Wrangler) or
1426
1729
  // before these tags existed. Fall back to matching the observed
1427
1730
  // cloud binding by binding name so adoption reuses the existing
1428
1731
  // class instead of asking Cloudflare to create one that already
1429
1732
  // exists (which fails the migration). This is the "first deploy
1430
1733
  // must match the existing class name" path; once we write the
1431
- // `alchemy:do:` tag, subsequent renames are driven by logical id.
1734
+ // `alchemy:dos:` tag, subsequent renames are driven by logical id.
1432
1735
  const observed = oldBindings.find(
1433
1736
  (old) =>
1434
1737
  old.type === "durable_object_namespace" &&
1435
1738
  "className" in old &&
1436
1739
  old.className &&
1740
+ // Only a *locally-owned* binding proves the class exists on
1741
+ // this script. A cross-script binding under the same name —
1742
+ // e.g. this worker previously referenced another host's
1743
+ // class and now hosts its own — points at a foreign
1744
+ // namespace and must not suppress the create migration
1745
+ // (Cloudflare rejects a local binding for a class the
1746
+ // script isn't configured to implement).
1747
+ (!("scriptName" in old) ||
1748
+ old.scriptName === undefined ||
1749
+ old.scriptName === name) &&
1437
1750
  old.name === binding.bindingName,
1438
1751
  );
1439
1752
  if (observed && "className" in observed && observed.className) {
@@ -1441,6 +1754,38 @@ export const LiveWorkerProvider = () =>
1441
1754
  }
1442
1755
  }
1443
1756
  if (!previousClassName) {
1757
+ // A class new to this script is a host move when the declaration
1758
+ // says so: `transferredFrom` lists the former host(s) — moves
1759
+ // are always declared, never inferred, because a class deleted
1760
+ // on one worker and created on another is otherwise ambiguous
1761
+ // between "move the data" and "delete + fresh namespace". The
1762
+ // declared source must be observed to still host the namespace;
1763
+ // otherwise (fresh stage, transfer already completed) fall
1764
+ // through to a plain create.
1765
+ const fromScript = dispatchNamespace
1766
+ ? undefined
1767
+ : yield* resolveTransferSource({
1768
+ accountId,
1769
+ selfScriptName: name,
1770
+ logicalId: binding.logicalId,
1771
+ className: binding.className,
1772
+ sources: normalizeTransferSources(
1773
+ binding.transferredFrom,
1774
+ name,
1775
+ ),
1776
+ observedNamespaces,
1777
+ });
1778
+ if (fromScript !== undefined) {
1779
+ // Data-preserving move: ship Cloudflare's
1780
+ // `transferred_classes` migration instead of creating a
1781
+ // fresh class.
1782
+ transferredClasses.push({
1783
+ from: binding.className,
1784
+ fromScript,
1785
+ to: binding.className,
1786
+ });
1787
+ continue;
1788
+ }
1444
1789
  // Default all new Durable Object classes to SQLite. Cloudflare
1445
1790
  // recommends SQLite for new namespaces, and container-backed
1446
1791
  // Durable Objects require it.
@@ -1460,29 +1805,28 @@ export const LiveWorkerProvider = () =>
1460
1805
  currentDoClassNameByLogicalId,
1461
1806
  deletedClasses,
1462
1807
  renamedClasses,
1808
+ transferredClasses,
1463
1809
  newSqliteClasses,
1464
1810
  },
1465
1811
  )}`,
1466
1812
  );
1467
1813
 
1468
- // Build alchemy:do:{logicalId}:{className} tags for each DO binding
1469
- const alchemyDoTags: string[] = [];
1470
- for (const binding of currentDoBindings) {
1471
- alchemyDoTags.push(
1472
- `alchemy:do:${binding.logicalId}:${binding.className}`,
1473
- );
1474
- }
1475
-
1814
+ // Pack every DO logical-id→class mapping into as few `alchemy:dos:`
1815
+ // tags as possible — one tag per DO blows Cloudflare's 10-tag limit
1816
+ // at 7+ bindings (#811).
1817
+ const alchemyDoTags = encodeDurableObjectTags(currentDoBindings);
1818
+
1819
+ const alchemyTags = [
1820
+ ...createAlchemyWorkerTags(id),
1821
+ ...alchemyDoTags,
1822
+ ...(newMigrationTag
1823
+ ? [`alchemy:migration-tag:${newMigrationTag}`]
1824
+ : []),
1825
+ ];
1476
1826
  const metadataTags = Array.from(
1477
- new Set([
1478
- ...createAlchemyWorkerTags(id),
1479
- ...alchemyDoTags,
1480
- ...(newMigrationTag
1481
- ? [`alchemy:migration-tag:${newMigrationTag}`]
1482
- : []),
1483
- ...(news.tags ?? []),
1484
- ]),
1827
+ new Set([...alchemyTags, ...(news.tags ?? [])]),
1485
1828
  );
1829
+ yield* validateWorkerTags(name, metadataTags, alchemyTags.length);
1486
1830
 
1487
1831
  const migrations = {
1488
1832
  oldTag: oldMigrationTag,
@@ -1490,7 +1834,7 @@ export const LiveWorkerProvider = () =>
1490
1834
  newClasses,
1491
1835
  deletedClasses,
1492
1836
  renamedClasses,
1493
- transferredClasses: [] as { from: string; to: string }[],
1837
+ transferredClasses,
1494
1838
  newSqliteClasses,
1495
1839
  };
1496
1840
 
@@ -1733,11 +2077,12 @@ export const LiveWorkerProvider = () =>
1733
2077
  return assetsHash !== output.hash?.assets;
1734
2078
  }
1735
2079
  if (props.vite) {
1736
- const input = yield* hashDirectory({
1737
- cwd: props.vite.rootDir,
1738
- memo: props.vite.memo,
1739
- });
1740
- return input !== output.hash?.input;
2080
+ const { hash } = yield* hashViteInput(
2081
+ props.vite.rootDir,
2082
+ props.vite.memo,
2083
+ Effect.succeed(output.hash?.additionalWorkspaces ?? []),
2084
+ );
2085
+ return hash !== output.hash?.input;
1741
2086
  }
1742
2087
  const bundleHash = yield* prepareBundle(id, props).pipe(
1743
2088
  Effect.map((b) => b.hash),
@@ -2009,10 +2354,17 @@ export const LiveWorkerProvider = () =>
2009
2354
  const exportDerived = Object.keys(exportMap)
2010
2355
  .filter((logicalId) => isDurableObjectExport(exportMap[logicalId]))
2011
2356
  .map((logicalId) => ({ logicalId, className: logicalId }));
2357
+ // Transfer-destination classes are excluded from the placeholder
2358
+ // entirely (class list, bindings, tags): Cloudflare forbids
2359
+ // creating the destination class of a `transferred_classes`
2360
+ // migration ahead of the transfer, so `reconcile` must perform it
2361
+ // with the real upload. (`transferredFrom` may still be an
2362
+ // unresolved Output here — precreate runs on raw props — but only
2363
+ // its presence matters.)
2012
2364
  const durableObjects = mergeDurableObjectClasses(
2013
2365
  exportDerived,
2014
2366
  getDurableObjectBindings(bindings, name),
2015
- );
2367
+ ).filter((binding) => !binding.transferredFrom);
2016
2368
  const doClasses = durableObjects.map((binding) => binding.className);
2017
2369
  // Only attach container metadata for classes actually fronted by a
2018
2370
  // Container binding (mirrors reconcile's `containerClassNames`).
@@ -2025,17 +2377,15 @@ export const LiveWorkerProvider = () =>
2025
2377
  ),
2026
2378
  ),
2027
2379
  ).map((className) => ({ className }));
2028
- const alchemyDoTags = durableObjects.map(
2029
- ({ logicalId, className }) =>
2030
- `alchemy:do:${logicalId}:${className}`,
2031
- );
2380
+ const alchemyDoTags = encodeDurableObjectTags(durableObjects);
2381
+ const alchemyTags = [
2382
+ ...createAlchemyWorkerTags(id),
2383
+ ...alchemyDoTags,
2384
+ ];
2032
2385
  const tags = Array.from(
2033
- new Set([
2034
- ...createAlchemyWorkerTags(id),
2035
- ...alchemyDoTags,
2036
- ...(news.tags ?? []),
2037
- ]),
2386
+ new Set([...alchemyTags, ...(news.tags ?? [])]),
2038
2387
  );
2388
+ yield* validateWorkerTags(name, tags, alchemyTags.length);
2039
2389
  yield* Effect.logInfo(
2040
2390
  `Cloudflare Worker precreate: starting ${name}`,
2041
2391
  );
@@ -2377,16 +2727,12 @@ export const LiveWorkerProvider = () =>
2377
2727
  );
2378
2728
  yield* Effect.logInfo(
2379
2729
  `Cloudflare Worker reconcile: existing durable object tags ${JSON.stringify(
2380
- (existingSettings?.tags ?? []).filter((tag) =>
2381
- tag.startsWith("alchemy:do:"),
2382
- ),
2730
+ (existingSettings?.tags ?? []).filter(isDurableObjectTag),
2383
2731
  )}`,
2384
2732
  );
2385
2733
  yield* Effect.logInfo(
2386
2734
  `Cloudflare Worker reconcile: previous durable object tags ${JSON.stringify(
2387
- (output?.tags ?? []).filter((tag) =>
2388
- tag.startsWith("alchemy:do:"),
2389
- ),
2735
+ (output?.tags ?? []).filter(isDurableObjectTag),
2390
2736
  )}`,
2391
2737
  );
2392
2738
 
@@ -2521,6 +2867,41 @@ const contentTypeFromExtension = (extension: string) => {
2521
2867
  }
2522
2868
  };
2523
2869
 
2870
+ /**
2871
+ * Observe every Durable Object namespace on the account as `(script, class)`
2872
+ * pairs. Namespace ownership is authoritative cloud state: after a
2873
+ * `transferred_classes` migration the namespace moves to the receiving
2874
+ * script, so this is how both sides of a transfer observe where a class
2875
+ * currently lives — the destination checks the source still hosts the class
2876
+ * before emitting the transfer, and the former host checks whether a class
2877
+ * it is about to delete has already been transferred away.
2878
+ */
2879
+ const listDurableObjectNamespaces = (accountId: string) =>
2880
+ durableObjectsApi.listNamespaces.items({ accountId }).pipe(
2881
+ Stream.runCollect,
2882
+ Effect.map((namespaces) =>
2883
+ Array.from(namespaces).flatMap((ns) =>
2884
+ ns.script && ns.class ? [{ script: ns.script, class: ns.class }] : [],
2885
+ ),
2886
+ ),
2887
+ );
2888
+
2889
+ /**
2890
+ * Coerce a resolved `transferredFrom` declaration to its list form, dropping
2891
+ * self-references (a worker naming itself as its own former host is
2892
+ * meaningless — the class already lives there).
2893
+ */
2894
+ const normalizeTransferSources = (
2895
+ value: string | readonly string[] | undefined,
2896
+ selfScriptName: string,
2897
+ ): string[] =>
2898
+ (value === undefined
2899
+ ? []
2900
+ : Array.isArray(value)
2901
+ ? value
2902
+ : [value as string]
2903
+ ).filter((source) => source !== selfScriptName);
2904
+
2524
2905
  function bumpMigrationTagVersion(
2525
2906
  oldTag: string | undefined,
2526
2907
  ): string | undefined {
@@ -2533,12 +2914,20 @@ function bumpMigrationTagVersion(
2533
2914
  /**
2534
2915
  * Merges a worker's export-derived and binding-derived Durable Object class
2535
2916
  * lists for the precreate placeholder, deduping by class name. The
2536
- * binding-derived entry wins on a collision so the `alchemy:do:` tag keys off
2917
+ * binding-derived entry wins on a collision so the `alchemy:dos:` tag keys off
2537
2918
  * the same logical id (the binding sid) that `reconcile` writes.
2538
2919
  */
2539
2920
  function mergeDurableObjectClasses(
2540
- exportDerived: ReadonlyArray<{ logicalId: string; className: string }>,
2541
- bindingDerived: ReadonlyArray<{ logicalId: string; className: string }>,
2921
+ exportDerived: ReadonlyArray<{
2922
+ logicalId: string;
2923
+ className: string;
2924
+ transferredFrom?: string | string[];
2925
+ }>,
2926
+ bindingDerived: ReadonlyArray<{
2927
+ logicalId: string;
2928
+ className: string;
2929
+ transferredFrom?: string | string[];
2930
+ }>,
2542
2931
  ) {
2543
2932
  return Array.from(
2544
2933
  new Map(
@@ -2582,22 +2971,180 @@ function getDurableObjectBindings(
2582
2971
  logicalId: binding.sid,
2583
2972
  bindingName: item.name,
2584
2973
  className: item.className,
2974
+ // Declared host history for a data-preserving
2975
+ // `transferred_classes` migration. Normalize an empty list to
2976
+ // "not declared"; self-references are dropped at resolution time.
2977
+ transferredFrom:
2978
+ Array.isArray(item.transferredFrom) &&
2979
+ item.transferredFrom.length === 0
2980
+ ? undefined
2981
+ : item.transferredFrom,
2585
2982
  },
2586
2983
  ];
2587
2984
  }),
2588
2985
  );
2589
2986
  }
2590
2987
 
2591
- function getDurableObjectTagMap(tags: ReadonlyArray<string>) {
2592
- return Object.fromEntries(
2593
- tags.flatMap((tag) => {
2594
- if (!tag.startsWith("alchemy:do:")) {
2595
- return [];
2596
- }
2988
+ /**
2989
+ * Cloudflare Worker script-tag limits, verified empirically against the live
2990
+ * API (2026-07):
2991
+ *
2992
+ * - at most **10 tags** per Worker (the 11th is rejected with `Forbidden`)
2993
+ * - each tag is at most **1024 bytes** (`BadRequest: Tag is too large`)
2994
+ * - tags may not contain `,` or `&`; everything else (`:`, `;`, `=`, `/`,
2995
+ * spaces, unicode) is accepted and round-trips intact
2996
+ *
2997
+ * Alchemy reserves 3 ownership tags (`alchemy:stack/stage/id`) and 1
2998
+ * migration tag, so the Durable Object logical-id→class mapping must not
2999
+ * spend one tag per DO (#811). Instead all mappings are packed into as few
3000
+ * `alchemy:dos:` tags as possible.
3001
+ */
3002
+ const MAX_TAGS_PER_WORKER = 10;
3003
+ const MAX_TAG_BYTES = 1024;
3004
+ const LEGACY_DO_TAG_PREFIX = "alchemy:do:";
3005
+ const PACKED_DO_TAG_PREFIX = "alchemy:dos:";
3006
+
3007
+ class InvalidWorkerTags extends Data.TaggedError("InvalidWorkerTags")<{
3008
+ scriptName: string;
3009
+ reason: string;
3010
+ }> {}
3011
+
3012
+ /**
3013
+ * Pack Durable Object logical-id→class mappings into `alchemy:dos:` tags.
3014
+ *
3015
+ * Each mapping is encoded as `logicalId=className` — elided to just
3016
+ * `className` when the two are equal (the common case for export-derived
3017
+ * classes) — with both components `encodeURIComponent`-escaped so the `;`
3018
+ * pair separator, the `=` delimiter, and Cloudflare's forbidden tag
3019
+ * characters (`,`, `&`) can never collide with user identifiers. Pairs are
3020
+ * sorted for deterministic output and greedily packed so each tag stays
3021
+ * within Cloudflare's 1024-byte tag limit; workers with more DOs than fit in
3022
+ * one tag spill into additional `alchemy:dos:` tags.
3023
+ *
3024
+ * `encodeURIComponent` output is pure ASCII, so `String.length` equals the
3025
+ * tag's byte length.
3026
+ *
3027
+ * @internal exported for unit testing.
3028
+ */
3029
+ export function encodeDurableObjectTags(
3030
+ durableObjects: ReadonlyArray<{ logicalId: string; className: string }>,
3031
+ ): string[] {
3032
+ const pairs = [...durableObjects]
3033
+ .sort((a, b) => a.logicalId.localeCompare(b.logicalId))
3034
+ .map(({ logicalId, className }) =>
3035
+ logicalId === className
3036
+ ? encodeURIComponent(className)
3037
+ : `${encodeURIComponent(logicalId)}=${encodeURIComponent(className)}`,
3038
+ );
3039
+ const tags: string[] = [];
3040
+ let payload = "";
3041
+ for (const pair of pairs) {
3042
+ const appended = payload === "" ? pair : `${payload};${pair}`;
3043
+ if (
3044
+ PACKED_DO_TAG_PREFIX.length + appended.length > MAX_TAG_BYTES &&
3045
+ payload !== ""
3046
+ ) {
3047
+ tags.push(`${PACKED_DO_TAG_PREFIX}${payload}`);
3048
+ payload = pair;
3049
+ } else {
3050
+ payload = appended;
3051
+ }
3052
+ }
3053
+ if (payload !== "") {
3054
+ tags.push(`${PACKED_DO_TAG_PREFIX}${payload}`);
3055
+ }
3056
+ return tags;
3057
+ }
3058
+
3059
+ /**
3060
+ * Parse the Durable Object logical-id→class mapping from a worker's script
3061
+ * tags. Reads both formats so workers deployed before the packed format roll
3062
+ * forward transparently:
3063
+ *
3064
+ * - legacy: one `alchemy:do:{logicalId}:{className}` tag per DO
3065
+ * - packed: `alchemy:dos:{pair};{pair};…` (see {@link encodeDurableObjectTags})
3066
+ *
3067
+ * A packed entry wins over a legacy entry for the same logical id.
3068
+ *
3069
+ * @internal exported for unit testing.
3070
+ */
3071
+ export function getDurableObjectTagMap(tags: ReadonlyArray<string>) {
3072
+ const map: Record<string, string> = {};
3073
+ for (const tag of tags) {
3074
+ if (tag.startsWith(LEGACY_DO_TAG_PREFIX)) {
2597
3075
  const parts = tag.split(":");
2598
3076
  const logicalId = parts[2];
2599
3077
  const className = parts.slice(3).join(":");
2600
- return logicalId && className ? [[logicalId, className]] : [];
2601
- }),
2602
- );
3078
+ if (logicalId && className && !(logicalId in map)) {
3079
+ map[logicalId] = className;
3080
+ }
3081
+ }
3082
+ }
3083
+ for (const tag of tags) {
3084
+ if (tag.startsWith(PACKED_DO_TAG_PREFIX)) {
3085
+ for (const pair of tag.slice(PACKED_DO_TAG_PREFIX.length).split(";")) {
3086
+ if (pair === "") continue;
3087
+ const eq = pair.indexOf("=");
3088
+ const logicalId = decodeURIComponent(
3089
+ eq === -1 ? pair : pair.slice(0, eq),
3090
+ );
3091
+ const className = decodeURIComponent(
3092
+ eq === -1 ? pair : pair.slice(eq + 1),
3093
+ );
3094
+ if (logicalId && className) {
3095
+ map[logicalId] = className;
3096
+ }
3097
+ }
3098
+ }
3099
+ }
3100
+ return map;
2603
3101
  }
3102
+
3103
+ const isDurableObjectTag = (tag: string) =>
3104
+ tag.startsWith(LEGACY_DO_TAG_PREFIX) || tag.startsWith(PACKED_DO_TAG_PREFIX);
3105
+
3106
+ /**
3107
+ * Fail fast — before the script upload — when a worker's tag set violates
3108
+ * Cloudflare's limits, instead of surfacing the raw `Forbidden`/`BadRequest`
3109
+ * at PUT time (#811). `alchemyTagCount` is the number of tags alchemy itself
3110
+ * generated (ownership + migration + packed DO tags) so the message can tell
3111
+ * the user how much of the budget is theirs.
3112
+ */
3113
+ const validateWorkerTags = (
3114
+ scriptName: string,
3115
+ tags: ReadonlyArray<string>,
3116
+ alchemyTagCount: number,
3117
+ ) =>
3118
+ Effect.gen(function* () {
3119
+ if (tags.length > MAX_TAGS_PER_WORKER) {
3120
+ return yield* Effect.fail(
3121
+ new InvalidWorkerTags({
3122
+ scriptName,
3123
+ reason:
3124
+ `worker "${scriptName}" needs ${tags.length} script tags but Cloudflare allows at most ${MAX_TAGS_PER_WORKER}. ` +
3125
+ `Alchemy reserves ${alchemyTagCount} (ownership, migration and durable-object metadata); ` +
3126
+ `${tags.length - alchemyTagCount} user tags were passed via \`tags\`. Remove ${tags.length - MAX_TAGS_PER_WORKER} tag(s).`,
3127
+ }),
3128
+ );
3129
+ }
3130
+ const encoder = new TextEncoder();
3131
+ for (const tag of tags) {
3132
+ if (tag.includes(",") || tag.includes("&")) {
3133
+ return yield* Effect.fail(
3134
+ new InvalidWorkerTags({
3135
+ scriptName,
3136
+ reason: `worker "${scriptName}" tag ${JSON.stringify(tag)} contains ',' or '&', which Cloudflare rejects.`,
3137
+ }),
3138
+ );
3139
+ }
3140
+ const bytes = yield* Effect.sync(() => encoder.encode(tag).length);
3141
+ if (bytes > MAX_TAG_BYTES) {
3142
+ return yield* Effect.fail(
3143
+ new InvalidWorkerTags({
3144
+ scriptName,
3145
+ reason: `worker "${scriptName}" tag ${JSON.stringify(tag.slice(0, 64))}… is ${bytes} bytes; Cloudflare allows at most ${MAX_TAG_BYTES}.`,
3146
+ }),
3147
+ );
3148
+ }
3149
+ }
3150
+ });