alchemy 2.0.0-beta.44 → 2.0.0-beta.46

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 (443) hide show
  1. package/bin/exec.js +67 -8
  2. package/bin/exec.js.map +1 -1
  3. package/lib/AWS/Lambda/Function.d.ts +17 -0
  4. package/lib/AWS/Lambda/Function.d.ts.map +1 -1
  5. package/lib/AWS/Lambda/Function.js +61 -16
  6. package/lib/AWS/Lambda/Function.js.map +1 -1
  7. package/lib/AWS/Providers.d.ts +2 -1
  8. package/lib/AWS/Providers.d.ts.map +1 -1
  9. package/lib/AWS/Providers.js +3 -1
  10. package/lib/AWS/Providers.js.map +1 -1
  11. package/lib/AlchemyContext.d.ts +0 -1
  12. package/lib/AlchemyContext.d.ts.map +1 -1
  13. package/lib/AlchemyContext.js.map +1 -1
  14. package/lib/Apply.js +17 -1
  15. package/lib/Apply.js.map +1 -1
  16. package/lib/Auth/AuthProvider.d.ts.map +1 -1
  17. package/lib/Auth/AuthProvider.js +24 -6
  18. package/lib/Auth/AuthProvider.js.map +1 -1
  19. package/lib/Auth/Credentials.d.ts.map +1 -1
  20. package/lib/Auth/Credentials.js +3 -3
  21. package/lib/Auth/Credentials.js.map +1 -1
  22. package/lib/Cli/commands/_shared.d.ts +13 -1
  23. package/lib/Cli/commands/_shared.d.ts.map +1 -1
  24. package/lib/Cli/commands/_shared.js +33 -2
  25. package/lib/Cli/commands/_shared.js.map +1 -1
  26. package/lib/Cli/commands/cloudflare.d.ts.map +1 -1
  27. package/lib/Cli/commands/cloudflare.js +5 -1
  28. package/lib/Cli/commands/cloudflare.js.map +1 -1
  29. package/lib/Cli/commands/dev.d.ts.map +1 -1
  30. package/lib/Cli/commands/dev.js +10 -1
  31. package/lib/Cli/commands/dev.js.map +1 -1
  32. package/lib/Cli/commands/login.d.ts.map +1 -1
  33. package/lib/Cli/commands/login.js +19 -5
  34. package/lib/Cli/commands/login.js.map +1 -1
  35. package/lib/Cli/commands/profile.d.ts.map +1 -1
  36. package/lib/Cli/commands/profile.js +7 -27
  37. package/lib/Cli/commands/profile.js.map +1 -1
  38. package/lib/Cli/main.d.ts.map +1 -1
  39. package/lib/Cli/main.js +0 -2
  40. package/lib/Cli/main.js.map +1 -1
  41. package/lib/Cloudflare/AiGateway/AiGateway.d.ts +0 -2
  42. package/lib/Cloudflare/AiGateway/AiGateway.d.ts.map +1 -1
  43. package/lib/Cloudflare/AiGateway/AiGateway.js +2 -4
  44. package/lib/Cloudflare/AiGateway/AiGateway.js.map +1 -1
  45. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts +116 -0
  46. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts.map +1 -0
  47. package/lib/Cloudflare/BrowserRendering/BrowserRendering.js +92 -0
  48. package/lib/Cloudflare/BrowserRendering/BrowserRendering.js.map +1 -0
  49. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts +55 -0
  50. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts.map +1 -0
  51. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.js +55 -0
  52. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.js.map +1 -0
  53. package/lib/Cloudflare/BrowserRendering/index.d.ts +3 -0
  54. package/lib/Cloudflare/BrowserRendering/index.d.ts.map +1 -0
  55. package/lib/Cloudflare/BrowserRendering/index.js +3 -0
  56. package/lib/Cloudflare/BrowserRendering/index.js.map +1 -0
  57. package/lib/Cloudflare/Container/Container.d.ts +3 -3
  58. package/lib/Cloudflare/Container/Container.js +3 -3
  59. package/lib/Cloudflare/Container/StartContainer.d.ts.map +1 -1
  60. package/lib/Cloudflare/Container/StartContainer.js.map +1 -1
  61. package/lib/Cloudflare/D1/D1Database.d.ts +1 -0
  62. package/lib/Cloudflare/D1/D1Database.d.ts.map +1 -1
  63. package/lib/Cloudflare/D1/D1Database.js +2 -1
  64. package/lib/Cloudflare/D1/D1Database.js.map +1 -1
  65. package/lib/Cloudflare/D1/D1DatabaseBinding.js +1 -1
  66. package/lib/Cloudflare/D1/D1DatabaseBinding.js.map +1 -1
  67. package/lib/Cloudflare/Fetcher.d.ts.map +1 -1
  68. package/lib/Cloudflare/Fetcher.js +0 -2
  69. package/lib/Cloudflare/Fetcher.js.map +1 -1
  70. package/lib/Cloudflare/Hyperdrive/Hyperdrive.d.ts +1 -0
  71. package/lib/Cloudflare/Hyperdrive/Hyperdrive.d.ts.map +1 -1
  72. package/lib/Cloudflare/Hyperdrive/Hyperdrive.js +3 -0
  73. package/lib/Cloudflare/Hyperdrive/Hyperdrive.js.map +1 -1
  74. package/lib/Cloudflare/Hyperdrive/HyperdriveBinding.d.ts +3 -2
  75. package/lib/Cloudflare/Hyperdrive/HyperdriveBinding.d.ts.map +1 -1
  76. package/lib/Cloudflare/Hyperdrive/HyperdriveBinding.js +32 -33
  77. package/lib/Cloudflare/Hyperdrive/HyperdriveBinding.js.map +1 -1
  78. package/lib/Cloudflare/KV/KVNamespace.d.ts +1 -0
  79. package/lib/Cloudflare/KV/KVNamespace.d.ts.map +1 -1
  80. package/lib/Cloudflare/KV/KVNamespace.js +2 -0
  81. package/lib/Cloudflare/KV/KVNamespace.js.map +1 -1
  82. package/lib/Cloudflare/Local.js +1 -1
  83. package/lib/Cloudflare/Local.js.map +1 -1
  84. package/lib/Cloudflare/Providers.d.ts +2 -1
  85. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  86. package/lib/Cloudflare/Providers.js +25 -17
  87. package/lib/Cloudflare/Providers.js.map +1 -1
  88. package/lib/Cloudflare/Queue/Queue.d.ts +1 -0
  89. package/lib/Cloudflare/Queue/Queue.d.ts.map +1 -1
  90. package/lib/Cloudflare/Queue/Queue.js +1 -0
  91. package/lib/Cloudflare/Queue/Queue.js.map +1 -1
  92. package/lib/Cloudflare/Queue/QueueConsumer.d.ts +2 -1
  93. package/lib/Cloudflare/Queue/QueueConsumer.d.ts.map +1 -1
  94. package/lib/Cloudflare/Queue/QueueConsumer.js +10 -3
  95. package/lib/Cloudflare/Queue/QueueConsumer.js.map +1 -1
  96. package/lib/Cloudflare/Queue/QueueEventSource.d.ts +30 -4
  97. package/lib/Cloudflare/Queue/QueueEventSource.d.ts.map +1 -1
  98. package/lib/Cloudflare/Queue/QueueEventSource.js +22 -1
  99. package/lib/Cloudflare/Queue/QueueEventSource.js.map +1 -1
  100. package/lib/Cloudflare/R2/R2Bucket.d.ts +1 -0
  101. package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
  102. package/lib/Cloudflare/R2/R2Bucket.js +18 -0
  103. package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
  104. package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
  105. package/lib/Cloudflare/SecretsStore/Secret.js +13 -34
  106. package/lib/Cloudflare/SecretsStore/Secret.js.map +1 -1
  107. package/lib/Cloudflare/SecretsStore/SecretBinding.d.ts +3 -2
  108. package/lib/Cloudflare/SecretsStore/SecretBinding.d.ts.map +1 -1
  109. package/lib/Cloudflare/SecretsStore/SecretBinding.js +3 -2
  110. package/lib/Cloudflare/SecretsStore/SecretBinding.js.map +1 -1
  111. package/lib/Cloudflare/StateStore/Api.d.ts +5 -13
  112. package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -1
  113. package/lib/Cloudflare/StateStore/Api.js +14 -8
  114. package/lib/Cloudflare/StateStore/Api.js.map +1 -1
  115. package/lib/Cloudflare/StateStore/State.d.ts +11 -65
  116. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  117. package/lib/Cloudflare/StateStore/State.js +320 -343
  118. package/lib/Cloudflare/StateStore/State.js.map +1 -1
  119. package/lib/Cloudflare/StateStore/Store.d.ts.map +1 -1
  120. package/lib/Cloudflare/StateStore/Store.js +21 -31
  121. package/lib/Cloudflare/StateStore/Store.js.map +1 -1
  122. package/lib/Cloudflare/Vectorize/VectorizeIndex.d.ts +114 -0
  123. package/lib/Cloudflare/Vectorize/VectorizeIndex.d.ts.map +1 -0
  124. package/lib/Cloudflare/Vectorize/VectorizeIndex.js +157 -0
  125. package/lib/Cloudflare/Vectorize/VectorizeIndex.js.map +1 -0
  126. package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.d.ts +38 -0
  127. package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.d.ts.map +1 -0
  128. package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.js +44 -0
  129. package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.js.map +1 -0
  130. package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.d.ts +72 -0
  131. package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.d.ts.map +1 -0
  132. package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.js +131 -0
  133. package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.js.map +1 -0
  134. package/lib/Cloudflare/Vectorize/index.d.ts +4 -0
  135. package/lib/Cloudflare/Vectorize/index.d.ts.map +1 -0
  136. package/lib/Cloudflare/Vectorize/index.js +4 -0
  137. package/lib/Cloudflare/Vectorize/index.js.map +1 -0
  138. package/lib/Cloudflare/VpcService/VpcService.d.ts +2 -2
  139. package/lib/Cloudflare/VpcService/VpcService.d.ts.map +1 -1
  140. package/lib/Cloudflare/Website/StaticSite.d.ts +5 -5
  141. package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
  142. package/lib/Cloudflare/Website/StaticSite.js +3 -0
  143. package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
  144. package/lib/Cloudflare/Website/Vite.d.ts +5 -5
  145. package/lib/Cloudflare/Website/Vite.d.ts.map +1 -1
  146. package/lib/Cloudflare/Website/Vite.js.map +1 -1
  147. package/lib/Cloudflare/Workers/Assets.d.ts.map +1 -1
  148. package/lib/Cloudflare/Workers/Assets.js +1 -1
  149. package/lib/Cloudflare/Workers/Assets.js.map +1 -1
  150. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
  151. package/lib/Cloudflare/Workers/DurableObjectBridge.js +4 -2
  152. package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
  153. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts +183 -6
  154. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts.map +1 -1
  155. package/lib/Cloudflare/Workers/DurableObjectNamespace.js +279 -76
  156. package/lib/Cloudflare/Workers/DurableObjectNamespace.js.map +1 -1
  157. package/lib/Cloudflare/Workers/HttpServer.d.ts +6 -4
  158. package/lib/Cloudflare/Workers/HttpServer.d.ts.map +1 -1
  159. package/lib/Cloudflare/Workers/HttpServer.js +20 -15
  160. package/lib/Cloudflare/Workers/HttpServer.js.map +1 -1
  161. package/lib/Cloudflare/Workers/InferEnv.d.ts +3 -2
  162. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  163. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +4 -7
  164. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  165. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +111 -66
  166. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  167. package/lib/Cloudflare/Workers/Rpc.d.ts +16 -0
  168. package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
  169. package/lib/Cloudflare/Workers/Rpc.js +22 -0
  170. package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
  171. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.d.ts +285 -0
  172. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.d.ts.map +1 -0
  173. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.js +288 -0
  174. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.js.map +1 -0
  175. package/lib/Cloudflare/Workers/RpcWorker.d.ts +305 -0
  176. package/lib/Cloudflare/Workers/RpcWorker.d.ts.map +1 -0
  177. package/lib/Cloudflare/Workers/RpcWorker.js +350 -0
  178. package/lib/Cloudflare/Workers/RpcWorker.js.map +1 -0
  179. package/lib/Cloudflare/Workers/Vite.d.ts +1 -1
  180. package/lib/Cloudflare/Workers/Vite.d.ts.map +1 -1
  181. package/lib/Cloudflare/Workers/Vite.js +2 -1
  182. package/lib/Cloudflare/Workers/Vite.js.map +1 -1
  183. package/lib/Cloudflare/Workers/Worker.d.ts +69 -24
  184. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  185. package/lib/Cloudflare/Workers/Worker.js +168 -47
  186. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  187. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts +1 -1
  188. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  189. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +166 -97
  190. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  191. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +12 -23
  192. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  193. package/lib/Cloudflare/Workers/WorkerBinding.js +3 -0
  194. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  195. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  196. package/lib/Cloudflare/Workers/WorkerBridge.js +5 -2
  197. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  198. package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
  199. package/lib/Cloudflare/Workers/WorkerBundle.js +4 -1
  200. package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
  201. package/lib/Cloudflare/Workers/WorkerRuntimeContext.d.ts.map +1 -1
  202. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js +9 -10
  203. package/lib/Cloudflare/Workers/WorkerRuntimeContext.js.map +1 -1
  204. package/lib/Cloudflare/Workers/Workflow.d.ts +5 -3
  205. package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
  206. package/lib/Cloudflare/Workers/Workflow.js +19 -1
  207. package/lib/Cloudflare/Workers/Workflow.js.map +1 -1
  208. package/lib/Cloudflare/Workers/index.d.ts +2 -0
  209. package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
  210. package/lib/Cloudflare/Workers/index.js +2 -0
  211. package/lib/Cloudflare/Workers/index.js.map +1 -1
  212. package/lib/Cloudflare/index.d.ts +2 -0
  213. package/lib/Cloudflare/index.d.ts.map +1 -1
  214. package/lib/Cloudflare/index.js +2 -0
  215. package/lib/Cloudflare/index.js.map +1 -1
  216. package/lib/Dependencies.d.ts +10 -0
  217. package/lib/Dependencies.d.ts.map +1 -0
  218. package/lib/Dependencies.js +1 -0
  219. package/lib/Dependencies.js.map +1 -0
  220. package/lib/Deploy.d.ts +2 -1
  221. package/lib/Deploy.d.ts.map +1 -1
  222. package/lib/Deploy.js +2 -2
  223. package/lib/Deploy.js.map +1 -1
  224. package/lib/Diff.d.ts +11 -0
  225. package/lib/Diff.d.ts.map +1 -1
  226. package/lib/Diff.js +11 -0
  227. package/lib/Diff.js.map +1 -1
  228. package/lib/Input.d.ts +2 -1
  229. package/lib/Input.d.ts.map +1 -1
  230. package/lib/KeyPair.d.ts +77 -0
  231. package/lib/KeyPair.d.ts.map +1 -0
  232. package/lib/KeyPair.js +102 -0
  233. package/lib/KeyPair.js.map +1 -0
  234. package/lib/Local/RpcProvider.d.ts +3 -1
  235. package/lib/Local/RpcProvider.d.ts.map +1 -1
  236. package/lib/Local/RpcProvider.js +28 -2
  237. package/lib/Local/RpcProvider.js.map +1 -1
  238. package/lib/Local/RpcServerEnvironment.js +1 -1
  239. package/lib/Local/RpcServerEnvironment.js.map +1 -1
  240. package/lib/Neon/Project.d.ts.map +1 -1
  241. package/lib/Neon/Project.js +14 -3
  242. package/lib/Neon/Project.js.map +1 -1
  243. package/lib/Output.d.ts.map +1 -1
  244. package/lib/Output.js +9 -2
  245. package/lib/Output.js.map +1 -1
  246. package/lib/Phase.d.ts +2 -1
  247. package/lib/Phase.d.ts.map +1 -1
  248. package/lib/Phase.js +6 -1
  249. package/lib/Phase.js.map +1 -1
  250. package/lib/Plan.d.ts.map +1 -1
  251. package/lib/Plan.js +14 -3
  252. package/lib/Plan.js.map +1 -1
  253. package/lib/Platform.d.ts +8 -6
  254. package/lib/Platform.d.ts.map +1 -1
  255. package/lib/Platform.js +33 -3
  256. package/lib/Platform.js.map +1 -1
  257. package/lib/Random.d.ts.map +1 -1
  258. package/lib/Random.js +9 -14
  259. package/lib/Random.js.map +1 -1
  260. package/lib/RuntimeContext.d.ts +21 -1
  261. package/lib/RuntimeContext.d.ts.map +1 -1
  262. package/lib/RuntimeContext.js +12 -0
  263. package/lib/RuntimeContext.js.map +1 -1
  264. package/lib/Stack.d.ts +3 -3
  265. package/lib/Stack.d.ts.map +1 -1
  266. package/lib/Stack.js.map +1 -1
  267. package/lib/State/HttpStateApi.d.ts +2 -2
  268. package/lib/State/HttpStateStore.d.ts +4 -0
  269. package/lib/State/HttpStateStore.d.ts.map +1 -1
  270. package/lib/State/HttpStateStore.js +13 -0
  271. package/lib/State/HttpStateStore.js.map +1 -1
  272. package/lib/State/StateEncoding.d.ts +8 -0
  273. package/lib/State/StateEncoding.d.ts.map +1 -1
  274. package/lib/State/StateEncoding.js +55 -5
  275. package/lib/State/StateEncoding.js.map +1 -1
  276. package/lib/Test/Bun.d.ts.map +1 -1
  277. package/lib/Test/Bun.js +0 -2
  278. package/lib/Test/Bun.js.map +1 -1
  279. package/lib/Test/Vitest.d.ts.map +1 -1
  280. package/lib/Test/Vitest.js +0 -2
  281. package/lib/Test/Vitest.js.map +1 -1
  282. package/lib/Util/Duration.d.ts +14 -0
  283. package/lib/Util/Duration.d.ts.map +1 -0
  284. package/lib/Util/Duration.js +18 -0
  285. package/lib/Util/Duration.js.map +1 -0
  286. package/lib/Util/index.d.ts +0 -6
  287. package/lib/Util/index.d.ts.map +1 -1
  288. package/lib/Util/index.js +0 -6
  289. package/lib/Util/index.js.map +1 -1
  290. package/lib/Util/poll.d.ts +30 -0
  291. package/lib/Util/poll.d.ts.map +1 -0
  292. package/lib/Util/poll.js +26 -0
  293. package/lib/Util/poll.js.map +1 -0
  294. package/lib/Util/types.d.ts +3 -1
  295. package/lib/Util/types.d.ts.map +1 -1
  296. package/lib/Util/types.js +5 -1
  297. package/lib/Util/types.js.map +1 -1
  298. package/lib/index.d.ts +2 -3
  299. package/lib/index.d.ts.map +1 -1
  300. package/lib/index.js +1 -3
  301. package/lib/index.js.map +1 -1
  302. package/lib/tsconfig.test.tsbuildinfo +1 -1
  303. package/package.json +20 -19
  304. package/src/AWS/Lambda/Function.ts +87 -34
  305. package/src/AWS/Providers.ts +3 -0
  306. package/src/AlchemyContext.ts +0 -1
  307. package/src/Apply.ts +18 -1
  308. package/src/Auth/AuthProvider.ts +25 -5
  309. package/src/Auth/Credentials.ts +8 -7
  310. package/src/Cli/commands/_shared.ts +47 -4
  311. package/src/Cli/commands/cloudflare.ts +5 -3
  312. package/src/Cli/commands/dev.ts +13 -1
  313. package/src/Cli/commands/login.ts +20 -4
  314. package/src/Cli/commands/profile.ts +24 -34
  315. package/src/Cli/main.ts +0 -3
  316. package/src/Cloudflare/AiGateway/AiGateway.ts +2 -6
  317. package/src/Cloudflare/BrowserRendering/BrowserRendering.ts +132 -0
  318. package/src/Cloudflare/BrowserRendering/BrowserRenderingBinding.ts +137 -0
  319. package/src/Cloudflare/BrowserRendering/index.ts +11 -0
  320. package/src/Cloudflare/Container/Container.ts +3 -3
  321. package/src/Cloudflare/Container/StartContainer.ts +1 -2
  322. package/src/Cloudflare/D1/D1Database.ts +4 -1
  323. package/src/Cloudflare/D1/D1DatabaseBinding.ts +1 -1
  324. package/src/Cloudflare/Fetcher.ts +0 -2
  325. package/src/Cloudflare/Hyperdrive/Hyperdrive.ts +5 -0
  326. package/src/Cloudflare/Hyperdrive/HyperdriveBinding.ts +41 -43
  327. package/src/Cloudflare/KV/KVNamespace.ts +4 -0
  328. package/src/Cloudflare/Local.ts +1 -1
  329. package/src/Cloudflare/Providers.ts +32 -16
  330. package/src/Cloudflare/Queue/Queue.ts +3 -0
  331. package/src/Cloudflare/Queue/QueueConsumer.ts +21 -8
  332. package/src/Cloudflare/Queue/QueueEventSource.ts +33 -5
  333. package/src/Cloudflare/R2/R2Bucket.ts +26 -0
  334. package/src/Cloudflare/SecretsStore/Secret.ts +14 -41
  335. package/src/Cloudflare/SecretsStore/SecretBinding.ts +11 -11
  336. package/src/Cloudflare/StateStore/Api.ts +15 -15
  337. package/src/Cloudflare/StateStore/State.ts +468 -468
  338. package/src/Cloudflare/StateStore/Store.ts +27 -37
  339. package/src/Cloudflare/Vectorize/VectorizeIndex.ts +261 -0
  340. package/src/Cloudflare/Vectorize/VectorizeIndexBinding.ts +103 -0
  341. package/src/Cloudflare/Vectorize/VectorizeMetadataIndex.ts +208 -0
  342. package/src/Cloudflare/Vectorize/index.ts +3 -0
  343. package/src/Cloudflare/VpcService/VpcService.ts +2 -2
  344. package/src/Cloudflare/Website/StaticSite.ts +8 -10
  345. package/src/Cloudflare/Website/Vite.ts +4 -7
  346. package/src/Cloudflare/Workers/Assets.ts +1 -1
  347. package/src/Cloudflare/Workers/DurableObjectBridge.ts +7 -4
  348. package/src/Cloudflare/Workers/DurableObjectNamespace.ts +380 -121
  349. package/src/Cloudflare/Workers/HttpServer.ts +31 -25
  350. package/src/Cloudflare/Workers/InferEnv.ts +15 -8
  351. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +139 -78
  352. package/src/Cloudflare/Workers/Rpc.ts +50 -0
  353. package/src/Cloudflare/Workers/RpcDurableObjectNamespace.ts +502 -0
  354. package/src/Cloudflare/Workers/RpcWorker.ts +562 -0
  355. package/src/Cloudflare/Workers/Vite.ts +2 -0
  356. package/src/Cloudflare/Workers/Worker.ts +274 -93
  357. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +165 -103
  358. package/src/Cloudflare/Workers/WorkerBinding.ts +12 -0
  359. package/src/Cloudflare/Workers/WorkerBridge.ts +7 -4
  360. package/src/Cloudflare/Workers/WorkerBundle.ts +4 -1
  361. package/src/Cloudflare/Workers/WorkerRuntimeContext.ts +8 -15
  362. package/src/Cloudflare/Workers/Workflow.ts +22 -3
  363. package/src/Cloudflare/Workers/index.ts +2 -0
  364. package/src/Cloudflare/index.ts +2 -0
  365. package/src/Dependencies.ts +10 -0
  366. package/src/Deploy.ts +3 -1
  367. package/src/Diff.ts +13 -0
  368. package/src/Input.ts +2 -0
  369. package/src/KeyPair.ts +146 -0
  370. package/src/Local/RpcProvider.ts +45 -1
  371. package/src/Local/RpcServerEnvironment.ts +1 -1
  372. package/src/Neon/Project.ts +16 -3
  373. package/src/Output.ts +8 -2
  374. package/src/Phase.ts +8 -0
  375. package/src/Plan.ts +16 -3
  376. package/src/Platform.ts +74 -27
  377. package/src/Random.ts +22 -27
  378. package/src/RuntimeContext.ts +23 -1
  379. package/src/Stack.ts +3 -3
  380. package/src/State/HttpStateStore.ts +28 -0
  381. package/src/State/StateEncoding.ts +58 -7
  382. package/src/Test/Bun.ts +0 -3
  383. package/src/Test/Vitest.ts +0 -3
  384. package/src/Util/Duration.ts +21 -0
  385. package/src/Util/index.ts +0 -6
  386. package/src/Util/poll.ts +47 -0
  387. package/src/Util/types.ts +9 -2
  388. package/src/index.ts +2 -3
  389. package/lib/Secret.d.ts +0 -50
  390. package/lib/Secret.d.ts.map +0 -1
  391. package/lib/Secret.js +0 -23
  392. package/lib/Secret.js.map +0 -1
  393. package/lib/Trait.d.ts +0 -43
  394. package/lib/Trait.d.ts.map +0 -1
  395. package/lib/Trait.js +0 -17
  396. package/lib/Trait.js.map +0 -1
  397. package/lib/Util/LocalhostDns.d.ts +0 -23
  398. package/lib/Util/LocalhostDns.d.ts.map +0 -1
  399. package/lib/Util/LocalhostDns.js +0 -46
  400. package/lib/Util/LocalhostDns.js.map +0 -1
  401. package/lib/Util/assert-never.d.ts +0 -12
  402. package/lib/Util/assert-never.d.ts.map +0 -1
  403. package/lib/Util/assert-never.js +0 -11
  404. package/lib/Util/assert-never.js.map +0 -1
  405. package/lib/Util/class.d.ts +0 -3
  406. package/lib/Util/class.d.ts.map +0 -1
  407. package/lib/Util/class.js +0 -1
  408. package/lib/Util/class.js.map +0 -1
  409. package/lib/Util/ignore.d.ts +0 -40
  410. package/lib/Util/ignore.d.ts.map +0 -1
  411. package/lib/Util/ignore.js +0 -591
  412. package/lib/Util/ignore.js.map +0 -1
  413. package/lib/Util/instance.d.ts +0 -4
  414. package/lib/Util/instance.d.ts.map +0 -1
  415. package/lib/Util/instance.js +0 -1
  416. package/lib/Util/instance.js.map +0 -1
  417. package/lib/Util/pointer.d.ts +0 -5
  418. package/lib/Util/pointer.d.ts.map +0 -1
  419. package/lib/Util/pointer.js +0 -1
  420. package/lib/Util/pointer.js.map +0 -1
  421. package/lib/Util/schema-to-type.d.ts +0 -42
  422. package/lib/Util/schema-to-type.d.ts.map +0 -1
  423. package/lib/Util/schema-to-type.js +0 -364
  424. package/lib/Util/schema-to-type.js.map +0 -1
  425. package/lib/Util/todo.d.ts +0 -3
  426. package/lib/Util/todo.d.ts.map +0 -1
  427. package/lib/Util/todo.js +0 -3
  428. package/lib/Util/todo.js.map +0 -1
  429. package/lib/Variable.d.ts +0 -29
  430. package/lib/Variable.d.ts.map +0 -1
  431. package/lib/Variable.js +0 -9
  432. package/lib/Variable.js.map +0 -1
  433. package/src/Secret.ts +0 -87
  434. package/src/Trait.ts +0 -105
  435. package/src/Util/LocalhostDns.ts +0 -52
  436. package/src/Util/assert-never.ts +0 -18
  437. package/src/Util/class.ts +0 -7
  438. package/src/Util/ignore.ts +0 -815
  439. package/src/Util/instance.ts +0 -9
  440. package/src/Util/pointer.ts +0 -5
  441. package/src/Util/schema-to-type.ts +0 -456
  442. package/src/Util/todo.ts +0 -4
  443. package/src/Variable.ts +0 -44
@@ -8,342 +8,220 @@ import * as HttpClient from "effect/unstable/http/HttpClient";
8
8
  import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
9
9
  import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient";
10
10
  import crypto from "node:crypto";
11
- import * as Output from "../../Output.js";
12
11
  import * as Config from "effect/Config";
12
+ import { isHttpClientError } from "effect/unstable/http/HttpClientError";
13
13
  import { adopt } from "../../AdoptPolicy.js";
14
14
  import { AuthError } from "../../Auth/AuthProvider.js";
15
15
  import { CredentialsStore, CredentialsStoreLive, } from "../../Auth/Credentials.js";
16
16
  import { ALCHEMY_PROFILE, ProfileLive } from "../../Auth/Profile.js";
17
17
  import * as Cloudflare from "../../Cloudflare/Providers.js";
18
18
  import { deploy } from "../../Deploy.js";
19
+ import * as Output from "../../Output.js";
19
20
  import * as Alchemy from "../../Stack.js";
20
21
  import { StateApi } from "../../State/HttpStateApi.js";
21
- import { makeHttpStateStore, } from "../../State/HttpStateStore.js";
22
- import * as CloudflareEnvironment from "../CloudflareEnvironment.js";
23
- import * as Credentials from "../Credentials.js";
24
- import { AlchemyContext } from "../../AlchemyContext.js";
22
+ import { checkHttpStateStoreAuth, makeHttpStateStore, } from "../../State/HttpStateStore.js";
25
23
  import { makeLocalState } from "../../State/LocalState.js";
26
24
  import { State } from "../../State/State.js";
27
25
  import { recordStateStoreInit, recordStateStoreOp, } from "../../Telemetry/Metrics.js";
28
26
  import * as Clank from "../../Util/Clank.js";
29
27
  import * as Access from "../Access.js";
30
28
  import { CloudflareAuth } from "../Auth/AuthProvider.js";
29
+ import * as CloudflareEnvironment from "../CloudflareEnvironment.js";
30
+ import * as Credentials from "../Credentials.js";
31
31
  import { EdgeSessionError, createEdgeSession } from "../EdgeSession.js";
32
32
  import Api, { STATE_STORE_SCRIPT_NAME, STATE_STORE_VERSION } from "./Api.js";
33
- import { AuthTokenSecretName, TokenValue } from "./Token.js";
33
+ import { AuthToken, AuthTokenSecretName, TokenValue } from "./Token.js";
34
+ const CI = Config.boolean("CI").pipe(Config.withDefault(false));
34
35
  /** Filename used for stored credentials under the profile directory. */
35
36
  const CREDENTIALS_FILE = "cloudflare-state-store";
36
- /**
37
- * SHA-256 hex digest of the Cloudflare account ID. Used as a stable
38
- * pseudonymous identifier on telemetry spans so the dashboard can
39
- * count distinct state-store deployments without leaking the raw
40
- * accountId. Mirrors the `alchemy.git.origin_hash` pattern in
41
- * `Telemetry/Attributes.ts`.
42
- */
43
- const hashAccountId = (accountId) => Effect.sync(() => crypto.createHash("sha256").update(accountId).digest("hex"));
44
- /**
45
- * Best-effort Cloudflare-account-hash annotation on the current span.
46
- * Resolves the accountId from {@link CloudflareEnvironment} and
47
- * attaches `alchemy.cloudflare.account_hash` to whichever span is
48
- * active. Silently no-ops if the environment isn't resolvable so
49
- * State-store layer construction still succeeds in degraded paths.
50
- *
51
- * `noTrack` controls whether the hash is attached:
52
- * - `true` — never annotate (caller-level opt-out).
53
- * - `false` — always annotate, regardless of env.
54
- * - `undefined` — fall back to the `NO_TRACK` env var; default off.
55
- */
56
- const annotateAccountHash = (noTrack) => Effect.gen(function* () {
57
- if (noTrack === true)
58
- return;
59
- if (noTrack === undefined) {
60
- const fromEnv = yield* Config.boolean("NO_TRACK").pipe(Config.withDefault(false));
61
- if (fromEnv)
62
- return;
63
- }
64
- const env = yield* Effect.serviceOption(CloudflareEnvironment.CloudflareEnvironment);
65
- if (env._tag !== "Some")
66
- return;
67
- const hash = yield* hashAccountId(env.value.accountId);
68
- yield* Effect.annotateCurrentSpan("alchemy.cloudflare.account_hash", hash);
69
- }).pipe(Effect.catch(() => Effect.void));
70
- /**
71
- * Manually bootstrap (or repair) the Cloudflare-hosted HTTP State
72
- * Store. Used by `alchemy bootstrap cloudflare`.
73
- *
74
- * The flow mirrors what {@link state} does on first use, but is
75
- * exposed as a standalone effect so users can trigger it (or recover
76
- * from a partially-failed previous deploy) explicitly:
77
- *
78
- * 1. Resume any in-progress local `CloudflareStateStore` stack.
79
- * 2. If the worker already exists in Cloudflare and `force` is not
80
- * set, adopt it: read the auth token via an edge-preview probe
81
- * and persist credentials.
82
- * 3. Otherwise run the full deploy with adoption enabled, sync local
83
- * state into the deployed store, persist credentials, and delete
84
- * the local stack.
85
- */
86
- export const bootstrap = (options = {}) => Effect.gen(function* () {
87
- const isCI = yield* Config.boolean("CI").pipe(Config.withDefault(false));
37
+ export const state = () => Layer.effect(State, Effect.gen(function* () {
38
+ const isCI = yield* CI;
39
+ const scriptName = STATE_STORE_SCRIPT_NAME;
88
40
  const profileName = yield* ALCHEMY_PROFILE;
89
- const scriptName = options.workerName ?? STATE_STORE_SCRIPT_NAME;
90
- const force = options.force ?? false;
91
- yield* Effect.annotateCurrentSpan({
92
- "alchemy.state_store.script_name": scriptName,
93
- "alchemy.state_store.profile": profileName,
94
- "alchemy.state_store.force": force,
95
- "alchemy.state_store.ci": isCI,
96
- });
97
- yield* annotateAccountHash();
98
- const localState = yield* makeLocalState();
99
- const hasLocalStack = yield* Effect.map(localState.listStages("CloudflareStateStore"), (stages) => stages.includes(scriptName));
100
- if (hasLocalStack) {
101
- yield* Clank.info(`Resuming Cloudflare State Store '${scriptName}' deployment...`);
102
- yield* finishBootstrap({ scriptName, profileName, localState, isCI });
103
- yield* Clank.success(`Cloudflare State Store '${scriptName}' is ready.`);
104
- return;
105
- }
106
- const { accountId } = yield* CloudflareEnvironment.CloudflareEnvironment;
107
- const workerExists = yield* workers
108
- .getScriptSetting({ accountId, scriptName })
109
- .pipe(Effect.map((setting) => setting !== undefined), Effect.catchTag("WorkerNotFound", () => Effect.succeed(false)));
110
- if (workerExists && !force) {
111
- yield* Clank.info(`Worker '${scriptName}' already exists; adopting and refreshing credentials. ` +
112
- `Use --force to redeploy.`);
113
- const credentials = yield* loginWithCloudflare();
114
- if (!isCI) {
115
- const store = yield* CredentialsStore;
116
- yield* store.write(profileName, CREDENTIALS_FILE, credentials);
117
- }
118
- yield* redeployIfStale({
41
+ const localStage = `${profileName}_${scriptName}`;
42
+ const credStore = yield* CredentialsStore;
43
+ if (yield* hasLocalStack(localStage)) {
44
+ // if there's still a local stack, then we need to finish the bootstrap
45
+ // TODO(sam): what if the local stack was
46
+ return yield* deployWithLocalState({
119
47
  scriptName,
120
48
  profileName,
121
- localState,
122
49
  isCI,
123
- url: credentials.url,
50
+ force: false,
124
51
  });
125
- yield* Clank.success(`Cloudflare State Store '${scriptName}' is ready.`);
126
- return;
127
52
  }
53
+ const ensureLatest = ({ url, authToken, }) => Effect.gen(function* () {
54
+ const { matches, expected, observed } = yield* checkStateStoreVersion(url);
55
+ if (observed === undefined) {
56
+ const shouldDeploy = yield* Clank.confirm({
57
+ message: `Cloudflare State Store '${scriptName}' is not available. Do you want to deploy it?`,
58
+ });
59
+ if (shouldDeploy) {
60
+ return yield* bootstrap({
61
+ workerName: scriptName,
62
+ profile: profileName,
63
+ });
64
+ }
65
+ else {
66
+ return yield* Effect.die(new Clank.PromptCancelled());
67
+ }
68
+ }
69
+ const httpState = yield* ensureAccess({ url, authToken });
70
+ if (matches) {
71
+ return httpState;
72
+ }
73
+ else if (isCI) {
74
+ return yield* Effect.die(new AuthError({
75
+ message: `Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile <your-ci-profile>' to deploy it first.`,
76
+ }));
77
+ }
78
+ else {
79
+ const shouldDeploy = yield* Clank.confirm({
80
+ message: `Cloudflare State Store '${scriptName}' is out of date ` +
81
+ `(expected v${expected}, observed v${observed ?? "unknown"})`,
82
+ });
83
+ if (shouldDeploy) {
84
+ const stateStoreOptions = yield* deployStateStore({
85
+ stage: scriptName,
86
+ state: httpState,
87
+ force: false,
88
+ });
89
+ return yield* makeCloudflareStateStore(stateStoreOptions);
90
+ }
91
+ else {
92
+ return yield* Effect.die(new Clank.PromptCancelled());
93
+ }
94
+ }
95
+ });
96
+ const ensureAccess = (credentials) => Effect.gen(function* () {
97
+ const isAuth = yield* checkHttpStateStoreAuth(credentials);
98
+ if (!isAuth) {
99
+ // our token is wrong, force a refresh
100
+ yield* Clank.info(`Cloudflare State store authentication failed, refreshing credentials...`);
101
+ const credentials = yield* loginWithCloudflare(profileName, true);
102
+ if (!(yield* checkHttpStateStoreAuth(credentials))) {
103
+ return yield* Effect.die(new AuthError({
104
+ message: `Cloudflare State store authentication failed, after refreshing credentials.`,
105
+ }));
106
+ }
107
+ return yield* makeCloudflareStateStore(credentials);
108
+ }
109
+ return yield* makeCloudflareStateStore(credentials);
110
+ });
111
+ const credentials = yield* credStore.read(profileName, CREDENTIALS_FILE);
112
+ if (credentials) {
113
+ return yield* ensureLatest(credentials);
114
+ }
115
+ const workerExists = yield* isStateStoreAvailable(scriptName);
128
116
  if (workerExists) {
129
- yield* Clank.info(`Forcing redeploy of Cloudflare State Store '${scriptName}'...`);
117
+ return yield* ensureLatest(yield* loginWithCloudflare(profileName, false));
118
+ }
119
+ else if (isCI) {
120
+ // TODO(sam): do we want to support bootstrapping the state store from CI?
121
+ // for now - just die here
122
+ return yield* Effect.die(new AuthError({
123
+ message: `Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile <your-ci-profile>' to deploy it first.`,
124
+ }));
130
125
  }
131
126
  else {
132
- yield* Clank.info(`Deploying Cloudflare State Store '${scriptName}'...`);
127
+ return yield* Clank.confirm({
128
+ message: "Cloudflare State Store not found. Do you want to deploy it?",
129
+ }).pipe(Effect.flatMap((shouldDeploy) => shouldDeploy
130
+ ? bootstrap()
131
+ : Effect.die(new Clank.PromptCancelled())));
133
132
  }
134
- yield* finishBootstrap({ scriptName, profileName, localState, isCI });
135
- yield* Clank.success(`Cloudflare State Store '${scriptName}' is ready.`);
136
- }).pipe(Effect.withSpan("state_store.bootstrap", {
137
- attributes: {
138
- "alchemy.state_store.op": "bootstrap",
139
- "alchemy.state_store.script_name": options.workerName ?? STATE_STORE_SCRIPT_NAME,
140
- },
141
- }));
142
- export const state = () => Layer.effect(State, Effect.gen(function* () {
143
- const isCI = yield* Config.boolean("CI").pipe(Config.withDefault(false));
144
- const alchemyContext = yield* AlchemyContext;
145
- const profileName = yield* ALCHEMY_PROFILE;
146
- const store = yield* CredentialsStore;
147
- const credentials = yield* store.read(profileName, CREDENTIALS_FILE);
148
- const scriptName = STATE_STORE_SCRIPT_NAME;
133
+ }).pipe(recordStateStoreInit, Effect.orDie)).pipe(Layer.provideMerge(Credentials.fromAuthProvider()), Layer.provideMerge(CloudflareEnvironment.fromProfile()), Layer.provideMerge(CloudflareAuth), Layer.provideMerge(Access.AccessLive), Layer.provideMerge(ProfileLive), Layer.provideMerge(CredentialsStoreLive), Layer.orDie);
134
+ export const bootstrap = (options = {}) => Effect.gen(function* () {
135
+ const isCI = yield* CI;
136
+ const profileName = options.profile ?? (yield* ALCHEMY_PROFILE);
137
+ const scriptName = options.workerName ?? STATE_STORE_SCRIPT_NAME;
138
+ const force = options.force ?? false;
139
+ const localStage = `${profileName}_${scriptName}`;
149
140
  yield* Effect.annotateCurrentSpan({
150
141
  "alchemy.state_store.script_name": scriptName,
151
142
  "alchemy.state_store.profile": profileName,
143
+ "alchemy.state_store.force": force,
152
144
  "alchemy.state_store.ci": isCI,
153
145
  });
154
- yield* annotateAccountHash(yield* Config.boolean("NO_TRACK").pipe(Config.withDefault(false)));
155
- // The bootstrap of the Cloudflare State Store is only considered
156
- // successful once two invariants hold:
157
- // 1. the worker has been deployed and the credentials persisted
158
- // 2. the local CloudflareStateStore stack has been synced into
159
- // the HTTP store and removed from disk
160
- //
161
- // If a previous run failed anywhere between deploying the worker
162
- // and deleting the local stack (e.g. the worker got created but
163
- // sync/credential-write/delete never ran) we will detect the
164
- // leftover local stack here and resume the bootstrap. This loop
165
- // re-runs deploy (idempotent over already-applied resources) and
166
- // finishes the sync/credential/cleanup steps until both
167
- // invariants are satisfied.
168
- const localState = yield* makeLocalState();
169
- const hasLocalStack = yield* Effect.map(localState.listStages("CloudflareStateStore"), (stages) => stages.includes(scriptName));
170
- if (hasLocalStack) {
146
+ yield* annotateAccountHash();
147
+ if (yield* hasLocalStack(localStage)) {
148
+ // if there's a local stack still, we can assume we did not finish hoisting it, so finish that
171
149
  yield* Clank.info(`Resuming Cloudflare State Store '${scriptName}' deployment...`);
172
- return yield* finishBootstrap({
150
+ // resume deployment
151
+ return yield* deployWithLocalState({
173
152
  scriptName,
174
153
  profileName,
175
- localState,
176
154
  isCI,
177
- });
178
- }
179
- if (credentials) {
180
- // We have local credentials. Before trusting them, verify
181
- // the deployed worker is on the version this CLI was built
182
- // against. If it isn't, `redeployIfStale` runs the idempotent
183
- // bootstrap flow and short-circuits with the freshly-deployed
184
- // store.
185
- const fresh = yield* redeployIfStale({
186
- scriptName,
187
- profileName,
188
- localState,
189
- isCI,
190
- url: credentials.url,
191
- });
192
- if (fresh)
193
- return fresh;
194
- const httpState = yield* makeCloudflareStateStore(credentials);
195
- if (alchemyContext.updateStateStore) {
196
- yield* deployStateStore(scriptName, httpState);
197
- }
198
- return httpState;
155
+ force,
156
+ }).pipe(Effect.tap(() => Clank.success(`Cloudflare State Store '${scriptName}' is ready.`)));
199
157
  }
200
- // our profile does not contain a reference to the state store, let's try and resolve it
201
- const { accountId } = yield* CloudflareEnvironment.CloudflareEnvironment;
202
- const workerExists = yield* workers
203
- .getScriptSetting({
204
- accountId,
205
- scriptName,
206
- })
207
- .pipe(Effect.map((setting) => setting !== undefined), Effect.catchTag("WorkerNotFound", () => Effect.succeed(false)));
158
+ const workerExists = yield* isStateStoreAvailable(scriptName);
208
159
  if (workerExists) {
209
- // it exists, so fetch the secret token
210
- const credentials = yield* loginWithCloudflare();
160
+ // this is a regular update, let's check if it needs an update and refresh credentials
161
+ if (!force) {
162
+ yield* Clank.info(`Worker '${scriptName}' already exists; adopting and refreshing credentials. ` +
163
+ `Use --force to redeploy.`);
164
+ }
165
+ const credentials = yield* loginWithCloudflare(profileName,
166
+ // force refresh during
167
+ true);
168
+ const { url, authToken } = credentials;
211
169
  if (!isCI) {
170
+ // we don't write credentials in CI because the file system is ephemeral
171
+ const store = yield* CredentialsStore;
212
172
  yield* store.write(profileName, CREDENTIALS_FILE, credentials);
213
173
  }
214
- const fresh = yield* redeployIfStale({
174
+ const { matches, expected, observed } = yield* checkStateStoreVersion(url);
175
+ const httpState = yield* makeCloudflareStateStore({ url, authToken });
176
+ if (!matches || force) {
177
+ if (matches && force) {
178
+ yield* Clank.info(`Cloudflare State Store '${scriptName}' is up to date; force redeploying...`);
179
+ }
180
+ else {
181
+ yield* Clank.info(`Cloudflare State Store '${scriptName}' is out of date ` +
182
+ `(expected v${expected}, observed v${observed ?? "unknown"}); redeploying...`);
183
+ }
184
+ return yield* makeCloudflareStateStore(yield* deployStateStore({
185
+ stage: scriptName,
186
+ state: httpState,
187
+ force,
188
+ }));
189
+ }
190
+ else {
191
+ return httpState;
192
+ }
193
+ }
194
+ else {
195
+ // fresh deploy - deploy from local for the first time
196
+ yield* Clank.info(`Deploying Cloudflare State Store '${scriptName}'...`);
197
+ return yield* deployWithLocalState({
215
198
  scriptName,
216
199
  profileName,
217
- localState,
218
200
  isCI,
219
- url: credentials.url,
220
- });
221
- if (fresh)
222
- return fresh;
223
- const httpState = yield* makeCloudflareStateStore(credentials);
224
- if (alchemyContext.updateStateStore) {
225
- yield* deployStateStore(scriptName, httpState);
226
- }
227
- return httpState;
228
- }
229
- if (isCI) {
230
- // TODO(sam): do we want to support bootstrapping the state store from CI?
231
- // for now - just die here
232
- return yield* Effect.die(new AuthError({
233
- message: `State store not found for script ${scriptName}. Deploy the state store first.`,
234
- }));
235
- }
236
- const shouldDeploy = yield* Clank.confirm({
237
- message: "Cloudflare State Store not found. Do you want to deploy it?",
238
- });
239
- if (!shouldDeploy) {
240
- return yield* Effect.die(new Clank.PromptCancelled());
201
+ force,
202
+ }).pipe(Effect.tap(() => Clank.success(`Cloudflare State Store '${scriptName}' is ready.`)));
241
203
  }
242
- return yield* finishBootstrap({
243
- scriptName,
244
- profileName,
245
- localState,
246
- isCI,
247
- });
248
- }).pipe(recordStateStoreInit, Effect.orDie)).pipe(Layer.provideMerge(Credentials.fromAuthProvider()), Layer.provideMerge(CloudflareEnvironment.fromProfile()), Layer.provideMerge(CloudflareAuth), Layer.provideMerge(Access.AccessLive), Layer.provideMerge(ProfileLive), Layer.provideMerge(CredentialsStoreLive), Layer.orDie);
249
- const makeCloudflareStateStore = Effect.fnUntraced(function* ({ url, authToken, }) {
250
- const access = yield* Access.Access;
251
- const accessHeaders = yield* access.getAccessHeaders(new URL(url).host);
252
- return yield* makeHttpStateStore({
253
- url,
254
- authToken,
255
- transformClient: HttpClientRequest.setHeaders(accessHeaders),
256
- id: "cloudflare-http",
257
- });
258
- });
259
- /**
260
- * Non-destructively copy every resource in the
261
- * `CloudflareStateStore/<scriptName>` stack from `source` into
262
- * `destination`, leaving every other stack in `destination` untouched.
263
- *
264
- * This intentionally does not delete anything from `destination`: at
265
- * bootstrap time the destination is the user's live remote state
266
- * store, and removing entries that happen to be missing locally would
267
- * be catastrophic.
268
- */
269
- const hoistBootstrapStack = Effect.fnUntraced(function* (source, destination, scriptName) {
270
- const stack = "CloudflareStateStore";
271
- const stage = scriptName;
272
- const fqns = yield* source.list({ stack, stage });
273
- yield* Effect.annotateCurrentSpan({
274
- "alchemy.state_store.stack": stack,
275
- "alchemy.state_store.stage": stage,
276
- "alchemy.state_store.resources.count": fqns.length,
277
- });
278
- yield* Effect.forEach(fqns, Effect.fnUntraced(function* (fqn) {
279
- const value = yield* source.get({ stack, stage, fqn });
280
- if (value) {
281
- yield* destination.set({ stack, stage, fqn, value });
282
- }
283
- }), { concurrency: "unbounded" });
284
- }, Effect.withSpan("state_store.hoist_bootstrap_stack"));
285
- /**
286
- * Finish (or resume) the bootstrap of the Cloudflare State Store using
287
- * the provided local state as the source of truth. This is idempotent
288
- * and safe to re-run: any resources already applied during a previous
289
- * partial run will be reconciled, the local stack will be synced into
290
- * the deployed HTTP store, credentials persisted, and finally the
291
- * local stack removed - which is what marks the bootstrap as complete.
292
- */
293
- const finishBootstrap = ({ scriptName, profileName, localState, isCI, }) => Effect.gen(function* () {
294
- yield* deployStateStore(scriptName, localState);
295
- // Don't trust the `authToken` returned by `deploy(...)`: when
296
- // adoption kicks in (the Secrets Store secret already existed),
297
- // the locally-generated `Random` value won't match the value
298
- // actually persisted in Cloudflare, and any HTTP call to the
299
- // worker would 401. Re-read the live token from the deployed
300
- // worker via the same edge-preview probe `loginWithCloudflare`
301
- // uses, so the credentials we persist always reflect what is
302
- // actually deployed. `loginWithCloudflare` also persists the
303
- // credentials file (skipping the write in CI), so we don't need
304
- // to do that explicitly here.
305
- const { url, authToken } = yield* loginWithCloudflare();
306
- const httpState = yield* makeCloudflareStateStore({ url, authToken });
307
- // `profileName` is intentionally unused here — `loginWithCloudflare`
308
- // resolves it itself. Reference it to keep the surrounding API
309
- // explicit and to avoid an unused-parameter lint.
310
- void profileName;
311
- void isCI;
312
- // Copy the freshly-deployed CloudflareStateStore stack into the
313
- // remote store. We deliberately do NOT call `syncState` here:
314
- // syncState is a *mirror* that deletes everything in the
315
- // destination that is not in the source, which would wipe every
316
- // user stack in the remote store on any subsequent bootstrap
317
- // (resume / --force / partial-failure recovery), since by that
318
- // point local state only contains the bootstrap stack itself.
319
- yield* hoistBootstrapStack(localState, httpState, scriptName);
320
- yield* localState.deleteStack({
321
- stack: "CloudflareStateStore",
322
- stage: scriptName,
323
- });
324
- return httpState;
325
- }).pipe(Effect.withSpan("state_store.finish_bootstrap", {
204
+ }).pipe(Effect.withSpan("state_store.bootstrap", {
326
205
  attributes: {
327
- "alchemy.state_store.op": "finish_bootstrap",
328
- "alchemy.state_store.script_name": scriptName,
329
- "alchemy.state_store.profile": profileName,
330
- "alchemy.state_store.ci": isCI,
206
+ "alchemy.state_store.op": "bootstrap",
207
+ "alchemy.state_store.script_name": options.workerName ?? STATE_STORE_SCRIPT_NAME,
331
208
  },
332
209
  }));
333
- const deployStateStore = (scriptName, state) => Effect.gen(function* () {
210
+ const deployStateStore = ({ stage, state, force, }) => Effect.gen(function* () {
334
211
  yield* annotateAccountHash();
335
- const localState = state ?? (yield* makeLocalState());
336
212
  // deploy it with local state (which we will then hoist into the Cloudflare state store)
337
- const stateLayer = Layer.succeed(State, localState);
213
+ const stateLayer = Layer.succeed(State, state);
338
214
  const { url, authToken } = yield* deploy({
339
215
  // use the script name as the stage name (so the user can have multiple state stores)
340
- stage: scriptName,
216
+ stage,
217
+ force,
341
218
  stack: Alchemy.Stack("CloudflareStateStore", {
342
219
  providers: Cloudflare.providers(),
343
220
  state: stateLayer,
344
221
  }, Effect.gen(function* () {
345
222
  const token = yield* TokenValue;
346
223
  const api = yield* Api;
224
+ yield* AuthToken; // make sure it's in the Secrets Store
347
225
  // Surface the bearer token so tests and clients can authenticate
348
226
  // after deploy. The underlying value lives in the Cloudflare
349
227
  // Secrets Store; this output carries the same generated string.
@@ -361,6 +239,7 @@ const deployStateStore = (scriptName, state) => Effect.gen(function* () {
361
239
  adopt(true),
362
240
  // TODO(sam): we should not need to do this, but types do complain. fix deploy
363
241
  Effect.provide(stateLayer));
242
+ yield* writeCredentials(url, authToken);
364
243
  // Cloudflare's worker upload is eventually consistent: the deploy
365
244
  // call returns as soon as the script upload is accepted, but the
366
245
  // edge can keep serving the previous version for several seconds
@@ -371,13 +250,92 @@ const deployStateStore = (scriptName, state) => Effect.gen(function* () {
371
250
  // observe stale data or trip the staleness check and recurse into
372
251
  // another redeploy.
373
252
  yield* waitForStateStoreVersion(url);
374
- return { url, authToken, localState };
253
+ return { url, authToken };
375
254
  }).pipe(Effect.withSpan("state_store.deploy", {
376
255
  attributes: {
377
- "alchemy.state_store.script_name": scriptName,
378
256
  "alchemy.state_store.op": "deploy",
379
257
  },
380
258
  }), recordStateStoreOp("deploy"));
259
+ const deployWithLocalState = ({ scriptName, isCI, force, profileName, }) => Effect.gen(function* () {
260
+ const localState = yield* makeLocalState();
261
+ const localStage = `${profileName}_${scriptName}`;
262
+ const remoteStage = scriptName;
263
+ const { authToken } = yield* deployStateStore({
264
+ stage: localStage,
265
+ state: localState,
266
+ force,
267
+ });
268
+ const { url } = yield* loginWithCloudflare(profileName, force);
269
+ const httpState = yield* makeCloudflareStateStore({ url, authToken });
270
+ yield* hoistBootstrapStack({
271
+ source: {
272
+ state: localState,
273
+ stage: localStage,
274
+ },
275
+ destination: {
276
+ state: httpState,
277
+ stage: remoteStage,
278
+ },
279
+ });
280
+ yield* localState.deleteStack({
281
+ stack: "CloudflareStateStore",
282
+ stage: localStage,
283
+ });
284
+ return httpState;
285
+ }).pipe(Effect.withSpan("state_store.finish_bootstrap", {
286
+ attributes: {
287
+ "alchemy.state_store.op": "finish_bootstrap",
288
+ "alchemy.state_store.ci": isCI,
289
+ },
290
+ }));
291
+ // check if there's a local stack that wasn't properly hoisted
292
+ const hasLocalStack = (stage) => Effect.gen(function* () {
293
+ const localState = yield* makeLocalState();
294
+ return yield* Effect.map(localState.listStages("CloudflareStateStore"),
295
+ // key off the profile name to avoid conflicts with other profiles
296
+ (stages) => stages.includes(stage));
297
+ });
298
+ /**
299
+ * Non-destructively copy every resource in the
300
+ * `CloudflareStateStore/<scriptName>` stack from `source` into
301
+ * `destination`, leaving every other stack in `destination` untouched.
302
+ *
303
+ * This intentionally does not delete anything from `destination`: at
304
+ * bootstrap time the destination is the user's live remote state
305
+ * store, and removing entries that happen to be missing locally would
306
+ * be catastrophic.
307
+ */
308
+ const hoistBootstrapStack = Effect.fnUntraced(function* ({ source, destination, }) {
309
+ const stack = "CloudflareStateStore";
310
+ const fqns = yield* source.state.list({ stack, stage: source.stage });
311
+ yield* Effect.annotateCurrentSpan({
312
+ "alchemy.state_store.stack": stack,
313
+ "alchemy.state_store.stage": source.stage,
314
+ "alchemy.state_store.resources.count": fqns.length,
315
+ });
316
+ yield* Effect.forEach(fqns, Effect.fnUntraced(function* (fqn) {
317
+ const value = yield* source.state.get({
318
+ stack,
319
+ stage: source.stage,
320
+ fqn,
321
+ });
322
+ if (value) {
323
+ yield* destination.state
324
+ .set({
325
+ stack,
326
+ stage: destination.stage,
327
+ fqn,
328
+ value,
329
+ })
330
+ .pipe(Effect.retry({
331
+ while: (error) => isHttpClientError(error.cause) &&
332
+ // worker can 404 for a bit on first deploy, retry these
333
+ error.cause.response?.status === 404,
334
+ schedule: Schedule.fixed(200),
335
+ }));
336
+ }
337
+ }), { concurrency: "unbounded" });
338
+ }, Effect.withSpan("state_store.hoist_bootstrap_stack"));
381
339
  /**
382
340
  * Log in to a Cloudflare-deployed HTTP state-store.
383
341
  *
@@ -394,10 +352,17 @@ const deployStateStore = (scriptName, state) => Effect.gen(function* () {
394
352
  * `CloudflareEnvironment`, `Credentials`, `HttpClient`, and
395
353
  * `FileSystem`.
396
354
  */
397
- export const loginWithCloudflare = () => Effect.gen(function* () {
398
- const isCI = yield* Config.boolean("CI").pipe(Config.withDefault(false));
399
- const profileName = yield* ALCHEMY_PROFILE;
355
+ export const loginWithCloudflare = (profileName, force) => Effect.gen(function* () {
356
+ const credStore = yield* CredentialsStore;
357
+ const isCI = yield* CI;
400
358
  const { accountId } = yield* CloudflareEnvironment.CloudflareEnvironment;
359
+ if (!force) {
360
+ // try and read from the cached credentials first if not forcing (force will always refresh)
361
+ const credentials = yield* credStore.read(profileName, CREDENTIALS_FILE);
362
+ if (credentials) {
363
+ return credentials;
364
+ }
365
+ }
401
366
  // 1. Locate the single Secrets Store on the account.
402
367
  const stores = yield* SecretsStore.listStores({ accountId });
403
368
  const store = stores.result[0];
@@ -406,23 +371,17 @@ export const loginWithCloudflare = () => Effect.gen(function* () {
406
371
  message: "No Secrets Store found on this account. Deploy the state store first.",
407
372
  }));
408
373
  }
409
- // 2. Fetch the auth-token value via an edge-preview worker.
410
- // We piggy-back on the already-deployed state-store script so the
411
- // `cf-workers-preview-token` header has a real workers.dev route to
412
- // swap onto. Uploading the probe under a brand-new script name fails
413
- // on accounts where that script has never been deployed (or where
414
- // workers.dev preview URLs are off by default — the post-2024
415
- // Cloudflare default for new accounts) because the host doesn't
416
- // resolve and Cloudflare's edge serves a 400 HTML error page
417
- // instead of routing to the preview.
418
- const authToken = yield* readSecretViaEdge(STATE_STORE_SCRIPT_NAME, store.id, AuthTokenSecretName);
374
+ // 2. Fetch the auth-token from Secrets Store with a temporary edge-preview worker
375
+ const authToken = yield* readSecretViaEdge(STATE_STORE_SCRIPT_NAME, store.id, AuthTokenSecretName).pipe(Effect.retry({
376
+ while: isWorkersPreviewConfigurationError,
377
+ schedule: Schedule.exponential(200).pipe(Schedule.both(Schedule.recurs(15))),
378
+ }));
419
379
  // 3. Derive the deployed worker URL.
420
380
  const { subdomain } = yield* workers.getSubdomain({ accountId });
421
381
  const url = `https://${STATE_STORE_SCRIPT_NAME}.${subdomain}.workers.dev`;
422
382
  if (!isCI) {
423
383
  // 4. Persist credentials. The profile entry is managed by
424
384
  // `loadOrConfigure` when this is invoked through `configure`.
425
- const credStore = yield* CredentialsStore;
426
385
  yield* credStore
427
386
  .write(profileName, CREDENTIALS_FILE, {
428
387
  url,
@@ -448,57 +407,21 @@ export const loginWithCloudflare = () => Effect.gen(function* () {
448
407
  "alchemy.state_store.script_name": STATE_STORE_SCRIPT_NAME,
449
408
  },
450
409
  }));
451
- /**
452
- * Run {@link finishBootstrap} iff the worker at `url` fails the
453
- * version probe; otherwise resolve to `undefined`. Centralises the
454
- * "is the deployed worker still compatible?" guard used by every
455
- * code path that already has a presumably-valid worker URL.
456
- */
457
- const redeployIfStale = ({ url, scriptName, profileName, localState, isCI, }) => Effect.gen(function* () {
458
- const { matches, expected, observed } = yield* checkStateStoreVersion(url);
459
- if (matches)
460
- return undefined;
461
- yield* Clank.info(`Cloudflare State Store '${scriptName}' is out of date ` +
462
- `(expected v${expected}, observed v${observed ?? "unknown"}); redeploying...`);
463
- return yield* finishBootstrap({
464
- scriptName,
465
- profileName,
466
- localState,
467
- isCI,
410
+ const isStateStoreAvailable = (scriptName = "alchemy-state-store") => Effect.gen(function* () {
411
+ // otherwise, the remote one might exist
412
+ const { accountId } = yield* CloudflareEnvironment.CloudflareEnvironment;
413
+ return yield* workers.getScriptSetting({ accountId, scriptName }).pipe(Effect.map((setting) => setting !== undefined), Effect.catchTag("WorkerNotFound", () => Effect.succeed(false)));
414
+ });
415
+ const makeCloudflareStateStore = Effect.fnUntraced(function* ({ url, authToken, }) {
416
+ const access = yield* Access.Access;
417
+ const accessHeaders = yield* access.getAccessHeaders(new URL(url).host);
418
+ return yield* makeHttpStateStore({
419
+ url,
420
+ authToken,
421
+ transformClient: HttpClientRequest.setHeaders(accessHeaders),
422
+ id: "cloudflare-http",
468
423
  });
469
- }).pipe(Effect.withSpan("state_store.redeploy_if_stale", {
470
- attributes: {
471
- "alchemy.state_store.op": "redeploy_if_stale",
472
- "alchemy.state_store.script_name": scriptName,
473
- "alchemy.state_store.url": url,
474
- },
475
- }));
476
- /**
477
- * Probe the deployed worker's `/version` endpoint and decide whether
478
- * it satisfies the current {@link STATE_STORE_VERSION} contract.
479
- *
480
- * Reuses the same typed {@link StateApi} client the rest of the store
481
- * speaks, just without the bearer-token transform — the `/version`
482
- * route is intentionally unauthenticated so we can probe it before
483
- * trusting any locally-cached credentials.
484
- *
485
- * Returns `true` only on a successful response carrying the matching
486
- * version. Any failure (404 from a pre-`/version` deploy, transport
487
- * error, schema mismatch, version mismatch) collapses to `false`,
488
- * since the caller's response in every case is the same: fall
489
- * through to the idempotent bootstrap flow.
490
- */
491
- /**
492
- * Block until the worker at `url` reports the {@link STATE_STORE_VERSION}
493
- * this CLI was built against, with bounded exponential retry. Used
494
- * post-deploy to wait out Cloudflare's edge cache so any subsequent
495
- * read or write goes to the new worker.
496
- *
497
- * Failures (transport errors, 404 from a pre-`/version` build, schema
498
- * mismatch, version mismatch) all collapse to "not ready yet" and are
499
- * retried; once the budget is exhausted we surface a hard failure
500
- * since continuing would talk to the wrong worker.
501
- */
424
+ });
502
425
  class StateStoreVersionNotReady extends Error {
503
426
  expected;
504
427
  observed;
@@ -529,13 +452,22 @@ const waitForStateStoreVersion = (url) => Effect.gen(function* () {
529
452
  }));
530
453
  const checkStateStoreVersion = (url) => Effect.gen(function* () {
531
454
  const client = yield* HttpApiClient.make(StateApi, { baseUrl: url });
455
+ const isAvailable = yield* Effect.cached(isStateStoreAvailable(STATE_STORE_SCRIPT_NAME));
532
456
  // The /version route may 404 transiently after a fresh deploy
533
457
  // while Cloudflare propagates the new script to the edge, and may
534
458
  // also surface transport-level blips on cold workers.dev hosts.
535
459
  // Retry the probe itself for ~10s before giving up — only after
536
460
  // exhausting that budget do we collapse to `undefined` and let
537
461
  // the caller treat it as a version mismatch.
538
- const result = yield* client.version.getVersion().pipe(Effect.retry({
462
+ const result = yield* client.version.getVersion().pipe(Effect.catchTag("HttpClientError", (e) =>
463
+ // if we get a 404 here, it means we assumed the worker shoudl exist, but it does not
464
+ // we should do a check to see if it does
465
+ e.response?.status === 404
466
+ ? isAvailable.pipe(Effect.flatMap((isAvailable) =>
467
+ // if the worker is available, then we should assume it was recently created and retry by propagating the error
468
+ // otherwise, return undefined (we don't know the version, there is no worker)
469
+ isAvailable ? Effect.fail(e) : Effect.succeed(undefined)))
470
+ : Effect.fail(e)), Effect.retry({
539
471
  schedule: Schedule.spaced("250 millis").pipe(Schedule.both(Schedule.recurs(40))),
540
472
  }), Effect.catch(() => Effect.succeed(undefined)));
541
473
  const matches = result?.version === STATE_STORE_VERSION;
@@ -618,4 +550,49 @@ const readSecretViaEdge = (scriptName, storeId, secretName) => Effect.gen(functi
618
550
  "alchemy.state_store.secret_name": secretName,
619
551
  },
620
552
  }));
553
+ const writeCredentials = (url, authToken) => Effect.gen(function* () {
554
+ const profileName = yield* ALCHEMY_PROFILE;
555
+ const credStore = yield* CredentialsStore;
556
+ yield* credStore.write(profileName, CREDENTIALS_FILE, {
557
+ url,
558
+ authToken,
559
+ });
560
+ });
561
+ const isWorkersPreviewConfigurationError = (error) => error instanceof EdgeSessionError &&
562
+ (error.message.includes("Invalid Workers Preview configuration") ||
563
+ error.message.includes("Error 1031"));
564
+ /**
565
+ * SHA-256 hex digest of the Cloudflare account ID. Used as a stable
566
+ * pseudonymous identifier on telemetry spans so the dashboard can
567
+ * count distinct state-store deployments without leaking the raw
568
+ * accountId. Mirrors the `alchemy.git.origin_hash` pattern in
569
+ * `Telemetry/Attributes.ts`.
570
+ */
571
+ const hashAccountId = (accountId) => Effect.sync(() => crypto.createHash("sha256").update(accountId).digest("hex"));
572
+ /**
573
+ * Best-effort Cloudflare-account-hash annotation on the current span.
574
+ * Resolves the accountId from {@link CloudflareEnvironment} and
575
+ * attaches `alchemy.cloudflare.account_hash` to whichever span is
576
+ * active. Silently no-ops if the environment isn't resolvable so
577
+ * State-store layer construction still succeeds in degraded paths.
578
+ *
579
+ * `noTrack` controls whether the hash is attached:
580
+ * - `true` — never annotate (caller-level opt-out).
581
+ * - `false` — always annotate, regardless of env.
582
+ * - `undefined` — fall back to the `NO_TRACK` env var; default off.
583
+ */
584
+ const annotateAccountHash = (noTrack) => Effect.gen(function* () {
585
+ if (noTrack === true)
586
+ return;
587
+ if (noTrack === undefined) {
588
+ const fromEnv = yield* Config.boolean("NO_TRACK").pipe(Config.withDefault(false));
589
+ if (fromEnv)
590
+ return;
591
+ }
592
+ const env = yield* Effect.serviceOption(CloudflareEnvironment.CloudflareEnvironment);
593
+ if (env._tag !== "Some")
594
+ return;
595
+ const hash = yield* hashAccountId(env.value.accountId);
596
+ yield* Effect.annotateCurrentSpan("alchemy.cloudflare.account_hash", hash);
597
+ }).pipe(Effect.catch(() => Effect.void));
621
598
  //# sourceMappingURL=State.js.map