alchemy 2.0.0-beta.46 → 2.0.0-beta.48

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 (295) hide show
  1. package/bin/exec.js +18 -10
  2. package/bin/exec.js.map +1 -1
  3. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  4. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  5. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  6. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  7. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  8. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  9. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  10. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  11. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  12. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  13. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  14. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  15. package/lib/Apply.js +2 -2
  16. package/lib/Apply.js.map +1 -1
  17. package/lib/Cli/commands/_shared.d.ts +7 -1
  18. package/lib/Cli/commands/_shared.d.ts.map +1 -1
  19. package/lib/Cli/commands/_shared.js +1 -0
  20. package/lib/Cli/commands/_shared.js.map +1 -1
  21. package/lib/Cli/commands/cloudflare.d.ts +4 -1
  22. package/lib/Cli/commands/cloudflare.d.ts.map +1 -1
  23. package/lib/Cli/commands/cloudflare.js +274 -1
  24. package/lib/Cli/commands/cloudflare.js.map +1 -1
  25. package/lib/Cli/commands/login.d.ts +1 -1
  26. package/lib/Cli/commands/login.d.ts.map +1 -1
  27. package/lib/Cli/commands/login.js +45 -42
  28. package/lib/Cli/commands/login.js.map +1 -1
  29. package/lib/Cli/commands/logs.js +1 -1
  30. package/lib/Cli/commands/logs.js.map +1 -1
  31. package/lib/Cli/commands/state.js +1 -1
  32. package/lib/Cli/commands/state.js.map +1 -1
  33. package/lib/Cli/commands/tail.js +1 -1
  34. package/lib/Cli/commands/tail.js.map +1 -1
  35. package/lib/Cloudflare/AiGateway/AiGateway.d.ts +112 -1
  36. package/lib/Cloudflare/AiGateway/AiGateway.d.ts.map +1 -1
  37. package/lib/Cloudflare/AiGateway/AiGateway.js +114 -2
  38. package/lib/Cloudflare/AiGateway/AiGateway.js.map +1 -1
  39. package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts +43 -10
  40. package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts.map +1 -1
  41. package/lib/Cloudflare/AiGateway/AiGatewayBinding.js +38 -10
  42. package/lib/Cloudflare/AiGateway/AiGatewayBinding.js.map +1 -1
  43. package/lib/Cloudflare/AiGateway/LanguageModel.d.ts +35 -0
  44. package/lib/Cloudflare/AiGateway/LanguageModel.d.ts.map +1 -0
  45. package/lib/Cloudflare/AiGateway/LanguageModel.js +576 -0
  46. package/lib/Cloudflare/AiGateway/LanguageModel.js.map +1 -0
  47. package/lib/Cloudflare/AiGateway/index.d.ts +1 -0
  48. package/lib/Cloudflare/AiGateway/index.d.ts.map +1 -1
  49. package/lib/Cloudflare/AiGateway/index.js +1 -0
  50. package/lib/Cloudflare/AiGateway/index.js.map +1 -1
  51. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +40 -2
  52. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
  53. package/lib/Cloudflare/ApiToken/AccountApiToken.js +49 -6
  54. package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
  55. package/lib/Cloudflare/ApiToken/Common.d.ts +26 -3
  56. package/lib/Cloudflare/ApiToken/Common.d.ts.map +1 -1
  57. package/lib/Cloudflare/ApiToken/Common.js +8 -0
  58. package/lib/Cloudflare/ApiToken/Common.js.map +1 -1
  59. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts +38 -2
  60. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -1
  61. package/lib/Cloudflare/ApiToken/UserApiToken.js +47 -6
  62. package/lib/Cloudflare/ApiToken/UserApiToken.js.map +1 -1
  63. package/lib/Cloudflare/Browser/Browser.d.ts +121 -0
  64. package/lib/Cloudflare/Browser/Browser.d.ts.map +1 -0
  65. package/lib/Cloudflare/{BrowserRendering/BrowserRendering.js → Browser/Browser.js} +19 -26
  66. package/lib/Cloudflare/Browser/Browser.js.map +1 -0
  67. package/lib/Cloudflare/Browser/BrowserBinding.d.ts +55 -0
  68. package/lib/Cloudflare/Browser/BrowserBinding.d.ts.map +1 -0
  69. package/lib/Cloudflare/{BrowserRendering/BrowserRenderingBinding.js → Browser/BrowserBinding.js} +11 -11
  70. package/lib/Cloudflare/Browser/BrowserBinding.js.map +1 -0
  71. package/lib/Cloudflare/Browser/index.d.ts +3 -0
  72. package/lib/Cloudflare/Browser/index.d.ts.map +1 -0
  73. package/lib/Cloudflare/Browser/index.js +3 -0
  74. package/lib/Cloudflare/Browser/index.js.map +1 -0
  75. package/lib/Cloudflare/Container/ContainerBinding.d.ts +2 -1
  76. package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
  77. package/lib/Cloudflare/D1/D1Database.d.ts.map +1 -1
  78. package/lib/Cloudflare/D1/D1Database.js +2 -1
  79. package/lib/Cloudflare/D1/D1Database.js.map +1 -1
  80. package/lib/Cloudflare/Email/EmailRouting.d.ts +1 -1
  81. package/lib/Cloudflare/Email/EmailRouting.d.ts.map +1 -1
  82. package/lib/Cloudflare/Email/EmailRouting.js +1 -1
  83. package/lib/Cloudflare/Email/EmailRouting.js.map +1 -1
  84. package/lib/Cloudflare/Email/EmailRule.d.ts +1 -1
  85. package/lib/Cloudflare/Email/EmailRule.d.ts.map +1 -1
  86. package/lib/Cloudflare/Email/EmailRule.js +1 -1
  87. package/lib/Cloudflare/Email/EmailRule.js.map +1 -1
  88. package/lib/Cloudflare/Images/Images.d.ts +32 -23
  89. package/lib/Cloudflare/Images/Images.d.ts.map +1 -1
  90. package/lib/Cloudflare/Images/Images.js +10 -13
  91. package/lib/Cloudflare/Images/Images.js.map +1 -1
  92. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  93. package/lib/Cloudflare/Providers.js +10 -3
  94. package/lib/Cloudflare/Providers.js.map +1 -1
  95. package/lib/Cloudflare/R2/R2Bucket.d.ts +1 -1
  96. package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
  97. package/lib/Cloudflare/R2/R2Bucket.js +13 -7
  98. package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
  99. package/lib/Cloudflare/RateLimit/RateLimit.d.ts +132 -0
  100. package/lib/Cloudflare/RateLimit/RateLimit.d.ts.map +1 -0
  101. package/lib/Cloudflare/RateLimit/RateLimit.js +80 -0
  102. package/lib/Cloudflare/RateLimit/RateLimit.js.map +1 -0
  103. package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts +30 -0
  104. package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts.map +1 -0
  105. package/lib/Cloudflare/RateLimit/RateLimitBinding.js +50 -0
  106. package/lib/Cloudflare/RateLimit/RateLimitBinding.js.map +1 -0
  107. package/lib/Cloudflare/RateLimit/index.d.ts +3 -0
  108. package/lib/Cloudflare/RateLimit/index.d.ts.map +1 -0
  109. package/lib/Cloudflare/RateLimit/index.js +3 -0
  110. package/lib/Cloudflare/RateLimit/index.js.map +1 -0
  111. package/lib/Cloudflare/SecretsStore/Secret.d.ts +2 -1
  112. package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
  113. package/lib/Cloudflare/SecretsStore/Secret.js +7 -4
  114. package/lib/Cloudflare/SecretsStore/Secret.js.map +1 -1
  115. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  116. package/lib/Cloudflare/StateStore/State.js +82 -78
  117. package/lib/Cloudflare/StateStore/State.js.map +1 -1
  118. package/lib/Cloudflare/Tunnel/Tunnel.d.ts +27 -0
  119. package/lib/Cloudflare/Tunnel/Tunnel.d.ts.map +1 -1
  120. package/lib/Cloudflare/Tunnel/Tunnel.js +27 -0
  121. package/lib/Cloudflare/Tunnel/Tunnel.js.map +1 -1
  122. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts +48 -0
  123. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts.map +1 -0
  124. package/lib/Cloudflare/Tunnel/TunnelBinding.js +56 -0
  125. package/lib/Cloudflare/Tunnel/TunnelBinding.js.map +1 -0
  126. package/lib/Cloudflare/Tunnel/TunnelRead.d.ts +83 -0
  127. package/lib/Cloudflare/Tunnel/TunnelRead.d.ts.map +1 -0
  128. package/lib/Cloudflare/Tunnel/TunnelRead.js +79 -0
  129. package/lib/Cloudflare/Tunnel/TunnelRead.js.map +1 -0
  130. package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts +70 -0
  131. package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts.map +1 -0
  132. package/lib/Cloudflare/Tunnel/TunnelReadWrite.js +63 -0
  133. package/lib/Cloudflare/Tunnel/TunnelReadWrite.js.map +1 -0
  134. package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts +92 -0
  135. package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts.map +1 -0
  136. package/lib/Cloudflare/Tunnel/TunnelWrite.js +88 -0
  137. package/lib/Cloudflare/Tunnel/TunnelWrite.js.map +1 -0
  138. package/lib/Cloudflare/Tunnel/index.d.ts +4 -0
  139. package/lib/Cloudflare/Tunnel/index.d.ts.map +1 -1
  140. package/lib/Cloudflare/Tunnel/index.js +4 -0
  141. package/lib/Cloudflare/Tunnel/index.js.map +1 -1
  142. package/lib/Cloudflare/Vectorize/VectorizeIndex.js +1 -0
  143. package/lib/Cloudflare/Vectorize/VectorizeIndex.js.map +1 -1
  144. package/lib/Cloudflare/VpcService/VpcService.d.ts +1 -1
  145. package/lib/Cloudflare/VpcService/VpcService.d.ts.map +1 -1
  146. package/lib/Cloudflare/VpcService/VpcService.js.map +1 -1
  147. package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
  148. package/lib/Cloudflare/Workers/DurableObjectBridge.js +10 -1
  149. package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
  150. package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts +119 -11
  151. package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts.map +1 -1
  152. package/lib/Cloudflare/Workers/DynamicWorkerLoader.js +88 -9
  153. package/lib/Cloudflare/Workers/DynamicWorkerLoader.js.map +1 -1
  154. package/lib/Cloudflare/Workers/InferEnv.d.ts +1 -1
  155. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  156. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  157. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +15 -1
  158. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  159. package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
  160. package/lib/Cloudflare/Workers/Rpc.js +20 -2
  161. package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
  162. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  163. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +18 -2
  164. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  165. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +5 -3
  166. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  167. package/lib/Cloudflare/Workers/WorkerBinding.js +1 -1
  168. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  169. package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
  170. package/lib/Cloudflare/Workers/WorkerBridge.js +20 -2
  171. package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
  172. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts +1 -1
  173. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -1
  174. package/lib/Cloudflare/Zaraz/ZarazConfig.js +1 -1
  175. package/lib/Cloudflare/Zaraz/ZarazConfig.js.map +1 -1
  176. package/lib/Cloudflare/Zone/Zone.d.ts +170 -0
  177. package/lib/Cloudflare/Zone/Zone.d.ts.map +1 -0
  178. package/lib/Cloudflare/Zone/Zone.js +180 -0
  179. package/lib/Cloudflare/Zone/Zone.js.map +1 -0
  180. package/lib/Cloudflare/Zone/index.d.ts +3 -0
  181. package/lib/Cloudflare/Zone/index.d.ts.map +1 -0
  182. package/lib/Cloudflare/Zone/index.js +3 -0
  183. package/lib/Cloudflare/Zone/index.js.map +1 -0
  184. package/lib/Cloudflare/Zone/lookup.d.ts +33 -0
  185. package/lib/Cloudflare/Zone/lookup.d.ts.map +1 -0
  186. package/lib/Cloudflare/{Zone.js → Zone/lookup.js} +2 -2
  187. package/lib/Cloudflare/Zone/lookup.js.map +1 -0
  188. package/lib/Cloudflare/index.d.ts +3 -1
  189. package/lib/Cloudflare/index.d.ts.map +1 -1
  190. package/lib/Cloudflare/index.js +3 -1
  191. package/lib/Cloudflare/index.js.map +1 -1
  192. package/lib/Output.js +2 -2
  193. package/lib/Output.js.map +1 -1
  194. package/lib/Plan.d.ts +2 -2
  195. package/lib/Plan.d.ts.map +1 -1
  196. package/lib/Plan.js +3 -3
  197. package/lib/Plan.js.map +1 -1
  198. package/lib/Platform.d.ts +5 -5
  199. package/lib/Platform.d.ts.map +1 -1
  200. package/lib/Platform.js.map +1 -1
  201. package/lib/RemovalPolicy.js +2 -2
  202. package/lib/RemovalPolicy.js.map +1 -1
  203. package/lib/Resource.d.ts +14 -1
  204. package/lib/Resource.d.ts.map +1 -1
  205. package/lib/Resource.js +3 -2
  206. package/lib/Resource.js.map +1 -1
  207. package/lib/Stack.d.ts.map +1 -1
  208. package/lib/Stack.js +6 -0
  209. package/lib/Stack.js.map +1 -1
  210. package/lib/State/InMemoryState.d.ts +2 -1
  211. package/lib/State/InMemoryState.d.ts.map +1 -1
  212. package/lib/State/InMemoryState.js +3 -3
  213. package/lib/State/InMemoryState.js.map +1 -1
  214. package/lib/State/LocalState.d.ts.map +1 -1
  215. package/lib/State/LocalState.js +5 -1
  216. package/lib/State/LocalState.js.map +1 -1
  217. package/lib/State/State.d.ts +2 -2
  218. package/lib/State/State.d.ts.map +1 -1
  219. package/lib/State/State.js.map +1 -1
  220. package/lib/tsconfig.test.tsbuildinfo +1 -1
  221. package/package.json +7 -7
  222. package/src/Apply.ts +2 -2
  223. package/src/Cli/commands/_shared.ts +7 -1
  224. package/src/Cli/commands/cloudflare.ts +394 -1
  225. package/src/Cli/commands/login.ts +69 -60
  226. package/src/Cli/commands/logs.ts +1 -1
  227. package/src/Cli/commands/state.ts +1 -1
  228. package/src/Cli/commands/tail.ts +1 -1
  229. package/src/Cloudflare/AiGateway/AiGateway.ts +114 -2
  230. package/src/Cloudflare/AiGateway/AiGatewayBinding.ts +52 -10
  231. package/src/Cloudflare/AiGateway/LanguageModel.ts +907 -0
  232. package/src/Cloudflare/AiGateway/index.ts +1 -0
  233. package/src/Cloudflare/ApiToken/AccountApiToken.ts +60 -7
  234. package/src/Cloudflare/ApiToken/Common.ts +32 -3
  235. package/src/Cloudflare/ApiToken/UserApiToken.ts +58 -7
  236. package/src/Cloudflare/Browser/Browser.ts +140 -0
  237. package/src/Cloudflare/Browser/BrowserBinding.ts +128 -0
  238. package/src/Cloudflare/Browser/index.ts +11 -0
  239. package/src/Cloudflare/D1/D1Database.ts +6 -2
  240. package/src/Cloudflare/Email/EmailRouting.ts +1 -1
  241. package/src/Cloudflare/Email/EmailRule.ts +6 -6
  242. package/src/Cloudflare/Images/Images.ts +40 -27
  243. package/src/Cloudflare/Providers.ts +15 -3
  244. package/src/Cloudflare/R2/R2Bucket.ts +17 -9
  245. package/src/Cloudflare/RateLimit/RateLimit.ts +160 -0
  246. package/src/Cloudflare/RateLimit/RateLimitBinding.ts +90 -0
  247. package/src/Cloudflare/RateLimit/index.ts +2 -0
  248. package/src/Cloudflare/SecretsStore/Secret.ts +12 -6
  249. package/src/Cloudflare/StateStore/State.ts +113 -107
  250. package/src/Cloudflare/Tunnel/Tunnel.ts +27 -0
  251. package/src/Cloudflare/Tunnel/TunnelBinding.ts +114 -0
  252. package/src/Cloudflare/Tunnel/TunnelRead.ts +168 -0
  253. package/src/Cloudflare/Tunnel/TunnelReadWrite.ts +88 -0
  254. package/src/Cloudflare/Tunnel/TunnelWrite.ts +194 -0
  255. package/src/Cloudflare/Tunnel/index.ts +4 -0
  256. package/src/Cloudflare/Vectorize/VectorizeIndex.ts +2 -1
  257. package/src/Cloudflare/VpcService/VpcService.ts +4 -2
  258. package/src/Cloudflare/Workers/DurableObjectBridge.ts +10 -1
  259. package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +139 -13
  260. package/src/Cloudflare/Workers/InferEnv.ts +19 -15
  261. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +26 -3
  262. package/src/Cloudflare/Workers/Rpc.ts +38 -9
  263. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +16 -2
  264. package/src/Cloudflare/Workers/WorkerBinding.ts +6 -2
  265. package/src/Cloudflare/Workers/WorkerBridge.ts +20 -2
  266. package/src/Cloudflare/Zaraz/ZarazConfig.ts +1 -1
  267. package/src/Cloudflare/Zone/Zone.ts +349 -0
  268. package/src/Cloudflare/Zone/index.ts +2 -0
  269. package/src/Cloudflare/{Zone.ts → Zone/lookup.ts} +16 -3
  270. package/src/Cloudflare/index.ts +3 -1
  271. package/src/Output.ts +2 -2
  272. package/src/Plan.ts +4 -4
  273. package/src/Platform.ts +8 -12
  274. package/src/RemovalPolicy.ts +2 -2
  275. package/src/Resource.ts +18 -1
  276. package/src/Stack.ts +6 -0
  277. package/src/State/InMemoryState.ts +87 -85
  278. package/src/State/LocalState.ts +15 -1
  279. package/src/State/State.ts +5 -4
  280. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts +0 -116
  281. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts.map +0 -1
  282. package/lib/Cloudflare/BrowserRendering/BrowserRendering.js.map +0 -1
  283. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts +0 -55
  284. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts.map +0 -1
  285. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.js.map +0 -1
  286. package/lib/Cloudflare/BrowserRendering/index.d.ts +0 -3
  287. package/lib/Cloudflare/BrowserRendering/index.d.ts.map +0 -1
  288. package/lib/Cloudflare/BrowserRendering/index.js +0 -3
  289. package/lib/Cloudflare/BrowserRendering/index.js.map +0 -1
  290. package/lib/Cloudflare/Zone.d.ts +0 -14
  291. package/lib/Cloudflare/Zone.d.ts.map +0 -1
  292. package/lib/Cloudflare/Zone.js.map +0 -1
  293. package/src/Cloudflare/BrowserRendering/BrowserRendering.ts +0 -132
  294. package/src/Cloudflare/BrowserRendering/BrowserRenderingBinding.ts +0 -137
  295. package/src/Cloudflare/BrowserRendering/index.ts +0 -11
@@ -57,124 +57,130 @@ export const state = () =>
57
57
  const profileName = yield* ALCHEMY_PROFILE;
58
58
  const localStage = `${profileName}_${scriptName}`;
59
59
  const credStore = yield* CredentialsStore;
60
- if (yield* hasLocalStack(localStage)) {
61
- // if there's still a local stack, then we need to finish the bootstrap
62
- // TODO(sam): what if the local stack was
63
- return yield* deployWithLocalState({
64
- scriptName,
65
- profileName,
66
- isCI,
67
- force: false,
68
- });
69
- }
70
-
71
- const ensureLatest = ({
72
- url,
73
- authToken,
74
- }: {
75
- url: string;
76
- authToken: string;
77
- }) =>
78
- Effect.gen(function* () {
79
- const { matches, expected, observed } =
80
- yield* checkStateStoreVersion(url);
81
-
82
- if (observed === undefined) {
83
- const shouldDeploy = yield* Clank.confirm({
84
- message: `Cloudflare State Store '${scriptName}' is not available. Do you want to deploy it?`,
85
- });
86
- if (shouldDeploy) {
87
- return yield* bootstrap({
88
- workerName: scriptName,
89
- profile: profileName,
90
- });
91
- } else {
92
- return yield* Effect.die(new Clank.PromptCancelled());
93
- }
94
- }
60
+ const context = yield* Effect.context<Effect.Services<typeof init>>();
61
+
62
+ const init = Effect.gen(function* () {
63
+ if (yield* hasLocalStack(localStage)) {
64
+ // if there's still a local stack, then we need to finish the bootstrap
65
+ // TODO(sam): what if the local stack was
66
+ return yield* deployWithLocalState({
67
+ scriptName,
68
+ profileName,
69
+ isCI,
70
+ force: false,
71
+ });
72
+ }
95
73
 
96
- const httpState = yield* ensureAccess({ url, authToken });
97
- if (matches) {
98
- return httpState;
99
- } else if (isCI) {
100
- return yield* Effect.die(
101
- new AuthError({
102
- message: `Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile <your-ci-profile>' to deploy it first.`,
103
- }),
104
- );
105
- } else {
106
- const shouldDeploy = yield* Clank.confirm({
107
- message:
108
- `Cloudflare State Store '${scriptName}' is out of date ` +
109
- `(expected v${expected}, observed v${observed ?? "unknown"})`,
110
- });
111
- if (shouldDeploy) {
112
- const stateStoreOptions = yield* deployStateStore({
113
- stage: scriptName,
114
- state: httpState,
115
- force: false,
74
+ const ensureLatest = ({
75
+ url,
76
+ authToken,
77
+ }: {
78
+ url: string;
79
+ authToken: string;
80
+ }) =>
81
+ Effect.gen(function* () {
82
+ const { matches, expected, observed } =
83
+ yield* checkStateStoreVersion(url);
84
+
85
+ if (observed === undefined) {
86
+ const shouldDeploy = yield* Clank.confirm({
87
+ message: `Cloudflare State Store '${scriptName}' is not available. Do you want to deploy it?`,
116
88
  });
117
- return yield* makeCloudflareStateStore(stateStoreOptions);
118
- } else {
119
- return yield* Effect.die(new Clank.PromptCancelled());
89
+ if (shouldDeploy) {
90
+ return yield* bootstrap({
91
+ workerName: scriptName,
92
+ profile: profileName,
93
+ });
94
+ } else {
95
+ return yield* Effect.die(new Clank.PromptCancelled());
96
+ }
120
97
  }
121
- }
122
- });
123
98
 
124
- const ensureAccess = (credentials: HttpStateStoreCredentials) =>
125
- Effect.gen(function* () {
126
- const isAuth = yield* checkHttpStateStoreAuth(credentials);
127
- if (!isAuth) {
128
- // our token is wrong, force a refresh
129
- yield* Clank.info(
130
- `Cloudflare State store authentication failed, refreshing credentials...`,
131
- );
132
- const credentials = yield* loginWithCloudflare(profileName, true);
133
- if (!(yield* checkHttpStateStoreAuth(credentials))) {
99
+ const httpState = yield* ensureAccess({ url, authToken });
100
+ if (matches) {
101
+ return httpState;
102
+ } else if (isCI) {
134
103
  return yield* Effect.die(
135
104
  new AuthError({
136
- message: `Cloudflare State store authentication failed, after refreshing credentials.`,
105
+ message: `Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile <your-ci-profile>' to deploy it first.`,
137
106
  }),
138
107
  );
108
+ } else {
109
+ const shouldDeploy = yield* Clank.confirm({
110
+ message:
111
+ `Cloudflare State Store '${scriptName}' is out of date ` +
112
+ `(expected v${expected}, observed v${observed ?? "unknown"})`,
113
+ });
114
+ if (shouldDeploy) {
115
+ const stateStoreOptions = yield* deployStateStore({
116
+ stage: scriptName,
117
+ state: httpState,
118
+ force: false,
119
+ });
120
+ return yield* makeCloudflareStateStore(stateStoreOptions);
121
+ } else {
122
+ return yield* Effect.die(new Clank.PromptCancelled());
123
+ }
124
+ }
125
+ });
126
+
127
+ const ensureAccess = (credentials: HttpStateStoreCredentials) =>
128
+ Effect.gen(function* () {
129
+ const isAuth = yield* checkHttpStateStoreAuth(credentials);
130
+ if (!isAuth) {
131
+ // our token is wrong, force a refresh
132
+ yield* Clank.info(
133
+ `Cloudflare State store authentication failed, refreshing credentials...`,
134
+ );
135
+ const credentials = yield* loginWithCloudflare(profileName, true);
136
+ if (!(yield* checkHttpStateStoreAuth(credentials))) {
137
+ return yield* Effect.die(
138
+ new AuthError({
139
+ message: `Cloudflare State store authentication failed, after refreshing credentials.`,
140
+ }),
141
+ );
142
+ }
143
+ return yield* makeCloudflareStateStore(credentials);
139
144
  }
140
145
  return yield* makeCloudflareStateStore(credentials);
141
- }
142
- return yield* makeCloudflareStateStore(credentials);
143
- });
146
+ });
144
147
 
145
- const credentials = yield* credStore.read<HttpStateStoreCredentials>(
146
- profileName,
147
- CREDENTIALS_FILE,
148
- );
149
- if (credentials) {
150
- return yield* ensureLatest(credentials);
151
- }
152
- const workerExists = yield* isStateStoreAvailable(scriptName);
153
- if (workerExists) {
154
- return yield* ensureLatest(
155
- yield* loginWithCloudflare(profileName, false),
156
- );
157
- } else if (isCI) {
158
- // TODO(sam): do we want to support bootstrapping the state store from CI?
159
- // for now - just die here
160
- return yield* Effect.die(
161
- new AuthError({
162
- message: `Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile <your-ci-profile>' to deploy it first.`,
163
- }),
164
- );
165
- } else {
166
- return yield* Clank.confirm({
167
- message:
168
- "Cloudflare State Store not found. Do you want to deploy it?",
169
- }).pipe(
170
- Effect.flatMap((shouldDeploy) =>
171
- shouldDeploy
172
- ? bootstrap()
173
- : Effect.die(new Clank.PromptCancelled()),
174
- ),
148
+ const credentials = yield* credStore.read<HttpStateStoreCredentials>(
149
+ profileName,
150
+ CREDENTIALS_FILE,
175
151
  );
176
- }
177
- }).pipe(recordStateStoreInit, Effect.orDie),
152
+ if (credentials) {
153
+ return yield* ensureLatest(credentials);
154
+ }
155
+ const workerExists = yield* isStateStoreAvailable(scriptName);
156
+ if (workerExists) {
157
+ return yield* ensureLatest(
158
+ yield* loginWithCloudflare(profileName, false),
159
+ );
160
+ } else if (isCI) {
161
+ // TODO(sam): do we want to support bootstrapping the state store from CI?
162
+ // for now - just die here
163
+ return yield* Effect.die(
164
+ new AuthError({
165
+ message: `Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile <your-ci-profile>' to deploy it first.`,
166
+ }),
167
+ );
168
+ } else {
169
+ return yield* Clank.confirm({
170
+ message:
171
+ "Cloudflare State Store not found. Do you want to deploy it?",
172
+ }).pipe(
173
+ Effect.flatMap((shouldDeploy) =>
174
+ shouldDeploy
175
+ ? bootstrap()
176
+ : Effect.die(new Clank.PromptCancelled()),
177
+ ),
178
+ );
179
+ }
180
+ }).pipe(recordStateStoreInit, Effect.orDie);
181
+
182
+ return yield* Effect.cached(init.pipe(Effect.provideContext(context)));
183
+ }),
178
184
  ).pipe(
179
185
  Layer.provideMerge(Credentials.fromAuthProvider()),
180
186
  Layer.provideMerge(CloudflareEnvironment.fromProfile()),
@@ -311,7 +317,7 @@ const deployStateStore = ({
311
317
  Effect.gen(function* () {
312
318
  yield* annotateAccountHash();
313
319
  // deploy it with local state (which we will then hoist into the Cloudflare state store)
314
- const stateLayer = Layer.succeed(State, state);
320
+ const stateLayer = Layer.succeed(State, Effect.succeed(state));
315
321
  const { url, authToken } = yield* deploy({
316
322
  // use the script name as the stage name (so the user can have multiple state stores)
317
323
  stage,
@@ -120,6 +120,33 @@ export type Tunnel = Resource<
120
120
  * ],
121
121
  * });
122
122
  * ```
123
+ *
124
+ * @section Managing Tunnels at Runtime
125
+ * The `Tunnel` resource manages a single, statically-declared tunnel as part of
126
+ * a stack. To create, read, update, or delete tunnels *on the fly* from inside
127
+ * a deployed Worker, bind one of the runtime tunnel clients instead. Each
128
+ * provisions a least-privilege {@link AccountApiToken} and injects it into the
129
+ * Worker:
130
+ *
131
+ * - {@link TunnelRead} — read-only (`get`, `list`, `getToken`,
132
+ * `getConfiguration`); scoped to `Cloudflare Tunnel Read`.
133
+ * - {@link TunnelWrite} — mutating (`create`, `update`, `delete`,
134
+ * `putConfiguration`); scoped to `Cloudflare Tunnel Write`.
135
+ * - {@link TunnelReadWrite} — the full CRUD surface; scoped to both.
136
+ *
137
+ * @example Create a tunnel on demand from a Worker
138
+ * ```typescript
139
+ * // init
140
+ * const tunnels = yield* Cloudflare.TunnelReadWrite.bind();
141
+ *
142
+ * return {
143
+ * fetch: Effect.gen(function* () {
144
+ * const tunnel = yield* tunnels.create({ name: "on-demand-tunnel" });
145
+ * const token = yield* tunnels.getToken(tunnel.id!);
146
+ * return HttpServerResponse.json({ id: tunnel.id, token });
147
+ * }),
148
+ * };
149
+ * ```
123
150
  */
124
151
  export const Tunnel = Resource<Tunnel>("Cloudflare.Tunnel");
125
152
 
@@ -0,0 +1,114 @@
1
+ import {
2
+ Credentials,
3
+ fromApiToken,
4
+ } from "@distilled.cloud/cloudflare/Credentials";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Layer from "effect/Layer";
7
+ import * as Redacted from "effect/Redacted";
8
+ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
9
+ import type { HttpClient } from "effect/unstable/http/HttpClient";
10
+ import type * as Binding from "../../Binding.ts";
11
+ import { RuntimeContext } from "../../RuntimeContext.ts";
12
+ import { AccountApiToken } from "../ApiToken/AccountApiToken.ts";
13
+ import type { ApiTokenPermissionGroupRef } from "../ApiToken/Common.ts";
14
+ import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
15
+ import { Worker } from "../Workers/Worker.ts";
16
+
17
+ /**
18
+ * Runtime accessors for a tunnel binding's token, obtained by binding the
19
+ * {@link AccountApiToken}'s outputs in the Worker's Init phase. Each accessor
20
+ * reads the value back from the Worker's environment at runtime.
21
+ */
22
+ export interface TunnelToken {
23
+ /** The token's plaintext value (injected as a `secret_text` binding). */
24
+ value: Effect.Effect<Redacted.Redacted<string>>;
25
+ /** The account id the token is scoped to. */
26
+ accountId: Effect.Effect<string>;
27
+ }
28
+
29
+ /**
30
+ * Bind an {@link AccountApiToken}'s outputs into the Worker so they can be read
31
+ * at runtime: `token.value` is injected as a `secret_text` binding and
32
+ * `token.accountId` as `plain_text`. Returns the {@link TunnelToken} accessors.
33
+ */
34
+ export const bindTunnelToken = (token: AccountApiToken) =>
35
+ Effect.gen(function* () {
36
+ const value = yield* token.value;
37
+ const accountId = yield* token.accountId;
38
+ return { value, accountId } satisfies TunnelToken;
39
+ });
40
+
41
+ /**
42
+ * Resolve credentials from a bound token and provide them (plus the
43
+ * fetch-based HTTP client) to a raw SDK operation.
44
+ */
45
+ export const authorizeWith =
46
+ (token: TunnelToken) =>
47
+ <A, E>(
48
+ eff: Effect.Effect<A, E, Credentials | HttpClient>,
49
+ ): Effect.Effect<A, E, RuntimeContext> =>
50
+ token.value.pipe(
51
+ Effect.flatMap((value) =>
52
+ eff.pipe(
53
+ Effect.provide(
54
+ fromApiToken({ apiToken: Redacted.value(value) }).pipe(
55
+ Layer.provideMerge(FetchHttpClient.layer),
56
+ ),
57
+ ),
58
+ ),
59
+ ),
60
+ );
61
+
62
+ /**
63
+ * Shared runtime body for a tunnel binding: create a scoped token, attach the
64
+ * (narrow) policy, bind the token's outputs into the Worker, then build the
65
+ * client. Pass the result to `Layer.effect(<Binding>, ...)`.
66
+ */
67
+ export const makeTunnelClient = <C>(
68
+ Policy: Binding.Policy<
69
+ any,
70
+ any,
71
+ (token: AccountApiToken) => Effect.Effect<void>
72
+ >,
73
+ makeClient: (token: TunnelToken) => C,
74
+ ) =>
75
+ Effect.gen(function* () {
76
+ const Token = yield* AccountApiToken;
77
+ const attach = yield* Policy;
78
+
79
+ return Effect.fn(function* () {
80
+ const ctx = yield* Worker;
81
+ const token = yield* Token(`${ctx.LogicalId}Token`);
82
+ yield* attach(token);
83
+ return makeClient(yield* bindTunnelToken(token));
84
+ });
85
+ });
86
+
87
+ /**
88
+ * Build the deploy-time policy layer for a tunnel binding: attach an allow
89
+ * policy with the given permission groups to the binding's token.
90
+ */
91
+ export const makeTunnelPolicyLive = <Self, Id extends string>(
92
+ Policy: Binding.Policy<
93
+ Self,
94
+ Id,
95
+ (token: AccountApiToken) => Effect.Effect<void>
96
+ >,
97
+ sid: string,
98
+ permissionGroups: ApiTokenPermissionGroupRef[],
99
+ ) =>
100
+ Policy.layer.effect(
101
+ Effect.gen(function* () {
102
+ const { accountId } = yield* CloudflareEnvironment;
103
+ return (_host, token) =>
104
+ token.bind(sid, {
105
+ policies: [
106
+ {
107
+ effect: "allow",
108
+ permissionGroups,
109
+ resources: { [`com.cloudflare.api.account.${accountId}`]: "*" },
110
+ },
111
+ ],
112
+ });
113
+ }),
114
+ );
@@ -0,0 +1,168 @@
1
+ import type {
2
+ GetTunnelCloudflaredConfigurationError,
3
+ GetTunnelCloudflaredConfigurationResponse,
4
+ GetTunnelCloudflaredError,
5
+ GetTunnelCloudflaredResponse,
6
+ GetTunnelCloudflaredTokenError,
7
+ GetTunnelCloudflaredTokenResponse,
8
+ ListTunnelCloudflaredsError,
9
+ ListTunnelCloudflaredsRequest,
10
+ ListTunnelCloudflaredsResponse,
11
+ } from "@distilled.cloud/cloudflare/zero-trust";
12
+ import * as zeroTrust from "@distilled.cloud/cloudflare/zero-trust";
13
+ import * as Effect from "effect/Effect";
14
+ import * as Layer from "effect/Layer";
15
+ import * as Binding from "../../Binding.ts";
16
+ import type { RuntimeContext } from "../../RuntimeContext.ts";
17
+ import type { AccountApiToken } from "../ApiToken/AccountApiToken.ts";
18
+ import type { Worker } from "../Workers/Worker.ts";
19
+ import {
20
+ authorizeWith,
21
+ makeTunnelClient,
22
+ makeTunnelPolicyLive,
23
+ type TunnelToken,
24
+ } from "./TunnelBinding.ts";
25
+
26
+ /** List-tunnels request, minus the account id (supplied by the binding). */
27
+ export type ListTunnelsRequest = Omit<
28
+ ListTunnelCloudflaredsRequest,
29
+ "accountId"
30
+ >;
31
+
32
+ /**
33
+ * Read-only tunnel operations. Backed by the `Cloudflare Tunnel Read`
34
+ * permission group.
35
+ */
36
+ export interface TunnelReadClient {
37
+ /** Fetch a single tunnel by id. */
38
+ get(
39
+ tunnelId: string,
40
+ ): Effect.Effect<
41
+ GetTunnelCloudflaredResponse,
42
+ GetTunnelCloudflaredError,
43
+ RuntimeContext
44
+ >;
45
+ /** List tunnels in the account. */
46
+ list(
47
+ request?: ListTunnelsRequest,
48
+ ): Effect.Effect<
49
+ ListTunnelCloudflaredsResponse,
50
+ ListTunnelCloudflaredsError,
51
+ RuntimeContext
52
+ >;
53
+ /** Fetch the connector token used to run the tunnel. */
54
+ getToken(
55
+ tunnelId: string,
56
+ ): Effect.Effect<
57
+ GetTunnelCloudflaredTokenResponse,
58
+ GetTunnelCloudflaredTokenError,
59
+ RuntimeContext
60
+ >;
61
+ /** Read the remotely-managed configuration (ingress rules) for a tunnel. */
62
+ getConfiguration(
63
+ tunnelId: string,
64
+ ): Effect.Effect<
65
+ GetTunnelCloudflaredConfigurationResponse,
66
+ GetTunnelCloudflaredConfigurationError,
67
+ RuntimeContext
68
+ >;
69
+ }
70
+
71
+ /** Build the read-only client over a bound token. */
72
+ export const readClient = (token: TunnelToken): TunnelReadClient => {
73
+ const authorize = authorizeWith(token);
74
+ return {
75
+ get: Effect.fn("Cloudflare.Tunnel.get")(function* (tunnelId) {
76
+ const accountId = yield* token.accountId;
77
+ return yield* authorize(
78
+ zeroTrust.getTunnelCloudflared({ accountId, tunnelId }),
79
+ );
80
+ }),
81
+ list: Effect.fn("Cloudflare.Tunnel.list")(function* (request) {
82
+ const accountId = yield* token.accountId;
83
+ return yield* authorize(
84
+ zeroTrust.listTunnelCloudflareds({ accountId, ...request }),
85
+ );
86
+ }),
87
+ getToken: Effect.fn("Cloudflare.Tunnel.getToken")(function* (tunnelId) {
88
+ const accountId = yield* token.accountId;
89
+ return yield* authorize(
90
+ zeroTrust.getTunnelCloudflaredToken({ accountId, tunnelId }),
91
+ );
92
+ }),
93
+ getConfiguration: Effect.fn("Cloudflare.Tunnel.getConfiguration")(
94
+ function* (tunnelId) {
95
+ const accountId = yield* token.accountId;
96
+ return yield* authorize(
97
+ zeroTrust.getTunnelCloudflaredConfiguration({ accountId, tunnelId }),
98
+ );
99
+ },
100
+ ),
101
+ };
102
+ };
103
+
104
+ /**
105
+ * Binding that lets a Worker read Cloudflare Tunnels at runtime.
106
+ *
107
+ * Creates a scoped {@link AccountApiToken} with only the `Cloudflare Tunnel
108
+ * Read` permission and binds its outputs into the Worker (the token value as a
109
+ * `secret_text` binding) so runtime code can authenticate.
110
+ *
111
+ * @binding
112
+ *
113
+ * @section Reading tunnels at runtime
114
+ * @example Bind the read client
115
+ * Bind once in the Init phase; every method is available on the returned client.
116
+ * ```typescript
117
+ * const tunnels = yield* Cloudflare.TunnelRead.bind();
118
+ * ```
119
+ *
120
+ * @example List tunnels
121
+ * ```typescript
122
+ * const { result } = yield* tunnels.list({ isDeleted: false });
123
+ * ```
124
+ *
125
+ * @example Fetch a tunnel and its connector token
126
+ * `getToken` returns the plaintext token used to run `cloudflared`.
127
+ * ```typescript
128
+ * const tunnel = yield* tunnels.get(tunnelId);
129
+ * const token = yield* tunnels.getToken(tunnelId);
130
+ * ```
131
+ *
132
+ * @example Read the ingress configuration
133
+ * ```typescript
134
+ * const { config } = yield* tunnels.getConfiguration(tunnelId);
135
+ * ```
136
+ *
137
+ * @section Runtime Layer
138
+ * Provide {@link TunnelReadLive} in the Worker's runtime layer.
139
+ * ```typescript
140
+ * Effect.provide(Cloudflare.TunnelReadLive)
141
+ * ```
142
+ */
143
+ export class TunnelRead extends Binding.Service<
144
+ TunnelRead,
145
+ () => Effect.Effect<TunnelReadClient, never, Worker>
146
+ >()("Cloudflare.TunnelRead") {}
147
+
148
+ /**
149
+ * Deploy-time policy for {@link TunnelRead}. Attaches the `Cloudflare Tunnel
150
+ * Read` permission to the token via its binding contract.
151
+ */
152
+ export class TunnelReadPolicy extends Binding.Policy<
153
+ TunnelReadPolicy,
154
+ (token: AccountApiToken) => Effect.Effect<void>
155
+ >()("Cloudflare.TunnelRead") {}
156
+
157
+ /** Runtime layer for {@link TunnelRead}. */
158
+ export const TunnelReadLive = Layer.effect(
159
+ TunnelRead,
160
+ makeTunnelClient(TunnelReadPolicy, readClient),
161
+ );
162
+
163
+ /** Live deploy-time policy layer for {@link TunnelReadPolicy}. */
164
+ export const TunnelReadPolicyLive = makeTunnelPolicyLive(
165
+ TunnelReadPolicy,
166
+ "Cloudflare.TunnelRead",
167
+ ["Cloudflare Tunnel Read"],
168
+ );
@@ -0,0 +1,88 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import * as Binding from "../../Binding.ts";
4
+ import type { AccountApiToken } from "../ApiToken/AccountApiToken.ts";
5
+ import type { Worker } from "../Workers/Worker.ts";
6
+ import {
7
+ makeTunnelClient,
8
+ makeTunnelPolicyLive,
9
+ type TunnelToken,
10
+ } from "./TunnelBinding.ts";
11
+ import { readClient, type TunnelReadClient } from "./TunnelRead.ts";
12
+ import { writeClient, type TunnelWriteClient } from "./TunnelWrite.ts";
13
+
14
+ /** Combined read + write tunnel operations. */
15
+ export interface TunnelReadWriteClient
16
+ extends TunnelReadClient, TunnelWriteClient {}
17
+
18
+ /** Build the combined read + write client over a bound token. */
19
+ export const readWriteClient = (token: TunnelToken): TunnelReadWriteClient => ({
20
+ ...readClient(token),
21
+ ...writeClient(token),
22
+ });
23
+
24
+ /**
25
+ * Binding that lets a Worker perform the full Cloudflare Tunnel CRUD surface at
26
+ * runtime.
27
+ *
28
+ * Creates a scoped {@link AccountApiToken} with both the `Cloudflare Tunnel
29
+ * Read` and `Cloudflare Tunnel Write` permissions and binds its outputs into
30
+ * the Worker (the token value as a `secret_text` binding) so runtime code can
31
+ * authenticate.
32
+ *
33
+ * @binding
34
+ *
35
+ * @section Managing tunnels at runtime
36
+ * @example Create, configure, and delete a tunnel from a request handler
37
+ * ```typescript
38
+ * // init
39
+ * const tunnels = yield* Cloudflare.TunnelReadWrite.bind();
40
+ *
41
+ * return {
42
+ * fetch: Effect.gen(function* () {
43
+ * const tunnel = yield* tunnels.create({ name: "on-demand-tunnel" });
44
+ * yield* tunnels.putConfiguration(tunnel.id!, {
45
+ * ingress: [
46
+ * { hostname: "app.example.com", service: "http://localhost:3000" },
47
+ * { service: "http_status:404" },
48
+ * ],
49
+ * });
50
+ * const token = yield* tunnels.getToken(tunnel.id!);
51
+ * return HttpServerResponse.json({ id: tunnel.id, token });
52
+ * }),
53
+ * };
54
+ * ```
55
+ *
56
+ * @section Runtime Layer
57
+ * Provide {@link TunnelReadWriteLive} in the Worker's runtime layer.
58
+ * ```typescript
59
+ * Effect.provide(Cloudflare.TunnelReadWriteLive)
60
+ * ```
61
+ */
62
+ export class TunnelReadWrite extends Binding.Service<
63
+ TunnelReadWrite,
64
+ () => Effect.Effect<TunnelReadWriteClient, never, Worker>
65
+ >()("Cloudflare.TunnelReadWrite") {}
66
+
67
+ /**
68
+ * Deploy-time policy for {@link TunnelReadWrite}. Attaches both the `Cloudflare
69
+ * Tunnel Read` and `Cloudflare Tunnel Write` permissions to the token via its
70
+ * binding contract.
71
+ */
72
+ export class TunnelReadWritePolicy extends Binding.Policy<
73
+ TunnelReadWritePolicy,
74
+ (token: AccountApiToken) => Effect.Effect<void>
75
+ >()("Cloudflare.TunnelReadWrite") {}
76
+
77
+ /** Runtime layer for {@link TunnelReadWrite}. */
78
+ export const TunnelReadWriteLive = Layer.effect(
79
+ TunnelReadWrite,
80
+ makeTunnelClient(TunnelReadWritePolicy, readWriteClient),
81
+ );
82
+
83
+ /** Live deploy-time policy layer for {@link TunnelReadWritePolicy}. */
84
+ export const TunnelReadWritePolicyLive = makeTunnelPolicyLive(
85
+ TunnelReadWritePolicy,
86
+ "Cloudflare.TunnelReadWrite",
87
+ ["Cloudflare Tunnel Read", "Cloudflare Tunnel Write"],
88
+ );