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";
@@ -25,13 +26,53 @@ import { readAssets, uploadAssets } from "./Assets.js";
25
26
  import { getCompatibility } from "./Compatibility.js";
26
27
  import { isDurableObjectExport } from "./DurableObject.js";
27
28
  import { LocalWorkerProvider } from "./LocalWorkerProvider.js";
28
- import { Worker } from "./Worker.js";
29
+ import { Worker, } from "./Worker.js";
29
30
  import { getCacheBinding, getCronBindings } from "./WorkerAsyncBindings.js";
30
31
  import { readPrebuiltWorkerBundle, WorkerBundle } from "./WorkerBundle.js";
31
32
  import { isWorkerLoader } from "./WorkerLoader.js";
32
33
  import { createWorkerName } from "./WorkerName.js";
33
34
  class MissingDurableObjects extends Data.TaggedError("MissingDurableObjects") {
34
35
  }
36
+ /**
37
+ * A Durable Object class is being dropped from this Worker while a binding in
38
+ * the same deploy still references it on another script — the class moved
39
+ * cross-script, but its namespace (and every stored object in it) still lives
40
+ * on this Worker. Cloudflare rejects a single upload that both deletes the
41
+ * class and ships a binding referencing it, and silently deleting would
42
+ * destroy the namespace's data irreversibly, so the deploy fails before any
43
+ * upload.
44
+ *
45
+ * Moving a Durable Object class between Workers is always declared: set
46
+ * `transferredFrom` on the Durable Object at its **new host** — naming the
47
+ * former host by Worker logical id (same stack) or physical script name — and
48
+ * Alchemy performs the data-preserving `transferred_classes` migration on the
49
+ * new host's deploy; this deploy then converges on its own. To abandon the
50
+ * data instead, remove the binding entirely in one deploy (which deletes the
51
+ * class and its data), then add the cross-script binding in a second deploy.
52
+ */
53
+ export class DurableObjectTransferRequired extends Data.TaggedError("DurableObjectTransferRequired") {
54
+ get message() {
55
+ return (`Durable Object class '${this.className}' still lives on Worker '${this.scriptName}' but this deploy re-binds it as a cross-script reference` +
56
+ (this.targetScriptName ? ` to '${this.targetScriptName}'` : "") +
57
+ ". Durable Object data does NOT move with the class automatically. " +
58
+ `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. ` +
59
+ "To abandon the data, remove the binding entirely in one deploy before re-adding it as a cross-script reference.");
60
+ }
61
+ }
62
+ /**
63
+ * More than one script matches the `transferredFrom` declaration of a Durable
64
+ * Object (e.g. an orphaned script left behind by a `name` prop change still
65
+ * carries the same alchemy tags, or the host history lists several scripts
66
+ * that each still hold a same-class namespace). Alchemy refuses to guess
67
+ * which namespace's data to move — narrow the declaration to the exact
68
+ * physical script name that holds the data.
69
+ */
70
+ export class AmbiguousDurableObjectTransfer extends Data.TaggedError("AmbiguousDurableObjectTransfer") {
71
+ get message() {
72
+ return (`Durable Object '${this.logicalId}' (class '${this.className}') is new to Worker '${this.scriptName}' and multiple scripts match its transferredFrom declaration: ${this.sources.join(", ")}. ` +
73
+ `Narrow transferredFrom to the exact physical script name that holds the data.`);
74
+ }
75
+ }
35
76
  /**
36
77
  * Resolve the Workers for Platforms dispatch-namespace *name* from a resolved
37
78
  * `namespace` prop or persisted attribute. The engine resolves a passed
@@ -626,6 +667,52 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
626
667
  const actualTags = new Set(tags ?? []);
627
668
  return createAlchemyWorkerTags(id).every((tag) => actualTags.has(tag));
628
669
  };
670
+ /**
671
+ * Resolve the source script of a declared Durable Object transfer for
672
+ * a class that is new to `selfScriptName`. `sources` is the
673
+ * `transferredFrom` host history: each entry names a former host either
674
+ * by *physical script name* or by *Worker logical id* in this stack +
675
+ * stage (matched via the `alchemy:id:`/stack/stage ownership tags).
676
+ * The namespace is transferred from whichever listed host currently
677
+ * holds a same-class namespace; when none does (fresh stage, or every
678
+ * transfer already completed) this returns `undefined` and the caller
679
+ * creates the class fresh — the declaration is inert and safe to keep.
680
+ * More than one match (e.g. an orphaned script from a `name` change)
681
+ * fails with {@link AmbiguousDurableObjectTransfer} rather than
682
+ * guessing whose data to move.
683
+ */
684
+ const resolveTransferSource = Effect.fn(function* (params) {
685
+ if (params.sources.length === 0) {
686
+ return undefined;
687
+ }
688
+ const candidates = Array.from(new Set(params.observedNamespaces.flatMap((ns) => ns.class === params.className &&
689
+ ns.script !== params.selfScriptName
690
+ ? [ns.script]
691
+ : [])));
692
+ const matched = [];
693
+ for (const script of candidates) {
694
+ if (params.sources.includes(script)) {
695
+ matched.push(script);
696
+ continue;
697
+ }
698
+ const settings = yield* getScriptSettings(params.accountId, script, undefined).pipe(Effect.catchTag("WorkerNotFound", () => Effect.succeed(undefined)), Effect.catchTag("WorkerHasNoVersions", () => Effect.succeed(undefined)));
699
+ const tags = new Set(settings?.tags ?? []);
700
+ if (tags.has(`alchemy:stack:${stack.name}`) &&
701
+ tags.has(`alchemy:stage:${stack.stage}`) &&
702
+ params.sources.some((source) => tags.has(`alchemy:id:${source}`))) {
703
+ matched.push(script);
704
+ }
705
+ }
706
+ if (matched.length > 1) {
707
+ return yield* Effect.fail(new AmbiguousDurableObjectTransfer({
708
+ scriptName: params.selfScriptName,
709
+ logicalId: params.logicalId,
710
+ className: params.className,
711
+ sources: matched,
712
+ }));
713
+ }
714
+ return matched[0];
715
+ });
629
716
  const getDurableObjects = (bindings) => {
630
717
  const namespaces = Object.fromEntries((bindings ?? []).flatMap((binding) => binding.type === "durable_object_namespace" &&
631
718
  binding.className &&
@@ -712,7 +799,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
712
799
  // (~0.5s), which is only needed for vite-based workers at build time —
713
800
  // not for every Worker definition at module-load time.
714
801
  const Vite = yield* Effect.promise(() => import("./Vite.js"));
715
- const { clientDirectory, serverBundle } = yield* Vite.viteBuild(props.vite?.rootDir, Object.fromEntries((yield* Effect.all(Object.entries(props.env ?? {}).map(Effect.fn(function* ([key, value]) {
802
+ const { clientDirectory, serverBundle, externalWorkspaces } = yield* Vite.viteBuild(props.vite?.rootDir, Object.fromEntries((yield* Effect.all(Object.entries(props.env ?? {}).map(Effect.fn(function* ([key, value]) {
716
803
  return [
717
804
  key,
718
805
  typeof value === "string"
@@ -736,7 +823,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
736
823
  compatibilityFlags: compatibility.flags,
737
824
  viteEnvironments: props.vite?.viteEnvironments,
738
825
  });
739
- const [assets, bundle] = yield* Effect.all([
826
+ const [assets, bundle, input] = yield* Effect.all([
740
827
  clientDirectory
741
828
  ? readAssets({
742
829
  ...(props.assets && typeof props.assets !== "string"
@@ -746,11 +833,34 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
746
833
  })
747
834
  : Effect.undefined,
748
835
  serverBundle,
836
+ hashViteInput(props.vite?.rootDir, props.vite?.memo, externalWorkspaces),
749
837
  ], { concurrency: "unbounded" });
750
838
  if (!assets && !bundle) {
751
839
  return yield* Effect.die(new Error("Vite build produced neither assets nor server output"));
752
840
  }
753
- return { assets, bundle };
841
+ return {
842
+ assets,
843
+ bundle,
844
+ input: input.hash,
845
+ additionalWorkspaces: input.workspaces,
846
+ };
847
+ });
848
+ const hashViteInput = Effect.fn(function* (rootDir = process.cwd(), options, additionalWorkspaces) {
849
+ const hashWorkspaceDirectory = (cwd, memo) => hashDirectory({ cwd: path.resolve(rootDir, cwd), memo }).pipe(Effect.map((hash) => `${path.relative(rootDir, cwd)}:${hash}`));
850
+ const hashRoot = hashWorkspaceDirectory(rootDir, options);
851
+ if (Array.isArray(options?.workspaces)) {
852
+ return yield* Effect.all([
853
+ hashRoot,
854
+ ...options.workspaces.map(({ cwd, ...options }) => hashWorkspaceDirectory(cwd, options)),
855
+ ], { concurrency: "unbounded" }).pipe(Effect.flatMap(([root, ...workspaces]) => sha256Object([root, ...workspaces.sort()])), Effect.map((hash) => ({ hash, workspaces: undefined })));
856
+ }
857
+ const [root, workspaces] = yield* Effect.all([hashRoot, additionalWorkspaces], { concurrency: "unbounded" });
858
+ const workspaceHashes = yield* Effect.forEach(workspaces, (cwd) => hashWorkspaceDirectory(cwd), { concurrency: "unbounded" });
859
+ const hash = yield* sha256Object([root, ...workspaceHashes.sort()]);
860
+ return {
861
+ hash,
862
+ workspaces: Array.from(workspaces).map((cwd) => path.relative(rootDir, cwd)),
863
+ };
754
864
  });
755
865
  const prepareAssetsAndBundle = (id, props, opts = {}) => Effect.gen(function* () {
756
866
  if (props.script !== undefined) {
@@ -766,24 +876,12 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
766
876
  files: [{ path: "main.js", content: props.script }],
767
877
  hash: bundleHash,
768
878
  },
879
+ input: undefined,
880
+ additionalWorkspaces: undefined,
769
881
  };
770
882
  }
771
883
  if (props.vite) {
772
- const [{ assets, bundle }, input] = yield* Effect.all([
773
- viteBuild(props),
774
- // hashDirectory expects `{ cwd, memo }`. The vite props
775
- // store the project root under `rootDir`, so map it
776
- // here. Without this, `cwd` falls back to
777
- // `process.cwd()` and the input hash is computed over
778
- // the wrong directory tree (often the entire monorepo
779
- // root), making it both slow and unable to detect
780
- // changes scoped to the actual Vite project.
781
- hashDirectory({
782
- cwd: props.vite.rootDir,
783
- memo: props.vite.memo,
784
- }),
785
- ], { concurrency: "unbounded" });
786
- return { assets, bundle, input };
884
+ return yield* viteBuild(props);
787
885
  }
788
886
  const [assets, bundle] = yield* Effect.all([
789
887
  opts.skipAssetsRead
@@ -791,8 +889,13 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
791
889
  : prepareAssets(props.assets),
792
890
  prepareBundle(id, props),
793
891
  ], { concurrency: "unbounded" });
794
- return { assets, bundle };
795
- }).pipe(Effect.map(({ assets, bundle, input }) => ({
892
+ return {
893
+ assets,
894
+ bundle,
895
+ input: undefined,
896
+ additionalWorkspaces: undefined,
897
+ };
898
+ }).pipe(Effect.map(({ assets, bundle, input, additionalWorkspaces }) => ({
796
899
  assets,
797
900
  bundle: {
798
901
  main: bundle?.files[0].path,
@@ -804,6 +907,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
804
907
  assets: assets?.hash,
805
908
  bundle: bundle?.hash,
806
909
  input,
910
+ additionalWorkspaces,
807
911
  },
808
912
  })));
809
913
  const normalizePrebuiltAssets = (assets, output) => {
@@ -847,7 +951,18 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
847
951
  assets: prebuiltAssets?.hash ?? preparedHash.assets,
848
952
  metadata: metadataHash,
849
953
  };
850
- const metadataBindings = bindings.flatMap((b) => b.data.bindings ?? []);
954
+ // `transferredFrom` is alchemy-only transfer metadata on
955
+ // durable_object_namespace bindings — it drives the
956
+ // `transferred_classes` migration below and must be stripped from the
957
+ // wire-shape binding before upload.
958
+ const metadataBindings = bindings.flatMap((b) => (b.data.bindings ?? []).map((item) => {
959
+ if (item.type === "durable_object_namespace" &&
960
+ item.transferredFrom !== undefined) {
961
+ const { transferredFrom: _, ...rest } = item;
962
+ return rest;
963
+ }
964
+ return item;
965
+ }));
851
966
  const expectedDurableObjectClassNames = getExpectedDurableObjectClassNames(metadataBindings, name);
852
967
  let metadataAssets;
853
968
  let keepAssets = false;
@@ -956,7 +1071,8 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
956
1071
  .pipe(Effect.map((s) => s), Effect.catch(() => Effect.succeed(undefined))));
957
1072
  const oldTags = Array.from(new Set(oldSettings?.tags ?? []));
958
1073
  const oldBindings = oldSettings?.bindings ?? [];
959
- // Parse alchemy:do:{logicalId}:{className} tags
1074
+ // Parse the DO logical-id→class mapping from script tags (packed
1075
+ // `alchemy:dos:` and legacy per-DO `alchemy:do:` formats)
960
1076
  const oldDoClassNameByLogicalId = getDurableObjectTagMap(oldTags);
961
1077
  const currentDoBindings = getDurableObjectBindings(bindings, name);
962
1078
  const currentDoClassNameByLogicalId = Object.fromEntries(currentDoBindings.map((binding) => [
@@ -968,11 +1084,13 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
968
1084
  ? [tag.slice("alchemy:migration-tag:".length)]
969
1085
  : [])[0];
970
1086
  const newMigrationTag = bumpMigrationTagVersion(oldMigrationTag);
971
- // Compute deleted classes
972
- const deletedClasses = [];
1087
+ // Compute delete-class candidates. Candidates are validated against
1088
+ // observed namespace ownership below — a class may already have been
1089
+ // transferred to another script by its new host's deploy.
1090
+ const deletedClassCandidates = [];
973
1091
  for (const [logicalId, className] of Object.entries(oldDoClassNameByLogicalId)) {
974
1092
  if (!currentDoClassNameByLogicalId[logicalId]) {
975
- deletedClasses.push(className);
1093
+ deletedClassCandidates.push(className);
976
1094
  }
977
1095
  }
978
1096
  // Backward compatibility for old workers that have DO bindings but no
@@ -988,36 +1106,150 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
988
1106
  oldBinding.className &&
989
1107
  ownedLocally &&
990
1108
  !currentDoBindings.some((binding) => binding.bindingName === oldBinding.name)) {
991
- deletedClasses.push(oldBinding.className);
1109
+ deletedClassCandidates.push(oldBinding.className);
992
1110
  }
993
1111
  }
994
1112
  }
1113
+ // Class names the current deploy references *cross-script* (mapped to
1114
+ // the foreign script). A class that both leaves the "hosted here" set
1115
+ // and shows up here has moved to another Worker — Cloudflare rejects
1116
+ // deleting a class while any binding in the upload references its
1117
+ // class name, and deleting would destroy the namespace's data.
1118
+ const crossScriptClassTargets = new Map();
1119
+ for (const item of metadataBindings) {
1120
+ if (item.type === "durable_object_namespace" &&
1121
+ item.className &&
1122
+ typeof item.scriptName === "string" &&
1123
+ item.scriptName !== name) {
1124
+ crossScriptClassTargets.set(item.className, item.scriptName);
1125
+ }
1126
+ }
1127
+ // One account-level namespace listing serves both sides of a
1128
+ // transfer: the destination checks the source still hosts the class
1129
+ // before emitting `transferred_classes`, and the former host checks
1130
+ // whether a to-be-deleted class was already transferred away.
1131
+ // Dispatch-namespace user workers keep the legacy behavior — their
1132
+ // namespaces don't surface on the account-level list.
1133
+ const mayTransferIn = currentDoBindings.some((binding) => !oldDoClassNameByLogicalId[binding.logicalId] &&
1134
+ binding.transferredFrom !== undefined);
1135
+ const observedNamespaces = !dispatchNamespace &&
1136
+ (deletedClassCandidates.length > 0 || mayTransferIn)
1137
+ ? yield* listDurableObjectNamespaces(accountId)
1138
+ : [];
1139
+ const hosts = (namespaces, scriptName, className) => namespaces.some((ns) => ns.script === scriptName && ns.class === className);
1140
+ const scriptHostsClass = (scriptName, className) => hosts(observedNamespaces, scriptName, className);
1141
+ const deletedClasses = [];
1142
+ for (const className of deletedClassCandidates) {
1143
+ if (dispatchNamespace) {
1144
+ deletedClasses.push(className);
1145
+ continue;
1146
+ }
1147
+ const targetScriptName = crossScriptClassTargets.get(className);
1148
+ if (targetScriptName === undefined) {
1149
+ // Plain removal. Delete only if the namespace actually still
1150
+ // lives here — it may have been transferred to another script by
1151
+ // that script's deploy, or removed out-of-band. The stale
1152
+ // alchemy:do tag drops out either way because tags are recomputed
1153
+ // from current bindings.
1154
+ if (scriptHostsClass(name, className)) {
1155
+ deletedClasses.push(className);
1156
+ }
1157
+ continue;
1158
+ }
1159
+ // The class went local → cross-script. When the new host's deploy
1160
+ // ran a `transferred_classes` migration moments ago, the account
1161
+ // listing can briefly still attribute the namespace to this script,
1162
+ // so re-observe with a short bounded budget until the transfer
1163
+ // becomes visible (namespace off this script) or the state is
1164
+ // conclusively a conflict (still here — including the case where
1165
+ // the target created a *fresh* namespace for the same class name).
1166
+ const namespaces = yield* listDurableObjectNamespaces(accountId).pipe(Effect.repeat({
1167
+ schedule: Schedule.spaced("2 seconds"),
1168
+ until: (observed) => !hosts(observed, name, className) ||
1169
+ hosts(observed, targetScriptName, className),
1170
+ times: 5,
1171
+ }));
1172
+ if (!hosts(namespaces, name, className)) {
1173
+ // Transferred away — nothing to delete.
1174
+ continue;
1175
+ }
1176
+ // local → cross-script transition without a transfer. Fail before
1177
+ // any upload: Cloudflare would reject the combined delete + binding
1178
+ // anyway, and silently deleting would destroy the namespace's
1179
+ // data. See the error's docs for the two ways out
1180
+ // (`transferredFrom` on the new host, or a two-phase removal).
1181
+ return yield* Effect.fail(new DurableObjectTransferRequired({
1182
+ scriptName: name,
1183
+ className,
1184
+ targetScriptName,
1185
+ }));
1186
+ }
995
1187
  // Collect container-backed class names so we can send container metadata
996
1188
  const containerClassNames = new Set(bindings.flatMap((b) => (b.data.containers ?? []).map((c) => c.className)));
997
- // Compute new and renamed classes
1189
+ // Compute new, renamed, and transferred classes
998
1190
  const newClasses = [];
999
1191
  const newSqliteClasses = [];
1000
1192
  const renamedClasses = [];
1193
+ const transferredClasses = [];
1001
1194
  for (const binding of currentDoBindings) {
1002
1195
  let previousClassName = oldDoClassNameByLogicalId[binding.logicalId];
1003
1196
  if (!previousClassName) {
1004
- // No `alchemy:do:` tag maps this logical id to a class — the
1197
+ // No DO metadata tag maps this logical id to a class — the
1005
1198
  // worker was created outside Alchemy (raw API / Wrangler) or
1006
1199
  // before these tags existed. Fall back to matching the observed
1007
1200
  // cloud binding by binding name so adoption reuses the existing
1008
1201
  // class instead of asking Cloudflare to create one that already
1009
1202
  // exists (which fails the migration). This is the "first deploy
1010
1203
  // must match the existing class name" path; once we write the
1011
- // `alchemy:do:` tag, subsequent renames are driven by logical id.
1204
+ // `alchemy:dos:` tag, subsequent renames are driven by logical id.
1012
1205
  const observed = oldBindings.find((old) => old.type === "durable_object_namespace" &&
1013
1206
  "className" in old &&
1014
1207
  old.className &&
1208
+ // Only a *locally-owned* binding proves the class exists on
1209
+ // this script. A cross-script binding under the same name —
1210
+ // e.g. this worker previously referenced another host's
1211
+ // class and now hosts its own — points at a foreign
1212
+ // namespace and must not suppress the create migration
1213
+ // (Cloudflare rejects a local binding for a class the
1214
+ // script isn't configured to implement).
1215
+ (!("scriptName" in old) ||
1216
+ old.scriptName === undefined ||
1217
+ old.scriptName === name) &&
1015
1218
  old.name === binding.bindingName);
1016
1219
  if (observed && "className" in observed && observed.className) {
1017
1220
  previousClassName = observed.className;
1018
1221
  }
1019
1222
  }
1020
1223
  if (!previousClassName) {
1224
+ // A class new to this script is a host move when the declaration
1225
+ // says so: `transferredFrom` lists the former host(s) — moves
1226
+ // are always declared, never inferred, because a class deleted
1227
+ // on one worker and created on another is otherwise ambiguous
1228
+ // between "move the data" and "delete + fresh namespace". The
1229
+ // declared source must be observed to still host the namespace;
1230
+ // otherwise (fresh stage, transfer already completed) fall
1231
+ // through to a plain create.
1232
+ const fromScript = dispatchNamespace
1233
+ ? undefined
1234
+ : yield* resolveTransferSource({
1235
+ accountId,
1236
+ selfScriptName: name,
1237
+ logicalId: binding.logicalId,
1238
+ className: binding.className,
1239
+ sources: normalizeTransferSources(binding.transferredFrom, name),
1240
+ observedNamespaces,
1241
+ });
1242
+ if (fromScript !== undefined) {
1243
+ // Data-preserving move: ship Cloudflare's
1244
+ // `transferred_classes` migration instead of creating a
1245
+ // fresh class.
1246
+ transferredClasses.push({
1247
+ from: binding.className,
1248
+ fromScript,
1249
+ to: binding.className,
1250
+ });
1251
+ continue;
1252
+ }
1021
1253
  // Default all new Durable Object classes to SQLite. Cloudflare
1022
1254
  // recommends SQLite for new namespaces, and container-backed
1023
1255
  // Durable Objects require it.
@@ -1035,28 +1267,29 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1035
1267
  currentDoClassNameByLogicalId,
1036
1268
  deletedClasses,
1037
1269
  renamedClasses,
1270
+ transferredClasses,
1038
1271
  newSqliteClasses,
1039
1272
  })}`);
1040
- // Build alchemy:do:{logicalId}:{className} tags for each DO binding
1041
- const alchemyDoTags = [];
1042
- for (const binding of currentDoBindings) {
1043
- alchemyDoTags.push(`alchemy:do:${binding.logicalId}:${binding.className}`);
1044
- }
1045
- const metadataTags = Array.from(new Set([
1273
+ // Pack every DO logical-id→class mapping into as few `alchemy:dos:`
1274
+ // tags as possible — one tag per DO blows Cloudflare's 10-tag limit
1275
+ // at 7+ bindings (#811).
1276
+ const alchemyDoTags = encodeDurableObjectTags(currentDoBindings);
1277
+ const alchemyTags = [
1046
1278
  ...createAlchemyWorkerTags(id),
1047
1279
  ...alchemyDoTags,
1048
1280
  ...(newMigrationTag
1049
1281
  ? [`alchemy:migration-tag:${newMigrationTag}`]
1050
1282
  : []),
1051
- ...(news.tags ?? []),
1052
- ]));
1283
+ ];
1284
+ const metadataTags = Array.from(new Set([...alchemyTags, ...(news.tags ?? [])]));
1285
+ yield* validateWorkerTags(name, metadataTags, alchemyTags.length);
1053
1286
  const migrations = {
1054
1287
  oldTag: oldMigrationTag,
1055
1288
  newTag: newMigrationTag,
1056
1289
  newClasses,
1057
1290
  deletedClasses,
1058
1291
  renamedClasses,
1059
- transferredClasses: [],
1292
+ transferredClasses,
1060
1293
  newSqliteClasses,
1061
1294
  };
1062
1295
  const metadataContainers = [...containerClassNames].map((className) => ({
@@ -1253,11 +1486,8 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1253
1486
  return assetsHash !== output.hash?.assets;
1254
1487
  }
1255
1488
  if (props.vite) {
1256
- const input = yield* hashDirectory({
1257
- cwd: props.vite.rootDir,
1258
- memo: props.vite.memo,
1259
- });
1260
- return input !== output.hash?.input;
1489
+ const { hash } = yield* hashViteInput(props.vite.rootDir, props.vite.memo, Effect.succeed(output.hash?.additionalWorkspaces ?? []));
1490
+ return hash !== output.hash?.input;
1261
1491
  }
1262
1492
  const bundleHash = yield* prepareBundle(id, props).pipe(Effect.map((b) => b.hash));
1263
1493
  if (bundleHash !== output.hash?.bundle) {
@@ -1489,19 +1719,27 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1489
1719
  const exportDerived = Object.keys(exportMap)
1490
1720
  .filter((logicalId) => isDurableObjectExport(exportMap[logicalId]))
1491
1721
  .map((logicalId) => ({ logicalId, className: logicalId }));
1492
- const durableObjects = mergeDurableObjectClasses(exportDerived, getDurableObjectBindings(bindings, name));
1722
+ // Transfer-destination classes are excluded from the placeholder
1723
+ // entirely (class list, bindings, tags): Cloudflare forbids
1724
+ // creating the destination class of a `transferred_classes`
1725
+ // migration ahead of the transfer, so `reconcile` must perform it
1726
+ // with the real upload. (`transferredFrom` may still be an
1727
+ // unresolved Output here — precreate runs on raw props — but only
1728
+ // its presence matters.)
1729
+ const durableObjects = mergeDurableObjectClasses(exportDerived, getDurableObjectBindings(bindings, name)).filter((binding) => !binding.transferredFrom);
1493
1730
  const doClasses = durableObjects.map((binding) => binding.className);
1494
1731
  // Only attach container metadata for classes actually fronted by a
1495
1732
  // Container binding (mirrors reconcile's `containerClassNames`).
1496
1733
  // Mapping every DO class to a container would wrongly mark plain DOs
1497
1734
  // as container-backed in the placeholder.
1498
1735
  const containers = Array.from(new Set(bindings.flatMap((b) => (b.data.containers ?? []).map((c) => c.className)))).map((className) => ({ className }));
1499
- const alchemyDoTags = durableObjects.map(({ logicalId, className }) => `alchemy:do:${logicalId}:${className}`);
1500
- const tags = Array.from(new Set([
1736
+ const alchemyDoTags = encodeDurableObjectTags(durableObjects);
1737
+ const alchemyTags = [
1501
1738
  ...createAlchemyWorkerTags(id),
1502
1739
  ...alchemyDoTags,
1503
- ...(news.tags ?? []),
1504
- ]));
1740
+ ];
1741
+ const tags = Array.from(new Set([...alchemyTags, ...(news.tags ?? [])]));
1742
+ yield* validateWorkerTags(name, tags, alchemyTags.length);
1505
1743
  yield* Effect.logInfo(`Cloudflare Worker precreate: starting ${name}`);
1506
1744
  yield* Effect.logInfo(`Cloudflare Worker precreate: durable objects ${JSON.stringify(durableObjects)}`);
1507
1745
  const existingSettings = yield* getScriptSettings(accountId, name, dispatchNamespace).pipe(
@@ -1729,8 +1967,8 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1729
1967
  // The dispatch-namespace endpoints raise
1730
1968
  // `DispatchNamespaceScriptNotFound` / `DispatchNamespaceNotFound`.
1731
1969
  Effect.catchTag("WorkerNotFound", () => Effect.succeed(undefined)), Effect.catchTag("WorkerHasNoVersions", () => Effect.succeed(undefined)), Effect.catchTag("DispatchNamespaceScriptNotFound", () => Effect.succeed(undefined)), Effect.catchTag("DispatchNamespaceNotFound", () => Effect.succeed(undefined)));
1732
- yield* Effect.logInfo(`Cloudflare Worker reconcile: existing durable object tags ${JSON.stringify((existingSettings?.tags ?? []).filter((tag) => tag.startsWith("alchemy:do:")))}`);
1733
- yield* Effect.logInfo(`Cloudflare Worker reconcile: previous durable object tags ${JSON.stringify((output?.tags ?? []).filter((tag) => tag.startsWith("alchemy:do:")))}`);
1970
+ yield* Effect.logInfo(`Cloudflare Worker reconcile: existing durable object tags ${JSON.stringify((existingSettings?.tags ?? []).filter(isDurableObjectTag))}`);
1971
+ yield* Effect.logInfo(`Cloudflare Worker reconcile: previous durable object tags ${JSON.stringify((output?.tags ?? []).filter(isDurableObjectTag))}`);
1734
1972
  return yield* putWorker(id, news, bindings, olds, output, session, existingSettings);
1735
1973
  }),
1736
1974
  delete: Effect.fn(function* ({ output }) {
@@ -1819,6 +2057,26 @@ const contentTypeFromExtension = (extension) => {
1819
2057
  return "application/octet-stream";
1820
2058
  }
1821
2059
  };
2060
+ /**
2061
+ * Observe every Durable Object namespace on the account as `(script, class)`
2062
+ * pairs. Namespace ownership is authoritative cloud state: after a
2063
+ * `transferred_classes` migration the namespace moves to the receiving
2064
+ * script, so this is how both sides of a transfer observe where a class
2065
+ * currently lives — the destination checks the source still hosts the class
2066
+ * before emitting the transfer, and the former host checks whether a class
2067
+ * it is about to delete has already been transferred away.
2068
+ */
2069
+ const listDurableObjectNamespaces = (accountId) => durableObjectsApi.listNamespaces.items({ accountId }).pipe(Stream.runCollect, Effect.map((namespaces) => Array.from(namespaces).flatMap((ns) => ns.script && ns.class ? [{ script: ns.script, class: ns.class }] : [])));
2070
+ /**
2071
+ * Coerce a resolved `transferredFrom` declaration to its list form, dropping
2072
+ * self-references (a worker naming itself as its own former host is
2073
+ * meaningless — the class already lives there).
2074
+ */
2075
+ const normalizeTransferSources = (value, selfScriptName) => (value === undefined
2076
+ ? []
2077
+ : Array.isArray(value)
2078
+ ? value
2079
+ : [value]).filter((source) => source !== selfScriptName);
1822
2080
  function bumpMigrationTagVersion(oldTag) {
1823
2081
  if (!oldTag)
1824
2082
  return undefined;
@@ -1830,7 +2088,7 @@ function bumpMigrationTagVersion(oldTag) {
1830
2088
  /**
1831
2089
  * Merges a worker's export-derived and binding-derived Durable Object class
1832
2090
  * lists for the precreate placeholder, deduping by class name. The
1833
- * binding-derived entry wins on a collision so the `alchemy:do:` tag keys off
2091
+ * binding-derived entry wins on a collision so the `alchemy:dos:` tag keys off
1834
2092
  * the same logical id (the binding sid) that `reconcile` writes.
1835
2093
  */
1836
2094
  function mergeDurableObjectClasses(exportDerived, bindingDerived) {
@@ -1864,19 +2122,150 @@ function getDurableObjectBindings(bindings, workerName) {
1864
2122
  logicalId: binding.sid,
1865
2123
  bindingName: item.name,
1866
2124
  className: item.className,
2125
+ // Declared host history for a data-preserving
2126
+ // `transferred_classes` migration. Normalize an empty list to
2127
+ // "not declared"; self-references are dropped at resolution time.
2128
+ transferredFrom: Array.isArray(item.transferredFrom) &&
2129
+ item.transferredFrom.length === 0
2130
+ ? undefined
2131
+ : item.transferredFrom,
1867
2132
  },
1868
2133
  ];
1869
2134
  }));
1870
2135
  }
1871
- function getDurableObjectTagMap(tags) {
1872
- return Object.fromEntries(tags.flatMap((tag) => {
1873
- if (!tag.startsWith("alchemy:do:")) {
1874
- return [];
2136
+ /**
2137
+ * Cloudflare Worker script-tag limits, verified empirically against the live
2138
+ * API (2026-07):
2139
+ *
2140
+ * - at most **10 tags** per Worker (the 11th is rejected with `Forbidden`)
2141
+ * - each tag is at most **1024 bytes** (`BadRequest: Tag is too large`)
2142
+ * - tags may not contain `,` or `&`; everything else (`:`, `;`, `=`, `/`,
2143
+ * spaces, unicode) is accepted and round-trips intact
2144
+ *
2145
+ * Alchemy reserves 3 ownership tags (`alchemy:stack/stage/id`) and 1
2146
+ * migration tag, so the Durable Object logical-id→class mapping must not
2147
+ * spend one tag per DO (#811). Instead all mappings are packed into as few
2148
+ * `alchemy:dos:` tags as possible.
2149
+ */
2150
+ const MAX_TAGS_PER_WORKER = 10;
2151
+ const MAX_TAG_BYTES = 1024;
2152
+ const LEGACY_DO_TAG_PREFIX = "alchemy:do:";
2153
+ const PACKED_DO_TAG_PREFIX = "alchemy:dos:";
2154
+ class InvalidWorkerTags extends Data.TaggedError("InvalidWorkerTags") {
2155
+ }
2156
+ /**
2157
+ * Pack Durable Object logical-id→class mappings into `alchemy:dos:` tags.
2158
+ *
2159
+ * Each mapping is encoded as `logicalId=className` — elided to just
2160
+ * `className` when the two are equal (the common case for export-derived
2161
+ * classes) — with both components `encodeURIComponent`-escaped so the `;`
2162
+ * pair separator, the `=` delimiter, and Cloudflare's forbidden tag
2163
+ * characters (`,`, `&`) can never collide with user identifiers. Pairs are
2164
+ * sorted for deterministic output and greedily packed so each tag stays
2165
+ * within Cloudflare's 1024-byte tag limit; workers with more DOs than fit in
2166
+ * one tag spill into additional `alchemy:dos:` tags.
2167
+ *
2168
+ * `encodeURIComponent` output is pure ASCII, so `String.length` equals the
2169
+ * tag's byte length.
2170
+ *
2171
+ * @internal exported for unit testing.
2172
+ */
2173
+ export function encodeDurableObjectTags(durableObjects) {
2174
+ const pairs = [...durableObjects]
2175
+ .sort((a, b) => a.logicalId.localeCompare(b.logicalId))
2176
+ .map(({ logicalId, className }) => logicalId === className
2177
+ ? encodeURIComponent(className)
2178
+ : `${encodeURIComponent(logicalId)}=${encodeURIComponent(className)}`);
2179
+ const tags = [];
2180
+ let payload = "";
2181
+ for (const pair of pairs) {
2182
+ const appended = payload === "" ? pair : `${payload};${pair}`;
2183
+ if (PACKED_DO_TAG_PREFIX.length + appended.length > MAX_TAG_BYTES &&
2184
+ payload !== "") {
2185
+ tags.push(`${PACKED_DO_TAG_PREFIX}${payload}`);
2186
+ payload = pair;
1875
2187
  }
1876
- const parts = tag.split(":");
1877
- const logicalId = parts[2];
1878
- const className = parts.slice(3).join(":");
1879
- return logicalId && className ? [[logicalId, className]] : [];
1880
- }));
2188
+ else {
2189
+ payload = appended;
2190
+ }
2191
+ }
2192
+ if (payload !== "") {
2193
+ tags.push(`${PACKED_DO_TAG_PREFIX}${payload}`);
2194
+ }
2195
+ return tags;
1881
2196
  }
2197
+ /**
2198
+ * Parse the Durable Object logical-id→class mapping from a worker's script
2199
+ * tags. Reads both formats so workers deployed before the packed format roll
2200
+ * forward transparently:
2201
+ *
2202
+ * - legacy: one `alchemy:do:{logicalId}:{className}` tag per DO
2203
+ * - packed: `alchemy:dos:{pair};{pair};…` (see {@link encodeDurableObjectTags})
2204
+ *
2205
+ * A packed entry wins over a legacy entry for the same logical id.
2206
+ *
2207
+ * @internal exported for unit testing.
2208
+ */
2209
+ export function getDurableObjectTagMap(tags) {
2210
+ const map = {};
2211
+ for (const tag of tags) {
2212
+ if (tag.startsWith(LEGACY_DO_TAG_PREFIX)) {
2213
+ const parts = tag.split(":");
2214
+ const logicalId = parts[2];
2215
+ const className = parts.slice(3).join(":");
2216
+ if (logicalId && className && !(logicalId in map)) {
2217
+ map[logicalId] = className;
2218
+ }
2219
+ }
2220
+ }
2221
+ for (const tag of tags) {
2222
+ if (tag.startsWith(PACKED_DO_TAG_PREFIX)) {
2223
+ for (const pair of tag.slice(PACKED_DO_TAG_PREFIX.length).split(";")) {
2224
+ if (pair === "")
2225
+ continue;
2226
+ const eq = pair.indexOf("=");
2227
+ const logicalId = decodeURIComponent(eq === -1 ? pair : pair.slice(0, eq));
2228
+ const className = decodeURIComponent(eq === -1 ? pair : pair.slice(eq + 1));
2229
+ if (logicalId && className) {
2230
+ map[logicalId] = className;
2231
+ }
2232
+ }
2233
+ }
2234
+ }
2235
+ return map;
2236
+ }
2237
+ const isDurableObjectTag = (tag) => tag.startsWith(LEGACY_DO_TAG_PREFIX) || tag.startsWith(PACKED_DO_TAG_PREFIX);
2238
+ /**
2239
+ * Fail fast — before the script upload — when a worker's tag set violates
2240
+ * Cloudflare's limits, instead of surfacing the raw `Forbidden`/`BadRequest`
2241
+ * at PUT time (#811). `alchemyTagCount` is the number of tags alchemy itself
2242
+ * generated (ownership + migration + packed DO tags) so the message can tell
2243
+ * the user how much of the budget is theirs.
2244
+ */
2245
+ const validateWorkerTags = (scriptName, tags, alchemyTagCount) => Effect.gen(function* () {
2246
+ if (tags.length > MAX_TAGS_PER_WORKER) {
2247
+ return yield* Effect.fail(new InvalidWorkerTags({
2248
+ scriptName,
2249
+ reason: `worker "${scriptName}" needs ${tags.length} script tags but Cloudflare allows at most ${MAX_TAGS_PER_WORKER}. ` +
2250
+ `Alchemy reserves ${alchemyTagCount} (ownership, migration and durable-object metadata); ` +
2251
+ `${tags.length - alchemyTagCount} user tags were passed via \`tags\`. Remove ${tags.length - MAX_TAGS_PER_WORKER} tag(s).`,
2252
+ }));
2253
+ }
2254
+ const encoder = new TextEncoder();
2255
+ for (const tag of tags) {
2256
+ if (tag.includes(",") || tag.includes("&")) {
2257
+ return yield* Effect.fail(new InvalidWorkerTags({
2258
+ scriptName,
2259
+ reason: `worker "${scriptName}" tag ${JSON.stringify(tag)} contains ',' or '&', which Cloudflare rejects.`,
2260
+ }));
2261
+ }
2262
+ const bytes = yield* Effect.sync(() => encoder.encode(tag).length);
2263
+ if (bytes > MAX_TAG_BYTES) {
2264
+ return yield* Effect.fail(new InvalidWorkerTags({
2265
+ scriptName,
2266
+ reason: `worker "${scriptName}" tag ${JSON.stringify(tag.slice(0, 64))}… is ${bytes} bytes; Cloudflare allows at most ${MAX_TAG_BYTES}.`,
2267
+ }));
2268
+ }
2269
+ }
2270
+ });
1882
2271
  //# sourceMappingURL=WorkerProvider.js.map