alchemy 2.0.0-beta.34 → 2.0.0-beta.35

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 (356) hide show
  1. package/bin/cli.js +8 -7
  2. package/bin/exec.js +226 -52
  3. package/bin/exec.js.map +1 -1
  4. package/lib/AWS/AuthProvider.d.ts +7 -6
  5. package/lib/AWS/AuthProvider.d.ts.map +1 -1
  6. package/lib/AWS/AuthProvider.js +143 -134
  7. package/lib/AWS/AuthProvider.js.map +1 -1
  8. package/lib/AWS/Bootstrap.d.ts +2 -2
  9. package/lib/AWS/Bootstrap.d.ts.map +1 -1
  10. package/lib/AWS/EC2/hosted.d.ts +1 -1
  11. package/lib/AWS/Environment.d.ts +25 -3
  12. package/lib/AWS/Environment.d.ts.map +1 -1
  13. package/lib/AWS/Environment.js +47 -3
  14. package/lib/AWS/Environment.js.map +1 -1
  15. package/lib/AWS/IAM/OpenIDConnectProvider.d.ts +14 -6
  16. package/lib/AWS/IAM/OpenIDConnectProvider.d.ts.map +1 -1
  17. package/lib/AWS/IAM/OpenIDConnectProvider.js +6 -3
  18. package/lib/AWS/IAM/OpenIDConnectProvider.js.map +1 -1
  19. package/lib/AWS/IAM/Policy.d.ts +1 -1
  20. package/lib/AWS/IAM/Policy.d.ts.map +1 -1
  21. package/lib/AWS/IAM/Policy.js.map +1 -1
  22. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  23. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  24. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  25. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  26. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  27. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  28. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  29. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  30. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  31. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  32. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  33. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  34. package/lib/AWS/Providers.d.ts +1 -1
  35. package/lib/AWS/Providers.d.ts.map +1 -1
  36. package/lib/AWS/Providers.js +2 -1
  37. package/lib/AWS/Providers.js.map +1 -1
  38. package/lib/Auth/Credentials.d.ts +21 -6
  39. package/lib/Auth/Credentials.d.ts.map +1 -1
  40. package/lib/Auth/Credentials.js +30 -31
  41. package/lib/Auth/Credentials.js.map +1 -1
  42. package/lib/Auth/Profile.d.ts +31 -34
  43. package/lib/Auth/Profile.d.ts.map +1 -1
  44. package/lib/Auth/Profile.js +46 -31
  45. package/lib/Auth/Profile.js.map +1 -1
  46. package/lib/Axiom/AuthProvider.d.ts +2 -1
  47. package/lib/Axiom/AuthProvider.d.ts.map +1 -1
  48. package/lib/Axiom/AuthProvider.js +149 -143
  49. package/lib/Axiom/AuthProvider.js.map +1 -1
  50. package/lib/Axiom/Credentials.d.ts +2 -1
  51. package/lib/Axiom/Credentials.d.ts.map +1 -1
  52. package/lib/Axiom/Credentials.js +4 -4
  53. package/lib/Axiom/Credentials.js.map +1 -1
  54. package/lib/Axiom/Providers.d.ts +1 -1
  55. package/lib/Axiom/Providers.d.ts.map +1 -1
  56. package/lib/Axiom/Providers.js +3 -1
  57. package/lib/Axiom/Providers.js.map +1 -1
  58. package/lib/Cli/LoggingCli.d.ts.map +1 -1
  59. package/lib/Cli/LoggingCli.js +105 -7
  60. package/lib/Cli/LoggingCli.js.map +1 -1
  61. package/lib/Cli/checkVersion.d.ts +13 -0
  62. package/lib/Cli/checkVersion.d.ts.map +1 -0
  63. package/lib/Cli/checkVersion.js +47 -0
  64. package/lib/Cli/checkVersion.js.map +1 -0
  65. package/lib/Cli/commands/_shared.d.ts +2 -2
  66. package/lib/Cli/commands/_shared.d.ts.map +1 -1
  67. package/lib/Cli/commands/_shared.js +3 -3
  68. package/lib/Cli/commands/_shared.js.map +1 -1
  69. package/lib/Cli/commands/aws.d.ts +6 -0
  70. package/lib/Cli/commands/aws.d.ts.map +1 -0
  71. package/lib/Cli/commands/aws.js +63 -0
  72. package/lib/Cli/commands/aws.js.map +1 -0
  73. package/lib/Cli/commands/cloudflare.d.ts +17 -0
  74. package/lib/Cli/commands/cloudflare.d.ts.map +1 -0
  75. package/lib/Cli/commands/cloudflare.js +111 -0
  76. package/lib/Cli/commands/cloudflare.js.map +1 -0
  77. package/lib/Cli/commands/deploy.d.ts +6 -5
  78. package/lib/Cli/commands/deploy.d.ts.map +1 -1
  79. package/lib/Cli/commands/deploy.js +10 -10
  80. package/lib/Cli/commands/deploy.js.map +1 -1
  81. package/lib/Cli/commands/dev.d.ts.map +1 -1
  82. package/lib/Cli/commands/dev.js +16 -16
  83. package/lib/Cli/commands/dev.js.map +1 -1
  84. package/lib/Cli/commands/login.d.ts +2 -1
  85. package/lib/Cli/commands/login.d.ts.map +1 -1
  86. package/lib/Cli/commands/login.js +9 -8
  87. package/lib/Cli/commands/login.js.map +1 -1
  88. package/lib/Cli/commands/logs.d.ts +1 -1
  89. package/lib/Cli/commands/logs.d.ts.map +1 -1
  90. package/lib/Cli/commands/logs.js +7 -7
  91. package/lib/Cli/commands/logs.js.map +1 -1
  92. package/lib/Cli/commands/profile.d.ts +3 -1
  93. package/lib/Cli/commands/profile.d.ts.map +1 -1
  94. package/lib/Cli/commands/profile.js +14 -11
  95. package/lib/Cli/commands/profile.js.map +1 -1
  96. package/lib/Cli/commands/state.d.ts +1 -1
  97. package/lib/Cli/commands/state.d.ts.map +1 -1
  98. package/lib/Cli/commands/state.js +8 -8
  99. package/lib/Cli/commands/state.js.map +1 -1
  100. package/lib/Cli/commands/tail.d.ts +1 -1
  101. package/lib/Cli/commands/tail.d.ts.map +1 -1
  102. package/lib/Cli/commands/tail.js +7 -7
  103. package/lib/Cli/commands/tail.js.map +1 -1
  104. package/lib/Cli/exec.d.ts.map +1 -1
  105. package/lib/Cli/exec.js +5 -3
  106. package/lib/Cli/exec.js.map +1 -1
  107. package/lib/Cli/main.d.ts.map +1 -1
  108. package/lib/Cli/main.js +17 -5
  109. package/lib/Cli/main.js.map +1 -1
  110. package/lib/Cli/selectCli.d.ts +12 -0
  111. package/lib/Cli/selectCli.d.ts.map +1 -0
  112. package/lib/Cli/selectCli.js +32 -0
  113. package/lib/Cli/selectCli.js.map +1 -0
  114. package/lib/Cloudflare/Auth/AuthProvider.d.ts +2 -1
  115. package/lib/Cloudflare/Auth/AuthProvider.d.ts.map +1 -1
  116. package/lib/Cloudflare/Auth/AuthProvider.js +235 -214
  117. package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
  118. package/lib/Cloudflare/CloudflareEnvironment.d.ts +2 -1
  119. package/lib/Cloudflare/CloudflareEnvironment.d.ts.map +1 -1
  120. package/lib/Cloudflare/CloudflareEnvironment.js +3 -2
  121. package/lib/Cloudflare/CloudflareEnvironment.js.map +1 -1
  122. package/lib/Cloudflare/Credentials.d.ts +2 -1
  123. package/lib/Cloudflare/Credentials.d.ts.map +1 -1
  124. package/lib/Cloudflare/Credentials.js +4 -4
  125. package/lib/Cloudflare/Credentials.js.map +1 -1
  126. package/lib/Cloudflare/D1/D1Clone.d.ts +2 -1
  127. package/lib/Cloudflare/D1/D1Clone.d.ts.map +1 -1
  128. package/lib/Cloudflare/D1/D1Database.d.ts +2 -1
  129. package/lib/Cloudflare/D1/D1Database.d.ts.map +1 -1
  130. package/lib/Cloudflare/D1/D1Import.d.ts +3 -1
  131. package/lib/Cloudflare/D1/D1Import.d.ts.map +1 -1
  132. package/lib/Cloudflare/Local/Sidecar.d.ts +11 -4
  133. package/lib/Cloudflare/Local/Sidecar.d.ts.map +1 -1
  134. package/lib/Cloudflare/Local/Sidecar.js +8 -6
  135. package/lib/Cloudflare/Local/Sidecar.js.map +1 -1
  136. package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +1 -1
  137. package/lib/Cloudflare/Local/SidecarHandlers.js +1 -5
  138. package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
  139. package/lib/Cloudflare/Local/SidecarServer.js +5 -2
  140. package/lib/Cloudflare/Local/SidecarServer.js.map +1 -1
  141. package/lib/Cloudflare/Logs.d.ts +5 -0
  142. package/lib/Cloudflare/Logs.d.ts.map +1 -1
  143. package/lib/Cloudflare/Logs.js +70 -1
  144. package/lib/Cloudflare/Logs.js.map +1 -1
  145. package/lib/Cloudflare/Providers.d.ts +1 -1
  146. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  147. package/lib/Cloudflare/Providers.js +4 -1
  148. package/lib/Cloudflare/Providers.js.map +1 -1
  149. package/lib/Cloudflare/Queue/Queue.d.ts +1 -1
  150. package/lib/Cloudflare/Queue/Queue.js +1 -1
  151. package/lib/Cloudflare/Queue/QueueConsumer.d.ts +6 -2
  152. package/lib/Cloudflare/Queue/QueueConsumer.d.ts.map +1 -1
  153. package/lib/Cloudflare/Queue/QueueConsumer.js +194 -53
  154. package/lib/Cloudflare/Queue/QueueConsumer.js.map +1 -1
  155. package/lib/Cloudflare/Queue/QueueEventSource.d.ts +106 -0
  156. package/lib/Cloudflare/Queue/QueueEventSource.d.ts.map +1 -0
  157. package/lib/Cloudflare/Queue/QueueEventSource.js +145 -0
  158. package/lib/Cloudflare/Queue/QueueEventSource.js.map +1 -0
  159. package/lib/Cloudflare/Queue/index.d.ts +1 -0
  160. package/lib/Cloudflare/Queue/index.d.ts.map +1 -1
  161. package/lib/Cloudflare/Queue/index.js +1 -0
  162. package/lib/Cloudflare/Queue/index.js.map +1 -1
  163. package/lib/Cloudflare/R2/R2Bucket.d.ts +94 -0
  164. package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
  165. package/lib/Cloudflare/R2/R2Bucket.js +186 -2
  166. package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
  167. package/lib/Cloudflare/StateStore/State.d.ts +4 -3
  168. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  169. package/lib/Cloudflare/StateStore/State.js +16 -8
  170. package/lib/Cloudflare/StateStore/State.js.map +1 -1
  171. package/lib/Cloudflare/VpcService/VpcServiceRef.d.ts +1 -1
  172. package/lib/Cloudflare/VpcService/VpcServiceRef.d.ts.map +1 -1
  173. package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
  174. package/lib/Cloudflare/Website/StaticSite.js +10 -1
  175. package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
  176. package/lib/Cloudflare/Workers/Assets.d.ts +1 -1
  177. package/lib/Cloudflare/Workers/Assets.d.ts.map +1 -1
  178. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  179. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +11 -1
  180. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  181. package/lib/Cloudflare/Workers/Worker.d.ts +9 -7
  182. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  183. package/lib/Cloudflare/Workers/Worker.js +62 -142
  184. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  185. package/lib/Cloudflare/Workers/WorkerBundle.d.ts +2 -0
  186. package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
  187. package/lib/Cloudflare/Workers/WorkerBundle.js +21 -7
  188. package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
  189. package/lib/Cloudflare/Zone.d.ts +14 -0
  190. package/lib/Cloudflare/Zone.d.ts.map +1 -0
  191. package/lib/Cloudflare/Zone.js +44 -0
  192. package/lib/Cloudflare/Zone.js.map +1 -0
  193. package/lib/Drizzle/Schema.d.ts.map +1 -1
  194. package/lib/Drizzle/Schema.js +20 -5
  195. package/lib/Drizzle/Schema.js.map +1 -1
  196. package/lib/GitHub/AuthProvider.d.ts +3 -1
  197. package/lib/GitHub/AuthProvider.d.ts.map +1 -1
  198. package/lib/GitHub/AuthProvider.js +114 -108
  199. package/lib/GitHub/AuthProvider.js.map +1 -1
  200. package/lib/GitHub/Credentials.d.ts +2 -1
  201. package/lib/GitHub/Credentials.d.ts.map +1 -1
  202. package/lib/GitHub/Credentials.js +4 -4
  203. package/lib/GitHub/Credentials.js.map +1 -1
  204. package/lib/GitHub/Providers.d.ts +1 -1
  205. package/lib/GitHub/Providers.d.ts.map +1 -1
  206. package/lib/GitHub/Providers.js +3 -1
  207. package/lib/GitHub/Providers.js.map +1 -1
  208. package/lib/GitHub/Secrets.d.ts.map +1 -1
  209. package/lib/GitHub/Secrets.js +14 -1
  210. package/lib/GitHub/Secrets.js.map +1 -1
  211. package/lib/GitHub/Variables.d.ts +40 -0
  212. package/lib/GitHub/Variables.d.ts.map +1 -0
  213. package/lib/GitHub/Variables.js +28 -0
  214. package/lib/GitHub/Variables.js.map +1 -0
  215. package/lib/GitHub/index.d.ts +1 -0
  216. package/lib/GitHub/index.d.ts.map +1 -1
  217. package/lib/GitHub/index.js +1 -0
  218. package/lib/GitHub/index.js.map +1 -1
  219. package/lib/Neon/AuthProvider.d.ts +3 -1
  220. package/lib/Neon/AuthProvider.d.ts.map +1 -1
  221. package/lib/Neon/AuthProvider.js +87 -80
  222. package/lib/Neon/AuthProvider.js.map +1 -1
  223. package/lib/Neon/Credentials.d.ts +2 -1
  224. package/lib/Neon/Credentials.d.ts.map +1 -1
  225. package/lib/Neon/Credentials.js +4 -4
  226. package/lib/Neon/Credentials.js.map +1 -1
  227. package/lib/Neon/NeonEnvironment.d.ts +2 -1
  228. package/lib/Neon/NeonEnvironment.d.ts.map +1 -1
  229. package/lib/Neon/NeonEnvironment.js +3 -2
  230. package/lib/Neon/NeonEnvironment.js.map +1 -1
  231. package/lib/Neon/Project.d.ts +16 -0
  232. package/lib/Neon/Project.d.ts.map +1 -1
  233. package/lib/Neon/Project.js +25 -0
  234. package/lib/Neon/Project.js.map +1 -1
  235. package/lib/Neon/Providers.d.ts +1 -1
  236. package/lib/Neon/Providers.d.ts.map +1 -1
  237. package/lib/Neon/Providers.js +3 -1
  238. package/lib/Neon/Providers.js.map +1 -1
  239. package/lib/Neon/api.d.ts +1 -0
  240. package/lib/Neon/api.d.ts.map +1 -1
  241. package/lib/Neon/api.js +1 -0
  242. package/lib/Neon/api.js.map +1 -1
  243. package/lib/Sidecar/RpcClient.d.ts.map +1 -1
  244. package/lib/Sidecar/RpcClient.js +8 -5
  245. package/lib/Sidecar/RpcClient.js.map +1 -1
  246. package/lib/Sidecar/RpcPaths.d.ts +5 -0
  247. package/lib/Sidecar/RpcPaths.d.ts.map +1 -1
  248. package/lib/Sidecar/RpcPaths.js +10 -1
  249. package/lib/Sidecar/RpcPaths.js.map +1 -1
  250. package/lib/Sidecar/RpcServer.d.ts +24 -4
  251. package/lib/Sidecar/RpcServer.d.ts.map +1 -1
  252. package/lib/Sidecar/RpcServer.js +70 -15
  253. package/lib/Sidecar/RpcServer.js.map +1 -1
  254. package/lib/Sidecar/RpcServerBun.d.ts +4 -0
  255. package/lib/Sidecar/RpcServerBun.d.ts.map +1 -0
  256. package/lib/Sidecar/RpcServerBun.js +28 -0
  257. package/lib/Sidecar/RpcServerBun.js.map +1 -0
  258. package/lib/Sidecar/RpcServerNode.d.ts +4 -0
  259. package/lib/Sidecar/RpcServerNode.d.ts.map +1 -0
  260. package/lib/Sidecar/RpcServerNode.js +39 -0
  261. package/lib/Sidecar/RpcServerNode.js.map +1 -0
  262. package/lib/Sidecar/RpcServices.d.ts +6 -0
  263. package/lib/Sidecar/RpcServices.d.ts.map +1 -0
  264. package/lib/Sidecar/RpcServices.js +29 -0
  265. package/lib/Sidecar/RpcServices.js.map +1 -0
  266. package/lib/Stack.d.ts +5 -3
  267. package/lib/Stack.d.ts.map +1 -1
  268. package/lib/Stack.js +3 -1
  269. package/lib/Stack.js.map +1 -1
  270. package/lib/Test/Core.d.ts.map +1 -1
  271. package/lib/Test/Core.js +5 -4
  272. package/lib/Test/Core.js.map +1 -1
  273. package/lib/Util/PlatformServices.js +1 -1
  274. package/lib/tsconfig.test.tsbuildinfo +1 -1
  275. package/package.json +15 -12
  276. package/src/AWS/AuthProvider.ts +269 -259
  277. package/src/AWS/Environment.ts +57 -3
  278. package/src/AWS/IAM/OpenIDConnectProvider.ts +19 -8
  279. package/src/AWS/IAM/Policy.ts +1 -1
  280. package/src/AWS/Providers.ts +2 -0
  281. package/src/Auth/Credentials.ts +82 -46
  282. package/src/Auth/Profile.ts +123 -69
  283. package/src/Axiom/AuthProvider.ts +263 -255
  284. package/src/Axiom/Credentials.ts +3 -4
  285. package/src/Axiom/Providers.ts +4 -0
  286. package/src/Cli/LoggingCli.ts +127 -11
  287. package/src/Cli/checkVersion.ts +57 -0
  288. package/src/Cli/commands/_shared.ts +4 -4
  289. package/src/Cli/commands/aws.ts +127 -0
  290. package/src/Cli/commands/cloudflare.ts +222 -0
  291. package/src/Cli/commands/deploy.ts +11 -10
  292. package/src/Cli/commands/dev.ts +28 -33
  293. package/src/Cli/commands/login.ts +9 -12
  294. package/src/Cli/commands/logs.ts +8 -8
  295. package/src/Cli/commands/profile.ts +14 -16
  296. package/src/Cli/commands/state.ts +8 -8
  297. package/src/Cli/commands/tail.ts +8 -8
  298. package/src/Cli/exec.ts +7 -3
  299. package/src/Cli/main.ts +20 -5
  300. package/src/Cli/selectCli.ts +33 -0
  301. package/src/Cloudflare/Auth/AuthProvider.ts +445 -430
  302. package/src/Cloudflare/CloudflareEnvironment.ts +3 -2
  303. package/src/Cloudflare/Credentials.ts +3 -4
  304. package/src/Cloudflare/Local/Sidecar.ts +13 -10
  305. package/src/Cloudflare/Local/SidecarHandlers.ts +8 -13
  306. package/src/Cloudflare/Local/SidecarServer.ts +6 -1
  307. package/src/Cloudflare/Logs.ts +115 -1
  308. package/src/Cloudflare/Providers.ts +6 -0
  309. package/src/Cloudflare/Queue/Queue.ts +1 -1
  310. package/src/Cloudflare/Queue/QueueConsumer.ts +262 -60
  311. package/src/Cloudflare/Queue/QueueEventSource.ts +234 -0
  312. package/src/Cloudflare/Queue/index.ts +1 -0
  313. package/src/Cloudflare/R2/R2Bucket.ts +337 -2
  314. package/src/Cloudflare/StateStore/State.ts +25 -16
  315. package/src/Cloudflare/Website/StaticSite.ts +11 -1
  316. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +9 -1
  317. package/src/Cloudflare/Workers/Worker.ts +83 -203
  318. package/src/Cloudflare/Workers/WorkerBundle.ts +38 -12
  319. package/src/Cloudflare/Zone.ts +88 -0
  320. package/src/Drizzle/Schema.ts +21 -5
  321. package/src/GitHub/AuthProvider.ts +221 -217
  322. package/src/GitHub/Credentials.ts +3 -4
  323. package/src/GitHub/Providers.ts +4 -0
  324. package/src/GitHub/Secrets.ts +25 -1
  325. package/src/GitHub/Variables.ts +54 -0
  326. package/src/GitHub/index.ts +1 -0
  327. package/src/Neon/AuthProvider.ts +160 -154
  328. package/src/Neon/Credentials.ts +3 -4
  329. package/src/Neon/NeonEnvironment.ts +3 -2
  330. package/src/Neon/Project.ts +42 -0
  331. package/src/Neon/Providers.ts +4 -0
  332. package/src/Neon/api.ts +2 -0
  333. package/src/Sidecar/RpcClient.ts +8 -7
  334. package/src/Sidecar/RpcPaths.ts +11 -1
  335. package/src/Sidecar/RpcServer.ts +97 -24
  336. package/src/Sidecar/RpcServerBun.ts +41 -0
  337. package/src/Sidecar/RpcServerNode.ts +51 -0
  338. package/src/Sidecar/RpcServices.ts +48 -0
  339. package/src/Stack.ts +8 -0
  340. package/src/Test/Core.ts +10 -4
  341. package/src/Util/PlatformServices.ts +1 -1
  342. package/lib/Cli/commands/bootstrap.d.ts +0 -20
  343. package/lib/Cli/commands/bootstrap.d.ts.map +0 -1
  344. package/lib/Cli/commands/bootstrap.js +0 -107
  345. package/lib/Cli/commands/bootstrap.js.map +0 -1
  346. package/lib/Sidecar/RpcTransport.d.ts +0 -22
  347. package/lib/Sidecar/RpcTransport.d.ts.map +0 -1
  348. package/lib/Sidecar/RpcTransport.js +0 -81
  349. package/lib/Sidecar/RpcTransport.js.map +0 -1
  350. package/lib/Test/TestCli.d.ts +0 -19
  351. package/lib/Test/TestCli.d.ts.map +0 -1
  352. package/lib/Test/TestCli.js +0 -175
  353. package/lib/Test/TestCli.js.map +0 -1
  354. package/src/Cli/commands/bootstrap.ts +0 -216
  355. package/src/Sidecar/RpcTransport.ts +0 -85
  356. package/src/Test/TestCli.ts +0 -221
@@ -1,11 +1,20 @@
1
1
  import * as queues from "@distilled.cloud/cloudflare/queues";
2
2
  import * as Effect from "effect/Effect";
3
+ import * as Option from "effect/Option";
4
+ import * as Schedule from "effect/Schedule";
5
+ import * as Stream from "effect/Stream";
3
6
  import { isResolved } from "../../Diff.ts";
4
7
  import * as Provider from "../../Provider.ts";
5
8
  import { Resource } from "../../Resource.ts";
6
9
  import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
7
10
  import type { Providers } from "../Providers.ts";
8
11
 
12
+ // ~60s budget — Worker reconcile uploads typically land in 2–10s,
13
+ // but a fresh container/asset deploy can stretch that.
14
+ const queueHandlerReadinessSchedule = Schedule.spaced("2 seconds").pipe(
15
+ Schedule.both(Schedule.recurs(30)),
16
+ );
17
+
9
18
  export type QueueConsumerProps = {
10
19
  /**
11
20
  * The queue ID to attach the consumer to.
@@ -68,6 +77,11 @@ export type QueueConsumer = Resource<
68
77
  * Register a Worker as a consumer of a Queue. The Worker's `queue()`
69
78
  * handler will be invoked with batches of messages.
70
79
  *
80
+ * Cloudflare allows at most one Worker consumer per queue (HTTP-pull
81
+ * consumers can coexist). The reconciler enforces this: if the queue
82
+ * already has a Worker consumer pointing at a different script, the
83
+ * deploy fails with a clear error rather than silently adopting it.
84
+ *
71
85
  * @section Registering a Consumer
72
86
  * @example Basic consumer
73
87
  * ```typescript
@@ -97,6 +111,20 @@ export const QueueConsumer = Resource<QueueConsumer>(
97
111
  "Cloudflare.QueueConsumer",
98
112
  );
99
113
 
114
+ type ObservedConsumer = {
115
+ consumerId: string;
116
+ script: string | undefined;
117
+ };
118
+
119
+ const toObserved = (c: {
120
+ consumerId?: string | null;
121
+ script?: string | null;
122
+ type?: "worker" | "http_pull" | null;
123
+ }): ObservedConsumer | undefined =>
124
+ c.consumerId && c.type === "worker"
125
+ ? { consumerId: c.consumerId, script: c.script ?? undefined }
126
+ : undefined;
127
+
100
128
  export const QueueConsumerProvider = () =>
101
129
  Provider.effect(
102
130
  QueueConsumer,
@@ -106,7 +134,21 @@ export const QueueConsumerProvider = () =>
106
134
  const getConsumer = yield* queues.getConsumer;
107
135
  const updateConsumer = yield* queues.updateConsumer;
108
136
  const deleteConsumer = yield* queues.deleteConsumer;
109
- const listConsumers = yield* queues.listConsumers;
137
+
138
+ // Cloudflare allows a single Worker consumer per queue, so the
139
+ // first match in the paginated stream is the only one. Using
140
+ // `.items` defeats single-page lookups that would otherwise
141
+ // miss late-arriving consumers under eventual consistency.
142
+ const findWorkerConsumer = (
143
+ acct: string,
144
+ queueId: string,
145
+ ): Effect.Effect<ObservedConsumer | undefined, any, any> =>
146
+ queues.listConsumers.items({ accountId: acct, queueId }).pipe(
147
+ Stream.map(toObserved),
148
+ Stream.filter((c): c is ObservedConsumer => c !== undefined),
149
+ Stream.runHead,
150
+ Effect.map(Option.getOrUndefined),
151
+ );
110
152
 
111
153
  return {
112
154
  stables: ["consumerId", "accountId"],
@@ -115,18 +157,29 @@ export const QueueConsumerProvider = () =>
115
157
  if ((output?.accountId ?? accountId) !== accountId) {
116
158
  return { action: "replace" } as const;
117
159
  }
118
- // Queue change requires replacement
160
+ // Queue change requires replacement — consumerId is bound
161
+ // to a queue and the API has no "move consumer" verb.
119
162
  if (output?.queueId && news.queueId !== output.queueId) {
120
- return { action: "replace" } as const;
121
- }
122
- // Script change requires replacement
123
- if (output?.scriptName && news.scriptName !== output.scriptName) {
124
- return { action: "replace" } as const;
163
+ return { action: "replace", deleteFirst: true } as const;
125
164
  }
126
- // Settings change is an update
165
+ // Settings / DLQ / script drift is an update. We DON'T
166
+ // escalate scriptName changes to `replace` because the
167
+ // engine resolves cross-resource Output<string> refs (a
168
+ // sibling Worker's `workerName`) lazily — when the upstream
169
+ // Worker is created in the same plan, `news` is partially
170
+ // unresolved at diff time and `isResolved(news)` short-
171
+ // circuits up top. Falling through to "update" lets the
172
+ // engine call reconcile with fully-resolved `news`, where
173
+ // we detect script drift and rebuild the consumer in
174
+ // place (Cloudflare's PUT silently ignores `script_name`
175
+ // changes, so reconcile does delete-then-create).
127
176
  if (
128
177
  JSON.stringify(olds.settings ?? {}) !==
129
- JSON.stringify(news.settings ?? {})
178
+ JSON.stringify(news.settings ?? {}) ||
179
+ (olds.deadLetterQueue ?? undefined) !==
180
+ (news.deadLetterQueue ?? undefined) ||
181
+ (output?.scriptName !== undefined &&
182
+ news.scriptName !== output.scriptName)
130
183
  ) {
131
184
  return { action: "update" } as const;
132
185
  }
@@ -136,33 +189,101 @@ export const QueueConsumerProvider = () =>
136
189
  const queueId =
137
190
  output?.queueId ?? (news.queueId as unknown as string);
138
191
 
139
- // Observe — re-fetch the cached consumer; fall back to a list
140
- // scan filtered by script so we recover from out-of-band
141
- // deletes or partial state-persistence failures (the create
142
- // call may have written the consumer but lost the response).
143
- let observed:
144
- | { consumerId?: string | null; script?: string | null }
145
- | undefined;
192
+ // Observe — prefer the cached consumerId, then fall back to
193
+ // listConsumers (paginated) to recover from out-of-band
194
+ // deletes or partial state-persistence failures. Track
195
+ // whether the observation came from the cached id or the
196
+ // list scan: a different-script worker consumer found via
197
+ // the list scan is potentially foreign (state was lost,
198
+ // someone else attached the consumer), and silently
199
+ // updating it could clobber another team's wiring.
200
+ let observed: ObservedConsumer | undefined;
201
+ let owned = false;
146
202
  if (output?.consumerId) {
147
- observed = yield* getConsumer({
203
+ const fetched = yield* getConsumer({
148
204
  accountId: acct,
149
- queueId: output.queueId,
205
+ queueId,
150
206
  consumerId: output.consumerId,
151
- }).pipe(Effect.catch(() => Effect.succeed(undefined)));
207
+ }).pipe(
208
+ Effect.catchTag("ConsumerNotFound", () =>
209
+ Effect.succeed(undefined),
210
+ ),
211
+ );
212
+ if (fetched) {
213
+ observed = toObserved(fetched);
214
+ owned = observed !== undefined;
215
+ }
152
216
  }
153
217
  if (!observed) {
154
- const existing = yield* listConsumers({
218
+ observed = yield* findWorkerConsumer(acct, queueId);
219
+ }
220
+
221
+ // Owned consumer pointing at a different script: rebuild
222
+ // it in place. Cloudflare's PUT consumer silently ignores
223
+ // `script_name` changes on existing consumers (the live
224
+ // record stays pinned to the original worker), and the
225
+ // platform allows only one Worker consumer per queue, so
226
+ // the only path to re-point is delete-then-create.
227
+ if (
228
+ owned &&
229
+ observed &&
230
+ observed.script !== undefined &&
231
+ observed.script !== news.scriptName
232
+ ) {
233
+ yield* deleteConsumer({
155
234
  accountId: acct,
156
235
  queueId,
157
- });
158
- observed = existing.result.find(
159
- (c) => "script" in c && c.script === news.scriptName,
236
+ consumerId: observed.consumerId,
237
+ }).pipe(Effect.catchTag("ConsumerNotFound", () => Effect.void));
238
+ // Wait for Cloudflare's worker subsystem to drop its
239
+ // claim on the old script so createConsumer below
240
+ // doesn't race the queue↔script propagation lag.
241
+ yield* getConsumer({
242
+ accountId: acct,
243
+ queueId,
244
+ consumerId: observed.consumerId,
245
+ }).pipe(
246
+ Effect.flatMap(() => Effect.fail("still-attached" as const)),
247
+ Effect.catchTag("ConsumerNotFound", () => Effect.void),
248
+ Effect.retry({
249
+ while: (e) => e === "still-attached",
250
+ schedule: Schedule.spaced("1 second").pipe(
251
+ Schedule.both(Schedule.recurs(30)),
252
+ ),
253
+ }),
254
+ Effect.ignore,
160
255
  );
256
+ observed = undefined;
257
+ owned = false;
161
258
  }
162
259
 
163
- // Ensure — create if missing. The Cloudflare API rejects a
164
- // duplicate consumer (same queue + script), so we tolerate
165
- // that race by adopting the existing one via list.
260
+ // Refuse to take over a foreign consumer on the state-loss
261
+ // path. With `owned=false` we found this via the list scan
262
+ // and the script mismatch means it belongs to another
263
+ // resource or was created out-of-band — silent adoption
264
+ // would clobber that.
265
+ if (
266
+ observed &&
267
+ !owned &&
268
+ observed.script !== undefined &&
269
+ observed.script !== news.scriptName
270
+ ) {
271
+ return yield* Effect.die(
272
+ `Cloudflare queue "${queueId}" already has a worker ` +
273
+ `consumer for script "${observed.script}", but this ` +
274
+ `resource is configured for "${news.scriptName}" and ` +
275
+ `local state for the consumer was missing. Each queue ` +
276
+ `can have only one worker consumer — delete the ` +
277
+ `existing one, update scriptName to match, or restore ` +
278
+ `the consumer's state entry before redeploying.`,
279
+ );
280
+ }
281
+
282
+ // Ensure — create if missing. ConsumerAlreadyExists is the
283
+ // race signal: another reconcile or peer beat us to it.
284
+ // Re-run the lookup; the paginated stream tolerates the
285
+ // single-page eventual-consistency window the previous
286
+ // implementation missed.
166
287
  let consumerId: string;
167
288
  if (!observed) {
168
289
  const created = yield* createConsumer({
@@ -173,41 +294,79 @@ export const QueueConsumerProvider = () =>
173
294
  deadLetterQueue: news.deadLetterQueue,
174
295
  settings: news.settings,
175
296
  }).pipe(
176
- Effect.catch(() =>
297
+ // The sibling Worker resource pre-creates a placeholder
298
+ // script with no `queue` handler; Cloudflare returns
299
+ // code 11001 until the real reconcile uploads the
300
+ // handler. Retry until the upload propagates (capped),
301
+ // then surface a real failure if it never does.
302
+ Effect.tapError((e) =>
303
+ e._tag === "QueueHandlerMissing"
304
+ ? Effect.logDebug(
305
+ `QueueConsumer create: worker ` +
306
+ `"${news.scriptName}" has no queue handler ` +
307
+ `yet (code 11001), retrying`,
308
+ )
309
+ : Effect.void,
310
+ ),
311
+ Effect.retry({
312
+ while: (e) => e._tag === "QueueHandlerMissing",
313
+ schedule: queueHandlerReadinessSchedule,
314
+ }),
315
+ Effect.catchTag("ConsumerAlreadyExists", (cause) =>
177
316
  Effect.gen(function* () {
178
- const existing = yield* listConsumers({
179
- accountId: acct,
180
- queueId,
181
- });
182
- const match = existing.result.find(
183
- (c) => "script" in c && c.script === news.scriptName,
184
- );
185
- if (match && match.consumerId) {
186
- return match;
317
+ const match = yield* findWorkerConsumer(acct, queueId);
318
+ if (!match) {
319
+ return yield* Effect.die(
320
+ `Cloudflare reported a worker consumer already ` +
321
+ `exists on queue "${queueId}", but listConsumers ` +
322
+ `returned none. Retry the deploy; if this ` +
323
+ `persists, the queue is in an inconsistent ` +
324
+ `state. Underlying error: ${cause.message}`,
325
+ );
187
326
  }
188
- return yield* Effect.die(
189
- `Consumer for script "${news.scriptName}" on queue "${queueId}" already exists but could not be found`,
190
- );
327
+ if (
328
+ match.script !== undefined &&
329
+ match.script !== news.scriptName
330
+ ) {
331
+ return yield* Effect.die(
332
+ `Cloudflare queue "${queueId}" already has a ` +
333
+ `worker consumer for script "${match.script}", ` +
334
+ `but this resource is configured for ` +
335
+ `"${news.scriptName}". Each queue can have only ` +
336
+ `one worker consumer — delete the existing one ` +
337
+ `or update scriptName to match before redeploying.`,
338
+ );
339
+ }
340
+ return match;
191
341
  }),
192
342
  ),
193
343
  );
194
344
  consumerId = created.consumerId!;
195
345
  } else {
196
- consumerId = observed.consumerId!;
197
- // Sync — update settings and dead-letter target on the
198
- // existing consumer. The Cloudflare API replaces all mutable
199
- // fields per call, so always issue this so adoption converges.
200
- yield* updateConsumer({
201
- accountId: acct,
202
- queueId,
203
- consumerId,
204
- scriptName: news.scriptName,
205
- type: "worker",
206
- settings: news.settings,
207
- deadLetterQueue: news.deadLetterQueue,
208
- });
346
+ consumerId = observed.consumerId;
209
347
  }
210
348
 
349
+ // Sync — Cloudflare replaces all mutable fields on
350
+ // updateConsumer, so always issue this so adoption converges
351
+ // and settings drift gets corrected on every reconcile.
352
+ // updateConsumer hits the same "queue handler missing" race
353
+ // window as create when the worker is mid-upload, so apply
354
+ // the same bounded retry.
355
+ yield* updateConsumer({
356
+ accountId: acct,
357
+ queueId,
358
+ consumerId,
359
+ scriptName: news.scriptName,
360
+ type: "worker",
361
+ settings: news.settings,
362
+ deadLetterQueue: news.deadLetterQueue,
363
+ }).pipe(
364
+ Effect.retry({
365
+ while: (e) => e._tag === "QueueHandlerMissing",
366
+ schedule: queueHandlerReadinessSchedule,
367
+ }),
368
+ );
369
+
211
370
  return {
212
371
  consumerId,
213
372
  queueId,
@@ -220,26 +379,69 @@ export const QueueConsumerProvider = () =>
220
379
  accountId: output.accountId,
221
380
  queueId: output.queueId,
222
381
  consumerId: output.consumerId,
223
- }).pipe(Effect.catch(() => Effect.void));
382
+ }).pipe(Effect.catchTag("ConsumerNotFound", () => Effect.void));
383
+
384
+ // Block until Cloudflare's worker subsystem stops claiming
385
+ // the script as a queue consumer. Without this the
386
+ // sibling Worker.delete races on `QueueConsumerConflict`
387
+ // (code 10064) — `deleteConsumer` returns success on the
388
+ // queue subsystem before the script-side view propagates.
389
+ yield* getConsumer({
390
+ accountId: output.accountId,
391
+ queueId: output.queueId,
392
+ consumerId: output.consumerId,
393
+ }).pipe(
394
+ Effect.flatMap(() => Effect.fail("still-attached" as const)),
395
+ Effect.catchTag("ConsumerNotFound", () => Effect.void),
396
+ Effect.retry({
397
+ while: (e) => e === "still-attached",
398
+ schedule: Schedule.spaced("1 second").pipe(
399
+ Schedule.both(Schedule.recurs(30)),
400
+ ),
401
+ }),
402
+ Effect.ignore,
403
+ );
224
404
  }),
225
405
  read: Effect.fn(function* ({ output }) {
226
406
  if (output?.consumerId) {
227
- return yield* getConsumer({
407
+ const fetched = yield* getConsumer({
228
408
  accountId: output.accountId,
229
409
  queueId: output.queueId,
230
410
  consumerId: output.consumerId,
231
411
  }).pipe(
232
- Effect.map((consumer) => ({
233
- consumerId: consumer.consumerId!,
412
+ Effect.catchTag("ConsumerNotFound", () =>
413
+ Effect.succeed(undefined),
414
+ ),
415
+ );
416
+ if (fetched) {
417
+ return {
418
+ consumerId: fetched.consumerId!,
234
419
  queueId: output.queueId,
235
420
  scriptName:
236
- ("script" in consumer
237
- ? (consumer.script as string)
421
+ ("script" in fetched && typeof fetched.script === "string"
422
+ ? fetched.script
238
423
  : output.scriptName) ?? output.scriptName,
239
424
  accountId: output.accountId,
240
- })),
241
- Effect.catch(() => Effect.succeed(undefined)),
425
+ };
426
+ }
427
+ }
428
+ // Fallback: a state loss can leave us without a consumerId
429
+ // even though the consumer is still alive on Cloudflare. The
430
+ // queue allows only one worker consumer, so finding it via
431
+ // listConsumers is unambiguous.
432
+ if (output?.queueId && output?.accountId) {
433
+ const match = yield* findWorkerConsumer(
434
+ output.accountId,
435
+ output.queueId,
242
436
  );
437
+ if (match) {
438
+ return {
439
+ consumerId: match.consumerId,
440
+ queueId: output.queueId,
441
+ scriptName: match.script ?? output.scriptName,
442
+ accountId: output.accountId,
443
+ };
444
+ }
243
445
  }
244
446
  return undefined;
245
447
  }),
@@ -0,0 +1,234 @@
1
+ import type * as cf from "@cloudflare/workers-types";
2
+ import * as Cause from "effect/Cause";
3
+ import * as Context from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Stream from "effect/Stream";
7
+ import * as Binding from "../../Binding.ts";
8
+ import type { ResourceLike } from "../../Resource.ts";
9
+ import { RuntimeContext } from "../../RuntimeContext.ts";
10
+ import type { FunctionContext } from "../../Serverless/Function.ts";
11
+ import { isWorker, isWorkerEvent } from "../Workers/Worker.ts";
12
+ import type { Queue } from "./Queue.ts";
13
+ import { QueueConsumer } from "./QueueConsumer.ts";
14
+
15
+ /**
16
+ * Subscriber settings — the same shape Cloudflare's `QueueConsumer`
17
+ * accepts. `messages(queue, props).subscribe(...)` passes these
18
+ * through to the auto-created `Cloudflare.QueueConsumer` so a single
19
+ * call captures both runtime and deploy-time intent.
20
+ */
21
+ export interface MessagesProps {
22
+ /** Maximum messages per batch. */
23
+ batchSize?: number;
24
+ /** Maximum concurrent invocations. */
25
+ maxConcurrency?: number;
26
+ /** Maximum delivery attempts before dead-lettering. */
27
+ maxRetries?: number;
28
+ /** Wait time, in ms, before flushing a partial batch. */
29
+ maxWaitTimeMs?: number;
30
+ /** Backoff, in seconds, applied to a retry. */
31
+ retryDelay?: number;
32
+ /** Optional dead-letter queue name. */
33
+ deadLetterQueue?: string;
34
+ }
35
+
36
+ /**
37
+ * A single queue message handed to the subscribe handler. Mirrors
38
+ * Cloudflare's runtime `Message<Body>` shape so per-message
39
+ * `ack()` / `retry()` semantics match the platform docs.
40
+ */
41
+ export type QueueMessage<Body = unknown> = cf.Message<Body>;
42
+
43
+ /**
44
+ * Subscribe to a Cloudflare Queue with an Effect stream handler.
45
+ *
46
+ * Mirrors `AWS.SQS.messages(queue).subscribe(...)` on the
47
+ * Cloudflare side. Wires both halves of the consumer in one call:
48
+ *
49
+ * - **Runtime**: registers a `queue` event listener on the Worker.
50
+ * Each batch is piped through `process` as a `Stream.Stream`.
51
+ * - **Deploy-time**: yields a `Cloudflare.QueueConsumer` resource
52
+ * so Cloudflare actually dispatches messages from `queue` to
53
+ * this Worker. No manual `QueueConsumer` wiring needed in
54
+ * `alchemy.run.ts`.
55
+ *
56
+ * Acking semantics: if `process` succeeds, every message in the
57
+ * batch is `ack()`ed; if it fails, every message is `retry()`ed
58
+ * and Cloudflare applies `maxRetries` / `retryDelay` from the
59
+ * settings before dead-lettering. Per-message control is still
60
+ * available by calling `msg.ack()` / `msg.retry()` inside the
61
+ * handler.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * import * as Cloudflare from "alchemy/Cloudflare";
66
+ * import * as Effect from "effect/Effect";
67
+ * import * as Stream from "effect/Stream";
68
+ *
69
+ * yield* Cloudflare.messages<MyEvent>(queueResource, {
70
+ * batchSize: 25,
71
+ * maxRetries: 3,
72
+ * }).subscribe((stream) =>
73
+ * Stream.runForEach(stream, (msg) =>
74
+ * Effect.log(`event ${msg.body.id}`),
75
+ * ),
76
+ * );
77
+ * ```
78
+ */
79
+ export const messages = <Body = unknown>(
80
+ queue: Queue,
81
+ props: MessagesProps = {},
82
+ ) => ({
83
+ subscribe: <Req = never>(
84
+ process: (
85
+ stream: Stream.Stream<QueueMessage<Body>>,
86
+ ) => Effect.Effect<void, unknown, Req>,
87
+ ) =>
88
+ QueueEventSource.use((source) => source<Body, Req>(queue, props, process)),
89
+ });
90
+
91
+ // `Req` is the handler's requirements. The service registers the
92
+ // handler with the Worker's runtime context, where the runtime
93
+ // machinery provides bindings and `WorkerEnvironment` when the
94
+ // dispatch fires — so the requirement is satisfied at handler
95
+ // invocation, NOT at subscribe time. We drop `Req` from the return
96
+ // to keep init effects clean (mirrors `AWS.SQS.QueueEventSourceService`).
97
+ export type QueueEventSourceService = <Body = unknown, Req = never>(
98
+ queue: Queue,
99
+ props: MessagesProps,
100
+ process: (
101
+ stream: Stream.Stream<QueueMessage<Body>>,
102
+ ) => Effect.Effect<void, unknown, Req>,
103
+ ) => Effect.Effect<void, never, never>;
104
+
105
+ /**
106
+ * Service tag for the Cloudflare Queue event source. Provided by
107
+ * {@link QueueEventSourceLive} on the Worker's runtime layer.
108
+ */
109
+ export class QueueEventSource extends Context.Service<
110
+ QueueEventSource,
111
+ QueueEventSourceService
112
+ >()("Cloudflare.Queue.QueueEventSource") {}
113
+
114
+ /**
115
+ * Deploy-time policy that yields a `Cloudflare.QueueConsumer`
116
+ * resource pointing the host Worker at the queue. Provided in
117
+ * `Cloudflare.providers()` and used by {@link QueueEventSourceLive}
118
+ * via `yield* QueueEventSourcePolicy(...)`. At runtime the policy
119
+ * is absent, so the call is a no-op.
120
+ */
121
+ export class QueueEventSourcePolicy extends Binding.Policy<
122
+ QueueEventSourcePolicy,
123
+ (queue: Queue, props: MessagesProps) => Effect.Effect<void>
124
+ >()("Cloudflare.Queue.QueueEventSource") {}
125
+
126
+ export const QueueEventSourcePolicyLive = QueueEventSourcePolicy.layer.succeed(
127
+ // Cast: yielding `QueueConsumer(...)` requires the Cloudflare
128
+ // `Providers` services, which the deploy-time stack provides
129
+ // when this policy runs (the worker's init scope inherits the
130
+ // stack's services). `Binding.Policy.layer.succeed` types the
131
+ // body as `Effect<void, never, never>` to keep most policies
132
+ // simple; we step around that constraint here because we
133
+ // genuinely need to spawn a sibling resource at deploy time.
134
+ ((host: ResourceLike, queue: Queue, props: MessagesProps) =>
135
+ Effect.gen(function* () {
136
+ if (!isWorker(host)) {
137
+ return yield* Effect.die(
138
+ `Cloudflare.messages(...).subscribe(...) is only supported on ` +
139
+ `Cloudflare.Worker hosts (got '${host.Type}').`,
140
+ );
141
+ }
142
+ // Yield the QueueConsumer resource as a sibling of the
143
+ // Worker. The engine creates / updates / destroys it
144
+ // alongside the Worker's lifecycle; the consumer's
145
+ // reconciler waits for the Worker upload to expose the
146
+ // `queue` handler before completing (see PR #257 for the
147
+ // 11001 retry).
148
+ yield* QueueConsumer(`${queue.LogicalId}Consumer`, {
149
+ queueId: queue.queueId,
150
+ scriptName: host.workerName,
151
+ settings: props,
152
+ deadLetterQueue: props.deadLetterQueue,
153
+ });
154
+ })) as unknown as (
155
+ host: ResourceLike,
156
+ queue: Queue,
157
+ props: MessagesProps,
158
+ ) => Effect.Effect<void>,
159
+ );
160
+
161
+ /**
162
+ * Runtime layer for {@link messages}. Wires each
163
+ * `messages(queue).subscribe(...)` call in the Worker init phase to
164
+ * a `queue` event listener on the runtime context, and asks the
165
+ * deploy-time policy ({@link QueueEventSourcePolicy}, provided in
166
+ * `Cloudflare.providers()`) to yield the matching
167
+ * `Cloudflare.QueueConsumer` resource.
168
+ *
169
+ * Provide alongside other Cloudflare runtime layers (e.g.
170
+ * `QueueBindingLive`) on the Worker effect.
171
+ */
172
+ export const QueueEventSourceLive = Layer.effect(
173
+ QueueEventSource,
174
+ Effect.gen(function* () {
175
+ const policy = yield* QueueEventSourcePolicy;
176
+ return Effect.fn(function* <Body, Req>(
177
+ queue: Queue,
178
+ props: MessagesProps,
179
+ process: (
180
+ stream: Stream.Stream<QueueMessage<Body>>,
181
+ ) => Effect.Effect<void, unknown, Req>,
182
+ ) {
183
+ // Deploy-time: ensure the QueueConsumer resource exists. At
184
+ // runtime this Layer's `policy` resolves to the no-op variant
185
+ // (Binding.Policy provides that automatically via
186
+ // `Effect.serviceOption`), so this becomes a no-op.
187
+ yield* policy(queue, props);
188
+
189
+ // Resolve the runtime context per-call rather than at layer
190
+ // construction. Capturing it on the layer would leak the
191
+ // requirement past `PlatformServices` exclusion when the
192
+ // Worker typechecks its init effect.
193
+ const ctx = (yield* RuntimeContext) as unknown as FunctionContext;
194
+ // Capture the queue-name accessor once; the listener body
195
+ // re-resolves it per event via `yield* QueueName`. A worker
196
+ // can consume multiple queues — each subscribe registers its
197
+ // own listener and they all see every queue event, so the
198
+ // queue-name match is what scopes the handler.
199
+ const QueueName = yield* queue.queueName;
200
+
201
+ yield* ctx.listen<void, Req>((event) => {
202
+ if (!isWorkerEvent(event) || event.type !== "queue") return;
203
+ const batch = event.input as cf.MessageBatch<Body>;
204
+
205
+ return Effect.gen(function* () {
206
+ const queueName = yield* QueueName;
207
+ if (batch.queue !== queueName) return;
208
+
209
+ yield* process(Stream.fromIterable(batch.messages)).pipe(
210
+ Effect.tap(() =>
211
+ Effect.sync(() => {
212
+ for (const msg of batch.messages) msg.ack();
213
+ }),
214
+ ),
215
+ Effect.onError((cause) =>
216
+ Effect.sync(() => {
217
+ // Surface the failure so the operator sees what
218
+ // tripped the retry path; without this the only
219
+ // signal is the message reappearing on the next
220
+ // attempt.
221
+ console.error(
222
+ `[QueueEventSource] handler failed on queue ` +
223
+ `"${queueName}": ${Cause.pretty(cause)}`,
224
+ );
225
+ for (const msg of batch.messages) msg.retry();
226
+ }),
227
+ ),
228
+ Effect.catchCause(() => Effect.void),
229
+ );
230
+ });
231
+ });
232
+ }) as QueueEventSourceService;
233
+ }),
234
+ );
@@ -1,3 +1,4 @@
1
1
  export * from "./Queue.ts";
2
2
  export * from "./QueueBinding.ts";
3
3
  export * from "./QueueConsumer.ts";
4
+ export * from "./QueueEventSource.ts";