alchemy 2.0.0-beta.65 → 2.0.0-beta.67

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 (959) hide show
  1. package/bin/exec.js +578 -224
  2. package/bin/exec.js.map +1 -1
  3. package/lib/AWS/ApiGateway/common.d.ts.map +1 -1
  4. package/lib/AWS/ApiGateway/common.js +8 -1
  5. package/lib/AWS/ApiGateway/common.js.map +1 -1
  6. package/lib/AWS/Bedrock/LanguageModel.d.ts +208 -0
  7. package/lib/AWS/Bedrock/LanguageModel.d.ts.map +1 -0
  8. package/lib/AWS/Bedrock/LanguageModel.js +508 -0
  9. package/lib/AWS/Bedrock/LanguageModel.js.map +1 -0
  10. package/lib/AWS/Bedrock/LanguageModelHttp.d.ts +10 -0
  11. package/lib/AWS/Bedrock/LanguageModelHttp.d.ts.map +1 -0
  12. package/lib/AWS/Bedrock/LanguageModelHttp.js +26 -0
  13. package/lib/AWS/Bedrock/LanguageModelHttp.js.map +1 -0
  14. package/lib/AWS/Bedrock/index.d.ts +2 -0
  15. package/lib/AWS/Bedrock/index.d.ts.map +1 -1
  16. package/lib/AWS/Bedrock/index.js +2 -0
  17. package/lib/AWS/Bedrock/index.js.map +1 -1
  18. package/lib/AWS/CloudFront/Distribution.d.ts +24 -0
  19. package/lib/AWS/CloudFront/Distribution.d.ts.map +1 -1
  20. package/lib/AWS/CloudFront/Distribution.js +89 -4
  21. package/lib/AWS/CloudFront/Distribution.js.map +1 -1
  22. package/lib/AWS/CostExplorer/AnomalyMonitor.d.ts +1 -1
  23. package/lib/AWS/CostExplorer/AnomalyMonitor.d.ts.map +1 -1
  24. package/lib/AWS/CostExplorer/AnomalySubscription.d.ts +1 -1
  25. package/lib/AWS/CostExplorer/AnomalySubscription.d.ts.map +1 -1
  26. package/lib/AWS/CostExplorer/BindingHttp.d.ts +3 -3
  27. package/lib/AWS/CostExplorer/BindingHttp.d.ts.map +1 -1
  28. package/lib/AWS/CostExplorer/CostCategory.d.ts +1 -1
  29. package/lib/AWS/CostExplorer/CostCategory.d.ts.map +1 -1
  30. package/lib/AWS/CostExplorer/GetAnomaliesHttp.d.ts +1 -1
  31. package/lib/AWS/CostExplorer/GetAnomaliesHttp.d.ts.map +1 -1
  32. package/lib/AWS/CostExplorer/GetApproximateUsageRecordsHttp.d.ts +1 -1
  33. package/lib/AWS/CostExplorer/GetApproximateUsageRecordsHttp.d.ts.map +1 -1
  34. package/lib/AWS/CostExplorer/GetCommitmentPurchaseAnalysisHttp.d.ts +1 -1
  35. package/lib/AWS/CostExplorer/GetCommitmentPurchaseAnalysisHttp.d.ts.map +1 -1
  36. package/lib/AWS/CostExplorer/GetCostAndUsageComparisonsHttp.d.ts +1 -1
  37. package/lib/AWS/CostExplorer/GetCostAndUsageComparisonsHttp.d.ts.map +1 -1
  38. package/lib/AWS/CostExplorer/GetCostAndUsageHttp.d.ts +1 -1
  39. package/lib/AWS/CostExplorer/GetCostAndUsageHttp.d.ts.map +1 -1
  40. package/lib/AWS/CostExplorer/GetCostAndUsageWithResourcesHttp.d.ts +1 -1
  41. package/lib/AWS/CostExplorer/GetCostAndUsageWithResourcesHttp.d.ts.map +1 -1
  42. package/lib/AWS/CostExplorer/GetCostCategoriesHttp.d.ts +1 -1
  43. package/lib/AWS/CostExplorer/GetCostCategoriesHttp.d.ts.map +1 -1
  44. package/lib/AWS/CostExplorer/GetCostComparisonDriversHttp.d.ts +1 -1
  45. package/lib/AWS/CostExplorer/GetCostComparisonDriversHttp.d.ts.map +1 -1
  46. package/lib/AWS/CostExplorer/GetCostForecastHttp.d.ts +1 -1
  47. package/lib/AWS/CostExplorer/GetCostForecastHttp.d.ts.map +1 -1
  48. package/lib/AWS/CostExplorer/GetDimensionValuesHttp.d.ts +1 -1
  49. package/lib/AWS/CostExplorer/GetDimensionValuesHttp.d.ts.map +1 -1
  50. package/lib/AWS/CostExplorer/GetReservationCoverageHttp.d.ts +1 -1
  51. package/lib/AWS/CostExplorer/GetReservationCoverageHttp.d.ts.map +1 -1
  52. package/lib/AWS/CostExplorer/GetReservationPurchaseRecommendationHttp.d.ts +1 -1
  53. package/lib/AWS/CostExplorer/GetReservationPurchaseRecommendationHttp.d.ts.map +1 -1
  54. package/lib/AWS/CostExplorer/GetReservationUtilizationHttp.d.ts +1 -1
  55. package/lib/AWS/CostExplorer/GetReservationUtilizationHttp.d.ts.map +1 -1
  56. package/lib/AWS/CostExplorer/GetRightsizingRecommendationHttp.d.ts +1 -1
  57. package/lib/AWS/CostExplorer/GetRightsizingRecommendationHttp.d.ts.map +1 -1
  58. package/lib/AWS/CostExplorer/GetSavingsPlanPurchaseRecommendationDetailsHttp.d.ts +1 -1
  59. package/lib/AWS/CostExplorer/GetSavingsPlanPurchaseRecommendationDetailsHttp.d.ts.map +1 -1
  60. package/lib/AWS/CostExplorer/GetSavingsPlansCoverageHttp.d.ts +1 -1
  61. package/lib/AWS/CostExplorer/GetSavingsPlansCoverageHttp.d.ts.map +1 -1
  62. package/lib/AWS/CostExplorer/GetSavingsPlansPurchaseRecommendationHttp.d.ts +1 -1
  63. package/lib/AWS/CostExplorer/GetSavingsPlansPurchaseRecommendationHttp.d.ts.map +1 -1
  64. package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationDetailsHttp.d.ts +1 -1
  65. package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationDetailsHttp.d.ts.map +1 -1
  66. package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationHttp.d.ts +1 -1
  67. package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationHttp.d.ts.map +1 -1
  68. package/lib/AWS/CostExplorer/GetTagsHttp.d.ts +1 -1
  69. package/lib/AWS/CostExplorer/GetTagsHttp.d.ts.map +1 -1
  70. package/lib/AWS/CostExplorer/GetUsageForecastHttp.d.ts +1 -1
  71. package/lib/AWS/CostExplorer/GetUsageForecastHttp.d.ts.map +1 -1
  72. package/lib/AWS/CostExplorer/ListCommitmentPurchaseAnalysesHttp.d.ts +1 -1
  73. package/lib/AWS/CostExplorer/ListCommitmentPurchaseAnalysesHttp.d.ts.map +1 -1
  74. package/lib/AWS/CostExplorer/ListCostAllocationTagBackfillHistoryHttp.d.ts +1 -1
  75. package/lib/AWS/CostExplorer/ListCostAllocationTagBackfillHistoryHttp.d.ts.map +1 -1
  76. package/lib/AWS/CostExplorer/ListCostAllocationTagsHttp.d.ts +1 -1
  77. package/lib/AWS/CostExplorer/ListCostAllocationTagsHttp.d.ts.map +1 -1
  78. package/lib/AWS/CostExplorer/ListCostCategoryResourceAssociationsHttp.d.ts +1 -1
  79. package/lib/AWS/CostExplorer/ListCostCategoryResourceAssociationsHttp.d.ts.map +1 -1
  80. package/lib/AWS/CostExplorer/ListSavingsPlansPurchaseRecommendationGenerationHttp.d.ts +1 -1
  81. package/lib/AWS/CostExplorer/ListSavingsPlansPurchaseRecommendationGenerationHttp.d.ts.map +1 -1
  82. package/lib/AWS/CostExplorer/ProvideAnomalyFeedbackHttp.d.ts +1 -1
  83. package/lib/AWS/CostExplorer/ProvideAnomalyFeedbackHttp.d.ts.map +1 -1
  84. package/lib/AWS/CostExplorer/StartCommitmentPurchaseAnalysisHttp.d.ts +1 -1
  85. package/lib/AWS/CostExplorer/StartCommitmentPurchaseAnalysisHttp.d.ts.map +1 -1
  86. package/lib/AWS/CostExplorer/StartCostAllocationTagBackfillHttp.d.ts +1 -1
  87. package/lib/AWS/CostExplorer/StartCostAllocationTagBackfillHttp.d.ts.map +1 -1
  88. package/lib/AWS/CostExplorer/StartSavingsPlansPurchaseRecommendationGenerationHttp.d.ts +1 -1
  89. package/lib/AWS/CostExplorer/StartSavingsPlansPurchaseRecommendationGenerationHttp.d.ts.map +1 -1
  90. package/lib/AWS/CostExplorer/UpdateCostAllocationTagsStatusHttp.d.ts +1 -1
  91. package/lib/AWS/CostExplorer/UpdateCostAllocationTagsStatusHttp.d.ts.map +1 -1
  92. package/lib/AWS/CostExplorer/common.d.ts +5 -3
  93. package/lib/AWS/CostExplorer/common.d.ts.map +1 -1
  94. package/lib/AWS/CostExplorer/common.js +16 -1
  95. package/lib/AWS/CostExplorer/common.js.map +1 -1
  96. package/lib/AWS/DataSync/Task.d.ts.map +1 -1
  97. package/lib/AWS/DataSync/Task.js +16 -2
  98. package/lib/AWS/DataSync/Task.js.map +1 -1
  99. package/lib/AWS/EC2/Vpc.d.ts.map +1 -1
  100. package/lib/AWS/EC2/Vpc.js +15 -2
  101. package/lib/AWS/EC2/Vpc.js.map +1 -1
  102. package/lib/AWS/EC2/hosted.d.ts.map +1 -1
  103. package/lib/AWS/EC2/hosted.js +10 -2
  104. package/lib/AWS/EC2/hosted.js.map +1 -1
  105. package/lib/AWS/ECR/Image.d.ts +132 -13
  106. package/lib/AWS/ECR/Image.d.ts.map +1 -1
  107. package/lib/AWS/ECR/ImageSource.d.ts.map +1 -1
  108. package/lib/AWS/ECR/ImageSource.js +10 -2
  109. package/lib/AWS/ECR/ImageSource.js.map +1 -1
  110. package/lib/AWS/ECR/Repository.d.ts +2 -0
  111. package/lib/AWS/ECR/Repository.d.ts.map +1 -1
  112. package/lib/AWS/ECR/Repository.js +28 -3
  113. package/lib/AWS/ECR/Repository.js.map +1 -1
  114. package/lib/AWS/ECS/Service.d.ts +27 -1
  115. package/lib/AWS/ECS/Service.d.ts.map +1 -1
  116. package/lib/AWS/ECS/Service.js +184 -22
  117. package/lib/AWS/ECS/Service.js.map +1 -1
  118. package/lib/AWS/ECS/Task.d.ts +32 -8
  119. package/lib/AWS/ECS/Task.d.ts.map +1 -1
  120. package/lib/AWS/ECS/Task.js +62 -29
  121. package/lib/AWS/ECS/Task.js.map +1 -1
  122. package/lib/AWS/EKS/Deployment.d.ts.map +1 -1
  123. package/lib/AWS/EKS/Deployment.js +10 -2
  124. package/lib/AWS/EKS/Deployment.js.map +1 -1
  125. package/lib/AWS/EKS/Job.d.ts.map +1 -1
  126. package/lib/AWS/EKS/Job.js +10 -2
  127. package/lib/AWS/EKS/Job.js.map +1 -1
  128. package/lib/AWS/Lambda/Function.d.ts +15 -0
  129. package/lib/AWS/Lambda/Function.d.ts.map +1 -1
  130. package/lib/AWS/Lambda/Function.js +67 -29
  131. package/lib/AWS/Lambda/Function.js.map +1 -1
  132. package/lib/AWS/Lambda/HttpServer.d.ts.map +1 -1
  133. package/lib/AWS/Lambda/HttpServer.js +7 -1
  134. package/lib/AWS/Lambda/HttpServer.js.map +1 -1
  135. package/lib/AWS/Lambda/LayerVersion.d.ts +176 -0
  136. package/lib/AWS/Lambda/LayerVersion.d.ts.map +1 -0
  137. package/lib/AWS/Lambda/LayerVersion.js +288 -0
  138. package/lib/AWS/Lambda/LayerVersion.js.map +1 -0
  139. package/lib/AWS/Lambda/MicrovmBundle.d.ts.map +1 -1
  140. package/lib/AWS/Lambda/MicrovmBundle.js +10 -2
  141. package/lib/AWS/Lambda/MicrovmBundle.js.map +1 -1
  142. package/lib/AWS/Lambda/index.d.ts +1 -0
  143. package/lib/AWS/Lambda/index.d.ts.map +1 -1
  144. package/lib/AWS/Lambda/index.js +1 -0
  145. package/lib/AWS/Lambda/index.js.map +1 -1
  146. package/lib/AWS/Providers.d.ts.map +1 -1
  147. package/lib/AWS/Providers.js +6 -1
  148. package/lib/AWS/Providers.js.map +1 -1
  149. package/lib/AWS/SES/ActiveReceiptRuleSet.d.ts +40 -0
  150. package/lib/AWS/SES/ActiveReceiptRuleSet.d.ts.map +1 -0
  151. package/lib/AWS/SES/ActiveReceiptRuleSet.js +58 -0
  152. package/lib/AWS/SES/ActiveReceiptRuleSet.js.map +1 -0
  153. package/lib/AWS/SES/ReceiptFilter.d.ts +67 -0
  154. package/lib/AWS/SES/ReceiptFilter.d.ts.map +1 -0
  155. package/lib/AWS/SES/ReceiptFilter.js +92 -0
  156. package/lib/AWS/SES/ReceiptFilter.js.map +1 -0
  157. package/lib/AWS/SES/ReceiptRule.d.ts +133 -0
  158. package/lib/AWS/SES/ReceiptRule.d.ts.map +1 -0
  159. package/lib/AWS/SES/ReceiptRule.js +178 -0
  160. package/lib/AWS/SES/ReceiptRule.js.map +1 -0
  161. package/lib/AWS/SES/ReceiptRuleSet.d.ts +55 -0
  162. package/lib/AWS/SES/ReceiptRuleSet.d.ts.map +1 -0
  163. package/lib/AWS/SES/ReceiptRuleSet.js +112 -0
  164. package/lib/AWS/SES/ReceiptRuleSet.js.map +1 -0
  165. package/lib/AWS/SES/SendBounce.d.ts +37 -0
  166. package/lib/AWS/SES/SendBounce.d.ts.map +1 -0
  167. package/lib/AWS/SES/SendBounce.js +3 -0
  168. package/lib/AWS/SES/SendBounce.js.map +1 -0
  169. package/lib/AWS/SES/SendBounceHttp.d.ts +4 -0
  170. package/lib/AWS/SES/SendBounceHttp.d.ts.map +1 -0
  171. package/lib/AWS/SES/SendBounceHttp.js +10 -0
  172. package/lib/AWS/SES/SendBounceHttp.js.map +1 -0
  173. package/lib/AWS/SES/index.d.ts +6 -0
  174. package/lib/AWS/SES/index.d.ts.map +1 -1
  175. package/lib/AWS/SES/index.js +6 -0
  176. package/lib/AWS/SES/index.js.map +1 -1
  177. package/lib/AWS/StateStore/State.d.ts +11 -0
  178. package/lib/AWS/StateStore/State.d.ts.map +1 -1
  179. package/lib/AWS/StateStore/State.js +117 -38
  180. package/lib/AWS/StateStore/State.js.map +1 -1
  181. package/lib/Apply.d.ts +37 -1
  182. package/lib/Apply.d.ts.map +1 -1
  183. package/lib/Apply.js +270 -194
  184. package/lib/Apply.js.map +1 -1
  185. package/lib/Axiom/Telemetry.d.ts +66 -0
  186. package/lib/Axiom/Telemetry.d.ts.map +1 -0
  187. package/lib/Axiom/Telemetry.js +69 -0
  188. package/lib/Axiom/Telemetry.js.map +1 -0
  189. package/lib/Axiom/index.d.ts +1 -0
  190. package/lib/Axiom/index.d.ts.map +1 -1
  191. package/lib/Axiom/index.js +1 -0
  192. package/lib/Axiom/index.js.map +1 -1
  193. package/lib/Bundle/Bundle.d.ts +1 -1
  194. package/lib/Bundle/Bundle.d.ts.map +1 -1
  195. package/lib/Bundle/Bundle.js +14 -6
  196. package/lib/Bundle/Bundle.js.map +1 -1
  197. package/lib/Bundle/BundleAnalyzerPlugin.d.ts +1 -1
  198. package/lib/Bundle/BundleAnalyzerPlugin.d.ts.map +1 -1
  199. package/lib/Bundle/BundleAnalyzerPlugin.js +10 -5
  200. package/lib/Bundle/BundleAnalyzerPlugin.js.map +1 -1
  201. package/lib/Bundle/PurePlugin.d.ts +1 -1
  202. package/lib/Bundle/PurePlugin.d.ts.map +1 -1
  203. package/lib/Bundle/PurePlugin.js +15 -7
  204. package/lib/Bundle/PurePlugin.js.map +1 -1
  205. package/lib/Cli/checkVersion.d.ts +11 -5
  206. package/lib/Cli/checkVersion.d.ts.map +1 -1
  207. package/lib/Cli/checkVersion.js +73 -12
  208. package/lib/Cli/checkVersion.js.map +1 -1
  209. package/lib/Cli/commands/_shared.d.ts.map +1 -1
  210. package/lib/Cli/commands/_shared.js +2 -1
  211. package/lib/Cli/commands/_shared.js.map +1 -1
  212. package/lib/Cli/commands/cloudflare.d.ts +1 -1
  213. package/lib/Cli/commands/deploy.d.ts.map +1 -1
  214. package/lib/Cli/commands/deploy.js +3 -11
  215. package/lib/Cli/commands/deploy.js.map +1 -1
  216. package/lib/Cli/commands/logs.d.ts.map +1 -1
  217. package/lib/Cli/commands/logs.js +3 -1
  218. package/lib/Cli/commands/logs.js.map +1 -1
  219. package/lib/Cli/commands/tail.d.ts.map +1 -1
  220. package/lib/Cli/commands/tail.js +3 -1
  221. package/lib/Cli/commands/tail.js.map +1 -1
  222. package/lib/Cli/main.d.ts.map +1 -1
  223. package/lib/Cli/main.js +5 -4
  224. package/lib/Cli/main.js.map +1 -1
  225. package/lib/Cloudflare/AI/Search.d.ts +4 -15
  226. package/lib/Cloudflare/AI/Search.d.ts.map +1 -1
  227. package/lib/Cloudflare/AI/Search.js +4 -6
  228. package/lib/Cloudflare/AI/Search.js.map +1 -1
  229. package/lib/Cloudflare/AI/SearchInstance.d.ts +7 -26
  230. package/lib/Cloudflare/AI/SearchInstance.d.ts.map +1 -1
  231. package/lib/Cloudflare/AI/SearchInstance.js +7 -26
  232. package/lib/Cloudflare/AI/SearchInstance.js.map +1 -1
  233. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +3 -0
  234. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
  235. package/lib/Cloudflare/ApiToken/AccountApiToken.js +3 -0
  236. package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
  237. package/lib/Cloudflare/Auth/AuthProvider.d.ts +2 -1
  238. package/lib/Cloudflare/Auth/AuthProvider.d.ts.map +1 -1
  239. package/lib/Cloudflare/Auth/AuthProvider.js +8 -3
  240. package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
  241. package/lib/Cloudflare/Auth/OAuthClient.d.ts +5 -0
  242. package/lib/Cloudflare/Auth/OAuthClient.d.ts.map +1 -1
  243. package/lib/Cloudflare/Auth/OAuthClient.js +50 -4
  244. package/lib/Cloudflare/Auth/OAuthClient.js.map +1 -1
  245. package/lib/Cloudflare/Containers/Container.d.ts +81 -3
  246. package/lib/Cloudflare/Containers/Container.d.ts.map +1 -1
  247. package/lib/Cloudflare/Containers/Container.js +70 -0
  248. package/lib/Cloudflare/Containers/Container.js.map +1 -1
  249. package/lib/Cloudflare/Containers/ContainerApplication.d.ts +22 -1
  250. package/lib/Cloudflare/Containers/ContainerApplication.d.ts.map +1 -1
  251. package/lib/Cloudflare/Containers/ContainerApplication.js +4 -1
  252. package/lib/Cloudflare/Containers/ContainerApplication.js.map +1 -1
  253. package/lib/Cloudflare/Containers/ContainerBundle.d.ts.map +1 -1
  254. package/lib/Cloudflare/Containers/ContainerBundle.js +10 -2
  255. package/lib/Cloudflare/Containers/ContainerBundle.js.map +1 -1
  256. package/lib/Cloudflare/Containers/ContainerProvider.d.ts.map +1 -1
  257. package/lib/Cloudflare/Containers/ContainerProvider.js +43 -0
  258. package/lib/Cloudflare/Containers/ContainerProvider.js.map +1 -1
  259. package/lib/Cloudflare/Credentials.d.ts +21 -1
  260. package/lib/Cloudflare/Credentials.d.ts.map +1 -1
  261. package/lib/Cloudflare/Credentials.js +52 -9
  262. package/lib/Cloudflare/Credentials.js.map +1 -1
  263. package/lib/Cloudflare/D1/ApplyMigrations.d.ts +30 -5
  264. package/lib/Cloudflare/D1/ApplyMigrations.d.ts.map +1 -1
  265. package/lib/Cloudflare/D1/ApplyMigrations.js +36 -26
  266. package/lib/Cloudflare/D1/ApplyMigrations.js.map +1 -1
  267. package/lib/Cloudflare/D1/Database.d.ts +18 -1
  268. package/lib/Cloudflare/D1/Database.d.ts.map +1 -1
  269. package/lib/Cloudflare/D1/Database.js +134 -2
  270. package/lib/Cloudflare/D1/Database.js.map +1 -1
  271. package/lib/Cloudflare/D1/LocalD1Gateway.d.ts +39 -0
  272. package/lib/Cloudflare/D1/LocalD1Gateway.d.ts.map +1 -0
  273. package/lib/Cloudflare/D1/LocalD1Gateway.js +100 -0
  274. package/lib/Cloudflare/D1/LocalD1Gateway.js.map +1 -0
  275. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts +31 -0
  276. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts.map +1 -1
  277. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js +13 -6
  278. package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js.map +1 -1
  279. package/lib/Cloudflare/D1/QueryDatabaseLocal.d.ts.map +1 -1
  280. package/lib/Cloudflare/D1/QueryDatabaseLocal.js +21 -2
  281. package/lib/Cloudflare/D1/QueryDatabaseLocal.js.map +1 -1
  282. package/lib/Cloudflare/DNS/Record.d.ts +3 -1
  283. package/lib/Cloudflare/DNS/Record.d.ts.map +1 -1
  284. package/lib/Cloudflare/DNS/Record.js +23 -4
  285. package/lib/Cloudflare/DNS/Record.js.map +1 -1
  286. package/lib/Cloudflare/Email/Address.d.ts.map +1 -1
  287. package/lib/Cloudflare/Email/Address.js +15 -1
  288. package/lib/Cloudflare/Email/Address.js.map +1 -1
  289. package/lib/Cloudflare/Email/SendBinding.d.ts.map +1 -1
  290. package/lib/Cloudflare/Email/SendBinding.js +1 -0
  291. package/lib/Cloudflare/Email/SendBinding.js.map +1 -1
  292. package/lib/Cloudflare/Email/SendEmail.d.ts +12 -0
  293. package/lib/Cloudflare/Email/SendEmail.d.ts.map +1 -1
  294. package/lib/Cloudflare/Email/SendEmail.js +1 -0
  295. package/lib/Cloudflare/Email/SendEmail.js.map +1 -1
  296. package/lib/Cloudflare/Flagship/App.d.ts.map +1 -1
  297. package/lib/Cloudflare/Flagship/App.js +8 -3
  298. package/lib/Cloudflare/Flagship/App.js.map +1 -1
  299. package/lib/Cloudflare/KV/LocalKVGateway.d.ts +15 -0
  300. package/lib/Cloudflare/KV/LocalKVGateway.d.ts.map +1 -0
  301. package/lib/Cloudflare/KV/LocalKVGateway.js +64 -0
  302. package/lib/Cloudflare/KV/LocalKVGateway.js.map +1 -0
  303. package/lib/Cloudflare/KV/Namespace.d.ts +9 -1
  304. package/lib/Cloudflare/KV/Namespace.d.ts.map +1 -1
  305. package/lib/Cloudflare/KV/Namespace.js +45 -1
  306. package/lib/Cloudflare/KV/Namespace.js.map +1 -1
  307. package/lib/Cloudflare/KV/NamespaceLocal.d.ts +16 -5
  308. package/lib/Cloudflare/KV/NamespaceLocal.d.ts.map +1 -1
  309. package/lib/Cloudflare/KV/NamespaceLocal.js +21 -4
  310. package/lib/Cloudflare/KV/NamespaceLocal.js.map +1 -1
  311. package/lib/Cloudflare/KV/ReadNamespaceLocal.d.ts.map +1 -1
  312. package/lib/Cloudflare/KV/ReadNamespaceLocal.js +5 -1
  313. package/lib/Cloudflare/KV/ReadNamespaceLocal.js.map +1 -1
  314. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.d.ts.map +1 -1
  315. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js +5 -1
  316. package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js.map +1 -1
  317. package/lib/Cloudflare/KV/WriteNamespaceLocal.d.ts.map +1 -1
  318. package/lib/Cloudflare/KV/WriteNamespaceLocal.js +5 -1
  319. package/lib/Cloudflare/KV/WriteNamespaceLocal.js.map +1 -1
  320. package/lib/Cloudflare/LocalGateway.d.ts +25 -0
  321. package/lib/Cloudflare/LocalGateway.d.ts.map +1 -0
  322. package/lib/Cloudflare/LocalGateway.js +70 -0
  323. package/lib/Cloudflare/LocalGateway.js.map +1 -0
  324. package/lib/Cloudflare/LocalRuntime.d.ts +14 -1
  325. package/lib/Cloudflare/LocalRuntime.d.ts.map +1 -1
  326. package/lib/Cloudflare/LocalRuntime.js +16 -1
  327. package/lib/Cloudflare/LocalRuntime.js.map +1 -1
  328. package/lib/Cloudflare/Logs.d.ts.map +1 -1
  329. package/lib/Cloudflare/Logs.js +18 -6
  330. package/lib/Cloudflare/Logs.js.map +1 -1
  331. package/lib/Cloudflare/Providers.d.ts +1 -1
  332. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  333. package/lib/Cloudflare/Providers.js +6 -2
  334. package/lib/Cloudflare/Providers.js.map +1 -1
  335. package/lib/Cloudflare/Queues/Consumer.d.ts +16 -3
  336. package/lib/Cloudflare/Queues/Consumer.d.ts.map +1 -1
  337. package/lib/Cloudflare/Queues/Consumer.js +51 -3
  338. package/lib/Cloudflare/Queues/Consumer.js.map +1 -1
  339. package/lib/Cloudflare/Queues/Queue.d.ts +4 -3
  340. package/lib/Cloudflare/Queues/Queue.d.ts.map +1 -1
  341. package/lib/Cloudflare/Queues/Queue.js +7 -17
  342. package/lib/Cloudflare/Queues/Queue.js.map +1 -1
  343. package/lib/Cloudflare/Queues/QueueBinding.d.ts.map +1 -1
  344. package/lib/Cloudflare/Queues/QueueBinding.js +19 -0
  345. package/lib/Cloudflare/Queues/QueueBinding.js.map +1 -1
  346. package/lib/Cloudflare/Queues/QueueShim.d.ts +55 -0
  347. package/lib/Cloudflare/Queues/QueueShim.d.ts.map +1 -0
  348. package/lib/Cloudflare/Queues/QueueShim.js +177 -0
  349. package/lib/Cloudflare/Queues/QueueShim.js.map +1 -0
  350. package/lib/Cloudflare/R2/Bucket.d.ts +14 -1
  351. package/lib/Cloudflare/R2/Bucket.d.ts.map +1 -1
  352. package/lib/Cloudflare/R2/Bucket.js +53 -1
  353. package/lib/Cloudflare/R2/Bucket.js.map +1 -1
  354. package/lib/Cloudflare/R2/BucketBinding.d.ts +10 -0
  355. package/lib/Cloudflare/R2/BucketBinding.d.ts.map +1 -1
  356. package/lib/Cloudflare/R2/BucketBinding.js +14 -3
  357. package/lib/Cloudflare/R2/BucketBinding.js.map +1 -1
  358. package/lib/Cloudflare/R2/BucketLocal.d.ts +13 -6
  359. package/lib/Cloudflare/R2/BucketLocal.d.ts.map +1 -1
  360. package/lib/Cloudflare/R2/BucketLocal.js +19 -6
  361. package/lib/Cloudflare/R2/BucketLocal.js.map +1 -1
  362. package/lib/Cloudflare/R2/LocalR2Gateway.d.ts +15 -0
  363. package/lib/Cloudflare/R2/LocalR2Gateway.d.ts.map +1 -0
  364. package/lib/Cloudflare/R2/LocalR2Gateway.js +65 -0
  365. package/lib/Cloudflare/R2/LocalR2Gateway.js.map +1 -0
  366. package/lib/Cloudflare/R2/ReadBucketLocal.d.ts.map +1 -1
  367. package/lib/Cloudflare/R2/ReadBucketLocal.js +5 -1
  368. package/lib/Cloudflare/R2/ReadBucketLocal.js.map +1 -1
  369. package/lib/Cloudflare/R2/ReadWriteBucketLocal.d.ts.map +1 -1
  370. package/lib/Cloudflare/R2/ReadWriteBucketLocal.js +5 -1
  371. package/lib/Cloudflare/R2/ReadWriteBucketLocal.js.map +1 -1
  372. package/lib/Cloudflare/R2/WriteBucketLocal.d.ts.map +1 -1
  373. package/lib/Cloudflare/R2/WriteBucketLocal.js +5 -1
  374. package/lib/Cloudflare/R2/WriteBucketLocal.js.map +1 -1
  375. package/lib/Cloudflare/Ruleset/AccountEntrypoint.d.ts.map +1 -1
  376. package/lib/Cloudflare/Ruleset/AccountEntrypoint.js +5 -1
  377. package/lib/Cloudflare/Ruleset/AccountEntrypoint.js.map +1 -1
  378. package/lib/Cloudflare/Ruleset/Ruleset.d.ts.map +1 -1
  379. package/lib/Cloudflare/Ruleset/Ruleset.js +19 -6
  380. package/lib/Cloudflare/Ruleset/Ruleset.js.map +1 -1
  381. package/lib/Cloudflare/Rum/Site.js +10 -2
  382. package/lib/Cloudflare/Rum/Site.js.map +1 -1
  383. package/lib/Cloudflare/StateStore/Api.d.ts +31 -2
  384. package/lib/Cloudflare/StateStore/Api.js +1 -1
  385. package/lib/Cloudflare/StateStore/Api.js.map +1 -1
  386. package/lib/Cloudflare/StateStore/State.d.ts +1 -1
  387. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  388. package/lib/Cloudflare/Tunnel/Tunnel.d.ts.map +1 -1
  389. package/lib/Cloudflare/Tunnel/Tunnel.js +20 -1
  390. package/lib/Cloudflare/Tunnel/Tunnel.js.map +1 -1
  391. package/lib/Cloudflare/Website/StaticSite.d.ts +34 -7
  392. package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
  393. package/lib/Cloudflare/Website/StaticSite.js +108 -24
  394. package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
  395. package/lib/Cloudflare/Website/Vite.d.ts +33 -19
  396. package/lib/Cloudflare/Website/Vite.d.ts.map +1 -1
  397. package/lib/Cloudflare/Website/Vite.js +33 -19
  398. package/lib/Cloudflare/Website/Vite.js.map +1 -1
  399. package/lib/Cloudflare/Workers/Compatibility.d.ts.map +1 -1
  400. package/lib/Cloudflare/Workers/Compatibility.js +26 -7
  401. package/lib/Cloudflare/Workers/Compatibility.js.map +1 -1
  402. package/lib/Cloudflare/Workers/DurableObject.d.ts +22 -0
  403. package/lib/Cloudflare/Workers/DurableObject.d.ts.map +1 -1
  404. package/lib/Cloudflare/Workers/DurableObject.js +22 -0
  405. package/lib/Cloudflare/Workers/DurableObject.js.map +1 -1
  406. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
  407. package/lib/Cloudflare/Workers/DurableObjectBridge.js +20 -5
  408. package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
  409. package/lib/Cloudflare/Workers/InferEnv.d.ts +4 -2
  410. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  411. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +7 -3
  412. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  413. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +483 -298
  414. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  415. package/lib/Cloudflare/Workers/Worker.d.ts +612 -13
  416. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  417. package/lib/Cloudflare/Workers/Worker.js +222 -1
  418. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  419. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts +13 -1
  420. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  421. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +171 -21
  422. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  423. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +130 -28
  424. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  425. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  426. package/lib/Cloudflare/Workers/WorkerBridge.d.ts +6 -2
  427. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  428. package/lib/Cloudflare/Workers/WorkerBridge.js +18 -2
  429. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  430. package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
  431. package/lib/Cloudflare/Workers/WorkerBundle.js +4 -1
  432. package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
  433. package/lib/Cloudflare/Workers/WorkerProvider.d.ts +186 -11
  434. package/lib/Cloudflare/Workers/WorkerProvider.d.ts.map +1 -1
  435. package/lib/Cloudflare/Workers/WorkerProvider.js +1765 -236
  436. package/lib/Cloudflare/Workers/WorkerProvider.js.map +1 -1
  437. package/lib/Cloudflare/Workflows/WorkflowBridge.d.ts +7 -1
  438. package/lib/Cloudflare/Workflows/WorkflowBridge.d.ts.map +1 -1
  439. package/lib/Cloudflare/Workflows/WorkflowBridge.js +14 -4
  440. package/lib/Cloudflare/Workflows/WorkflowBridge.js.map +1 -1
  441. package/lib/Command/Build.d.ts +2 -2
  442. package/lib/Command/Build.d.ts.map +1 -1
  443. package/lib/Command/Build.js +4 -7
  444. package/lib/Command/Build.js.map +1 -1
  445. package/lib/Command/Command.d.ts +34 -4
  446. package/lib/Command/Command.d.ts.map +1 -1
  447. package/lib/Command/Command.js +125 -30
  448. package/lib/Command/Command.js.map +1 -1
  449. package/lib/Command/Dev.d.ts +2 -2
  450. package/lib/Command/Dev.d.ts.map +1 -1
  451. package/lib/Command/Dev.js +51 -73
  452. package/lib/Command/Dev.js.map +1 -1
  453. package/lib/Command/Exec.d.ts +11 -2
  454. package/lib/Command/Exec.d.ts.map +1 -1
  455. package/lib/Command/Exec.js +9 -0
  456. package/lib/Command/Exec.js.map +1 -1
  457. package/lib/Command/Memo.d.ts +9 -1
  458. package/lib/Command/Memo.d.ts.map +1 -1
  459. package/lib/Command/Memo.js +14 -2
  460. package/lib/Command/Memo.js.map +1 -1
  461. package/lib/Command/Providers.d.ts +1 -1
  462. package/lib/Command/Providers.d.ts.map +1 -1
  463. package/lib/Command/Redaction.d.ts +10 -0
  464. package/lib/Command/Redaction.d.ts.map +1 -0
  465. package/lib/Command/Redaction.js +119 -0
  466. package/lib/Command/Redaction.js.map +1 -0
  467. package/lib/Deploy.d.ts +2 -1
  468. package/lib/Deploy.d.ts.map +1 -1
  469. package/lib/Destroy.d.ts +2 -1
  470. package/lib/Destroy.d.ts.map +1 -1
  471. package/lib/Destroy.js +1 -9
  472. package/lib/Destroy.js.map +1 -1
  473. package/lib/Docker/Container.d.ts +15 -1
  474. package/lib/Docker/Container.d.ts.map +1 -1
  475. package/lib/Docker/Container.js +34 -11
  476. package/lib/Docker/Container.js.map +1 -1
  477. package/lib/Docker/Context.d.ts +84 -0
  478. package/lib/Docker/Context.d.ts.map +1 -0
  479. package/lib/Docker/Context.js +145 -0
  480. package/lib/Docker/Context.js.map +1 -0
  481. package/lib/Docker/Docker.d.ts +190 -14
  482. package/lib/Docker/Docker.d.ts.map +1 -1
  483. package/lib/Docker/Docker.js +149 -28
  484. package/lib/Docker/Docker.js.map +1 -1
  485. package/lib/Docker/Image.d.ts +12 -0
  486. package/lib/Docker/Image.d.ts.map +1 -1
  487. package/lib/Docker/Image.js +24 -7
  488. package/lib/Docker/Image.js.map +1 -1
  489. package/lib/Docker/Network.d.ts +7 -0
  490. package/lib/Docker/Network.d.ts.map +1 -1
  491. package/lib/Docker/Network.js +16 -7
  492. package/lib/Docker/Network.js.map +1 -1
  493. package/lib/Docker/Providers.d.ts +1 -1
  494. package/lib/Docker/Providers.d.ts.map +1 -1
  495. package/lib/Docker/Providers.js +13 -1
  496. package/lib/Docker/Providers.js.map +1 -1
  497. package/lib/Docker/RemoteImage.d.ts +12 -0
  498. package/lib/Docker/RemoteImage.d.ts.map +1 -1
  499. package/lib/Docker/RemoteImage.js +22 -8
  500. package/lib/Docker/RemoteImage.js.map +1 -1
  501. package/lib/Docker/Service.d.ts +445 -0
  502. package/lib/Docker/Service.d.ts.map +1 -0
  503. package/lib/Docker/Service.js +578 -0
  504. package/lib/Docker/Service.js.map +1 -0
  505. package/lib/Docker/ServiceImage.d.ts +78 -0
  506. package/lib/Docker/ServiceImage.d.ts.map +1 -0
  507. package/lib/Docker/ServiceImage.js +232 -0
  508. package/lib/Docker/ServiceImage.js.map +1 -0
  509. package/lib/Docker/Swarm.d.ts +117 -0
  510. package/lib/Docker/Swarm.d.ts.map +1 -0
  511. package/lib/Docker/Swarm.js +149 -0
  512. package/lib/Docker/Swarm.js.map +1 -0
  513. package/lib/Docker/Volume.d.ts +11 -0
  514. package/lib/Docker/Volume.d.ts.map +1 -1
  515. package/lib/Docker/Volume.js +21 -6
  516. package/lib/Docker/Volume.js.map +1 -1
  517. package/lib/Docker/index.d.ts +3 -0
  518. package/lib/Docker/index.d.ts.map +1 -1
  519. package/lib/Docker/index.js +3 -0
  520. package/lib/Docker/index.js.map +1 -1
  521. package/lib/Http.d.ts +8 -1
  522. package/lib/Http.d.ts.map +1 -1
  523. package/lib/Http.js +7 -2
  524. package/lib/Http.js.map +1 -1
  525. package/lib/Local/LocalProvider.d.ts +182 -0
  526. package/lib/Local/LocalProvider.d.ts.map +1 -0
  527. package/lib/Local/LocalProvider.js +226 -0
  528. package/lib/Local/LocalProvider.js.map +1 -0
  529. package/lib/Local/ProviderLayer.d.ts +43 -2
  530. package/lib/Local/ProviderLayer.d.ts.map +1 -1
  531. package/lib/Local/ProviderLayer.js +80 -2
  532. package/lib/Local/ProviderLayer.js.map +1 -1
  533. package/lib/Local/RpcProvider.d.ts +7 -0
  534. package/lib/Local/RpcProvider.d.ts.map +1 -1
  535. package/lib/Local/RpcProvider.js +8 -3
  536. package/lib/Local/RpcProvider.js.map +1 -1
  537. package/lib/Local/RpcProviderProxy.d.ts.map +1 -1
  538. package/lib/Local/RpcProviderProxy.js +3 -0
  539. package/lib/Local/RpcProviderProxy.js.map +1 -1
  540. package/lib/Local/RpcServer.d.ts +8 -1
  541. package/lib/Local/RpcServer.d.ts.map +1 -1
  542. package/lib/Local/RpcServer.js +4 -1
  543. package/lib/Local/RpcServer.js.map +1 -1
  544. package/lib/Plan.d.ts +34 -0
  545. package/lib/Plan.d.ts.map +1 -1
  546. package/lib/Plan.js +127 -40
  547. package/lib/Plan.js.map +1 -1
  548. package/lib/Planetscale/AuthProvider.d.ts.map +1 -1
  549. package/lib/Planetscale/AuthProvider.js +6 -2
  550. package/lib/Planetscale/AuthProvider.js.map +1 -1
  551. package/lib/Planetscale/Branch.d.ts.map +1 -1
  552. package/lib/Planetscale/Branch.js +36 -6
  553. package/lib/Planetscale/Branch.js.map +1 -1
  554. package/lib/Planetscale/MySQL/MySQLDatabase.d.ts.map +1 -1
  555. package/lib/Planetscale/MySQL/MySQLDatabase.js +31 -6
  556. package/lib/Planetscale/MySQL/MySQLDatabase.js.map +1 -1
  557. package/lib/Planetscale/OAuthClient.d.ts +7 -1
  558. package/lib/Planetscale/OAuthClient.d.ts.map +1 -1
  559. package/lib/Planetscale/OAuthClient.js +51 -4
  560. package/lib/Planetscale/OAuthClient.js.map +1 -1
  561. package/lib/Planetscale/Postgres/PostgresDatabase.d.ts.map +1 -1
  562. package/lib/Planetscale/Postgres/PostgresDatabase.js +30 -5
  563. package/lib/Planetscale/Postgres/PostgresDatabase.js.map +1 -1
  564. package/lib/Platform.d.ts.map +1 -1
  565. package/lib/Platform.js +25 -14
  566. package/lib/Platform.js.map +1 -1
  567. package/lib/Prisma/App.d.ts +93 -0
  568. package/lib/Prisma/App.d.ts.map +1 -0
  569. package/lib/Prisma/App.js +251 -0
  570. package/lib/Prisma/App.js.map +1 -0
  571. package/lib/Prisma/AuthProvider.d.ts +28 -0
  572. package/lib/Prisma/AuthProvider.d.ts.map +1 -0
  573. package/lib/Prisma/AuthProvider.js +131 -0
  574. package/lib/Prisma/AuthProvider.js.map +1 -0
  575. package/lib/Prisma/Branch.d.ts +96 -0
  576. package/lib/Prisma/Branch.d.ts.map +1 -0
  577. package/lib/Prisma/Branch.js +236 -0
  578. package/lib/Prisma/Branch.js.map +1 -0
  579. package/lib/Prisma/Client.d.ts +178 -0
  580. package/lib/Prisma/Client.d.ts.map +1 -0
  581. package/lib/Prisma/Client.js +562 -0
  582. package/lib/Prisma/Client.js.map +1 -0
  583. package/lib/Prisma/Compute.d.ts +585 -0
  584. package/lib/Prisma/Compute.d.ts.map +1 -0
  585. package/lib/Prisma/Compute.js +1545 -0
  586. package/lib/Prisma/Compute.js.map +1 -0
  587. package/lib/Prisma/ComputeArchive.d.ts +63 -0
  588. package/lib/Prisma/ComputeArchive.d.ts.map +1 -0
  589. package/lib/Prisma/ComputeArchive.js +227 -0
  590. package/lib/Prisma/ComputeArchive.js.map +1 -0
  591. package/lib/Prisma/ComputeBuild.d.ts +106 -0
  592. package/lib/Prisma/ComputeBuild.d.ts.map +1 -0
  593. package/lib/Prisma/ComputeBuild.js +987 -0
  594. package/lib/Prisma/ComputeBuild.js.map +1 -0
  595. package/lib/Prisma/ComputeLifecycle.d.ts +84 -0
  596. package/lib/Prisma/ComputeLifecycle.d.ts.map +1 -0
  597. package/lib/Prisma/ComputeLifecycle.js +186 -0
  598. package/lib/Prisma/ComputeLifecycle.js.map +1 -0
  599. package/lib/Prisma/Connect.d.ts +122 -0
  600. package/lib/Prisma/Connect.d.ts.map +1 -0
  601. package/lib/Prisma/Connect.js +187 -0
  602. package/lib/Prisma/Connect.js.map +1 -0
  603. package/lib/Prisma/Connection.d.ts +219 -0
  604. package/lib/Prisma/Connection.d.ts.map +1 -0
  605. package/lib/Prisma/Connection.js +360 -0
  606. package/lib/Prisma/Connection.js.map +1 -0
  607. package/lib/Prisma/CustomDomain.d.ts +91 -0
  608. package/lib/Prisma/CustomDomain.d.ts.map +1 -0
  609. package/lib/Prisma/CustomDomain.js +184 -0
  610. package/lib/Prisma/CustomDomain.js.map +1 -0
  611. package/lib/Prisma/Database.d.ts +222 -0
  612. package/lib/Prisma/Database.d.ts.map +1 -0
  613. package/lib/Prisma/Database.js +418 -0
  614. package/lib/Prisma/Database.js.map +1 -0
  615. package/lib/Prisma/Deployment.d.ts +141 -0
  616. package/lib/Prisma/Deployment.d.ts.map +1 -0
  617. package/lib/Prisma/Deployment.js +353 -0
  618. package/lib/Prisma/Deployment.js.map +1 -0
  619. package/lib/Prisma/EnvironmentVariable.d.ts +105 -0
  620. package/lib/Prisma/EnvironmentVariable.d.ts.map +1 -0
  621. package/lib/Prisma/EnvironmentVariable.js +229 -0
  622. package/lib/Prisma/EnvironmentVariable.js.map +1 -0
  623. package/lib/Prisma/Internal/AppIdentity.d.ts +10 -0
  624. package/lib/Prisma/Internal/AppIdentity.d.ts.map +1 -0
  625. package/lib/Prisma/Internal/AppIdentity.js +6 -0
  626. package/lib/Prisma/Internal/AppIdentity.js.map +1 -0
  627. package/lib/Prisma/Internal/AppPromotion.d.ts +19 -0
  628. package/lib/Prisma/Internal/AppPromotion.d.ts.map +1 -0
  629. package/lib/Prisma/Internal/AppPromotion.js +107 -0
  630. package/lib/Prisma/Internal/AppPromotion.js.map +1 -0
  631. package/lib/Prisma/Internal/ArchivePlatform.d.ts +42 -0
  632. package/lib/Prisma/Internal/ArchivePlatform.d.ts.map +1 -0
  633. package/lib/Prisma/Internal/ArchivePlatform.js +214 -0
  634. package/lib/Prisma/Internal/ArchivePlatform.js.map +1 -0
  635. package/lib/Prisma/Internal/ArtifactFile.d.ts +58 -0
  636. package/lib/Prisma/Internal/ArtifactFile.d.ts.map +1 -0
  637. package/lib/Prisma/Internal/ArtifactFile.js +134 -0
  638. package/lib/Prisma/Internal/ArtifactFile.js.map +1 -0
  639. package/lib/Prisma/Internal/ArtifactUpload.d.ts +5 -0
  640. package/lib/Prisma/Internal/ArtifactUpload.d.ts.map +1 -0
  641. package/lib/Prisma/Internal/ArtifactUpload.js +51 -0
  642. package/lib/Prisma/Internal/ArtifactUpload.js.map +1 -0
  643. package/lib/Prisma/Internal/BundlePaths.d.ts +4 -0
  644. package/lib/Prisma/Internal/BundlePaths.d.ts.map +1 -0
  645. package/lib/Prisma/Internal/BundlePaths.js +16 -0
  646. package/lib/Prisma/Internal/BundlePaths.js.map +1 -0
  647. package/lib/Prisma/Internal/CleanupFailure.d.ts +8 -0
  648. package/lib/Prisma/Internal/CleanupFailure.d.ts.map +1 -0
  649. package/lib/Prisma/Internal/CleanupFailure.js +8 -0
  650. package/lib/Prisma/Internal/CleanupFailure.js.map +1 -0
  651. package/lib/Prisma/Internal/DatabaseSecrets.d.ts +35 -0
  652. package/lib/Prisma/Internal/DatabaseSecrets.d.ts.map +1 -0
  653. package/lib/Prisma/Internal/DatabaseSecrets.js +85 -0
  654. package/lib/Prisma/Internal/DatabaseSecrets.js.map +1 -0
  655. package/lib/Prisma/Internal/DeploymentActions.d.ts +5 -0
  656. package/lib/Prisma/Internal/DeploymentActions.d.ts.map +1 -0
  657. package/lib/Prisma/Internal/DeploymentActions.js +16 -0
  658. package/lib/Prisma/Internal/DeploymentActions.js.map +1 -0
  659. package/lib/Prisma/Internal/DeploymentIdentity.d.ts +8 -0
  660. package/lib/Prisma/Internal/DeploymentIdentity.d.ts.map +1 -0
  661. package/lib/Prisma/Internal/DeploymentIdentity.js +12 -0
  662. package/lib/Prisma/Internal/DeploymentIdentity.js.map +1 -0
  663. package/lib/Prisma/Internal/DeploymentObserve.d.ts +3 -0
  664. package/lib/Prisma/Internal/DeploymentObserve.d.ts.map +1 -0
  665. package/lib/Prisma/Internal/DeploymentObserve.js +2 -0
  666. package/lib/Prisma/Internal/DeploymentObserve.js.map +1 -0
  667. package/lib/Prisma/Internal/EnvName.d.ts +7 -0
  668. package/lib/Prisma/Internal/EnvName.d.ts.map +1 -0
  669. package/lib/Prisma/Internal/EnvName.js +23 -0
  670. package/lib/Prisma/Internal/EnvName.js.map +1 -0
  671. package/lib/Prisma/Internal/HttpClient.d.ts +21 -0
  672. package/lib/Prisma/Internal/HttpClient.d.ts.map +1 -0
  673. package/lib/Prisma/Internal/HttpClient.js +23 -0
  674. package/lib/Prisma/Internal/HttpClient.js.map +1 -0
  675. package/lib/Prisma/Operations.d.ts +143 -0
  676. package/lib/Prisma/Operations.d.ts.map +1 -0
  677. package/lib/Prisma/Operations.js +75 -0
  678. package/lib/Prisma/Operations.js.map +1 -0
  679. package/lib/Prisma/Postgres.d.ts +25 -0
  680. package/lib/Prisma/Postgres.d.ts.map +1 -0
  681. package/lib/Prisma/Postgres.js +24 -0
  682. package/lib/Prisma/Postgres.js.map +1 -0
  683. package/lib/Prisma/PostgresOrigin.d.ts +31 -0
  684. package/lib/Prisma/PostgresOrigin.d.ts.map +1 -0
  685. package/lib/Prisma/PostgresOrigin.js +30 -0
  686. package/lib/Prisma/PostgresOrigin.js.map +1 -0
  687. package/lib/Prisma/PrismaDevDatabase.d.ts +25 -0
  688. package/lib/Prisma/PrismaDevDatabase.d.ts.map +1 -0
  689. package/lib/Prisma/PrismaDevDatabase.js +280 -0
  690. package/lib/Prisma/PrismaDevDatabase.js.map +1 -0
  691. package/lib/Prisma/PrismaEnvironment.d.ts +14 -0
  692. package/lib/Prisma/PrismaEnvironment.d.ts.map +1 -0
  693. package/lib/Prisma/PrismaEnvironment.js +50 -0
  694. package/lib/Prisma/PrismaEnvironment.js.map +1 -0
  695. package/lib/Prisma/PrismaLogs.d.ts +42 -0
  696. package/lib/Prisma/PrismaLogs.d.ts.map +1 -0
  697. package/lib/Prisma/PrismaLogs.js +318 -0
  698. package/lib/Prisma/PrismaLogs.js.map +1 -0
  699. package/lib/Prisma/Project.d.ts +123 -0
  700. package/lib/Prisma/Project.d.ts.map +1 -0
  701. package/lib/Prisma/Project.js +366 -0
  702. package/lib/Prisma/Project.js.map +1 -0
  703. package/lib/Prisma/Providers.d.ts +63 -0
  704. package/lib/Prisma/Providers.d.ts.map +1 -0
  705. package/lib/Prisma/Providers.js +306 -0
  706. package/lib/Prisma/Providers.js.map +1 -0
  707. package/lib/Prisma/Refs.d.ts +19 -0
  708. package/lib/Prisma/Refs.d.ts.map +1 -0
  709. package/lib/Prisma/Refs.js +25 -0
  710. package/lib/Prisma/Refs.js.map +1 -0
  711. package/lib/Prisma/SourceRepository.d.ts +127 -0
  712. package/lib/Prisma/SourceRepository.d.ts.map +1 -0
  713. package/lib/Prisma/SourceRepository.js +239 -0
  714. package/lib/Prisma/SourceRepository.js.map +1 -0
  715. package/lib/Prisma/Types.d.ts +539 -0
  716. package/lib/Prisma/Types.d.ts.map +1 -0
  717. package/lib/Prisma/Types.js +13 -0
  718. package/lib/Prisma/Types.js.map +1 -0
  719. package/lib/Prisma/index.d.ts +25 -0
  720. package/lib/Prisma/index.d.ts.map +1 -0
  721. package/lib/Prisma/index.js +24 -0
  722. package/lib/Prisma/index.js.map +1 -0
  723. package/lib/Provider.d.ts +57 -5
  724. package/lib/Provider.d.ts.map +1 -1
  725. package/lib/Provider.js +54 -30
  726. package/lib/Provider.js.map +1 -1
  727. package/lib/ProviderMode.d.ts +93 -0
  728. package/lib/ProviderMode.d.ts.map +1 -0
  729. package/lib/ProviderMode.js +77 -0
  730. package/lib/ProviderMode.js.map +1 -0
  731. package/lib/Resource.d.ts +8 -0
  732. package/lib/Resource.d.ts.map +1 -1
  733. package/lib/Resource.js +31 -0
  734. package/lib/Resource.js.map +1 -1
  735. package/lib/RuntimeContext.d.ts +7 -0
  736. package/lib/RuntimeContext.d.ts.map +1 -1
  737. package/lib/RuntimeContext.js.map +1 -1
  738. package/lib/Server/Process.d.ts +8 -0
  739. package/lib/Server/Process.d.ts.map +1 -1
  740. package/lib/Server/Process.js +29 -0
  741. package/lib/Server/Process.js.map +1 -1
  742. package/lib/State/InMemoryState.d.ts.map +1 -1
  743. package/lib/State/InMemoryState.js +2 -0
  744. package/lib/State/InMemoryState.js.map +1 -1
  745. package/lib/State/ResourceState.d.ts +18 -0
  746. package/lib/State/ResourceState.d.ts.map +1 -1
  747. package/lib/Sync.d.ts.map +1 -1
  748. package/lib/Sync.js +8 -2
  749. package/lib/Sync.js.map +1 -1
  750. package/lib/Telemetry.d.ts +150 -0
  751. package/lib/Telemetry.d.ts.map +1 -0
  752. package/lib/Telemetry.js +512 -0
  753. package/lib/Telemetry.js.map +1 -0
  754. package/lib/Test/Alchemy.d.ts.map +1 -1
  755. package/lib/Test/Alchemy.js +3 -1
  756. package/lib/Test/Alchemy.js.map +1 -1
  757. package/lib/Test/Core.d.ts +2 -2
  758. package/lib/Test/Core.d.ts.map +1 -1
  759. package/lib/Test/Core.js +1 -8
  760. package/lib/Test/Core.js.map +1 -1
  761. package/lib/Util/Clank.d.ts +11 -6
  762. package/lib/Util/Clank.d.ts.map +1 -1
  763. package/lib/Util/Clank.js +22 -9
  764. package/lib/Util/Clank.js.map +1 -1
  765. package/lib/Util/zip.d.ts +11 -0
  766. package/lib/Util/zip.d.ts.map +1 -1
  767. package/lib/Util/zip.js +30 -0
  768. package/lib/Util/zip.js.map +1 -1
  769. package/lib/index.d.ts +3 -0
  770. package/lib/index.d.ts.map +1 -1
  771. package/lib/index.js +3 -0
  772. package/lib/index.js.map +1 -1
  773. package/package.json +46 -13
  774. package/src/AWS/ApiGateway/common.ts +9 -0
  775. package/src/AWS/Bedrock/LanguageModel.ts +883 -0
  776. package/src/AWS/Bedrock/LanguageModelHttp.ts +36 -0
  777. package/src/AWS/Bedrock/index.ts +2 -0
  778. package/src/AWS/CloudFront/Distribution.ts +116 -6
  779. package/src/AWS/CostExplorer/common.ts +23 -2
  780. package/src/AWS/DataSync/Task.ts +30 -15
  781. package/src/AWS/EC2/Vpc.ts +35 -21
  782. package/src/AWS/EC2/hosted.ts +10 -2
  783. package/src/AWS/ECR/ImageSource.ts +10 -2
  784. package/src/AWS/ECR/Repository.ts +39 -4
  785. package/src/AWS/ECS/Service.ts +322 -22
  786. package/src/AWS/ECS/Task.ts +82 -32
  787. package/src/AWS/EKS/Deployment.ts +10 -2
  788. package/src/AWS/EKS/Job.ts +10 -2
  789. package/src/AWS/Lambda/Function.ts +101 -43
  790. package/src/AWS/Lambda/HttpServer.ts +7 -1
  791. package/src/AWS/Lambda/LayerVersion.ts +490 -0
  792. package/src/AWS/Lambda/MicrovmBundle.ts +10 -2
  793. package/src/AWS/Lambda/index.ts +1 -0
  794. package/src/AWS/Providers.ts +10 -0
  795. package/src/AWS/SES/ActiveReceiptRuleSet.ts +88 -0
  796. package/src/AWS/SES/ReceiptFilter.ts +169 -0
  797. package/src/AWS/SES/ReceiptRule.ts +300 -0
  798. package/src/AWS/SES/ReceiptRuleSet.ts +164 -0
  799. package/src/AWS/SES/SendBounce.ts +44 -0
  800. package/src/AWS/SES/SendBounceHttp.ts +13 -0
  801. package/src/AWS/SES/index.ts +6 -0
  802. package/src/AWS/StateStore/State.ts +159 -42
  803. package/src/Apply.ts +199 -83
  804. package/src/Axiom/Telemetry.ts +119 -0
  805. package/src/Axiom/index.ts +1 -0
  806. package/src/Bundle/Bundle.ts +17 -7
  807. package/src/Bundle/BundleAnalyzerPlugin.ts +9 -4
  808. package/src/Bundle/PurePlugin.ts +19 -9
  809. package/src/Cli/checkVersion.ts +102 -16
  810. package/src/Cli/commands/_shared.ts +2 -0
  811. package/src/Cli/commands/deploy.ts +3 -14
  812. package/src/Cli/commands/logs.ts +6 -1
  813. package/src/Cli/commands/tail.ts +6 -1
  814. package/src/Cli/main.ts +5 -4
  815. package/src/Cloudflare/AI/Search.ts +4 -18
  816. package/src/Cloudflare/AI/SearchInstance.ts +7 -26
  817. package/src/Cloudflare/ApiToken/AccountApiToken.ts +3 -0
  818. package/src/Cloudflare/Auth/AuthProvider.ts +17 -3
  819. package/src/Cloudflare/Auth/OAuthClient.ts +58 -2
  820. package/src/Cloudflare/Containers/Container.ts +92 -3
  821. package/src/Cloudflare/Containers/ContainerApplication.ts +31 -5
  822. package/src/Cloudflare/Containers/ContainerBundle.ts +10 -2
  823. package/src/Cloudflare/Containers/ContainerProvider.ts +62 -0
  824. package/src/Cloudflare/Credentials.ts +69 -9
  825. package/src/Cloudflare/D1/ApplyMigrations.ts +72 -77
  826. package/src/Cloudflare/D1/Database.ts +169 -2
  827. package/src/Cloudflare/D1/LocalD1Gateway.ts +150 -0
  828. package/src/Cloudflare/D1/QueryDatabaseHttpClient.ts +49 -11
  829. package/src/Cloudflare/D1/QueryDatabaseLocal.ts +44 -2
  830. package/src/Cloudflare/DNS/Record.ts +30 -4
  831. package/src/Cloudflare/Email/Address.ts +18 -1
  832. package/src/Cloudflare/Email/SendBinding.ts +1 -0
  833. package/src/Cloudflare/Email/SendEmail.ts +13 -0
  834. package/src/Cloudflare/Flagship/App.ts +18 -6
  835. package/src/Cloudflare/KV/LocalKVGateway.ts +86 -0
  836. package/src/Cloudflare/KV/Namespace.ts +47 -1
  837. package/src/Cloudflare/KV/NamespaceLocal.ts +29 -6
  838. package/src/Cloudflare/KV/ReadNamespaceLocal.ts +5 -1
  839. package/src/Cloudflare/KV/ReadWriteNamespaceLocal.ts +5 -1
  840. package/src/Cloudflare/KV/WriteNamespaceLocal.ts +5 -1
  841. package/src/Cloudflare/LocalGateway.ts +86 -0
  842. package/src/Cloudflare/LocalRuntime.ts +21 -1
  843. package/src/Cloudflare/Logs.ts +58 -28
  844. package/src/Cloudflare/Providers.ts +4 -2
  845. package/src/Cloudflare/Queues/Consumer.ts +70 -2
  846. package/src/Cloudflare/Queues/Queue.ts +7 -16
  847. package/src/Cloudflare/Queues/QueueBinding.ts +21 -0
  848. package/src/Cloudflare/Queues/QueueShim.ts +193 -0
  849. package/src/Cloudflare/R2/Bucket.ts +55 -1
  850. package/src/Cloudflare/R2/BucketBinding.ts +17 -3
  851. package/src/Cloudflare/R2/BucketLocal.ts +25 -8
  852. package/src/Cloudflare/R2/LocalR2Gateway.ts +84 -0
  853. package/src/Cloudflare/R2/ReadBucketLocal.ts +7 -1
  854. package/src/Cloudflare/R2/ReadWriteBucketLocal.ts +7 -1
  855. package/src/Cloudflare/R2/WriteBucketLocal.ts +7 -1
  856. package/src/Cloudflare/Ruleset/AccountEntrypoint.ts +5 -1
  857. package/src/Cloudflare/Ruleset/Ruleset.ts +20 -6
  858. package/src/Cloudflare/Rum/Site.ts +11 -4
  859. package/src/Cloudflare/StateStore/Api.ts +1 -1
  860. package/src/Cloudflare/Tunnel/Tunnel.ts +23 -1
  861. package/src/Cloudflare/Website/StaticSite.ts +117 -28
  862. package/src/Cloudflare/Website/Vite.ts +33 -19
  863. package/src/Cloudflare/Workers/Compatibility.ts +27 -7
  864. package/src/Cloudflare/Workers/DurableObject.ts +22 -0
  865. package/src/Cloudflare/Workers/DurableObjectBridge.ts +29 -10
  866. package/src/Cloudflare/Workers/InferEnv.ts +74 -59
  867. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +619 -401
  868. package/src/Cloudflare/Workers/Worker.ts +676 -28
  869. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +209 -25
  870. package/src/Cloudflare/Workers/WorkerBinding.ts +67 -3
  871. package/src/Cloudflare/Workers/WorkerBridge.ts +26 -4
  872. package/src/Cloudflare/Workers/WorkerBundle.ts +6 -1
  873. package/src/Cloudflare/Workers/WorkerProvider.ts +2269 -277
  874. package/src/Cloudflare/Workflows/WorkflowBridge.ts +31 -13
  875. package/src/Command/Build.ts +8 -8
  876. package/src/Command/Command.ts +217 -42
  877. package/src/Command/Dev.ts +79 -107
  878. package/src/Command/Exec.ts +11 -2
  879. package/src/Command/Memo.ts +27 -3
  880. package/src/Command/Redaction.ts +181 -0
  881. package/src/Destroy.ts +1 -10
  882. package/src/Docker/Container.ts +55 -15
  883. package/src/Docker/Context.ts +236 -0
  884. package/src/Docker/Docker.ts +393 -43
  885. package/src/Docker/Image.ts +28 -7
  886. package/src/Docker/Network.ts +38 -14
  887. package/src/Docker/Providers.ts +16 -1
  888. package/src/Docker/RemoteImage.ts +23 -7
  889. package/src/Docker/Service.ts +1119 -0
  890. package/src/Docker/ServiceImage.ts +331 -0
  891. package/src/Docker/Swarm.ts +226 -0
  892. package/src/Docker/Volume.ts +25 -6
  893. package/src/Docker/index.ts +3 -0
  894. package/src/Http.ts +15 -2
  895. package/src/Local/LocalProvider.ts +518 -0
  896. package/src/Local/ProviderLayer.ts +113 -11
  897. package/src/Local/RpcProvider.ts +10 -5
  898. package/src/Local/RpcProviderProxy.ts +4 -1
  899. package/src/Local/RpcServer.ts +20 -2
  900. package/src/Plan.ts +182 -54
  901. package/src/Planetscale/AuthProvider.ts +15 -2
  902. package/src/Planetscale/Branch.ts +39 -6
  903. package/src/Planetscale/MySQL/MySQLDatabase.ts +34 -6
  904. package/src/Planetscale/OAuthClient.ts +59 -3
  905. package/src/Planetscale/Postgres/PostgresDatabase.ts +33 -5
  906. package/src/Platform.ts +29 -17
  907. package/src/Prisma/App.ts +433 -0
  908. package/src/Prisma/AuthProvider.ts +260 -0
  909. package/src/Prisma/Branch.ts +417 -0
  910. package/src/Prisma/Client.ts +1337 -0
  911. package/src/Prisma/Compute.ts +2878 -0
  912. package/src/Prisma/ComputeArchive.ts +432 -0
  913. package/src/Prisma/ComputeBuild.ts +1652 -0
  914. package/src/Prisma/ComputeLifecycle.ts +391 -0
  915. package/src/Prisma/Connect.ts +433 -0
  916. package/src/Prisma/Connection.ts +643 -0
  917. package/src/Prisma/CustomDomain.ts +373 -0
  918. package/src/Prisma/Database.ts +850 -0
  919. package/src/Prisma/Deployment.ts +661 -0
  920. package/src/Prisma/EnvironmentVariable.ts +417 -0
  921. package/src/Prisma/Internal/AppIdentity.ts +19 -0
  922. package/src/Prisma/Internal/AppPromotion.ts +172 -0
  923. package/src/Prisma/Internal/ArchivePlatform.ts +320 -0
  924. package/src/Prisma/Internal/ArtifactFile.ts +242 -0
  925. package/src/Prisma/Internal/ArtifactUpload.ts +86 -0
  926. package/src/Prisma/Internal/BundlePaths.ts +28 -0
  927. package/src/Prisma/Internal/CleanupFailure.ts +17 -0
  928. package/src/Prisma/Internal/DatabaseSecrets.ts +175 -0
  929. package/src/Prisma/Internal/DeploymentActions.ts +59 -0
  930. package/src/Prisma/Internal/DeploymentIdentity.ts +25 -0
  931. package/src/Prisma/Internal/DeploymentObserve.ts +6 -0
  932. package/src/Prisma/Internal/EnvName.ts +24 -0
  933. package/src/Prisma/Internal/HttpClient.ts +30 -0
  934. package/src/Prisma/Operations.ts +259 -0
  935. package/src/Prisma/Postgres.ts +25 -0
  936. package/src/Prisma/PostgresOrigin.ts +52 -0
  937. package/src/Prisma/PrismaDevDatabase.ts +395 -0
  938. package/src/Prisma/PrismaEnvironment.ts +89 -0
  939. package/src/Prisma/PrismaLogs.ts +434 -0
  940. package/src/Prisma/Project.ts +687 -0
  941. package/src/Prisma/Providers.ts +471 -0
  942. package/src/Prisma/Refs.ts +69 -0
  943. package/src/Prisma/SourceRepository.ts +455 -0
  944. package/src/Prisma/Types.ts +611 -0
  945. package/src/Prisma/index.ts +24 -0
  946. package/src/Provider.ts +155 -39
  947. package/src/ProviderMode.ts +123 -0
  948. package/src/Resource.ts +46 -0
  949. package/src/RuntimeContext.ts +7 -0
  950. package/src/Server/Process.ts +33 -0
  951. package/src/State/InMemoryState.ts +2 -0
  952. package/src/State/ResourceState.ts +18 -0
  953. package/src/Sync.ts +14 -2
  954. package/src/Telemetry.ts +736 -0
  955. package/src/Test/Alchemy.ts +3 -1
  956. package/src/Test/Core.ts +1 -8
  957. package/src/Util/Clank.ts +37 -11
  958. package/src/Util/zip.ts +39 -0
  959. package/src/index.ts +3 -0
@@ -1,14 +1,19 @@
1
1
  import * as durableObjectsApi from "@distilled.cloud/cloudflare/durable-objects";
2
+ import * as rulesets from "@distilled.cloud/cloudflare/rulesets";
2
3
  import * as workers from "@distilled.cloud/cloudflare/workers";
3
4
  import * as wfp from "@distilled.cloud/cloudflare/workers-for-platforms";
4
5
  import * as zones from "@distilled.cloud/cloudflare/zones";
6
+ import * as Config from "effect/Config";
5
7
  import * as Data from "effect/Data";
6
8
  import * as Effect from "effect/Effect";
9
+ import * as Layer from "effect/Layer";
10
+ import * as Option from "effect/Option";
7
11
  import * as Path from "effect/Path";
8
12
  import * as Predicate from "effect/Predicate";
9
13
  import * as Redacted from "effect/Redacted";
10
14
  import * as Schedule from "effect/Schedule";
11
15
  import * as Stream from "effect/Stream";
16
+ import { isHttpClientError } from "effect/unstable/http/HttpClientError";
12
17
  import * as crypto from "node:crypto";
13
18
  import { Unowned } from "../../AdoptPolicy.js";
14
19
  import * as Artifacts from "../../Artifacts.js";
@@ -18,16 +23,18 @@ import * as ProviderLayer from "../../Local/ProviderLayer.js";
18
23
  import * as Provider from "../../Provider.js";
19
24
  import {} from "../../Resource.js";
20
25
  import { Stack } from "../../Stack.js";
26
+ import { cachedFunction } from "../../Util/cached-function.js";
21
27
  import { sha256Object } from "../../Util/sha256.js";
22
28
  import { CloudflareEnvironment } from "../CloudflareEnvironment.js";
29
+ import { localRuntimeServices } from "../LocalRuntime.js";
23
30
  import { CloudflareLogs } from "../Logs.js";
24
- import { listAllZones, resolveZoneId } from "../Zone/lookup.js";
31
+ import { resolveZoneId } from "../Zone/lookup.js";
25
32
  import { mergeAssetsConfigFiles, readAssets, readAssetsConfigFiles, uploadAssets, } from "./Assets.js";
26
33
  import { getCompatibility } from "./Compatibility.js";
27
34
  import { isDurableObjectExport } from "./DurableObject.js";
28
35
  import { LocalWorkerProvider } from "./LocalWorkerProvider.js";
29
- import { Worker, } from "./Worker.js";
30
- import { getCacheBinding, getCronBindings } from "./WorkerAsyncBindings.js";
36
+ import { isSelfUrl, Worker, } from "./Worker.js";
37
+ import { getCacheBinding, getCronBindings, isContainerDecl, } from "./WorkerAsyncBindings.js";
31
38
  import { isPythonMain, readPythonWorkerBundle } from "./PythonWorkerBundle.js";
32
39
  import { readPrebuiltWorkerBundle, WorkerBundle } from "./WorkerBundle.js";
33
40
  import { isWorkerLoader } from "./WorkerLoader.js";
@@ -90,6 +97,298 @@ export const resolveNamespaceName = (namespace) => {
90
97
  return namespace;
91
98
  return namespace.name;
92
99
  };
100
+ /**
101
+ * A Worker's `version` configuration is invalid — a prop that can't be
102
+ * combined with `version.parent` (script-level settings belong to the
103
+ * parent), a locally-hosted Durable Object / Workflow class on a version
104
+ * worker, an out-of-range `traffic`, or a gradual rollout that requires
105
+ * changes the versions API can't carry (assets, DO migrations).
106
+ */
107
+ export class WorkerVersionConfigError extends Data.TaggedError("WorkerVersionConfigError") {
108
+ }
109
+ /**
110
+ * Resolve the parent script *name* from a resolved `version.parent` prop or
111
+ * persisted props. The engine resolves a passed {@link Worker} (or
112
+ * `Worker.ref(...)`) to its Attributes object — possibly stables-only during
113
+ * planning, but `workerName` is always a stable — so the value is either the
114
+ * script name string, that attributes object, or `undefined`.
115
+ *
116
+ * @internal
117
+ */
118
+ export const resolveVersionParentName = (version) => {
119
+ const parent = version?.parent;
120
+ if (parent == null)
121
+ return undefined;
122
+ if (typeof parent === "string")
123
+ return parent;
124
+ const workerName = parent.workerName;
125
+ return typeof workerName === "string" ? workerName : undefined;
126
+ };
127
+ /**
128
+ * The traffic percentage a *self-owned* Worker's new version should receive,
129
+ * or `undefined` for the default full cutover. Only a `version` prop without
130
+ * a `parent` participates — version workers handle traffic separately.
131
+ *
132
+ * @internal
133
+ */
134
+ const getSelfRolloutTraffic = (news) => {
135
+ if (!news.version || news.version.parent != null)
136
+ return undefined;
137
+ const traffic = news.version.traffic;
138
+ return traffic === undefined || traffic >= 100 ? undefined : traffic;
139
+ };
140
+ const validateTraffic = (traffic) => traffic !== undefined &&
141
+ (!Number.isFinite(traffic) || traffic < 0 || traffic > 100)
142
+ ? Effect.fail(new WorkerVersionConfigError({
143
+ message: `version.traffic must be a percentage between 0 and 100, got ${traffic}`,
144
+ }))
145
+ : Effect.void;
146
+ /** The request header Cloudflare hashes to pin a request to a version. */
147
+ const AFFINITY_HEADER = "Cloudflare-Workers-Version-Key";
148
+ // Cookie / header names are interpolated into a double-quoted Rules-language
149
+ // string literal — restrict them to the token characters real-world names
150
+ // use so a name can never terminate the literal or smuggle expression text.
151
+ const AFFINITY_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
152
+ /**
153
+ * Validate `version.affinity` and normalize it to its key source: exactly
154
+ * one of `cookie` / `header` / `key`, or `ip: true` alone; `ip` combines
155
+ * with `cookie` / `header` as the absent-source fallback.
156
+ *
157
+ * @internal exported for unit testing.
158
+ */
159
+ export const resolveVersionAffinity = (affinity) => Effect.gen(function* () {
160
+ const declared = [
161
+ ...(affinity.cookie !== undefined ? ["cookie"] : []),
162
+ ...(affinity.header !== undefined ? ["header"] : []),
163
+ ...(affinity.key !== undefined ? ["key"] : []),
164
+ ];
165
+ if (declared.length > 1) {
166
+ return yield* Effect.fail(new WorkerVersionConfigError({
167
+ message: `version.affinity accepts exactly one key source, got ${declared.join(" and ")}. Combine sources with a raw \`key\` expression instead.`,
168
+ }));
169
+ }
170
+ if (declared.length === 0 && affinity.ip !== true) {
171
+ return yield* Effect.fail(new WorkerVersionConfigError({
172
+ message: "version.affinity requires a key source: set `cookie`, `header`, `key`, or `ip: true`.",
173
+ }));
174
+ }
175
+ if (affinity.key !== undefined && affinity.ip === true) {
176
+ return yield* Effect.fail(new WorkerVersionConfigError({
177
+ message: "version.affinity: `ip` is the fallback for an absent `cookie`/`header` — a raw `key` expression has no absence condition to fall back from. Fold `ip.src` into the expression instead.",
178
+ }));
179
+ }
180
+ for (const [prop, name] of [
181
+ ["cookie", affinity.cookie],
182
+ ["header", affinity.header],
183
+ ]) {
184
+ if (name !== undefined && !AFFINITY_NAME_PATTERN.test(name)) {
185
+ return yield* Effect.fail(new WorkerVersionConfigError({
186
+ message: `version.affinity.${prop} '${name}' is not a valid ${prop} name: expected only letters, digits, '_', '.', and '-'.`,
187
+ }));
188
+ }
189
+ }
190
+ const source = affinity.cookie !== undefined
191
+ ? { kind: "cookie", name: affinity.cookie }
192
+ : affinity.header !== undefined
193
+ ? // Rules-language header map keys are lowercase.
194
+ { kind: "header", name: affinity.header.toLowerCase() }
195
+ : affinity.key !== undefined
196
+ ? { kind: "key", expression: affinity.key }
197
+ : { kind: "ip" };
198
+ return {
199
+ source,
200
+ ipFallback: affinity.ip === true &&
201
+ (source.kind === "cookie" || source.kind === "header"),
202
+ };
203
+ });
204
+ /**
205
+ * The `http.host` clause scoping a zone's affinity rules to the Worker's
206
+ * own hostnames, so unrelated zone traffic — and other Workers' rollouts
207
+ * on the same zone — never get this Worker's version key.
208
+ *
209
+ * @internal exported for unit testing.
210
+ */
211
+ export const affinityHostExpression = (hosts) => {
212
+ const dedupe = (values) => [...new Set(values)].sort();
213
+ const exact = dedupe(hosts.filter((h) => !h.wildcard).map((h) => h.host));
214
+ const wild = dedupe(hosts.filter((h) => h.wildcard).map((h) => h.host));
215
+ const clauses = [
216
+ ...(exact.length === 1
217
+ ? [`http.host eq "${exact[0]}"`]
218
+ : exact.length > 1
219
+ ? [`http.host in {${exact.map((h) => `"${h}"`).join(" ")}}`]
220
+ : []),
221
+ ...wild.map((h) => `http.host wildcard "${h}"`),
222
+ ];
223
+ return clauses.length === 1 ? clauses[0] : `(${clauses.join(" or ")})`;
224
+ };
225
+ const affinityRulePrefix = (scriptName) => `alchemy:worker:${scriptName}:affinity`;
226
+ /**
227
+ * The transform rules pinning one zone's traffic: a primary rule filling
228
+ * the version-key header from the configured source, plus — for
229
+ * `cookie`/`header` sources with `ip: true` — a fallback rule keying
230
+ * requests that lack the source by client IP.
231
+ *
232
+ * @internal exported for unit testing.
233
+ */
234
+ export const buildAffinityZoneRules = (scriptName, affinity, hosts) => {
235
+ const prefix = affinityRulePrefix(scriptName);
236
+ const hostExpr = affinityHostExpression(hosts);
237
+ const { source } = affinity;
238
+ if (source.kind === "ip" || source.kind === "key") {
239
+ return [
240
+ {
241
+ description: `${prefix}:key`,
242
+ expression: hostExpr,
243
+ value: source.kind === "ip" ? "to_string(ip.src)" : source.expression,
244
+ },
245
+ ];
246
+ }
247
+ const field = source.kind === "cookie"
248
+ ? `http.request.cookies["${source.name}"]`
249
+ : `http.request.headers["${source.name}"]`;
250
+ return [
251
+ {
252
+ description: `${prefix}:key`,
253
+ expression: `${hostExpr} and len(${field}) > 0`,
254
+ value: `${field}[0]`,
255
+ },
256
+ ...(affinity.ipFallback
257
+ ? [
258
+ {
259
+ // An absent cookie/header is a *missing* value in the Rules
260
+ // language, and every comparison on missing evaluates false —
261
+ // `len(field) == 0` can never match. `not (len(field) > 0)`
262
+ // is the complement that does: missing → false → `not` → true.
263
+ description: `${prefix}:ip`,
264
+ expression: `${hostExpr} and not (len(${field}) > 0)`,
265
+ value: "to_string(ip.src)",
266
+ },
267
+ ]
268
+ : []),
269
+ ];
270
+ };
271
+ /**
272
+ * A Worker's `domain` configuration is invalid — a hostname appears in more
273
+ * than one role (name/aliases/redirects), or a redirect targets itself.
274
+ */
275
+ export class WorkerDomainConfigError extends Data.TaggedError("WorkerDomainConfigError") {
276
+ }
277
+ /**
278
+ * Resolve the `workersDev` prop to its full shape. `true` / omitted means
279
+ * "default workers.dev behavior" (stable URL + version previews), `false`
280
+ * disables both, and the object form fills unset toggles with `true`.
281
+ *
282
+ * @internal exported for unit testing.
283
+ */
284
+ export const resolveWorkersDev = (workersDev) => {
285
+ if (workersDev === undefined || workersDev === true) {
286
+ return { enabled: true, previewsEnabled: true };
287
+ }
288
+ if (workersDev === false) {
289
+ return { enabled: false, previewsEnabled: false };
290
+ }
291
+ return {
292
+ enabled: workersDev.enabled ?? true,
293
+ previewsEnabled: workersDev.previewsEnabled ?? true,
294
+ };
295
+ };
296
+ // Convert non-ASCII hostnames (emoji, IDN, etc.) to punycode so the
297
+ // Cloudflare API receives the form it stores domains in. `new URL(...)`
298
+ // does IDNA via WHATWG URL parsing — `📦.alchemy.run` → `xn--5z8h.alchemy.run`.
299
+ const toPunycode = (hostname) => {
300
+ try {
301
+ return new URL(`https://${hostname}`).hostname;
302
+ }
303
+ catch {
304
+ return hostname;
305
+ }
306
+ };
307
+ /**
308
+ * Resolve the `domain` prop to its full shape — a bare string is shorthand
309
+ * for `{ name }`. Hostnames are punycode-normalized and de-duplicated;
310
+ * a hostname may only play one role, so aliases/redirects that repeat the
311
+ * canonical name (or each other) fail with a typed error.
312
+ *
313
+ * @internal exported for unit testing.
314
+ */
315
+ export const resolveWorkerDomain = (
316
+ // `string[]` is the pre-redesign prop shape — it can still reach us from
317
+ // persisted `olds` written by older providers (read's classification).
318
+ domain) => Effect.gen(function* () {
319
+ if (domain === undefined || domain === null)
320
+ return undefined;
321
+ // Legacy array form: the first hostname was the primary custom domain
322
+ // (`url = domains[0]` back then), the rest map to aliases. A legacy
323
+ // empty array was the explicit detach-all — no domain.
324
+ const config = typeof domain === "string"
325
+ ? { name: domain }
326
+ : Array.isArray(domain)
327
+ ? domain.length > 0
328
+ ? { name: domain[0], aliases: domain.slice(1) }
329
+ : undefined
330
+ : domain;
331
+ if (config === undefined)
332
+ return undefined;
333
+ const name = toPunycode(config.name);
334
+ const aliases = Array.from(new Set((config.aliases ?? []).map(toPunycode)));
335
+ const redirects = Array.from(new Set((config.redirects ?? []).map(toPunycode)));
336
+ const overlap = [
337
+ ...aliases.filter((h) => h === name),
338
+ ...redirects.filter((h) => h === name || aliases.includes(h)),
339
+ ];
340
+ if (overlap.length > 0) {
341
+ return yield* Effect.fail(new WorkerDomainConfigError({
342
+ message: `Each hostname may play only one role in a Worker's domain config; ${[...new Set(overlap)].map((h) => `'${h}'`).join(", ")} appears in more than one of name/aliases/redirects.`,
343
+ }));
344
+ }
345
+ return { name, aliases, redirects };
346
+ });
347
+ const isWorkersDevHostname = (hostname) => hostname.endsWith(".workers.dev");
348
+ // Hostnames that only appear in local-dev state (the dev server's
349
+ // localhost/LAN URLs), never as attachable custom domains.
350
+ const isLocalDevHostname = (hostname) => hostname === "localhost" ||
351
+ hostname === "::1" ||
352
+ /^\d+\.\d+\.\d+\.\d+$/.test(hostname);
353
+ const urlHostname = (url) => {
354
+ try {
355
+ return new URL(url).hostname;
356
+ }
357
+ catch {
358
+ return url;
359
+ }
360
+ };
361
+ /**
362
+ * The *custom domain* hostnames recorded in a Worker's persisted legacy
363
+ * `domains` state — minus the workers.dev (stable or preview) and local-dev
364
+ * entries that shared the list in older formats.
365
+ *
366
+ * @internal exported for unit testing.
367
+ */
368
+ export const stateCustomDomains = (domains) => normalizeStateDomains(domains).filter((hostname) => !isWorkersDevHostname(hostname) && !isLocalDevHostname(hostname));
369
+ /**
370
+ * The Worker's persisted domain configuration: the `domain` attribute for
371
+ * state written by the current format, else re-derived from the legacy
372
+ * `domains` list (first custom hostname = canonical name, rest = aliases;
373
+ * legacy state had no redirects).
374
+ *
375
+ * @internal exported for unit testing.
376
+ */
377
+ export const stateWorkerDomain = (output) => {
378
+ const state = output;
379
+ const domain = state?.domain;
380
+ if (domain && typeof domain.name === "string") {
381
+ return {
382
+ name: domain.name,
383
+ aliases: (domain.aliases ?? []).filter((h) => typeof h === "string"),
384
+ redirects: (domain.redirects ?? []).filter((h) => typeof h === "string"),
385
+ };
386
+ }
387
+ const legacy = stateCustomDomains(state?.domains);
388
+ return legacy.length > 0
389
+ ? { name: legacy[0], aliases: legacy.slice(1), redirects: [] }
390
+ : undefined;
391
+ };
93
392
  // Workers for Platforms "user workers" live inside a dispatch namespace and
94
393
  // use a parallel family of script endpoints (`/workers/dispatch/namespaces/
95
394
  // :namespace/scripts/...`). The request/response shapes are identical to the
@@ -149,6 +448,15 @@ const isBindingTargetNotFound = (e) => e._tag === "SecretsStoreBindingNotFound"
149
448
  e._tag === "DispatchNamespaceNotFound" ||
150
449
  e._tag === "MtlsCertificateNotFound";
151
450
  const bindingTargetNotFoundRetrySchedule = () => Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(10)]);
451
+ /**
452
+ * Script PUT is an idempotent upsert, so a pure transport failure (the
453
+ * request died before any response — e.g. Cloudflare closing a keep-alive
454
+ * socket that idled while slow upstream resources provisioned earlier in the
455
+ * deploy) is safe to replay. Errors that carry a response are real API
456
+ * verdicts and are NOT retried here.
457
+ */
458
+ const isScriptPutTransportError = (e) => isHttpClientError(e) && e.reason._tag === "TransportError";
459
+ const retryableScriptPut = (e) => isBindingTargetNotFound(e) || isScriptPutTransportError(e);
152
460
  /**
153
461
  * Upsert a Worker script, routing to the dispatch-namespace endpoint when
154
462
  * `dispatchNamespace` is set. The metadata/files contract is identical, and
@@ -168,7 +476,7 @@ const putWorkerScript = (params) => Effect.gen(function* () {
168
476
  files: params.files,
169
477
  })
170
478
  .pipe(Effect.retry({
171
- while: isBindingTargetNotFound,
479
+ while: retryableScriptPut,
172
480
  schedule: bindingTargetNotFoundRetrySchedule(),
173
481
  }));
174
482
  }
@@ -180,7 +488,7 @@ const putWorkerScript = (params) => Effect.gen(function* () {
180
488
  files: params.files,
181
489
  })
182
490
  .pipe(Effect.retry({
183
- while: isBindingTargetNotFound,
491
+ while: retryableScriptPut,
184
492
  schedule: bindingTargetNotFoundRetrySchedule(),
185
493
  }));
186
494
  });
@@ -202,23 +510,70 @@ const deleteWorkerScript = (accountId, scriptName, dispatchNamespace) => Effect.
202
510
  return yield* workers.deleteScript({ accountId, scriptName, force: true });
203
511
  });
204
512
  /**
205
- * Normalize a Worker's persisted `domains` state to `https://<hostname>`
206
- * strings. Alchemy <= beta.44 stored each custom domain as a
207
- * `{ id, hostname, zoneId }` object; beta.45+ stores `https://<hostname>`
208
- * strings and the diff path calls string methods on each entry. Older state is
209
- * coerced back to strings so `.endsWith` does not throw `u.endsWith is not a
210
- * function` (#546). Entries that are neither a string nor an object with a
211
- * string `hostname` are dropped rather than turned into a bogus `https://`
212
- * value that would skew the diff.
513
+ * Normalize a Worker's persisted *legacy* `domains` state to bare
514
+ * hostnames. Alchemy <= beta.44 stored each custom domain as a
515
+ * `{ id, hostname, zoneId }` object; beta.45+ stored `https://<hostname>`
516
+ * URL strings (with the workers.dev URL mixed in); current state stores the
517
+ * `domain` config object instead of a `domains` list. All legacy
518
+ * generations coerce to hostnames so the diff never throws on older state
519
+ * (#546). Entries that fit no generation are dropped rather than turned
520
+ * into a bogus hostname that would skew the diff.
213
521
  *
214
522
  * @internal exported for unit testing.
215
523
  */
216
524
  export const normalizeStateDomains = (domains) => (domains ?? []).flatMap((u) => {
217
- if (typeof u === "string")
218
- return [u];
525
+ if (typeof u === "string") {
526
+ if (u.includes("://")) {
527
+ try {
528
+ return [new URL(u).hostname];
529
+ }
530
+ catch {
531
+ return [];
532
+ }
533
+ }
534
+ return u.length > 0 ? [u] : [];
535
+ }
219
536
  const hostname = u?.hostname;
220
- return typeof hostname === "string" ? [`https://${hostname}`] : [];
537
+ return typeof hostname === "string" ? [hostname] : [];
221
538
  });
539
+ /**
540
+ * Custom domains Alchemy is responsible for on this Worker — either declared
541
+ * on props (`domain`) or already persisted as non-`workers.dev` URLs in state.
542
+ * Used by `read` to skip `listDomains` when the surface is unmanaged (#926).
543
+ *
544
+ * @internal exported for unit testing.
545
+ */
546
+ export const shouldObserveWorkerDomains = (olds, output) => olds?.domain !== undefined || stateWorkerDomain(output) !== undefined;
547
+ /**
548
+ * Zone routes Alchemy is responsible for on this Worker. Used by `read` to
549
+ * skip account-wide zone/route fan-out when the surface is unmanaged (#926).
550
+ *
551
+ * @internal exported for unit testing.
552
+ */
553
+ export const shouldObserveWorkerRoutes = (olds, output) => olds?.routes !== undefined || (output?.routes?.length ?? 0) > 0;
554
+ /**
555
+ * Cron triggers Alchemy is responsible for on this Worker. Used by `read` to
556
+ * skip `getScriptSchedule` when the surface is unmanaged (#926). Effect-native
557
+ * `cron()` bindings persist into `output.crons` after the first reconcile, so
558
+ * subsequent reads still observe them.
559
+ *
560
+ * @internal exported for unit testing.
561
+ */
562
+ export const shouldObserveWorkerCrons = (olds, output) => olds?.crons !== undefined || (output?.crons?.length ?? 0) > 0;
563
+ /**
564
+ * Optional override for the account's stable `workers.dev` subdomain
565
+ * (`<subdomain>` in `https://<script>.<subdomain>.workers.dev`). When set,
566
+ * Worker URL construction skips `GET /accounts/{id}/workers/subdomain`.
567
+ */
568
+ const CLOUDFLARE_WORKERS_SUBDOMAIN = Config.string("CLOUDFLARE_WORKERS_SUBDOMAIN").pipe(Config.map((value) => value.trim()), Config.option);
569
+ const WORKERS_SUBDOMAIN_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
570
+ /**
571
+ * Max concurrent `GET /zones/{id}/workers/routes` calls when observing the
572
+ * routes attached to a Worker. Route listing is per-zone, so this fans out
573
+ * with the number of zones the Worker has routes in; unbounded fan-out here
574
+ * is what trips Cloudflare account-level 429 / code 971 throttling (#926).
575
+ */
576
+ const WORKER_ROUTE_LIST_CONCURRENCY = 8;
222
577
  /**
223
578
  * Deeply materialize an arbitrary value into a JSON-stable shape for hashing:
224
579
  * unwrap `Redacted` secrets by value, ISO-stringify `Date`s, keep plain
@@ -289,8 +644,9 @@ const workerAssetConfigForHash = (assets) => {
289
644
  * the bundle/vite/asset-content hashes, so a change to e.g. a compatibility
290
645
  * flag or observability config planned as a noop and silently never deployed.
291
646
  */
292
- const resolveWorkerMetadataHash = ({ props, bindings, accountId, stack, }) => resolveMetadataHashValue({
647
+ const resolveWorkerMetadataHash = ({ props, bindings, accountId, stack, selfUrl, }) => resolveMetadataHashValue({
293
648
  accountId,
649
+ selfUrl,
294
650
  stack: { name: stack.name, stage: stack.stage },
295
651
  compatibility: getCompatibility(props),
296
652
  env: props.env,
@@ -304,13 +660,30 @@ const resolveWorkerMetadataHash = ({ props, bindings, accountId, stack, }) => re
304
660
  logpush: props.logpush,
305
661
  observability: props.observability,
306
662
  placement: props.placement,
307
- subdomain: props.subdomain,
308
663
  tags: props.tags,
309
- url: props.url,
664
+ workersDev: resolveWorkersDev(props.workersDev),
665
+ // Reduce `version.parent` to the parent's script name: the resolved
666
+ // parent is a full attributes object whose *other* fields (hash, url,
667
+ // ...) change on every parent deploy, which would spuriously re-version
668
+ // this Worker even though nothing about the version itself changed.
669
+ version: props.version
670
+ ? {
671
+ parent: resolveVersionParentName(props.version),
672
+ traffic: props.version.traffic,
673
+ alias: props.version.alias,
674
+ affinity: props.version.affinity,
675
+ message: props.version.message,
676
+ tag: props.version.tag,
677
+ }
678
+ : undefined,
310
679
  }).pipe(Effect.flatMap((metadata) => sha256Object({ metadata })));
311
- export const WorkerProvider = () => ProviderLayer.select({
680
+ export const WorkerProvider = () => ProviderLayer.dual(Worker, {
312
681
  live: () => LiveWorkerProvider(),
313
- local: () => LocalWorkerProvider(),
682
+ // The local runtime deps (workerd, WorkerProxy, LocalRuntimeState)
683
+ // compose INTO the local variant so a live deploy only constructs them
684
+ // if the local provider is actually demanded (e.g. deleting a local
685
+ // dev worker's state row). See ProviderLayer.dual.
686
+ local: () => LocalWorkerProvider().pipe(Layer.provide(localRuntimeServices())),
314
687
  });
315
688
  export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(function* () {
316
689
  const path = yield* Path.Path;
@@ -329,41 +702,53 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
329
702
  // const deleteDomain = yield* workers.deleteDomain;
330
703
  // const listZones = yield* zones.listZones;
331
704
  const telemetry = yield* CloudflareLogs;
332
- const getAccountSubdomain = (accountId) => workers
333
- .getSubdomain({
334
- accountId,
335
- })
336
- .pipe(Effect.map((result) => result.subdomain));
337
- // Toggle the workers.dev subdomain via `POST /subdomain` with
338
- // `enabled: true | false`. Mirrors the upstream Alchemy
339
- // implementation in `.vendor/alchemy/.../worker-subdomain.ts`.
340
- // When enabling we also set `previewsEnabled: true` so the
341
- // script is reachable both at its stable workers.dev URL and at
342
- // version-preview URLs; on disable we send just `enabled: false`.
343
- const setWorkerSubdomain = Effect.fn(function* (name, enabled) {
705
+ // Account subdomain is invariant for the life of a provider layer —
706
+ // memoize so N Workers (or repeated plan/deploy reads) don't each hit
707
+ // GET /accounts/{id}/workers/subdomain and trip Cloudflare's 429/971
708
+ // throttle (#926). `CLOUDFLARE_WORKERS_SUBDOMAIN` bypasses the lookup
709
+ // entirely when the operator already knows the value.
710
+ const getAccountSubdomain = yield* cachedFunction((accountId) => Effect.gen(function* () {
711
+ const configured = yield* CLOUDFLARE_WORKERS_SUBDOMAIN;
712
+ if (Option.isSome(configured) && configured.value !== "") {
713
+ if (!WORKERS_SUBDOMAIN_PATTERN.test(configured.value)) {
714
+ return yield* Effect.die(new Error(`Invalid CLOUDFLARE_WORKERS_SUBDOMAIN "${configured.value}": ` +
715
+ "expected a workers.dev subdomain label (lowercase letters, digits, hyphens)"));
716
+ }
717
+ return configured.value;
718
+ }
719
+ const result = yield* workers.getSubdomain({ accountId });
720
+ return result.subdomain;
721
+ }));
722
+ // Converge the script's workers.dev settings via `POST /subdomain`.
723
+ // The two toggles are independent on the Cloudflare API: `enabled`
724
+ // drives the stable `<name>.<account>.workers.dev` URL and
725
+ // `previews_enabled` the per-version preview URLs.
726
+ const setWorkerSubdomain = Effect.fn(function* (name, desired) {
344
727
  const { accountId } = yield* yield* CloudflareEnvironment;
345
728
  return yield* workers.createScriptSubdomain({
346
729
  accountId,
347
730
  scriptName: name,
348
- enabled,
349
- previewsEnabled: enabled ? true : undefined,
731
+ enabled: desired.enabled,
732
+ previewsEnabled: desired.previewsEnabled,
350
733
  });
351
734
  });
352
- // Convert non-ASCII hostnames (emoji, IDN, etc.) to punycode so the
353
- // Cloudflare API receives the form it stores domains in. `new URL(...)`
354
- // does IDNA via WHATWG URL parsing — `📦.alchemy.run` → `xn--5z8h.alchemy.run`.
355
- const toPunycode = (hostname) => {
356
- try {
357
- return new URL(`https://${hostname}`).hostname;
735
+ const normalizeCrons = (crons) => Array.from(new Set(crons ?? []));
736
+ const hasSelfUrlBinding = (bindings) => bindings.some((b) => (b.data.bindings ?? []).some((item) => item.type === "self_url"));
737
+ // Resolve the URL this Worker will be served at — the same formula that
738
+ // produces the `url` attribute (first custom domain in user order, else
739
+ // the workers.dev URL) — usable BEFORE the script upload, so `Worker.URL`
740
+ // bindings can be lowered into plain_text bindings and VITE_*-prefixed
741
+ // env entries can be inlined into the client bundle at build time.
742
+ const resolveSelfUrl = Effect.fn(function* (name, props, accountId) {
743
+ const domain = yield* resolveWorkerDomain(props.domain);
744
+ if (domain) {
745
+ return `https://${domain.name}`;
358
746
  }
359
- catch {
360
- return hostname;
747
+ if (!resolveWorkersDev(props.workersDev).enabled) {
748
+ return yield* Effect.die(`Worker "${name}" binds its own URL (Worker.URL) but has none: the stable workers.dev URL is disabled (workersDev) and no custom domain is configured.`);
361
749
  }
362
- };
363
- const normalizeDomains = (domain) => domain === undefined
364
- ? []
365
- : Array.from(new Set((Array.isArray(domain) ? domain : [domain]).map(toPunycode)));
366
- const normalizeCrons = (crons) => Array.from(new Set(crons ?? []));
750
+ return `https://${name}.${yield* getAccountSubdomain(accountId)}.workers.dev`;
751
+ });
367
752
  const getWorkerCrons = Effect.fn(function* (scriptName) {
368
753
  const { accountId } = yield* yield* CloudflareEnvironment;
369
754
  return yield* workers
@@ -519,6 +904,227 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
519
904
  });
520
905
  return applied;
521
906
  });
907
+ // The preview URL of the Worker's *current* version:
908
+ // `https://<version-prefix>-<name>.<subdomain>.workers.dev`, where the
909
+ // prefix is the first 8 characters of the version id (read from the
910
+ // latest deployment's majority version). Resolves to `undefined` when
911
+ // no deployment is visible yet — callers treat the preview URL as
912
+ // best-effort. Only consulted in previews-only mode
913
+ // (`workersDev: { enabled: false, previewsEnabled: true }`), where it
914
+ // is the Worker's only workers.dev surface.
915
+ const getPreviewUrl = Effect.fn(function* (scriptName, accountSubdomain) {
916
+ const { accountId } = yield* yield* CloudflareEnvironment;
917
+ const deployments = yield* workers
918
+ .listScriptDeployments({ accountId, scriptName })
919
+ .pipe(Effect.map((response) => response.deployments ?? []), Effect.catch(() => Effect.succeed([])));
920
+ const latest = [...deployments].sort((a, b) => b.createdOn.localeCompare(a.createdOn))[0];
921
+ const version = latest?.versions?.reduce((max, candidate) => max === undefined || candidate.percentage > max.percentage
922
+ ? candidate
923
+ : max, undefined);
924
+ if (!version?.versionId)
925
+ return undefined;
926
+ return `https://${version.versionId.slice(0, 8)}-${scriptName}.${accountSubdomain}.workers.dev`;
927
+ });
928
+ /**
929
+ * Assemble every URL a deployed, script-owning Worker serves at, most
930
+ * significant first — the first entry becomes the `url` attribute:
931
+ *
932
+ * 1. `https://<domain.name>` — the canonical custom domain
933
+ * 2. aliases, in declared order
934
+ * 3. the stable workers.dev URL (`workersDev.enabled`)
935
+ * 4. preview URLs of the version this deploy uploaded (gradual
936
+ * rollouts), or — in previews-only mode — the current version's
937
+ * preview URL, which is then the only workers.dev surface
938
+ *
939
+ * Redirect hostnames never appear: they don't serve the Worker.
940
+ * (Version workers don't use this — their `urls` are their preview
941
+ * URLs, assembled in `putWorkerVersion`.)
942
+ */
943
+ const computeWorkerUrls = Effect.fn(function* (params) {
944
+ const { workersDev, domain, scriptName } = params;
945
+ const urls = [];
946
+ if (domain) {
947
+ urls.push(`https://${domain.name}`, ...domain.aliases.map((hostname) => `https://${hostname}`));
948
+ }
949
+ if (workersDev.enabled || workersDev.previewsEnabled) {
950
+ const { accountId } = yield* yield* CloudflareEnvironment;
951
+ const accountSubdomain = yield* getAccountSubdomain(accountId);
952
+ if (workersDev.enabled) {
953
+ urls.push(`https://${scriptName}.${accountSubdomain}.workers.dev`);
954
+ }
955
+ if (workersDev.previewsEnabled) {
956
+ if (params.uploadedVersionId !== undefined) {
957
+ if (params.uploadedVersionAlias !== undefined) {
958
+ urls.push(`https://${params.uploadedVersionAlias}-${scriptName}.${accountSubdomain}.workers.dev`);
959
+ }
960
+ urls.push(`https://${params.uploadedVersionId.split("-")[0]}-${scriptName}.${accountSubdomain}.workers.dev`);
961
+ }
962
+ else if (!workersDev.enabled) {
963
+ const previewUrl = yield* getPreviewUrl(scriptName, accountSubdomain);
964
+ if (previewUrl)
965
+ urls.push(previewUrl);
966
+ }
967
+ }
968
+ }
969
+ return urls;
970
+ });
971
+ /**
972
+ * Converge the redirect rules for a Worker's `domain.redirects` in
973
+ * each affected zone's `http_request_dynamic_redirect` phase
974
+ * entrypoint. Rules are identified by a
975
+ * `alchemy:worker:<script>:redirect:<host>` description; only our own
976
+ * rules are added/removed — every other rule in the shared entrypoint
977
+ * ruleset passes through untouched. Zones that previously held our
978
+ * rules but no longer should (removed redirects, removed domain) are
979
+ * cleaned the same way.
980
+ */
981
+ const reconcileRedirectRules = Effect.fn(function* (params) {
982
+ const prefix = `alchemy:worker:${params.scriptName}:redirect:`;
983
+ const desired = params.domain?.redirects ?? [];
984
+ const targetName = params.domain?.name;
985
+ // Zones to touch: every zone hosting a desired redirect, plus every
986
+ // zone that hosted one before (so removals converge). Hostnames
987
+ // whose zone we can't resolve (e.g. the domain was already
988
+ // detached) are skipped — their rules become unreachable dead
989
+ // config at worst, never a failed destroy.
990
+ const zoneIds = new Set();
991
+ for (const hostname of [...desired, ...params.previousRedirects]) {
992
+ const zoneId = params.zoneIdByHostname.get(hostname);
993
+ if (zoneId)
994
+ zoneIds.add(zoneId);
995
+ }
996
+ if (zoneIds.size === 0)
997
+ return;
998
+ for (const zoneId of zoneIds) {
999
+ const entrypoint = yield* rulesets
1000
+ .getPhasForZone({
1001
+ zoneId,
1002
+ rulesetPhase: "http_request_dynamic_redirect",
1003
+ })
1004
+ .pipe(Effect.catch(() => Effect.succeed(undefined)));
1005
+ const existingRules = entrypoint?.rules ?? [];
1006
+ const ourDesired = desired
1007
+ .filter((hostname) => params.zoneIdByHostname.get(hostname) === zoneId)
1008
+ .map((hostname) => ({
1009
+ action: "redirect",
1010
+ expression: `http.host eq "${hostname}"`,
1011
+ description: `${prefix}${hostname}`,
1012
+ enabled: true,
1013
+ actionParameters: {
1014
+ fromValue: {
1015
+ statusCode: 301,
1016
+ preserveQueryString: true,
1017
+ targetUrl: {
1018
+ expression: `concat("https://${targetName}", http.request.uri.path)`,
1019
+ },
1020
+ },
1021
+ },
1022
+ }));
1023
+ const isOurs = (rule) => (rule.description ?? "").startsWith(prefix);
1024
+ const ourExisting = existingRules.filter(isOurs);
1025
+ const converged = ourExisting.length === ourDesired.length &&
1026
+ ourDesired.every((rule) => ourExisting.some((existing) => existing.description === rule.description &&
1027
+ existing.expression === rule.expression));
1028
+ if (converged)
1029
+ continue;
1030
+ // Pass foreign rules through untouched (minus the read-only
1031
+ // fields the PUT schema doesn't accept) and replace our own set.
1032
+ const foreign = existingRules
1033
+ .filter((rule) => !isOurs(rule))
1034
+ .map((rule) => {
1035
+ const { lastUpdated: _lastUpdated, version: _version, ...rest } = rule;
1036
+ return rest;
1037
+ });
1038
+ yield* rulesets.putPhasForZone({
1039
+ zoneId,
1040
+ rulesetPhase: "http_request_dynamic_redirect",
1041
+ rules: [...foreign, ...ourDesired],
1042
+ });
1043
+ }
1044
+ });
1045
+ /**
1046
+ * Converge the version-affinity transform rules (`version.affinity`)
1047
+ * in each serving zone's `http_request_late_transform` phase
1048
+ * entrypoint: `rewrite` rules that fill the
1049
+ * `Cloudflare-Workers-Version-Key` header from the configured source,
1050
+ * scoped to the Worker's hostnames in that zone. Rules are identified
1051
+ * by an `alchemy:worker:<script>:affinity` description prefix; only
1052
+ * our own rules are added/updated/removed — every other rule in the
1053
+ * shared entrypoint passes through untouched. Zones that previously
1054
+ * held our rules but no longer should (affinity removed, zone no
1055
+ * longer served) are cleaned the same way. Returns the zone ids now
1056
+ * holding rules, for persistence in `affinityZoneIds`.
1057
+ */
1058
+ const reconcileAffinityRules = Effect.fn(function* (params) {
1059
+ const prefix = affinityRulePrefix(params.scriptName);
1060
+ const zoneIds = new Set([
1061
+ ...(params.desired?.hostsByZone.keys() ?? []),
1062
+ ...params.previousZoneIds,
1063
+ ]);
1064
+ for (const zoneId of zoneIds) {
1065
+ const hosts = params.desired?.hostsByZone.get(zoneId);
1066
+ const ourDesired = params.desired !== undefined && hosts !== undefined
1067
+ ? buildAffinityZoneRules(params.scriptName, params.desired.affinity, hosts)
1068
+ : [];
1069
+ const entrypoint = yield* rulesets
1070
+ .getPhasForZone({
1071
+ zoneId,
1072
+ rulesetPhase: "http_request_late_transform",
1073
+ })
1074
+ .pipe(Effect.catch(() => Effect.succeed(undefined)));
1075
+ const existingRules = entrypoint?.rules ?? [];
1076
+ const isOurs = (rule) => (rule.description ?? "").startsWith(prefix);
1077
+ // The header-value expression of an existing rewrite rule, for
1078
+ // convergence comparison. `headers` is an untyped wire record:
1079
+ // `{ "<Header-Name>": { operation, expression | value } }`.
1080
+ const existingValue = (rule) => {
1081
+ const headers = "actionParameters" in rule
1082
+ ? rule.actionParameters?.headers
1083
+ : undefined;
1084
+ const header = headers?.[AFFINITY_HEADER];
1085
+ return typeof header?.expression === "string"
1086
+ ? header.expression
1087
+ : undefined;
1088
+ };
1089
+ const ourExisting = existingRules.filter(isOurs);
1090
+ const converged = ourExisting.length === ourDesired.length &&
1091
+ ourDesired.every((rule) => ourExisting.some((existing) => existing.description === rule.description &&
1092
+ existing.expression === rule.expression &&
1093
+ existingValue(existing) === rule.value));
1094
+ if (converged)
1095
+ continue;
1096
+ // Pass foreign rules through untouched (minus the read-only
1097
+ // fields the PUT schema doesn't accept) and replace our own set.
1098
+ const foreign = existingRules
1099
+ .filter((rule) => !isOurs(rule))
1100
+ .map((rule) => {
1101
+ const { lastUpdated: _lastUpdated, version: _version, ...rest } = rule;
1102
+ return rest;
1103
+ });
1104
+ yield* rulesets.putPhasForZone({
1105
+ zoneId,
1106
+ rulesetPhase: "http_request_late_transform",
1107
+ rules: [
1108
+ ...foreign,
1109
+ ...ourDesired.map((rule) => ({
1110
+ action: "rewrite",
1111
+ description: rule.description,
1112
+ enabled: true,
1113
+ expression: rule.expression,
1114
+ actionParameters: {
1115
+ headers: {
1116
+ [AFFINITY_HEADER]: {
1117
+ operation: "set",
1118
+ expression: rule.value,
1119
+ },
1120
+ },
1121
+ },
1122
+ })),
1123
+ ],
1124
+ });
1125
+ }
1126
+ return [...(params.desired?.hostsByZone.keys() ?? [])].sort();
1127
+ });
522
1128
  const routeKey = (route) => `${route.zoneId}:${route.pattern}`;
523
1129
  // Derive a concrete hostname inside the zone from a route pattern so
524
1130
  // zone inference can walk the DNS label hierarchy. A wildcard label
@@ -572,19 +1178,30 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
572
1178
  }
573
1179
  const routesByZone = Effect.all(uniqueZoneIds.map((zoneId) => workers.listRoutes({ zoneId }).pipe(Effect.map((response) => (response.result ?? []).flatMap((route) => route.id && route.pattern && route.script === scriptName
574
1180
  ? [{ id: route.id, pattern: route.pattern, zoneId }]
575
- : [])), Effect.catch(() => Effect.succeed([])))), { concurrency: "unbounded" });
1181
+ : [])), Effect.catch(() => Effect.succeed([])))),
1182
+ // Bounded: this issues one request per zone, so a Worker with routes
1183
+ // spread across many zones would otherwise burst the account's whole
1184
+ // API budget in a single tick (#926).
1185
+ { concurrency: WORKER_ROUTE_LIST_CONCURRENCY });
576
1186
  return Effect.map(routesByZone, (routes) => routes.flat());
577
1187
  };
578
- // Observe every route attached to `scriptName` account-wide. Routes
579
- // are zone-scoped with no account-level enumeration API, so fan out
580
- // over all of the account's zones. Any failure to enumerate zones
581
- // (e.g. a token without zone read scope) degrades to "no routes"
582
- // rather than failing the read.
583
- const readWorkerRoutes = (scriptName) => Effect.gen(function* () {
584
- const { accountId } = yield* yield* CloudflareEnvironment;
585
- const accountZones = yield* listAllZones(accountId).pipe(Effect.catch(() => Effect.succeed([])));
586
- return yield* listWorkerRoutesInZones(scriptName, accountZones.map((zone) => zone.id));
587
- });
1188
+ // Observe the routes attached to `scriptName` in the zones Alchemy
1189
+ // already associates with this Worker.
1190
+ //
1191
+ // This used to enumerate every zone on the account and then list routes
1192
+ // in each one. Routes are zone-scoped with no account-level enumeration
1193
+ // API, so that costs O(zones) requests on *every* Worker read — on an
1194
+ // account with a few hundred zones a couple of plan/deploy cycles
1195
+ // exhausts Cloudflare's per-user API budget and unrelated calls start
1196
+ // coming back 429 / code 971 (#926).
1197
+ //
1198
+ // `reconcileRoutes` already scopes itself to the zones implied by
1199
+ // `desired ∪ previous` rather than sweeping the account, so `read` now
1200
+ // uses the same bounded zone set: the zones of the routes already
1201
+ // recorded in state. The blind spot this introduces — a route added out
1202
+ // of band in a zone this Worker has never had a route in — is the one
1203
+ // `reconcileRoutes` already has.
1204
+ const readWorkerRoutes = (scriptName, knownRoutes) => listWorkerRoutesInZones(scriptName, (knownRoutes ?? []).map((route) => route.zoneId));
588
1205
  // Converge the zone routes attached to `scriptName` to `desired`.
589
1206
  // Observed cloud state (not `previous`) is the diff baseline —
590
1207
  // `previous` only contributes zone ids so routes moved out of a zone
@@ -800,7 +1417,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
800
1417
  extraOptions: props.build,
801
1418
  })).pipe(Artifacts.cached("build"));
802
1419
  const hashScript = (script) => Effect.sync(() => crypto.createHash("sha256").update(script).digest("hex"));
803
- const viteBuild = Effect.fn(function* (props) {
1420
+ const viteBuild = Effect.fn(function* (props, selfUrl) {
804
1421
  const compatibility = getCompatibility(props);
805
1422
  // Loaded lazily: `./Vite.ts` pulls in `@distilled.cloud/cloudflare-vite-plugin`
806
1423
  // (~0.5s), which is only needed for vite-based workers at build time —
@@ -814,18 +1431,37 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
814
1431
  : Redacted.isRedacted(value) &&
815
1432
  typeof Redacted.value(value) === "string"
816
1433
  ? Redacted.value(value)
817
- : // A `WorkerLoader` is a real Effect that also carries
818
- // the `~alchemy/Kind` marker — it is a binding, not a
819
- // runnable env value. Check it before `Effect.isEffect`
820
- // so we don't execute it as an inlined env entry.
821
- isWorkerLoader(value)
822
- ? undefined
823
- : Effect.isEffect(value)
824
- ? yield* value
825
- : undefined,
1434
+ : // `Worker.URL` (bare tag or called) — resolved to
1435
+ // this Worker's own URL. The bare tag is
1436
+ // Effect-shaped, so check before `Effect.isEffect`.
1437
+ isSelfUrl(value)
1438
+ ? selfUrl
1439
+ : // A `WorkerLoader` is a real Effect that also carries
1440
+ // the `~alchemy/Kind` marker — it is a binding, not a
1441
+ // runnable env value. Check it before `Effect.isEffect`
1442
+ // so we don't execute it as an inlined env entry.
1443
+ isWorkerLoader(value)
1444
+ ? undefined
1445
+ : // A `Cloudflare.Container` declaration is likewise
1446
+ // Effect-shaped but is a binding (DO namespace +
1447
+ // ContainerApplication) — yielding it would resolve
1448
+ // the started-instance tag, which only exists inside
1449
+ // a Durable Object (#997).
1450
+ isContainerDecl(value)
1451
+ ? undefined
1452
+ : Effect.isEffect(value)
1453
+ ? yield* value
1454
+ : undefined,
826
1455
  ];
827
1456
  })))).filter(([_, value]) => value !== undefined)), {
828
- main: props.vite?.main,
1457
+ // A relative `vite.main` is documented to resolve from the Vite
1458
+ // root. The rolldown plugin resolves the worker entry with no
1459
+ // importer (i.e. against `process.cwd()`), which breaks when the
1460
+ // deploy runs from a different directory (e.g. a monorepo infra
1461
+ // package) — absolutize before handing it over (#796).
1462
+ main: props.vite?.main
1463
+ ? path.resolve(props.vite.rootDir ?? process.cwd(), props.vite.main)
1464
+ : undefined,
829
1465
  compatibilityDate: compatibility.date,
830
1466
  compatibilityFlags: compatibility.flags,
831
1467
  viteEnvironments: props.vite?.viteEnvironments,
@@ -888,7 +1524,25 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
888
1524
  };
889
1525
  }
890
1526
  if (props.vite) {
891
- return yield* viteBuild(props);
1527
+ return yield* viteBuild(props, opts.selfUrl);
1528
+ }
1529
+ // Assets-only Worker: no entry module at all. The script PUT goes
1530
+ // out with no modules and no main_module — Cloudflare's asset
1531
+ // layer serves every request and applies `notFoundHandling`
1532
+ // (including SPA fallback) itself, exactly like Wrangler's
1533
+ // assets-only deploys.
1534
+ if (props.main === undefined) {
1535
+ if (!props.assets) {
1536
+ return yield* Effect.die(new Error(`Worker "${id}" has no main, script, or assets. Provide an entry module (main / script) or an assets directory to deploy an assets-only Worker.`));
1537
+ }
1538
+ return {
1539
+ assets: opts.skipAssetsRead
1540
+ ? undefined
1541
+ : yield* prepareAssets(props.assets),
1542
+ bundle: undefined,
1543
+ input: undefined,
1544
+ additionalWorkspaces: undefined,
1545
+ };
892
1546
  }
893
1547
  const [assets, bundle] = yield* Effect.all([
894
1548
  opts.skipAssetsRead
@@ -923,6 +1577,428 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
923
1577
  const { directory, hash, ...config } = assets;
924
1578
  return { directory, config, hash, skip: hash === output?.hash?.assets };
925
1579
  };
1580
+ /**
1581
+ * Append the standard Alchemy runtime bindings plus the user's `env`
1582
+ * entries (routed by shape: `Redacted` → secret_text, string →
1583
+ * plain_text, everything else → json) to a metadata binding list.
1584
+ * Shared between the full script upload and the version upload.
1585
+ */
1586
+ const appendAlchemyAndEnvBindings = (metadataBindings, news, accountId, workerName) => {
1587
+ metadataBindings.push({
1588
+ type: "plain_text",
1589
+ name: "ALCHEMY_PHASE",
1590
+ text: "runtime",
1591
+ }, {
1592
+ type: "plain_text",
1593
+ name: "ALCHEMY_WORKER_NAME",
1594
+ text: workerName,
1595
+ }, {
1596
+ type: "plain_text",
1597
+ name: "ALCHEMY_STACK_NAME",
1598
+ text: stack.name,
1599
+ }, {
1600
+ type: "plain_text",
1601
+ name: "ALCHEMY_STAGE",
1602
+ text: stack.stage,
1603
+ }, {
1604
+ type: "plain_text",
1605
+ name: "ALCHEMY_CLOUDFLARE_ACCOUNT_ID",
1606
+ text: accountId,
1607
+ });
1608
+ // Add environment variables as metadata bindings
1609
+ if (news.env) {
1610
+ for (const [key, value] of Object.entries(news.env)) {
1611
+ if (value === undefined)
1612
+ continue;
1613
+ if (metadataBindings.some((b) => b.name === key))
1614
+ continue;
1615
+ if (Redacted.isRedacted(value)) {
1616
+ const unredacted = Redacted.value(value);
1617
+ metadataBindings.push({
1618
+ type: "secret_text",
1619
+ name: key,
1620
+ text: typeof unredacted === "string"
1621
+ ? unredacted
1622
+ : JSON.stringify(unredacted),
1623
+ });
1624
+ }
1625
+ else if (typeof value === "string") {
1626
+ metadataBindings.push({
1627
+ type: "plain_text",
1628
+ name: key,
1629
+ text: value,
1630
+ });
1631
+ }
1632
+ else {
1633
+ metadataBindings.push({
1634
+ type: "json",
1635
+ name: key,
1636
+ json: value,
1637
+ });
1638
+ }
1639
+ }
1640
+ }
1641
+ };
1642
+ /**
1643
+ * Create a deployment routing `traffic`% to `versionId`, with the
1644
+ * remainder staying on the currently-live version (the
1645
+ * highest-percentage version of the script's latest deployment).
1646
+ * `traffic >= 100` — or a script with no other live version to split
1647
+ * against — deploys the new version at 100%.
1648
+ */
1649
+ const deployVersionTraffic = Effect.fn(function* (params) {
1650
+ const { accountId, scriptName, versionId, traffic } = params;
1651
+ const split = yield* Effect.gen(function* () {
1652
+ if (traffic >= 100) {
1653
+ return [{ versionId, percentage: 100 }];
1654
+ }
1655
+ const { deployments } = yield* workers.listScriptDeployments({
1656
+ accountId,
1657
+ scriptName,
1658
+ });
1659
+ // Deployments are returned newest-first; the live version is the
1660
+ // highest-percentage version of the most recent deployment.
1661
+ const stable = deployments[0]?.versions
1662
+ .filter((v) => v.versionId !== versionId)
1663
+ .sort((a, b) => b.percentage - a.percentage)[0];
1664
+ if (!stable) {
1665
+ // Nothing to split against (first deployment of this script).
1666
+ return [{ versionId, percentage: 100 }];
1667
+ }
1668
+ return [
1669
+ { versionId, percentage: traffic },
1670
+ { versionId: stable.versionId, percentage: 100 - traffic },
1671
+ ];
1672
+ });
1673
+ const deployment = yield* workers.createScriptDeployment({
1674
+ accountId,
1675
+ scriptName,
1676
+ strategy: "percentage",
1677
+ versions: split,
1678
+ annotations: params.message
1679
+ ? { workersMessage: params.message }
1680
+ : undefined,
1681
+ });
1682
+ return deployment.id;
1683
+ });
1684
+ /**
1685
+ * Resolve the preview-URL alias for a version worker: the
1686
+ * user-provided `version.alias` (validated against Cloudflare's
1687
+ * naming rules), or an alias derived from the stack, stage, and
1688
+ * logical id — stable across deploys, so the aliased preview URL
1689
+ * (`<alias>-<script>.<subdomain>.workers.dev`) is a durable link
1690
+ * that always points at the latest uploaded version. Returns
1691
+ * `undefined` when the parent's script name leaves no room in the
1692
+ * 63-character DNS label and no explicit alias was given.
1693
+ */
1694
+ const resolveVersionAlias = Effect.fn(function* (id, news, parentName) {
1695
+ // `<alias>-<script name>` is a single DNS label (≤ 63 chars).
1696
+ const budget = 63 - parentName.length - 1;
1697
+ const userAlias = news.version?.alias;
1698
+ if (userAlias !== undefined) {
1699
+ if (!/^[a-z][a-z0-9-]*$/.test(userAlias)) {
1700
+ return yield* Effect.fail(new WorkerVersionConfigError({
1701
+ message: `version.alias '${userAlias}' is invalid: aliases must start with a lowercase letter and contain only lowercase letters, digits, and dashes.`,
1702
+ }));
1703
+ }
1704
+ if (userAlias.length > budget) {
1705
+ return yield* Effect.fail(new WorkerVersionConfigError({
1706
+ message: `version.alias '${userAlias}' is too long: '<alias>-${parentName}' must fit in a 63-character DNS label, leaving ${Math.max(budget, 0)} characters for the alias.`,
1707
+ }));
1708
+ }
1709
+ return userAlias;
1710
+ }
1711
+ if (budget < 4) {
1712
+ return undefined;
1713
+ }
1714
+ // Deterministic per (stack, stage, id) — survives updates AND
1715
+ // replacements so the aliased preview URL never moves. A short
1716
+ // hash disambiguates different stacks/stages whose readable
1717
+ // prefixes collide after truncation.
1718
+ const hash = yield* Effect.sync(() => crypto
1719
+ .createHash("sha256")
1720
+ .update(`${stack.name}/${stack.stage}/${id}`)
1721
+ .digest("hex")
1722
+ .slice(0, 6));
1723
+ const readable = `${stack.stage}-${id}`
1724
+ .toLowerCase()
1725
+ .replace(/[^a-z0-9-]+/g, "-")
1726
+ .replace(/^-+|-+$/g, "");
1727
+ const alias = [readable, hash]
1728
+ .filter((part) => part.length > 0)
1729
+ .join("-")
1730
+ .slice(0, budget)
1731
+ .replace(/-+$/, "");
1732
+ return /^[a-z]/.test(alias) ? alias : `v${alias}`.slice(0, budget);
1733
+ });
1734
+ /**
1735
+ * The parent's workers.dev preview settings + account subdomain,
1736
+ * fetched once per version reconcile. Preview URLs (aliased and
1737
+ * versioned) only serve when the parent has previews enabled.
1738
+ */
1739
+ const resolveVersionPreviewContext = Effect.fn(function* (accountId, scriptName) {
1740
+ const subdomain = yield* workers
1741
+ .getScriptSubdomain({ accountId, scriptName })
1742
+ .pipe(Effect.orElseSucceed(() => ({
1743
+ enabled: false,
1744
+ previewsEnabled: false,
1745
+ })));
1746
+ const accountSubdomain = yield* getAccountSubdomain(accountId);
1747
+ return {
1748
+ previewsEnabled: subdomain.previewsEnabled === true,
1749
+ accountSubdomain,
1750
+ };
1751
+ });
1752
+ /**
1753
+ * Reject props that can't ride along on a *version worker*
1754
+ * (`version.parent` set). A version carries only code, bindings, and
1755
+ * compatibility settings; everything script-level belongs to the
1756
+ * parent and must not be mutated from a version resource. Locally
1757
+ * hosted Durable Object / Workflow classes are rejected because their
1758
+ * migrations would apply to the parent script when the version
1759
+ * deploys.
1760
+ */
1761
+ const validateVersionWorkerProps = Effect.fn(function* (news, bindings, parentName) {
1762
+ const forbidden = [
1763
+ ["name", news.name],
1764
+ ["assets", news.assets],
1765
+ ["namespace", news.namespace],
1766
+ ["crons", news.crons],
1767
+ ["domain", news.domain],
1768
+ ["routes", news.routes],
1769
+ ["tags", news.tags],
1770
+ ["logpush", news.logpush],
1771
+ ["observability", news.observability],
1772
+ ["placement", news.placement],
1773
+ ["limits", news.limits],
1774
+ ["workersDev", news.workersDev],
1775
+ ["vite", news.vite],
1776
+ ].flatMap(([key, value]) => (value !== undefined ? [key] : []));
1777
+ if (forbidden.length > 0) {
1778
+ return yield* Effect.fail(new WorkerVersionConfigError({
1779
+ message: `version.parent uploads a version of '${parentName}' — script-level settings belong to the parent Worker and cannot be set here: ${forbidden.join(", ")}. ` +
1780
+ `Remove ${forbidden.length === 1 ? "this prop" : "these props"} or configure ${forbidden.length === 1 ? "it" : "them"} on the parent.`,
1781
+ }));
1782
+ }
1783
+ yield* validateTraffic(news.version?.traffic);
1784
+ const cronBindings = getCronBindings(bindings);
1785
+ if (cronBindings.length > 0) {
1786
+ return yield* Effect.fail(new WorkerVersionConfigError({
1787
+ message: `Cron Triggers are script-level settings and cannot be registered from a version of '${parentName}'. Configure crons on the parent Worker.`,
1788
+ }));
1789
+ }
1790
+ // Any locally-hosted DO class (a durable_object_namespace binding
1791
+ // without a foreign scriptName) or DO/Workflow export would require
1792
+ // migrations, which the versions API can't carry — and which would
1793
+ // mutate the parent's namespaces.
1794
+ const hostedClasses = getDurableObjectBindings(bindings, parentName);
1795
+ const exportedClasses = Object.keys(news.exports ?? {});
1796
+ if (hostedClasses.length > 0 || exportedClasses.length > 0) {
1797
+ return yield* Effect.fail(new WorkerVersionConfigError({
1798
+ message: `A version of '${parentName}' cannot host Durable Object or Workflow classes (${[
1799
+ ...new Set([
1800
+ ...hostedClasses.map((c) => c.className),
1801
+ ...exportedClasses,
1802
+ ]),
1803
+ ].join(", ")}): class migrations apply to the parent script. Host the classes on the parent Worker and reference them cross-script instead.`,
1804
+ }));
1805
+ }
1806
+ });
1807
+ /**
1808
+ * Reconcile a *version worker*: upload this Worker's code + bindings
1809
+ * as an immutable version of the parent's script (no script of its
1810
+ * own), then optionally shift `version.traffic`% of the parent's
1811
+ * traffic to it. Idempotent in the reconciler sense: every run
1812
+ * converges cloud state to "the parent's script has a version with
1813
+ * exactly this content, receiving exactly this traffic" — re-running
1814
+ * with changed content simply uploads the next immutable version.
1815
+ */
1816
+ const putWorkerVersion = Effect.fn(function* (id, news, bindings, session, output) {
1817
+ const { accountId } = yield* yield* CloudflareEnvironment;
1818
+ const version = news.version;
1819
+ const parentName = resolveVersionParentName(version);
1820
+ if (parentName === undefined) {
1821
+ return yield* Effect.fail(new WorkerVersionConfigError({
1822
+ message: `version.parent did not resolve to a Worker script name. Pass a Worker (e.g. \`yield* Cloudflare.Worker.ref(id, { stage })\`) or a literal script name.`,
1823
+ }));
1824
+ }
1825
+ yield* validateVersionWorkerProps(news, bindings, parentName);
1826
+ const traffic = version.traffic ?? 0;
1827
+ // Resolve the alias and preview context BEFORE the upload: the
1828
+ // aliased preview URL (`<alias>-<script>.<subdomain>.workers.dev`)
1829
+ // is deterministic ahead of time — unlike the versioned URL, whose
1830
+ // prefix comes from the server-assigned version id — which is what
1831
+ // lets a `Worker.URL` (self_url) binding be baked into the
1832
+ // version's own bindings.
1833
+ const alias = yield* resolveVersionAlias(id, news, parentName);
1834
+ const { previewsEnabled, accountSubdomain } = yield* resolveVersionPreviewContext(accountId, parentName);
1835
+ const aliasedUrl = alias !== undefined && previewsEnabled
1836
+ ? `https://${alias}-${parentName}.${accountSubdomain}.workers.dev`
1837
+ : undefined;
1838
+ const selfUrl = hasSelfUrlBinding(bindings) ? aliasedUrl : undefined;
1839
+ if (hasSelfUrlBinding(bindings) && selfUrl === undefined) {
1840
+ return yield* Effect.fail(new WorkerVersionConfigError({
1841
+ message: previewsEnabled
1842
+ ? `A version of '${parentName}' binds its own URL (Worker.URL), but no preview alias fits: '<alias>-${parentName}' must stay within a 63-character DNS label. Set a short version.alias or shorten the parent's name.`
1843
+ : `A version of '${parentName}' binds its own URL (Worker.URL), but the parent's workers.dev previews are disabled — a version's URL is its aliased preview URL. Enable the parent's workers.dev subdomain (url: true, the default).`,
1844
+ }));
1845
+ }
1846
+ yield* Effect.logInfo(`Cloudflare Worker version: preparing bundle for ${parentName} (from ${id})`);
1847
+ const { bundle, hash: preparedHash } = yield* prepareAssetsAndBundle(id, news, { skipAssetsRead: true });
1848
+ const metadataHash = yield* resolveWorkerMetadataHash({
1849
+ props: news,
1850
+ bindings,
1851
+ accountId,
1852
+ stack: { name: stack.name, stage: stack.stage },
1853
+ selfUrl,
1854
+ });
1855
+ const hash = {
1856
+ ...preparedHash,
1857
+ metadata: metadataHash,
1858
+ };
1859
+ // Lower the `Worker.URL` sentinel into the aliased preview URL —
1860
+ // same lowering `putWorker` performs, with the alias standing in
1861
+ // for the script's own URL.
1862
+ const metadataBindings = bindings.flatMap((b) => (b.data.bindings ?? []).map((item) => item.type === "self_url"
1863
+ ? { type: "plain_text", name: item.name, text: selfUrl }
1864
+ : item));
1865
+ appendAlchemyAndEnvBindings(metadataBindings, news, accountId, parentName);
1866
+ const compatibility = getCompatibility(news);
1867
+ yield* session.note(`Uploading version of ${parentName} ...`);
1868
+ const created = yield* workers
1869
+ .createScriptVersion({
1870
+ accountId,
1871
+ scriptName: parentName,
1872
+ metadata: {
1873
+ mainModule: bundle.main,
1874
+ bindings: metadataBindings,
1875
+ compatibilityDate: compatibility.date,
1876
+ compatibilityFlags: compatibility.flags,
1877
+ cache: news.cache ?? getCacheBinding(bindings),
1878
+ annotations: alias !== undefined ||
1879
+ version.message !== undefined ||
1880
+ version.tag !== undefined
1881
+ ? {
1882
+ workersAlias: alias,
1883
+ workersMessage: version.message,
1884
+ workersTag: version.tag,
1885
+ }
1886
+ : undefined,
1887
+ },
1888
+ files: bundle.files,
1889
+ })
1890
+ .pipe(Effect.retry({
1891
+ while: isBindingTargetNotFound,
1892
+ schedule: bindingTargetNotFoundRetrySchedule(),
1893
+ }), Effect.catchTag("WorkerNotFound", () => Effect.fail(new WorkerVersionConfigError({
1894
+ message: `version.parent script '${parentName}' does not exist. Deploy the parent Worker first (or check the referenced stage/stack).`,
1895
+ }))));
1896
+ const versionId = created.id ?? undefined;
1897
+ if (versionId === undefined) {
1898
+ return yield* Effect.fail(new WorkerVersionConfigError({
1899
+ message: `Cloudflare did not return a version id for the uploaded version of '${parentName}'.`,
1900
+ }));
1901
+ }
1902
+ let deploymentId;
1903
+ if (traffic > 0) {
1904
+ yield* session.note(`Deploying version at ${traffic}% of ${parentName}'s traffic ...`);
1905
+ deploymentId = yield* deployVersionTraffic({
1906
+ accountId,
1907
+ scriptName: parentName,
1908
+ versionId,
1909
+ traffic,
1910
+ message: version.message,
1911
+ });
1912
+ }
1913
+ // Version-affinity transform rules (`version.affinity`): a canary
1914
+ // rides the *parent's* zone traffic, so the rules land on the
1915
+ // parent's zones — observed custom-domain attachments plus, when
1916
+ // `parent` was passed as a Worker resource, its recorded routes.
1917
+ const previousAffinityZoneIds = output?.affinityZoneIds ?? [];
1918
+ let affinityZoneIds;
1919
+ if (version.affinity !== undefined || previousAffinityZoneIds.length) {
1920
+ const resolvedAffinity = version.affinity !== undefined
1921
+ ? yield* resolveVersionAffinity(version.affinity)
1922
+ : undefined;
1923
+ let hostsByZone;
1924
+ if (resolvedAffinity !== undefined) {
1925
+ hostsByZone = new Map();
1926
+ const addHost = (zoneId, host) => {
1927
+ const hosts = hostsByZone.get(zoneId) ?? [];
1928
+ hosts.push(host);
1929
+ hostsByZone.set(zoneId, hosts);
1930
+ };
1931
+ // A `version.parent` passed as a Worker resource resolves to
1932
+ // the parent's Attributes at reconcile time (see
1933
+ // resolveVersionParentName) — its recorded routes and domain
1934
+ // tell us the zones the parent serves on.
1935
+ const parentAttrs = typeof version.parent === "object" && version.parent !== null
1936
+ ? version.parent
1937
+ : undefined;
1938
+ const redirectHosts = new Set(parentAttrs?.domain?.redirects ?? []);
1939
+ const attached = yield* workers
1940
+ .listDomains({ accountId, service: parentName })
1941
+ .pipe(Effect.map((r) => r.result ?? []));
1942
+ for (const d of attached) {
1943
+ if (!d.hostname || !d.zoneId || redirectHosts.has(d.hostname)) {
1944
+ continue;
1945
+ }
1946
+ addHost(d.zoneId, { host: d.hostname, wildcard: false });
1947
+ }
1948
+ for (const route of parentAttrs?.routes ?? []) {
1949
+ const host = route.pattern.split("/")[0];
1950
+ if (!host || host.includes('"'))
1951
+ continue;
1952
+ addHost(route.zoneId, { host, wildcard: host.includes("*") });
1953
+ }
1954
+ if (hostsByZone.size === 0) {
1955
+ return yield* Effect.fail(new WorkerVersionConfigError({
1956
+ message: `version.affinity pins users via a zone Transform Rule setting the ${AFFINITY_HEADER} header, which only sees zone traffic — the parent '${parentName}' has no custom domains (or recorded zone routes) to place the rule on. ` +
1957
+ `Give the parent a \`domain\` or zone \`routes\`, or pass the parent as a Worker resource so its routes are visible here.`,
1958
+ }));
1959
+ }
1960
+ yield* session.note("Reconciling version-affinity rules ...");
1961
+ }
1962
+ const placed = yield* reconcileAffinityRules({
1963
+ scriptName: parentName,
1964
+ desired: resolvedAffinity !== undefined && hostsByZone !== undefined
1965
+ ? { affinity: resolvedAffinity, hostsByZone }
1966
+ : undefined,
1967
+ previousZoneIds: previousAffinityZoneIds,
1968
+ });
1969
+ affinityZoneIds = placed.length > 0 ? placed : undefined;
1970
+ }
1971
+ // The aliased URL is primary (`url`): it is stable across deploys,
1972
+ // re-pointing at each newly uploaded version. The per-version URL
1973
+ // (prefixed by the version id) rides along in `urls`.
1974
+ const versionedUrl = previewsEnabled
1975
+ ? `https://${versionId.split("-")[0]}-${parentName}.${accountSubdomain}.workers.dev`
1976
+ : undefined;
1977
+ const urls = [
1978
+ ...(aliasedUrl ? [aliasedUrl] : []),
1979
+ ...(versionedUrl ? [versionedUrl] : []),
1980
+ ];
1981
+ return {
1982
+ workerId: parentName,
1983
+ workerName: parentName,
1984
+ namespace: undefined,
1985
+ logpush: undefined,
1986
+ url: urls[0],
1987
+ urls,
1988
+ domain: undefined,
1989
+ tags: undefined,
1990
+ durableObjectNamespaces: {},
1991
+ accountId,
1992
+ routes: [],
1993
+ crons: [],
1994
+ versionOf: parentName,
1995
+ versionId,
1996
+ versionAlias: alias,
1997
+ deploymentId,
1998
+ affinityZoneIds,
1999
+ hash,
2000
+ };
2001
+ });
926
2002
  const putWorker = Effect.fn(function* (id, news, bindings, olds, output, session, existingSettings) {
927
2003
  const { accountId } = yield* yield* CloudflareEnvironment;
928
2004
  // Prefer the deployed name: regenerating would target a different
@@ -933,6 +2009,22 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
933
2009
  // account-level script. The put/settings calls switch endpoints and
934
2010
  // the subdomain / custom-domain / cron reconciliation is skipped.
935
2011
  const dispatchNamespace = resolveNamespaceName(news?.namespace);
2012
+ yield* validateTraffic(news.version?.traffic);
2013
+ if (news.version !== undefined && dispatchNamespace) {
2014
+ return yield* Effect.fail(new WorkerVersionConfigError({
2015
+ message: `Workers for Platforms user workers do not support versions or gradual deployments — remove the version prop from '${name}'.`,
2016
+ }));
2017
+ }
2018
+ // Resolve the Worker's own URL up front when a `Worker.URL` binding
2019
+ // is present: the value must exist before the bundle is built (Vite
2020
+ // inlines VITE_*-prefixed env into the client bundle) and before the
2021
+ // metadata bindings are assembled (the `self_url` sentinel lowers
2022
+ // into a plain_text binding below).
2023
+ const selfUrl = hasSelfUrlBinding(bindings)
2024
+ ? dispatchNamespace
2025
+ ? yield* Effect.die(`Worker "${name}" binds its own URL (Worker.URL), but a dispatch-namespace user worker is invoked via dynamic dispatch and has no URL of its own.`)
2026
+ : yield* resolveSelfUrl(name, news, accountId)
2027
+ : undefined;
936
2028
  yield* Effect.logInfo(`Cloudflare Worker ${olds ? "update" : "create"}: preparing bundle for ${name}`);
937
2029
  // If the caller handed us a precomputed asset hash that matches
938
2030
  // what we previously stored, we can skip walking the directory
@@ -942,6 +2034,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
942
2034
  const prebuiltAssets = normalizePrebuiltAssets(news.assets, output);
943
2035
  const { assets, bundle, hash: preparedHash, } = yield* prepareAssetsAndBundle(id, news, {
944
2036
  skipAssetsRead: prebuiltAssets?.skip,
2037
+ selfUrl,
945
2038
  });
946
2039
  // When the caller supplied a precomputed hash (e.g. via
947
2040
  // `Command.Build`), store *that* hash in output state so the
@@ -954,6 +2047,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
954
2047
  bindings,
955
2048
  accountId,
956
2049
  stack: { name: stack.name, stage: stack.stage },
2050
+ selfUrl,
957
2051
  });
958
2052
  const hash = {
959
2053
  ...preparedHash,
@@ -965,11 +2059,24 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
965
2059
  // `transferred_classes` migration below and must be stripped from the
966
2060
  // wire-shape binding before upload.
967
2061
  const metadataBindings = bindings.flatMap((b) => (b.data.bindings ?? []).map((item) => {
2062
+ // Lower the `Worker.URL` sentinel into the resolved URL —
2063
+ // Cloudflare has no native binding for it.
2064
+ if (item.type === "self_url") {
2065
+ return { type: "plain_text", name: item.name, text: selfUrl };
2066
+ }
968
2067
  if (item.type === "durable_object_namespace" &&
969
2068
  item.transferredFrom !== undefined) {
970
2069
  const { transferredFrom: _, ...rest } = item;
971
2070
  return rest;
972
2071
  }
2072
+ // `queueId` (mode discrimination) and `shim` (dev-mode remote
2073
+ // producer) are alchemy-only metadata on queue bindings — strip
2074
+ // them from the wire shape.
2075
+ if (item.type === "queue" &&
2076
+ (item.queueId !== undefined || item.shim !== undefined)) {
2077
+ const { queueId: _, shim: __, ...rest } = item;
2078
+ return rest;
2079
+ }
973
2080
  return item;
974
2081
  }));
975
2082
  const expectedDurableObjectClassNames = getExpectedDurableObjectClassNames(metadataBindings, name);
@@ -1018,56 +2125,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1018
2125
  name: "ASSETS",
1019
2126
  });
1020
2127
  }
1021
- metadataBindings.push({
1022
- type: "plain_text",
1023
- name: "ALCHEMY_PHASE",
1024
- text: "runtime",
1025
- }, {
1026
- type: "plain_text",
1027
- name: "ALCHEMY_STACK_NAME",
1028
- text: stack.name,
1029
- }, {
1030
- type: "plain_text",
1031
- name: "ALCHEMY_STAGE",
1032
- text: stack.stage,
1033
- }, {
1034
- type: "plain_text",
1035
- name: "ALCHEMY_CLOUDFLARE_ACCOUNT_ID",
1036
- text: accountId,
1037
- });
1038
- // Add environment variables as metadata bindings
1039
- if (news.env) {
1040
- for (const [key, value] of Object.entries(news.env)) {
1041
- if (value === undefined)
1042
- continue;
1043
- if (metadataBindings.some((b) => b.name === key))
1044
- continue;
1045
- if (Redacted.isRedacted(value)) {
1046
- const unredacted = Redacted.value(value);
1047
- metadataBindings.push({
1048
- type: "secret_text",
1049
- name: key,
1050
- text: typeof unredacted === "string"
1051
- ? unredacted
1052
- : JSON.stringify(unredacted),
1053
- });
1054
- }
1055
- else if (typeof value === "string") {
1056
- metadataBindings.push({
1057
- type: "plain_text",
1058
- name: key,
1059
- text: value,
1060
- });
1061
- }
1062
- else {
1063
- metadataBindings.push({
1064
- type: "json",
1065
- name: key,
1066
- json: value,
1067
- });
1068
- }
1069
- }
1070
- }
2128
+ appendAlchemyAndEnvBindings(metadataBindings, news, accountId, name);
1071
2129
  yield* Effect.logInfo(`Cloudflare Worker ${olds ? "update" : "create"}: uploading script for ${name}`);
1072
2130
  const size = bundle.files
1073
2131
  ?.filter((file) => !file.name.endsWith(".map"))
@@ -1337,41 +2395,131 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1337
2395
  tailConsumers: undefined,
1338
2396
  usageModel: undefined,
1339
2397
  };
1340
- const worker = yield* putWorkerScript({
1341
- accountId,
1342
- scriptName: name,
1343
- dispatchNamespace,
1344
- metadata,
1345
- files: bundle.files,
1346
- }).pipe(Effect.catch((err) => {
1347
- // When adopting a Worker managed by Wrangler (or after a previous
1348
- // deploy with mismatched migrations), the old_tag precondition
1349
- // fails. The only way to discover the actual tag is through the
1350
- // error message — getScriptSettings is meant to return it but
1351
- // doesn't at runtime.
1352
- const msg = String(typeof err === "object" && err !== null && "message" in err
1353
- ? err.message
1354
- : err);
1355
- const expectedTag = msg.match(/when expected tag is ['"]?([^'"]+)['"]?/)?.[1];
1356
- if (expectedTag) {
1357
- return putWorkerScript({
2398
+ const rolloutTraffic = getSelfRolloutTraffic(news);
2399
+ let versionId;
2400
+ let deploymentId;
2401
+ let worker;
2402
+ // A gradual rollout (`version.traffic` < 100) deploys through the
2403
+ // versions API instead of the full-cutover script PUT. That's only
2404
+ // possible when the script already has a live deployment to split
2405
+ // traffic against (otherwise the first deploy takes 100%), and only
2406
+ // for changes a version can carry — code, bindings, compatibility
2407
+ // settings, cache. Script-level settings in the upload metadata
2408
+ // (tags, observability, limits, placement, logpush) keep their live
2409
+ // values until the next full deploy, matching
2410
+ // `wrangler versions upload` semantics.
2411
+ if (rolloutTraffic !== undefined &&
2412
+ existingSettings !== undefined &&
2413
+ output?.hash !== undefined &&
2414
+ !dispatchNamespace) {
2415
+ if (metadataAssets !== undefined) {
2416
+ return yield* Effect.fail(new WorkerVersionConfigError({
2417
+ message: `Worker '${name}' has static assets, which the versions API cannot carry — gradual rollouts (version.traffic) are not supported for Workers with assets.`,
2418
+ }));
2419
+ }
2420
+ const migratedClasses = [
2421
+ ...migrations.newClasses,
2422
+ ...migrations.newSqliteClasses,
2423
+ ...migrations.deletedClasses,
2424
+ ...migrations.renamedClasses.map((r) => r.to),
2425
+ ...migrations.transferredClasses.map((t) => t.to),
2426
+ ];
2427
+ if (migratedClasses.length > 0) {
2428
+ return yield* Effect.fail(new WorkerVersionConfigError({
2429
+ message: `This deploy of '${name}' changes Durable Object classes (${migratedClasses.join(", ")}), which requires a migration — migrations cannot ride a gradual rollout. Deploy at 100% (remove version.traffic) first, then resume gradual rollouts.`,
2430
+ }));
2431
+ }
2432
+ yield* session.note(`Uploading version of ${name} (${bundleSize}) ...`);
2433
+ const created = yield* workers
2434
+ .createScriptVersion({
2435
+ accountId,
2436
+ scriptName: name,
2437
+ metadata: {
2438
+ mainModule: metadata.mainModule,
2439
+ bindings: metadata.bindings,
2440
+ compatibilityDate: metadata.compatibilityDate,
2441
+ compatibilityFlags: metadata.compatibilityFlags,
2442
+ cache: metadata.cache,
2443
+ annotations: news.version?.alias !== undefined ||
2444
+ news.version?.message !== undefined ||
2445
+ news.version?.tag !== undefined
2446
+ ? {
2447
+ workersAlias: news.version?.alias,
2448
+ workersMessage: news.version?.message,
2449
+ workersTag: news.version?.tag,
2450
+ }
2451
+ : undefined,
2452
+ },
2453
+ files: bundle.files,
2454
+ })
2455
+ .pipe(Effect.retry({
2456
+ while: isBindingTargetNotFound,
2457
+ schedule: bindingTargetNotFoundRetrySchedule(),
2458
+ }));
2459
+ versionId = created.id ?? undefined;
2460
+ if (versionId === undefined) {
2461
+ return yield* Effect.fail(new WorkerVersionConfigError({
2462
+ message: `Cloudflare did not return a version id for the uploaded version of '${name}'.`,
2463
+ }));
2464
+ }
2465
+ if (rolloutTraffic > 0) {
2466
+ yield* session.note(`Deploying version at ${rolloutTraffic}% of traffic ...`);
2467
+ deploymentId = yield* deployVersionTraffic({
1358
2468
  accountId,
1359
2469
  scriptName: name,
1360
- dispatchNamespace,
1361
- metadata: {
1362
- ...metadata,
1363
- migrations: {
1364
- ...migrations,
1365
- oldTag: expectedTag,
1366
- newTag: bumpMigrationTagVersion(expectedTag),
1367
- },
1368
- },
1369
- files: bundle.files,
2470
+ versionId,
2471
+ traffic: rolloutTraffic,
2472
+ message: news.version?.message,
1370
2473
  });
1371
2474
  }
1372
- // @effect-diagnostics-next-line anyUnknownInErrorContext:off
1373
- return Effect.fail(err);
1374
- }));
2475
+ worker = { id: name, logpush: existingSettings.logpush };
2476
+ }
2477
+ else {
2478
+ if (rolloutTraffic !== undefined) {
2479
+ // First deploy of this script (or a pre-create placeholder is
2480
+ // the only thing live) — there is no previous version worth
2481
+ // splitting traffic with, so this deploy takes 100%.
2482
+ yield* Effect.logInfo(`Cloudflare Worker ${name}: no previous live version to split traffic with; deploying at 100%`);
2483
+ }
2484
+ worker = yield* putWorkerScriptWithMigrationRecovery();
2485
+ }
2486
+ function putWorkerScriptWithMigrationRecovery() {
2487
+ return putWorkerScript({
2488
+ accountId,
2489
+ scriptName: name,
2490
+ dispatchNamespace,
2491
+ metadata,
2492
+ files: bundle.files,
2493
+ }).pipe(Effect.catch((err) => {
2494
+ // When adopting a Worker managed by Wrangler (or after a previous
2495
+ // deploy with mismatched migrations), the old_tag precondition
2496
+ // fails. The only way to discover the actual tag is through the
2497
+ // error message — getScriptSettings is meant to return it but
2498
+ // doesn't at runtime.
2499
+ const msg = String(typeof err === "object" && err !== null && "message" in err
2500
+ ? err.message
2501
+ : err);
2502
+ const expectedTag = msg.match(/when expected tag is ['"]?([^'"]+)['"]?/)?.[1];
2503
+ if (expectedTag) {
2504
+ return putWorkerScript({
2505
+ accountId,
2506
+ scriptName: name,
2507
+ dispatchNamespace,
2508
+ metadata: {
2509
+ ...metadata,
2510
+ migrations: {
2511
+ ...migrations,
2512
+ oldTag: expectedTag,
2513
+ newTag: bumpMigrationTagVersion(expectedTag),
2514
+ },
2515
+ },
2516
+ files: bundle.files,
2517
+ });
2518
+ }
2519
+ // @effect-diagnostics-next-line anyUnknownInErrorContext:off
2520
+ return Effect.fail(err);
2521
+ }));
2522
+ }
1375
2523
  const { settings, durableObjectNamespaces } = yield* getWorkerSettingsWithDurableObjects(name, expectedDurableObjectClassNames, dispatchNamespace);
1376
2524
  // Workers for Platforms user workers are invoked via dynamic dispatch,
1377
2525
  // never routed directly — they have no workers.dev subdomain, custom
@@ -1387,20 +2535,21 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1387
2535
  tags: settings.tags ?? metadata.tags,
1388
2536
  durableObjectNamespaces,
1389
2537
  accountId,
1390
- domains: [],
2538
+ urls: [],
2539
+ domain: undefined,
1391
2540
  routes: [],
1392
2541
  crons: [],
1393
2542
  hash,
1394
2543
  };
1395
2544
  }
1396
- // Reconcile workers.dev subdomain against observed cloud state.
1397
- // We can't diff `news.url` against `olds.url` here because both
1398
- // default to `undefined` (meaning "enable") — that comparison
1399
- // would skip the API call on every deploy where the user never
1400
- // explicitly set `url`, leaving the subdomain in whatever state
2545
+ // Reconcile the workers.dev settings against observed cloud state.
2546
+ // We can't diff `news.workersDev` against `olds.workersDev` here
2547
+ // because both default to `undefined` (meaning "enable") — that
2548
+ // comparison would skip the API call on every deploy where the user
2549
+ // never set the prop, leaving the subdomain in whatever state
1401
2550
  // Cloudflare currently has it (disabled by default, or whatever
1402
2551
  // a previous failed/external action left it as).
1403
- const desiredSubdomainEnabled = news.url !== false;
2552
+ const workersDev = resolveWorkersDev(news.workersDev);
1404
2553
  const observedSubdomain = yield* workers
1405
2554
  .getScriptSubdomain({
1406
2555
  accountId,
@@ -1410,9 +2559,9 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1410
2559
  enabled: false,
1411
2560
  previewsEnabled: false,
1412
2561
  })));
1413
- if (desiredSubdomainEnabled !== observedSubdomain.enabled ||
1414
- desiredSubdomainEnabled !== observedSubdomain.previewsEnabled) {
1415
- yield* session.note(`${desiredSubdomainEnabled ? "Enabling" : "Disabling"} workers.dev subdomain...`);
2562
+ if (workersDev.enabled !== observedSubdomain.enabled ||
2563
+ workersDev.previewsEnabled !== observedSubdomain.previewsEnabled) {
2564
+ yield* session.note(`${workersDev.enabled || workersDev.previewsEnabled ? "Enabling" : "Disabling"} workers.dev subdomain...`);
1416
2565
  // Cloudflare's script registry is eventually consistent — for the
1417
2566
  // first few hundred ms after `putScript` returns, POST /subdomain
1418
2567
  // can still get back `WorkerNotFound` (a generic "unknown error"
@@ -1421,7 +2570,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1421
2570
  // Retry the subdomain toggle on those transient tags with a short
1422
2571
  // exponential backoff; same pattern we use elsewhere in this
1423
2572
  // provider for DO-namespace propagation and for `putScript` itself.
1424
- yield* setWorkerSubdomain(name, desiredSubdomainEnabled).pipe(Effect.retry({
2573
+ yield* setWorkerSubdomain(name, workersDev).pipe(Effect.retry({
1425
2574
  while: (error) => error._tag === "WorkerNotFound" ||
1426
2575
  error._tag === "InternalServerError" ||
1427
2576
  error._tag === "UnknownCloudflareError",
@@ -1431,38 +2580,169 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1431
2580
  ]),
1432
2581
  }));
1433
2582
  }
1434
- const desiredDomains = normalizeDomains(news.domain);
1435
- const previousDomains = output?.domains ?? [];
1436
- if (desiredDomains.length > 0 || previousDomains.length > 0) {
1437
- yield* session.note(`Reconciling custom domains (${desiredDomains.length}) ...`);
2583
+ // Custom domains are managed only when `domain` is declared on props
2584
+ // (#942): an omitted `domain` leaves live attachments alone — and
2585
+ // spares a plain workers.dev Worker the listDomains call on every
2586
+ // deploy (#926) — while `domain: null` explicitly detaches
2587
+ // everything. When managed, the canonical name + aliases + redirect
2588
+ // hostnames are all attached (DNS + edge certificate); redirect
2589
+ // hostnames additionally get a redirect rule, which runs before the
2590
+ // Worker so those requests never invoke it. Unmanaged domains
2591
+ // persisted in state carry forward so read keeps observing them.
2592
+ const manageCustomDomains = news.domain !== undefined;
2593
+ const domainConfig = yield* resolveWorkerDomain(news.domain);
2594
+ const previousDomain = stateWorkerDomain(output);
2595
+ let effectiveDomain = manageCustomDomains
2596
+ ? domainConfig
2597
+ : previousDomain;
2598
+ if (!manageCustomDomains && previousDomain !== undefined) {
2599
+ // Unmanaged, but state remembers domains: observe which of them
2600
+ // are actually still attached and carry only those forward —
2601
+ // stale state entries (e.g. legacy records whose attach never
2602
+ // happened, or hostnames detached out-of-band) must not
2603
+ // resurface in `urls`. Plain workers.dev Workers (no persisted
2604
+ // domains) never pay this listDomains call (#926).
2605
+ const live = new Set(yield* workers.listDomains({ accountId, service: name }).pipe(Effect.map((r) => (r.result ?? []).flatMap((d) => d.hostname ? [d.hostname] : [])), Effect.catch(() => Effect.succeed([]))));
2606
+ const serving = [
2607
+ ...(live.has(previousDomain.name) ? [previousDomain.name] : []),
2608
+ ...previousDomain.aliases.filter((h) => live.has(h)),
2609
+ ];
2610
+ effectiveDomain =
2611
+ serving.length > 0
2612
+ ? {
2613
+ name: serving[0],
2614
+ aliases: serving.slice(1),
2615
+ redirects: previousDomain.redirects.filter((h) => live.has(h)),
2616
+ }
2617
+ : undefined;
1438
2618
  }
1439
- const reconciled = yield* reconcileDomains(name, desiredDomains);
1440
- const workersDevUrl = news.url !== false
1441
- ? `https://${name}.${yield* getAccountSubdomain(accountId)}.workers.dev`
1442
- : undefined;
1443
- const domains = [
1444
- ...reconciled.map((d) => `https://${d.hostname}`),
1445
- ...(workersDevUrl ? [workersDevUrl] : []),
1446
- ];
2619
+ if (manageCustomDomains) {
2620
+ const desiredHostnames = domainConfig
2621
+ ? [
2622
+ domainConfig.name,
2623
+ ...domainConfig.aliases,
2624
+ ...domainConfig.redirects,
2625
+ ]
2626
+ : [];
2627
+ yield* session.note(`Reconciling custom domains (${desiredHostnames.length}) ...`);
2628
+ // Capture hostname → zone for *currently attached* domains before
2629
+ // reconcile detaches removed ones — a removed redirect hostname's
2630
+ // zone is otherwise unresolvable and its rule couldn't be cleaned.
2631
+ const liveBeforeReconcile = yield* workers
2632
+ .listDomains({ accountId, service: name })
2633
+ .pipe(Effect.map((r) => (r.result ?? []).flatMap((d) => d.hostname && d.zoneId
2634
+ ? [[d.hostname, d.zoneId]]
2635
+ : [])), Effect.catch(() => Effect.succeed([])));
2636
+ const reconciled = yield* reconcileDomains(name, desiredHostnames);
2637
+ const zoneIdByHostname = new Map([
2638
+ ...liveBeforeReconcile,
2639
+ ...reconciled.map((d) => [d.hostname, d.zoneId]),
2640
+ ]);
2641
+ yield* reconcileRedirectRules({
2642
+ scriptName: name,
2643
+ domain: domainConfig,
2644
+ zoneIdByHostname,
2645
+ previousRedirects: previousDomain?.redirects ?? [],
2646
+ });
2647
+ effectiveDomain = domainConfig;
2648
+ }
2649
+ const urls = yield* computeWorkerUrls({
2650
+ scriptName: name,
2651
+ workersDev,
2652
+ domain: effectiveDomain,
2653
+ uploadedVersionId: versionId,
2654
+ uploadedVersionAlias: news.version?.alias,
2655
+ });
1447
2656
  const desiredRoutes = yield* normalizeRoutes(news.routes);
1448
2657
  const previousRoutes = output?.routes ?? [];
1449
2658
  if (desiredRoutes.length > 0 || previousRoutes.length > 0) {
1450
2659
  yield* session.note(`Reconciling worker routes (${desiredRoutes.length}) ...`);
1451
2660
  }
1452
2661
  const routes = yield* reconcileRoutes(name, desiredRoutes, previousRoutes);
1453
- const crons = yield* reconcileCrons(name, normalizeCrons([...getCronBindings(bindings), ...(news.crons ?? [])]), output?.crons ?? [], session);
2662
+ // Version-affinity transform rules (`version.affinity`): pin each
2663
+ // user to one version during gradual rollouts by filling the
2664
+ // version-key header from a stable request property, on every zone
2665
+ // this Worker serves on. Runs after the domain/route reconciles so
2666
+ // freshly attached surfaces resolve their zones, and also when only
2667
+ // *previous* state holds rules, so removing affinity converges.
2668
+ const affinity = news.version?.parent == null ? news.version?.affinity : undefined;
2669
+ const previousAffinityZoneIds = output?.affinityZoneIds ?? [];
2670
+ let affinityZoneIds;
2671
+ if (affinity !== undefined || previousAffinityZoneIds.length > 0) {
2672
+ const resolvedAffinity = affinity !== undefined
2673
+ ? yield* resolveVersionAffinity(affinity)
2674
+ : undefined;
2675
+ let hostsByZone;
2676
+ if (resolvedAffinity !== undefined) {
2677
+ hostsByZone = new Map();
2678
+ const addHost = (zoneId, host) => {
2679
+ const hosts = hostsByZone.get(zoneId) ?? [];
2680
+ hosts.push(host);
2681
+ hostsByZone.set(zoneId, hosts);
2682
+ };
2683
+ // Serving hostnames from *observed* domain attachments (covers
2684
+ // managed, unmanaged, and adopted domains alike); redirect
2685
+ // hostnames 301 before the Worker and never need a key.
2686
+ const redirectHosts = new Set(effectiveDomain?.redirects ?? []);
2687
+ const attached = yield* workers
2688
+ .listDomains({ accountId, service: name })
2689
+ .pipe(Effect.map((r) => r.result ?? []));
2690
+ for (const d of attached) {
2691
+ if (!d.hostname || !d.zoneId || redirectHosts.has(d.hostname)) {
2692
+ continue;
2693
+ }
2694
+ addHost(d.zoneId, { host: d.hostname, wildcard: false });
2695
+ }
2696
+ for (const route of routes) {
2697
+ const host = route.pattern.split("/")[0];
2698
+ if (!host || host.includes('"'))
2699
+ continue;
2700
+ addHost(route.zoneId, { host, wildcard: host.includes("*") });
2701
+ }
2702
+ if (hostsByZone.size === 0) {
2703
+ return yield* Effect.fail(new WorkerVersionConfigError({
2704
+ message: `version.affinity pins users via a zone Transform Rule setting the ${AFFINITY_HEADER} header, which only sees zone traffic — give '${name}' a custom domain (\`domain\`) or zone \`routes\`. ` +
2705
+ `On the bare workers.dev URL, clients must send the header themselves.`,
2706
+ }));
2707
+ }
2708
+ yield* session.note("Reconciling version-affinity rules ...");
2709
+ }
2710
+ const placed = yield* reconcileAffinityRules({
2711
+ scriptName: name,
2712
+ desired: resolvedAffinity !== undefined && hostsByZone !== undefined
2713
+ ? { affinity: resolvedAffinity, hostsByZone }
2714
+ : undefined,
2715
+ previousZoneIds: previousAffinityZoneIds,
2716
+ });
2717
+ affinityZoneIds = placed.length > 0 ? placed : undefined;
2718
+ }
2719
+ const desiredCrons = normalizeCrons([
2720
+ ...getCronBindings(bindings),
2721
+ ...(news.crons ?? []),
2722
+ ]);
2723
+ const previousCrons = output?.crons ?? [];
2724
+ // Same gating as read: skip getScriptSchedule when neither props nor
2725
+ // prior state indicate cron management (#926).
2726
+ const crons = desiredCrons.length > 0 || previousCrons.length > 0
2727
+ ? yield* reconcileCrons(name, desiredCrons, previousCrons, session)
2728
+ : [];
1454
2729
  return {
1455
2730
  workerId: worker.id ?? name,
1456
2731
  workerName: name,
1457
2732
  namespace: undefined,
1458
2733
  logpush: worker.logpush ?? undefined,
1459
- url: domains[0],
2734
+ url: urls[0],
2735
+ urls,
2736
+ domain: effectiveDomain,
1460
2737
  tags: settings.tags ?? metadata.tags,
1461
2738
  durableObjectNamespaces,
1462
2739
  accountId,
1463
- domains,
1464
2740
  routes,
1465
2741
  crons,
2742
+ versionOf: undefined,
2743
+ versionId,
2744
+ deploymentId,
2745
+ affinityZoneIds,
1466
2746
  hash,
1467
2747
  };
1468
2748
  });
@@ -1474,11 +2754,29 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1474
2754
  // unresolved: the hash can't be computed deterministically here, and
1475
2755
  // the eventual apply stores it once bindings resolve.
1476
2756
  if (bindings) {
2757
+ // For a version worker, `Worker.URL` resolves to the *aliased
2758
+ // preview URL* (see `putWorkerVersion`) — recompute the same
2759
+ // value here so the metadata hash matches what the apply stored
2760
+ // and unchanged deploys stay noops.
2761
+ const versionParent = props.version?.parent != null
2762
+ ? (resolveVersionParentName(props.version) ?? output.versionOf)
2763
+ : undefined;
2764
+ const selfUrl = hasSelfUrlBinding(bindings)
2765
+ ? versionParent !== undefined
2766
+ ? yield* Effect.gen(function* () {
2767
+ const alias = yield* resolveVersionAlias(id, props, versionParent);
2768
+ return alias !== undefined
2769
+ ? `https://${alias}-${versionParent}.${yield* getAccountSubdomain(accountId)}.workers.dev`
2770
+ : undefined;
2771
+ })
2772
+ : yield* resolveSelfUrl(output.workerName, props, accountId)
2773
+ : undefined;
1477
2774
  const metadataHash = yield* resolveWorkerMetadataHash({
1478
2775
  props,
1479
2776
  bindings,
1480
2777
  accountId,
1481
2778
  stack: { name: stack.name, stage: stack.stage },
2779
+ selfUrl,
1482
2780
  });
1483
2781
  if (metadataHash !== output.hash?.metadata) {
1484
2782
  return true;
@@ -1504,6 +2802,24 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1504
2802
  const { hash } = yield* hashViteInput(props.vite.rootDir, props.vite.memo, Effect.succeed(output.hash?.additionalWorkspaces ?? []));
1505
2803
  return hash !== output.hash?.input;
1506
2804
  }
2805
+ // Assets-only Worker — there is no bundle to hash. A stored bundle
2806
+ // hash means the Worker previously had a script and is being
2807
+ // converted to assets-only, which must deploy.
2808
+ if (props.main === undefined) {
2809
+ if (output.hash?.bundle !== undefined) {
2810
+ return true;
2811
+ }
2812
+ const assetsHash = props.assets && Predicate.hasProperty(props.assets, "hash")
2813
+ ? props.assets.hash
2814
+ : undefined;
2815
+ if (assetsHash === undefined) {
2816
+ // Same conservative rule as the directory-shaped `assets` below:
2817
+ // don't read the directory during diff; `putWorker` reads once
2818
+ // and keeps the existing manifest if nothing actually changed.
2819
+ return true;
2820
+ }
2821
+ return assetsHash !== output.hash?.assets;
2822
+ }
1507
2823
  const bundleHash = yield* prepareBundle(id, props).pipe(Effect.map((b) => b.hash));
1508
2824
  if (bundleHash !== output.hash?.bundle) {
1509
2825
  return true;
@@ -1572,7 +2888,8 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1572
2888
  url: undefined,
1573
2889
  tags: script.tags ?? undefined,
1574
2890
  durableObjectNamespaces: {},
1575
- domains: [],
2891
+ urls: [],
2892
+ domain: undefined,
1576
2893
  routes: [],
1577
2894
  crons: [],
1578
2895
  },
@@ -1604,6 +2921,23 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1604
2921
  if (newNamespace !== oldNamespace) {
1605
2922
  return { action: "replace" };
1606
2923
  }
2924
+ // A version worker (version.parent) and a script-owning Worker are
2925
+ // distinct cloud resources, and a version belongs to exactly one
2926
+ // parent script — switching mode or parent is a replacement.
2927
+ const newIsVersion = news.version?.parent != null;
2928
+ const oldIsVersion = output?.versionOf !== undefined || olds?.version?.parent != null;
2929
+ if (newIsVersion !== oldIsVersion) {
2930
+ return { action: "replace" };
2931
+ }
2932
+ if (newIsVersion) {
2933
+ const newParent = resolveVersionParentName(news.version);
2934
+ const oldParent = output?.versionOf ?? resolveVersionParentName(olds?.version);
2935
+ if (newParent !== undefined &&
2936
+ oldParent !== undefined &&
2937
+ newParent !== oldParent) {
2938
+ return { action: "replace" };
2939
+ }
2940
+ }
1607
2941
  const oldWorkerName = output?.workerName ?? (yield* createWorkerName(id, olds?.name));
1608
2942
  // Auto-generated names are engine-owned: the deployed name stays
1609
2943
  // authoritative even if the generator would name this id
@@ -1617,14 +2951,21 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1617
2951
  if (!output) {
1618
2952
  return;
1619
2953
  }
1620
- const newDomains = normalizeDomains(news.domain)
1621
- .map((h) => `https://${h}`)
1622
- .sort();
1623
- const oldDomains = normalizeStateDomains(output?.domains)
1624
- .filter((u) => !u.endsWith(".workers.dev"))
1625
- .sort();
1626
- const domainsChanged = newDomains.length !== oldDomains.length ||
1627
- newDomains.some((d, i) => d !== oldDomains[i]);
2954
+ // Compare the full domain config — name, aliases (ordered: they
2955
+ // drive `urls` order), and redirects — against persisted state
2956
+ // (with legacy `domains`-list state migrated on the fly).
2957
+ const newDomainConfig = yield* resolveWorkerDomain(news.domain);
2958
+ const oldDomainConfig = stateWorkerDomain(output);
2959
+ const domainKey = (d) => d === undefined
2960
+ ? ""
2961
+ : JSON.stringify([d.name, d.aliases, [...d.redirects].sort()]);
2962
+ // An omitted `domain` unmanages the surface (#942): reconcile
2963
+ // carries previously-observed custom domains forward in state, so
2964
+ // comparing the empty desired config against those would report a
2965
+ // dirty plan on every deploy, forever. Only a declared `domain`
2966
+ // (including `null`, the explicit detach-all) participates.
2967
+ const domainsChanged = news.domain !== undefined &&
2968
+ domainKey(newDomainConfig) !== domainKey(oldDomainConfig);
1628
2969
  const newCrons = normalizeCrons([
1629
2970
  ...(Array.isArray(newBindings)
1630
2971
  ? getCronBindings(newBindings)
@@ -1640,24 +2981,28 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1640
2981
  const oldRouteKeys = (output?.routes ?? []).map(routeKey).sort();
1641
2982
  const routesChanged = newRouteKeys.length !== oldRouteKeys.length ||
1642
2983
  newRouteKeys.some((key, index) => key !== oldRouteKeys[index]);
1643
- // `url` is `domains[0]`: the first custom domain in user order if
1644
- // any, otherwise the workers.dev URL (derived from the stable
1645
- // worker name + account subdomain). It's stable across this update
1646
- // exactly when that first domain is unchanged — which is NOT the
1647
- // same as "the domain set is unchanged": adding a second custom
1648
- // domain leaves `url` put, while reordering changes it even though
1649
- // the set is equal. Compute the resulting `url` and carry it
1650
- // forward as a stable only when it matches the old one, so
1651
- // downstream resources that reference `worker.url` (e.g. a GitHub
1652
- // Webhook delivery URL built via `Output.interpolate`) resolve it
1653
- // to a concrete value during planning instead of an unresolved
1654
- // Output — otherwise every worker update spuriously re-updates them.
1655
- const newCustomDomains = normalizeDomains(news.domain);
1656
- const newUrl = newCustomDomains.length > 0
1657
- ? `https://${newCustomDomains[0]}`
1658
- : news.url !== false
1659
- ? normalizeStateDomains(output.domains).find((u) => u.endsWith(".workers.dev"))
1660
- : undefined;
2984
+ // `url` is `urls[0]`: the canonical custom domain when one is
2985
+ // configured (or carried forward from state when the surface is
2986
+ // unmanaged, #942/#975), else the stable workers.dev URL. It's
2987
+ // stable across this update exactly when the recomputed value
2988
+ // matches what's deployed. Carry it forward as a stable only
2989
+ // then, so downstream resources that reference `worker.url`
2990
+ // (e.g. a GitHub Webhook delivery URL built via
2991
+ // `Output.interpolate`) resolve it to a concrete value during
2992
+ // planning instead of an unresolved Output — otherwise every
2993
+ // worker update spuriously re-updates them. Previews-only mode
2994
+ // and version workers derive `url` from version preview URLs —
2995
+ // never assumed stable here.
2996
+ const newWorkersDev = resolveWorkersDev(news.workersDev);
2997
+ const effectiveDomainConfig = news.domain !== undefined ? newDomainConfig : oldDomainConfig;
2998
+ const newUrl = newIsVersion
2999
+ ? undefined
3000
+ : effectiveDomainConfig !== undefined
3001
+ ? `https://${effectiveDomainConfig.name}`
3002
+ : newWorkersDev.enabled
3003
+ ? (output.urls ?? []).find((u) => isWorkersDevHostname(urlHostname(u)) &&
3004
+ urlHostname(u).startsWith(`${workerName}.`))
3005
+ : undefined;
1661
3006
  const urlStable = newUrl !== undefined && newUrl === output.url;
1662
3007
  // `durableObjectNamespaces` maps each hosted DO class name to the
1663
3008
  // namespace id Cloudflare assigned it. Those ids are permanent for
@@ -1708,6 +3053,30 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1708
3053
  precreate: Effect.fn(function* ({ id, news, session, bindings }) {
1709
3054
  const { accountId } = yield* yield* CloudflareEnvironment;
1710
3055
  const name = yield* createWorkerName(id, news.name);
3056
+ // A version worker uploads to its parent's script during
3057
+ // reconcile; pre-creating a placeholder script under this
3058
+ // resource's own generated name would leave a stray script behind.
3059
+ // Version workers can't host Durable Object / Workflow classes, so
3060
+ // no placeholder is needed for circular bindings either. (`news`
3061
+ // is raw here — `version.parent` may be an unresolved ref, but its
3062
+ // *presence* is statically known.)
3063
+ if (news.version?.parent != null) {
3064
+ yield* Effect.logInfo(`Cloudflare Worker precreate: skipping stub for version worker ${id}`);
3065
+ return {
3066
+ workerId: name,
3067
+ workerName: name,
3068
+ namespace: undefined,
3069
+ logpush: undefined,
3070
+ url: undefined,
3071
+ tags: undefined,
3072
+ durableObjectNamespaces: {},
3073
+ accountId,
3074
+ urls: [],
3075
+ domain: undefined,
3076
+ routes: [],
3077
+ crons: [],
3078
+ };
3079
+ }
1711
3080
  // A Workers for Platforms user worker can't be pre-created: precreate
1712
3081
  // runs on raw, *unresolved* props (so resources in a dependency cycle
1713
3082
  // can signal early), meaning a `namespace` that references the
@@ -1727,7 +3096,8 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1727
3096
  tags: undefined,
1728
3097
  durableObjectNamespaces: {},
1729
3098
  accountId,
1730
- domains: [],
3099
+ urls: [],
3100
+ domain: undefined,
1731
3101
  routes: [],
1732
3102
  crons: [],
1733
3103
  };
@@ -1787,6 +3157,7 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1787
3157
  }
1788
3158
  else {
1789
3159
  yield* session.note("Pre-creating worker...");
3160
+ const compatibility = getCompatibility(news);
1790
3161
  const mainModule = "main.js";
1791
3162
  const placeholderScript = `${doClasses.length > 0 ? 'import { DurableObject } from "cloudflare:workers";\n\n' : ""}export default { fetch() { return new Response("Alchemy worker is being deployed...") } };\n${doClasses
1792
3163
  .map((className) => `export class ${className} extends DurableObject {}`)
@@ -1804,7 +3175,11 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1804
3175
  className,
1805
3176
  }))
1806
3177
  : undefined,
1807
- ...getCompatibility(news),
3178
+ // Spreading `getCompatibility` here would set `date`/`flags`,
3179
+ // which are not metadata keys — the placeholder would upload
3180
+ // with no compatibility date or flags at all.
3181
+ compatibilityDate: compatibility.date,
3182
+ compatibilityFlags: compatibility.flags,
1808
3183
  containers,
1809
3184
  migrations: doClasses.length > 0
1810
3185
  ? {
@@ -1865,13 +3240,35 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1865
3240
  tags: existingSettings?.tags ?? tags,
1866
3241
  durableObjectNamespaces,
1867
3242
  accountId,
1868
- domains: [],
3243
+ urls: [],
3244
+ domain: undefined,
1869
3245
  routes: [],
1870
3246
  crons: [],
1871
3247
  };
1872
3248
  }),
1873
3249
  read: Effect.fn(function* ({ id, output, olds }) {
1874
3250
  const { accountId } = yield* yield* CloudflareEnvironment;
3251
+ // Version workers don't own a script — their `workerName` is the
3252
+ // *parent's* script, so the normal read below would hydrate (and
3253
+ // potentially brand as adoptable) a resource we don't own. The
3254
+ // version itself is immutable; all we verify is that it still
3255
+ // exists on the parent.
3256
+ if (output?.versionOf !== undefined ||
3257
+ olds?.version?.parent != null) {
3258
+ if (output?.versionOf === undefined || !output.versionId) {
3259
+ // No recorded version (e.g. state was written before the
3260
+ // upload completed) — nothing to observe; reconcile will
3261
+ // upload a fresh version.
3262
+ return undefined;
3263
+ }
3264
+ return yield* workers
3265
+ .getScriptVersion({
3266
+ accountId,
3267
+ scriptName: output.versionOf,
3268
+ versionId: output.versionId,
3269
+ })
3270
+ .pipe(Effect.map(() => output), Effect.catchTag(["WorkerNotFound", "VersionNotFound"], () => Effect.succeed(undefined)));
3271
+ }
1875
3272
  const workerName = output?.workerName ?? (yield* createWorkerName(id, olds?.name));
1876
3273
  const dispatchNamespace = output?.namespace ?? resolveNamespaceName(olds?.namespace);
1877
3274
  yield* Effect.logInfo(`Cloudflare Worker read: checking ${workerName}`);
@@ -1890,7 +3287,8 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1890
3287
  url: undefined,
1891
3288
  tags: settings.tags ?? undefined,
1892
3289
  durableObjectNamespaces: getDurableObjects(settings.bindings),
1893
- domains: [],
3290
+ urls: [],
3291
+ domain: undefined,
1894
3292
  routes: [],
1895
3293
  crons: [],
1896
3294
  };
@@ -1906,6 +3304,18 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1906
3304
  // `WorkerNotFound` if the script doesn't exist, which the
1907
3305
  // surrounding `Effect.catchTag` turns into `undefined` — that's
1908
3306
  // all the existence check we need.
3307
+ //
3308
+ // Domain / route / cron observation is gated on whether Alchemy
3309
+ // manages that surface for this Worker (#926). A plain workers.dev
3310
+ // Worker otherwise pays for listDomains + zone route listings +
3311
+ // getScriptSchedule on every plan/deploy read. Route observation
3312
+ // is additionally scoped to the zones state already associates
3313
+ // with this Worker — see readWorkerRoutes. Empty-array props
3314
+ // (`domain: []`, `routes: []`, `crons: []`) still observe so we
3315
+ // can detect drift and converge deletions.
3316
+ const observeDomains = shouldObserveWorkerDomains(olds, output);
3317
+ const observeRoutes = shouldObserveWorkerRoutes(olds, output);
3318
+ const observeCrons = shouldObserveWorkerCrons(olds, output);
1909
3319
  const [subdomain, settings, domainsList, routesList] = yield* Effect.all([
1910
3320
  workers.getScriptSubdomain({
1911
3321
  accountId,
@@ -1915,34 +3325,62 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1915
3325
  accountId,
1916
3326
  scriptName: workerName,
1917
3327
  }),
1918
- workers
1919
- .listDomains({
1920
- accountId,
1921
- service: workerName,
1922
- })
1923
- .pipe(Effect.map((r) => r.result ?? [])),
1924
- readWorkerRoutes(workerName),
1925
- ], { concurrency: "unbounded" });
1926
- // Preserve the order the user provided in `olds.domain`. The
1927
- // Cloudflare API returns domains in non-deterministic order,
1928
- // which would cause downstream `worker.domains[0]` reads to flip
1929
- // between deploys. Drift (domains we don't know about) is
1930
- // appended after the user-ordered ones.
1931
- const userOrder = normalizeDomains(olds?.domain);
1932
- const orderedHostnames = [
1933
- ...userOrder.flatMap((h) => domainsList.find((d) => d.hostname === h)?.hostname ?? []),
1934
- ...domainsList.flatMap((d) => d.hostname && !userOrder.includes(d.hostname)
1935
- ? [d.hostname]
1936
- : []),
3328
+ observeDomains
3329
+ ? workers
3330
+ .listDomains({
3331
+ accountId,
3332
+ service: workerName,
3333
+ })
3334
+ .pipe(Effect.map((r) => r.result ?? []))
3335
+ : Effect.succeed([]),
3336
+ observeRoutes
3337
+ ? readWorkerRoutes(workerName, output?.routes)
3338
+ : Effect.succeed([]),
3339
+ ],
3340
+ // Bound concurrency so a single Worker read doesn't stampede
3341
+ // the account alongside every other Worker's lifecycle calls.
3342
+ { concurrency: 1 });
3343
+ // Classify the observed hostnames using the declared config
3344
+ // (`olds.domain`): the canonical name and each alias/redirect
3345
+ // keep their declared role while still attached; drift (attached
3346
+ // hostnames we don't know about) is appended to the aliases. The
3347
+ // Cloudflare API returns domains in non-deterministic order, so
3348
+ // the declared order is what keeps `url`/`urls` from flipping
3349
+ // between reads.
3350
+ const declared = yield* resolveWorkerDomain(olds?.domain).pipe(Effect.orElseSucceed(() => undefined));
3351
+ const observed = new Set(domainsList.flatMap((d) => (d.hostname ? [d.hostname] : [])));
3352
+ const keptName = declared !== undefined && observed.has(declared.name)
3353
+ ? declared.name
3354
+ : undefined;
3355
+ const keptAliases = declared?.aliases.filter((h) => observed.has(h)) ?? [];
3356
+ const keptRedirects = declared?.redirects.filter((h) => observed.has(h)) ?? [];
3357
+ const classified = new Set([
3358
+ ...(keptName ? [keptName] : []),
3359
+ ...keptAliases,
3360
+ ...keptRedirects,
3361
+ ]);
3362
+ const drift = [...observed].filter((h) => !classified.has(h));
3363
+ const serving = [
3364
+ ...(keptName ? [keptName] : []),
3365
+ ...keptAliases,
3366
+ ...drift,
1937
3367
  ];
1938
- const workersDevUrl = subdomain.enabled
1939
- ? `https://${workerName}.${yield* getAccountSubdomain(accountId)}.workers.dev`
3368
+ const observedDomain = serving.length > 0
3369
+ ? {
3370
+ name: serving[0],
3371
+ aliases: serving.slice(1),
3372
+ redirects: keptRedirects,
3373
+ }
1940
3374
  : undefined;
1941
- const domains = [
1942
- ...orderedHostnames.map((h) => `https://${h}`),
1943
- ...(workersDevUrl ? [workersDevUrl] : []),
3375
+ const urls = [
3376
+ ...serving.map((h) => `https://${h}`),
3377
+ ...(subdomain.enabled
3378
+ ? [
3379
+ `https://${workerName}.${yield* getAccountSubdomain(accountId)}.workers.dev`,
3380
+ ]
3381
+ : []),
1944
3382
  ];
1945
- const crons = yield* getWorkerCrons(workerName);
3383
+ const crons = observeCrons ? yield* getWorkerCrons(workerName) : [];
1946
3384
  yield* Effect.logInfo(`Cloudflare Worker read: found ${workerName}`);
1947
3385
  const attrs = {
1948
3386
  accountId,
@@ -1950,12 +3388,17 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1950
3388
  workerName,
1951
3389
  namespace: undefined,
1952
3390
  logpush: settings.logpush ?? undefined,
1953
- url: domains[0],
3391
+ url: urls[0],
3392
+ urls,
3393
+ domain: observedDomain,
1954
3394
  tags: settings.tags ?? undefined,
1955
3395
  durableObjectNamespaces: getDurableObjects(settings.bindings),
1956
- domains,
1957
3396
  routes: routesList,
1958
3397
  crons,
3398
+ // Rule placement is provider-managed state, not observed here
3399
+ // (a getPhas call per known zone on every read); carry the
3400
+ // cleanup list forward like any other stable cache.
3401
+ affinityZoneIds: output?.affinityZoneIds,
1959
3402
  };
1960
3403
  // Centralized ownership decision: the engine routes `read`'s
1961
3404
  // return value based on `AdoptPolicy`. We hand it the attrs
@@ -1973,6 +3416,12 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
1973
3416
  // `DispatchNamespaceScriptNotFound` / `DispatchNamespaceNotFound`.
1974
3417
  Effect.catchTag("WorkerNotFound", () => Effect.succeed(undefined)), Effect.catchTag("WorkerHasNoVersions", () => Effect.succeed(undefined)), Effect.catchTag("DispatchNamespaceScriptNotFound", () => Effect.succeed(undefined)), Effect.catchTag("DispatchNamespaceNotFound", () => Effect.succeed(undefined)))),
1975
3418
  reconcile: Effect.fn(function* ({ id, news, olds, bindings, output, session, }) {
3419
+ // A version worker uploads an immutable version to its parent's
3420
+ // script instead of owning a script of its own — none of the
3421
+ // script-level observation below applies.
3422
+ if (news.version?.parent != null) {
3423
+ return yield* putWorkerVersion(id, news, bindings, session, output);
3424
+ }
1976
3425
  const { accountId } = yield* yield* CloudflareEnvironment;
1977
3426
  const name = output?.workerName ?? (yield* createWorkerName(id, news.name));
1978
3427
  const durableObjects = getDurableObjectBindings(bindings, name).map(({ logicalId, className }) => ({
@@ -2000,6 +3449,60 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
2000
3449
  return yield* putWorker(id, news, bindings, olds, output, session, existingSettings);
2001
3450
  }),
2002
3451
  delete: Effect.fn(function* ({ output }) {
3452
+ // Version workers own a version, not the script — `workerName` is
3453
+ // the *parent's* script and must never be deleted here. Versions
3454
+ // can't be deleted through the API (they age out of Cloudflare's
3455
+ // retention window); all we clean up is live traffic: if the
3456
+ // current deployment routes traffic to our version, restore 100%
3457
+ // to the other version in the split.
3458
+ if (output.versionOf !== undefined) {
3459
+ // Remove our affinity rules from the parent's zones — the
3460
+ // canary owned them, and with the canary gone there is no
3461
+ // split left to pin. Best-effort: a zone we can no longer
3462
+ // touch must not fail the destroy.
3463
+ if (output.affinityZoneIds?.length) {
3464
+ yield* reconcileAffinityRules({
3465
+ scriptName: output.versionOf,
3466
+ desired: undefined,
3467
+ previousZoneIds: output.affinityZoneIds,
3468
+ }).pipe(Effect.catch(() => Effect.void));
3469
+ }
3470
+ if (!output.versionId)
3471
+ return;
3472
+ yield* Effect.logInfo(`Cloudflare Worker delete: releasing version ${output.versionId} of ${output.versionOf}`);
3473
+ const { deployments } = yield* workers
3474
+ .listScriptDeployments({
3475
+ accountId: output.accountId,
3476
+ scriptName: output.versionOf,
3477
+ })
3478
+ .pipe(Effect.catchTag("WorkerNotFound", () => Effect.succeed({
3479
+ deployments: [],
3480
+ })));
3481
+ const latest = deployments[0];
3482
+ if (!latest?.versions.some((v) => v.versionId === output.versionId)) {
3483
+ // Not part of the live deployment — the version just ages out.
3484
+ return;
3485
+ }
3486
+ const stable = latest.versions
3487
+ .filter((v) => v.versionId !== output.versionId)
3488
+ .sort((a, b) => b.percentage - a.percentage)[0];
3489
+ if (!stable) {
3490
+ // Our version holds 100% of traffic; there is no other version
3491
+ // to restore. Deleting the resource must not take the parent's
3492
+ // script down — leave the deployment in place.
3493
+ yield* Effect.logWarning(`Cloudflare Worker delete: version ${output.versionId} serves 100% of '${output.versionOf}' traffic; leaving the deployment as-is. Re-deploy the parent to move off it.`);
3494
+ return;
3495
+ }
3496
+ yield* workers
3497
+ .createScriptDeployment({
3498
+ accountId: output.accountId,
3499
+ scriptName: output.versionOf,
3500
+ strategy: "percentage",
3501
+ versions: [{ versionId: stable.versionId, percentage: 100 }],
3502
+ })
3503
+ .pipe(Effect.catchTag("WorkerNotFound", () => Effect.void));
3504
+ return;
3505
+ }
2003
3506
  yield* Effect.logInfo(`Cloudflare Worker delete: deleting ${output.workerName}`);
2004
3507
  // Workers for Platforms user workers have no custom domains; delete
2005
3508
  // the script straight out of its dispatch namespace.
@@ -2020,6 +3523,32 @@ export const LiveWorkerProvider = () => Provider.effect(Worker, Effect.gen(funct
2020
3523
  service: output.workerName,
2021
3524
  })
2022
3525
  .pipe(Effect.map((r) => r.result ?? []), Effect.catch(() => Effect.succeed([])));
3526
+ // Remove our redirect rules from each affected zone's dynamic
3527
+ // redirect entrypoint *before* detaching the domains — the live
3528
+ // domain list is what resolves each redirect hostname's zone.
3529
+ // Reconciling with an empty domain config removes exactly the
3530
+ // rules tagged `alchemy:worker:<script>:redirect:*`, leaving
3531
+ // everything else in the shared entrypoint untouched.
3532
+ // Remove our version-affinity rules from every zone state says
3533
+ // holds them. Best-effort, like the redirect rules below.
3534
+ if (output.affinityZoneIds?.length) {
3535
+ yield* reconcileAffinityRules({
3536
+ scriptName: output.workerName,
3537
+ desired: undefined,
3538
+ previousZoneIds: output.affinityZoneIds,
3539
+ }).pipe(Effect.catch(() => Effect.void));
3540
+ }
3541
+ const redirects = stateWorkerDomain(output)?.redirects ?? [];
3542
+ if (redirects.length > 0) {
3543
+ yield* reconcileRedirectRules({
3544
+ scriptName: output.workerName,
3545
+ domain: undefined,
3546
+ zoneIdByHostname: new Map(liveDomains.flatMap((d) => d.hostname && d.zoneId
3547
+ ? [[d.hostname, d.zoneId]]
3548
+ : [])),
3549
+ previousRedirects: redirects,
3550
+ }).pipe(Effect.catch(() => Effect.void));
3551
+ }
2023
3552
  if (liveDomains.length) {
2024
3553
  yield* Effect.all(liveDomains.flatMap((d) => d.id
2025
3554
  ? [