alchemy 2.0.0-beta.47 → 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 (214) hide show
  1. package/bin/exec.js.map +1 -1
  2. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  3. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  4. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  5. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  6. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  7. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  8. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  9. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  10. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  11. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  12. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  13. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  14. package/lib/Cli/commands/cloudflare.d.ts +4 -1
  15. package/lib/Cli/commands/cloudflare.d.ts.map +1 -1
  16. package/lib/Cli/commands/cloudflare.js +274 -1
  17. package/lib/Cli/commands/cloudflare.js.map +1 -1
  18. package/lib/Cloudflare/AiGateway/AiGateway.d.ts.map +1 -1
  19. package/lib/Cloudflare/AiGateway/AiGateway.js +2 -1
  20. package/lib/Cloudflare/AiGateway/AiGateway.js.map +1 -1
  21. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +40 -2
  22. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
  23. package/lib/Cloudflare/ApiToken/AccountApiToken.js +49 -6
  24. package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
  25. package/lib/Cloudflare/ApiToken/Common.d.ts +26 -3
  26. package/lib/Cloudflare/ApiToken/Common.d.ts.map +1 -1
  27. package/lib/Cloudflare/ApiToken/Common.js +8 -0
  28. package/lib/Cloudflare/ApiToken/Common.js.map +1 -1
  29. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts +38 -2
  30. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -1
  31. package/lib/Cloudflare/ApiToken/UserApiToken.js +47 -6
  32. package/lib/Cloudflare/ApiToken/UserApiToken.js.map +1 -1
  33. package/lib/Cloudflare/Browser/Browser.d.ts +121 -0
  34. package/lib/Cloudflare/Browser/Browser.d.ts.map +1 -0
  35. package/lib/Cloudflare/{BrowserRendering/BrowserRendering.js → Browser/Browser.js} +19 -26
  36. package/lib/Cloudflare/Browser/Browser.js.map +1 -0
  37. package/lib/Cloudflare/Browser/BrowserBinding.d.ts +55 -0
  38. package/lib/Cloudflare/Browser/BrowserBinding.d.ts.map +1 -0
  39. package/lib/Cloudflare/{BrowserRendering/BrowserRenderingBinding.js → Browser/BrowserBinding.js} +11 -11
  40. package/lib/Cloudflare/Browser/BrowserBinding.js.map +1 -0
  41. package/lib/Cloudflare/Browser/index.d.ts +3 -0
  42. package/lib/Cloudflare/Browser/index.d.ts.map +1 -0
  43. package/lib/Cloudflare/Browser/index.js +3 -0
  44. package/lib/Cloudflare/Browser/index.js.map +1 -0
  45. package/lib/Cloudflare/Container/ContainerBinding.d.ts +2 -1
  46. package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
  47. package/lib/Cloudflare/D1/D1Database.d.ts.map +1 -1
  48. package/lib/Cloudflare/D1/D1Database.js +2 -1
  49. package/lib/Cloudflare/D1/D1Database.js.map +1 -1
  50. package/lib/Cloudflare/Email/EmailRouting.d.ts +1 -1
  51. package/lib/Cloudflare/Email/EmailRouting.d.ts.map +1 -1
  52. package/lib/Cloudflare/Email/EmailRouting.js +1 -1
  53. package/lib/Cloudflare/Email/EmailRouting.js.map +1 -1
  54. package/lib/Cloudflare/Email/EmailRule.d.ts +1 -1
  55. package/lib/Cloudflare/Email/EmailRule.d.ts.map +1 -1
  56. package/lib/Cloudflare/Email/EmailRule.js +1 -1
  57. package/lib/Cloudflare/Email/EmailRule.js.map +1 -1
  58. package/lib/Cloudflare/Images/Images.d.ts +32 -23
  59. package/lib/Cloudflare/Images/Images.d.ts.map +1 -1
  60. package/lib/Cloudflare/Images/Images.js +10 -13
  61. package/lib/Cloudflare/Images/Images.js.map +1 -1
  62. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  63. package/lib/Cloudflare/Providers.js +10 -3
  64. package/lib/Cloudflare/Providers.js.map +1 -1
  65. package/lib/Cloudflare/R2/R2Bucket.d.ts +1 -1
  66. package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
  67. package/lib/Cloudflare/R2/R2Bucket.js +13 -7
  68. package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
  69. package/lib/Cloudflare/RateLimit/RateLimit.d.ts +132 -0
  70. package/lib/Cloudflare/RateLimit/RateLimit.d.ts.map +1 -0
  71. package/lib/Cloudflare/RateLimit/RateLimit.js +80 -0
  72. package/lib/Cloudflare/RateLimit/RateLimit.js.map +1 -0
  73. package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts +30 -0
  74. package/lib/Cloudflare/RateLimit/RateLimitBinding.d.ts.map +1 -0
  75. package/lib/Cloudflare/RateLimit/RateLimitBinding.js +50 -0
  76. package/lib/Cloudflare/RateLimit/RateLimitBinding.js.map +1 -0
  77. package/lib/Cloudflare/RateLimit/index.d.ts +3 -0
  78. package/lib/Cloudflare/RateLimit/index.d.ts.map +1 -0
  79. package/lib/Cloudflare/RateLimit/index.js +3 -0
  80. package/lib/Cloudflare/RateLimit/index.js.map +1 -0
  81. package/lib/Cloudflare/SecretsStore/Secret.d.ts +2 -1
  82. package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
  83. package/lib/Cloudflare/SecretsStore/Secret.js +7 -4
  84. package/lib/Cloudflare/SecretsStore/Secret.js.map +1 -1
  85. package/lib/Cloudflare/Tunnel/Tunnel.d.ts +27 -0
  86. package/lib/Cloudflare/Tunnel/Tunnel.d.ts.map +1 -1
  87. package/lib/Cloudflare/Tunnel/Tunnel.js +27 -0
  88. package/lib/Cloudflare/Tunnel/Tunnel.js.map +1 -1
  89. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts +48 -0
  90. package/lib/Cloudflare/Tunnel/TunnelBinding.d.ts.map +1 -0
  91. package/lib/Cloudflare/Tunnel/TunnelBinding.js +56 -0
  92. package/lib/Cloudflare/Tunnel/TunnelBinding.js.map +1 -0
  93. package/lib/Cloudflare/Tunnel/TunnelRead.d.ts +83 -0
  94. package/lib/Cloudflare/Tunnel/TunnelRead.d.ts.map +1 -0
  95. package/lib/Cloudflare/Tunnel/TunnelRead.js +79 -0
  96. package/lib/Cloudflare/Tunnel/TunnelRead.js.map +1 -0
  97. package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts +70 -0
  98. package/lib/Cloudflare/Tunnel/TunnelReadWrite.d.ts.map +1 -0
  99. package/lib/Cloudflare/Tunnel/TunnelReadWrite.js +63 -0
  100. package/lib/Cloudflare/Tunnel/TunnelReadWrite.js.map +1 -0
  101. package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts +92 -0
  102. package/lib/Cloudflare/Tunnel/TunnelWrite.d.ts.map +1 -0
  103. package/lib/Cloudflare/Tunnel/TunnelWrite.js +88 -0
  104. package/lib/Cloudflare/Tunnel/TunnelWrite.js.map +1 -0
  105. package/lib/Cloudflare/Tunnel/index.d.ts +4 -0
  106. package/lib/Cloudflare/Tunnel/index.d.ts.map +1 -1
  107. package/lib/Cloudflare/Tunnel/index.js +4 -0
  108. package/lib/Cloudflare/Tunnel/index.js.map +1 -1
  109. package/lib/Cloudflare/Vectorize/VectorizeIndex.js +1 -0
  110. package/lib/Cloudflare/Vectorize/VectorizeIndex.js.map +1 -1
  111. package/lib/Cloudflare/VpcService/VpcService.d.ts +1 -1
  112. package/lib/Cloudflare/VpcService/VpcService.d.ts.map +1 -1
  113. package/lib/Cloudflare/VpcService/VpcService.js.map +1 -1
  114. package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts +119 -11
  115. package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts.map +1 -1
  116. package/lib/Cloudflare/Workers/DynamicWorkerLoader.js +88 -9
  117. package/lib/Cloudflare/Workers/DynamicWorkerLoader.js.map +1 -1
  118. package/lib/Cloudflare/Workers/InferEnv.d.ts +1 -1
  119. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  120. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +2 -0
  121. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  122. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  123. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +18 -2
  124. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  125. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +5 -3
  126. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  127. package/lib/Cloudflare/Workers/WorkerBinding.js +1 -1
  128. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  129. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts +1 -1
  130. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -1
  131. package/lib/Cloudflare/Zaraz/ZarazConfig.js +1 -1
  132. package/lib/Cloudflare/Zaraz/ZarazConfig.js.map +1 -1
  133. package/lib/Cloudflare/Zone/Zone.d.ts +170 -0
  134. package/lib/Cloudflare/Zone/Zone.d.ts.map +1 -0
  135. package/lib/Cloudflare/Zone/Zone.js +180 -0
  136. package/lib/Cloudflare/Zone/Zone.js.map +1 -0
  137. package/lib/Cloudflare/Zone/index.d.ts +3 -0
  138. package/lib/Cloudflare/Zone/index.d.ts.map +1 -0
  139. package/lib/Cloudflare/Zone/index.js +3 -0
  140. package/lib/Cloudflare/Zone/index.js.map +1 -0
  141. package/lib/Cloudflare/Zone/lookup.d.ts +33 -0
  142. package/lib/Cloudflare/Zone/lookup.d.ts.map +1 -0
  143. package/lib/Cloudflare/{Zone.js → Zone/lookup.js} +2 -2
  144. package/lib/Cloudflare/Zone/lookup.js.map +1 -0
  145. package/lib/Cloudflare/index.d.ts +3 -1
  146. package/lib/Cloudflare/index.d.ts.map +1 -1
  147. package/lib/Cloudflare/index.js +3 -1
  148. package/lib/Cloudflare/index.js.map +1 -1
  149. package/lib/Platform.d.ts +5 -5
  150. package/lib/Platform.d.ts.map +1 -1
  151. package/lib/Platform.js.map +1 -1
  152. package/lib/RemovalPolicy.js +2 -2
  153. package/lib/RemovalPolicy.js.map +1 -1
  154. package/lib/Resource.d.ts +14 -1
  155. package/lib/Resource.d.ts.map +1 -1
  156. package/lib/Resource.js +3 -2
  157. package/lib/Resource.js.map +1 -1
  158. package/lib/tsconfig.test.tsbuildinfo +1 -1
  159. package/package.json +7 -7
  160. package/src/Cli/commands/cloudflare.ts +394 -1
  161. package/src/Cloudflare/AiGateway/AiGateway.ts +2 -1
  162. package/src/Cloudflare/ApiToken/AccountApiToken.ts +60 -7
  163. package/src/Cloudflare/ApiToken/Common.ts +32 -3
  164. package/src/Cloudflare/ApiToken/UserApiToken.ts +58 -7
  165. package/src/Cloudflare/Browser/Browser.ts +140 -0
  166. package/src/Cloudflare/Browser/BrowserBinding.ts +128 -0
  167. package/src/Cloudflare/Browser/index.ts +11 -0
  168. package/src/Cloudflare/D1/D1Database.ts +6 -2
  169. package/src/Cloudflare/Email/EmailRouting.ts +1 -1
  170. package/src/Cloudflare/Email/EmailRule.ts +6 -6
  171. package/src/Cloudflare/Images/Images.ts +40 -27
  172. package/src/Cloudflare/Providers.ts +15 -3
  173. package/src/Cloudflare/R2/R2Bucket.ts +17 -9
  174. package/src/Cloudflare/RateLimit/RateLimit.ts +160 -0
  175. package/src/Cloudflare/RateLimit/RateLimitBinding.ts +90 -0
  176. package/src/Cloudflare/RateLimit/index.ts +2 -0
  177. package/src/Cloudflare/SecretsStore/Secret.ts +12 -6
  178. package/src/Cloudflare/Tunnel/Tunnel.ts +27 -0
  179. package/src/Cloudflare/Tunnel/TunnelBinding.ts +114 -0
  180. package/src/Cloudflare/Tunnel/TunnelRead.ts +168 -0
  181. package/src/Cloudflare/Tunnel/TunnelReadWrite.ts +88 -0
  182. package/src/Cloudflare/Tunnel/TunnelWrite.ts +194 -0
  183. package/src/Cloudflare/Tunnel/index.ts +4 -0
  184. package/src/Cloudflare/Vectorize/VectorizeIndex.ts +2 -1
  185. package/src/Cloudflare/VpcService/VpcService.ts +4 -2
  186. package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +139 -13
  187. package/src/Cloudflare/Workers/InferEnv.ts +19 -15
  188. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +13 -2
  189. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +16 -2
  190. package/src/Cloudflare/Workers/WorkerBinding.ts +6 -2
  191. package/src/Cloudflare/Zaraz/ZarazConfig.ts +1 -1
  192. package/src/Cloudflare/Zone/Zone.ts +349 -0
  193. package/src/Cloudflare/Zone/index.ts +2 -0
  194. package/src/Cloudflare/{Zone.ts → Zone/lookup.ts} +16 -3
  195. package/src/Cloudflare/index.ts +3 -1
  196. package/src/Platform.ts +8 -12
  197. package/src/RemovalPolicy.ts +2 -2
  198. package/src/Resource.ts +18 -1
  199. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts +0 -116
  200. package/lib/Cloudflare/BrowserRendering/BrowserRendering.d.ts.map +0 -1
  201. package/lib/Cloudflare/BrowserRendering/BrowserRendering.js.map +0 -1
  202. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts +0 -55
  203. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.d.ts.map +0 -1
  204. package/lib/Cloudflare/BrowserRendering/BrowserRenderingBinding.js.map +0 -1
  205. package/lib/Cloudflare/BrowserRendering/index.d.ts +0 -3
  206. package/lib/Cloudflare/BrowserRendering/index.d.ts.map +0 -1
  207. package/lib/Cloudflare/BrowserRendering/index.js +0 -3
  208. package/lib/Cloudflare/BrowserRendering/index.js.map +0 -1
  209. package/lib/Cloudflare/Zone.d.ts +0 -14
  210. package/lib/Cloudflare/Zone.d.ts.map +0 -1
  211. package/lib/Cloudflare/Zone.js.map +0 -1
  212. package/src/Cloudflare/BrowserRendering/BrowserRendering.ts +0 -132
  213. package/src/Cloudflare/BrowserRendering/BrowserRenderingBinding.ts +0 -137
  214. package/src/Cloudflare/BrowserRendering/index.ts +0 -11
@@ -8,9 +8,11 @@ import { Resource } from "../../Resource.ts";
8
8
  import type { Providers } from "../Providers.ts";
9
9
  import {
10
10
  buildConditionPayload,
11
+ collectPolicies,
11
12
  conditionFingerprint,
12
13
  policyFingerprint,
13
14
  resolvePolicies,
15
+ type ApiTokenBinding,
14
16
  type ApiTokenProps,
15
17
  } from "./Common.ts";
16
18
 
@@ -27,7 +29,7 @@ export type UserApiToken = Resource<
27
29
  */
28
30
  value: Redacted.Redacted<string>;
29
31
  },
30
- never,
32
+ ApiTokenBinding,
31
33
  Providers
32
34
  >;
33
35
 
@@ -60,6 +62,42 @@ export type UserApiToken = Resource<
60
62
  * ],
61
63
  * });
62
64
  * ```
65
+ *
66
+ * @section Attaching Policies via Bindings
67
+ * @example Let a downstream capability contribute its own policies
68
+ * A token can be created with no `policies` of its own; the policies are
69
+ * supplied through its binding contract (see {@link ApiTokenBinding}).
70
+ * ```typescript
71
+ * const token = yield* Cloudflare.UserApiToken("scoped-token");
72
+ *
73
+ * yield* token.bind("MyCapability", {
74
+ * policies: [
75
+ * {
76
+ * effect: "allow",
77
+ * permissionGroups: ["Workers Scripts Read"],
78
+ * resources: { [`com.cloudflare.api.account.${accountId}`]: "*" },
79
+ * },
80
+ * ],
81
+ * });
82
+ * ```
83
+ *
84
+ * @section Exposing a Token to a Worker
85
+ * @example Read the token value at runtime
86
+ * Bind the token's value output in the Worker's Init phase to get a runtime
87
+ * accessor. Binding it injects a `secret_text` Worker binding; the returned
88
+ * accessor reads it back (as `Redacted`) at runtime.
89
+ * ```typescript
90
+ * // init
91
+ * const value = yield* token.value; // Accessor<Redacted<string>>
92
+ *
93
+ * return {
94
+ * fetch: Effect.gen(function* () {
95
+ * const apiToken = yield* value; // Redacted<string>
96
+ * // ... call the Cloudflare API with `apiToken`
97
+ * return HttpServerResponse.text("ok");
98
+ * }),
99
+ * };
100
+ * ```
63
101
  */
64
102
  export const UserApiToken = Resource<UserApiToken>("Cloudflare.UserApiToken");
65
103
 
@@ -83,26 +121,32 @@ export const UserApiTokenProvider = () =>
83
121
  tokenData: {
84
122
  id?: string | null;
85
123
  name?: string | null;
86
- status?: "active" | "disabled" | "expired" | null;
124
+ // Distilled widened generated string enums to open unions (`string & {}`).
125
+ status?: string | null;
87
126
  },
88
127
  value: Redacted.Redacted<string>,
89
128
  ): UserApiTokenAttributes => ({
90
129
  tokenId: tokenData.id ?? "",
91
130
  name: tokenData.name ?? "",
92
- status: tokenData.status ?? "active",
131
+ status: (tokenData.status ?? "active") as
132
+ | "active"
133
+ | "disabled"
134
+ | "expired",
93
135
  value,
94
136
  });
95
137
 
96
138
  return {
97
139
  stables: ["tokenId"],
98
- diff: Effect.fn(function* ({ id, olds, news, output }) {
140
+ diff: Effect.fn(function* ({ id, olds, news = {}, output }) {
99
141
  if (!isResolved(news)) return undefined;
100
142
  const oldName = output?.name ?? (yield* resolveName(id, olds?.name));
101
143
  const newName = yield* resolveName(id, news.name);
102
144
  const oldPolicyFp = policyFingerprint(
103
145
  resolvePolicies(olds?.policies ?? []),
104
146
  );
105
- const newPolicyFp = policyFingerprint(resolvePolicies(news.policies));
147
+ const newPolicyFp = policyFingerprint(
148
+ resolvePolicies(news.policies ?? []),
149
+ );
106
150
  const oldCondFp = conditionFingerprint(olds?.condition);
107
151
  const newCondFp = conditionFingerprint(news.condition);
108
152
  if (
@@ -115,9 +159,16 @@ export const UserApiTokenProvider = () =>
115
159
  return { action: "update" } as const;
116
160
  }
117
161
  }),
118
- reconcile: Effect.fn(function* ({ id, news, output }) {
162
+ reconcile: Effect.fn(function* ({ id, news = {}, output, bindings }) {
119
163
  const name = yield* resolveName(id, news.name);
120
- const policies = resolvePolicies(news.policies);
164
+ const collected = collectPolicies(news.policies, bindings);
165
+ if (collected.length === 0) {
166
+ return yield* Effect.die(
167
+ `Cloudflare requires at least one policy on token "${name}". ` +
168
+ "Pass `policies` or attach them via a binding.",
169
+ );
170
+ }
171
+ const policies = resolvePolicies(collected);
121
172
 
122
173
  // Observe — fetch current state if we know the token id;
123
174
  // Cloudflare reports a deleted token as `TokenNotFound`, which
@@ -0,0 +1,140 @@
1
+ import type * as Effect from "effect/Effect";
2
+ import { SingleShotGen } from "effect/Utils";
3
+ import { BrowserBinding, type BrowserClient } from "./BrowserBinding.ts";
4
+
5
+ type BrowserTypeId = typeof BrowserTypeId;
6
+ const BrowserTypeId = "Cloudflare.Browser" as const;
7
+
8
+ export type BrowserProps = {
9
+ /**
10
+ * Binding name used when `Browser` is bound from inside a Worker init phase
11
+ * (`yield* Cloudflare.Browser(...)`). When passed through
12
+ * `Worker({ env: { ... } })`, the object key remains the binding name.
13
+ *
14
+ * @default "BROWSER"
15
+ */
16
+ name?: string;
17
+ };
18
+
19
+ /**
20
+ * The Effect yielded when a `Browser` marker is used inside a Worker init
21
+ * phase: it attaches the `browser` binding to the surrounding Worker and
22
+ * resolves to the runtime {@link BrowserClient}.
23
+ */
24
+ type BindEffect = Effect.Effect<BrowserClient, never, BrowserBinding>;
25
+
26
+ /**
27
+ * Marker for a Cloudflare Browser Rendering binding.
28
+ *
29
+ * It is a plain data structure (so it can be declared directly on a Worker's
30
+ * `env`) that is **also** yieldable inside an Effect-native Worker. Yielding it
31
+ * (`yield* Cloudflare.Browser(...)`) attaches the binding to the surrounding
32
+ * Worker and returns the runtime {@link BrowserClient} — no separate
33
+ * `.bind(...)` step required.
34
+ *
35
+ * The divergence is achieved via `[Symbol.iterator]`: the object is not an
36
+ * `Effect` (so `InferEnv` resolves it to the native `Fetcher` in the `env`
37
+ * position), but it is iterable as one when `yield*`-ed.
38
+ */
39
+ export interface Browser {
40
+ kind: BrowserTypeId;
41
+ name: string;
42
+ asEffect(): BindEffect;
43
+ [Symbol.iterator](): SingleShotGen<BindEffect, BrowserClient>;
44
+ }
45
+
46
+ export const isBrowser = (value: unknown): value is Browser =>
47
+ typeof value === "object" && (value as Browser)?.kind === BrowserTypeId;
48
+
49
+ /**
50
+ * A Cloudflare Browser Rendering binding for launching headless browser
51
+ * sessions from Workers via `@cloudflare/puppeteer`.
52
+ *
53
+ * @binding
54
+ *
55
+ * @section Effect-style Worker (recommended)
56
+ * @example Render a page title with managed browser cleanup
57
+ * ```typescript
58
+ * import puppeteer from "@cloudflare/puppeteer";
59
+ * import * as Effect from "effect/Effect";
60
+ *
61
+ * Cloudflare.Worker(
62
+ * "BrowserWorker",
63
+ * { main: import.meta.filename },
64
+ * Effect.gen(function* () {
65
+ * // Attaches the binding to this Worker AND returns the runtime client.
66
+ * const browser = yield* Cloudflare.Browser({ name: "BROWSER" });
67
+ *
68
+ * return {
69
+ * fetch: browser.withBrowser(puppeteer, (browser) =>
70
+ * Effect.gen(function* () {
71
+ * const page = yield* Effect.tryPromise(() => browser.newPage());
72
+ * yield* Effect.tryPromise(() => page.goto("https://example.com"));
73
+ * const title = yield* Effect.tryPromise(() => page.title());
74
+ * return Response.json({ title });
75
+ * }),
76
+ * ),
77
+ * };
78
+ * }).pipe(Effect.provide(Cloudflare.BrowserBindingLive)),
79
+ * );
80
+ * ```
81
+ *
82
+ * @section Worker binding metadata
83
+ * @example
84
+ * ```typescript
85
+ * export const Worker = Cloudflare.Worker("Worker", {
86
+ * main: "./src/worker.ts",
87
+ * env: {
88
+ * BROWSER: Cloudflare.Browser(),
89
+ * },
90
+ * });
91
+ *
92
+ * export type WorkerEnv = Cloudflare.InferEnv<typeof Worker>;
93
+ * // { BROWSER: Fetcher }
94
+ * ```
95
+ *
96
+ * @example Async-style worker with the raw runtime binding
97
+ * ```typescript
98
+ * import puppeteer from "@cloudflare/puppeteer";
99
+ * import type { WorkerEnv } from "../alchemy.run.ts";
100
+ *
101
+ * export default {
102
+ * async fetch(request: Request, env: WorkerEnv) {
103
+ * const browser = await puppeteer.launch(env.BROWSER);
104
+ * const page = await browser.newPage();
105
+ * await page.goto("https://example.com");
106
+ * const screenshot = await page.screenshot();
107
+ * await browser.close();
108
+ *
109
+ * return new Response(screenshot, {
110
+ * headers: { "content-type": "image/png" },
111
+ * });
112
+ * },
113
+ * };
114
+ * ```
115
+ *
116
+ * @see https://developers.cloudflare.com/browser-rendering/workers-binding-api/
117
+ */
118
+ export const Browser: {
119
+ (props?: BrowserProps): Browser;
120
+ /**
121
+ * Bind an existing `Browser` marker to the surrounding Worker, returning the
122
+ * runtime client. Equivalent to `yield* browser` — prefer yielding the marker
123
+ * directly.
124
+ */
125
+ bind: typeof BrowserBinding.bind;
126
+ } = Object.assign(
127
+ (props?: BrowserProps): Browser => {
128
+ const self: Browser = {
129
+ kind: BrowserTypeId,
130
+ name: props?.name ?? "BROWSER",
131
+ asEffect: () => BrowserBinding.bind(self),
132
+ [Symbol.iterator]: () => new SingleShotGen(BrowserBinding.bind(self)),
133
+ };
134
+ return self;
135
+ },
136
+ {
137
+ bind: (...args: Parameters<typeof BrowserBinding.bind>) =>
138
+ BrowserBinding.bind(...args),
139
+ },
140
+ );
@@ -0,0 +1,128 @@
1
+ import type * as cf from "@cloudflare/workers-types";
2
+ import * as Data from "effect/Data";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Layer from "effect/Layer";
5
+ import * as Binding from "../../Binding.ts";
6
+ import type { ResourceLike } from "../../Resource.ts";
7
+ import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts";
8
+ import type { Browser as BrowserLike } from "./Browser.ts";
9
+
10
+ export class BrowserError extends Data.TaggedError("BrowserError")<{
11
+ message: string;
12
+ cause: unknown;
13
+ }> {}
14
+
15
+ export interface BrowserHandle {
16
+ close(): Promise<void>;
17
+ }
18
+
19
+ export interface BrowserPuppeteer<Browser extends BrowserHandle> {
20
+ launch(binding: cf.Fetcher): Promise<Browser>;
21
+ }
22
+
23
+ /**
24
+ * Effect-native client for a Cloudflare Browser Rendering binding.
25
+ *
26
+ * Browser Rendering's Workers binding is consumed by `@cloudflare/puppeteer`.
27
+ * Alchemy keeps Puppeteer as a caller-provided dependency while wrapping
28
+ * launch and cleanup in Effects.
29
+ */
30
+ export interface BrowserClient {
31
+ /**
32
+ * Effect resolving to the raw Cloudflare Browser Rendering runtime binding.
33
+ */
34
+ raw: Effect.Effect<cf.Fetcher, never, WorkerEnvironment>;
35
+ /**
36
+ * Launch a Browser Rendering session through `@cloudflare/puppeteer`.
37
+ */
38
+ launch<Browser extends BrowserHandle>(
39
+ puppeteer: BrowserPuppeteer<Browser>,
40
+ ): Effect.Effect<Browser, BrowserError, WorkerEnvironment>;
41
+ /**
42
+ * Launch a browser, run an Effect with it, and close it afterward.
43
+ */
44
+ withBrowser<Browser extends BrowserHandle, A, E, R>(
45
+ puppeteer: BrowserPuppeteer<Browser>,
46
+ use: (browser: Browser) => Effect.Effect<A, E, R>,
47
+ ): Effect.Effect<A, BrowserError | E, WorkerEnvironment | R>;
48
+ }
49
+
50
+ export class BrowserBinding extends Binding.Service<
51
+ BrowserBinding,
52
+ (browser: BrowserLike) => Effect.Effect<BrowserClient>
53
+ >()("Cloudflare.Browser.Binding") {}
54
+
55
+ export const BrowserBindingLive = Layer.effect(
56
+ BrowserBinding,
57
+ Effect.gen(function* () {
58
+ const Policy = yield* BrowserBindingPolicy;
59
+
60
+ return Effect.fn(function* (browser: BrowserLike) {
61
+ yield* Policy(browser);
62
+ // Cloudflare exposes Browser Rendering as a service-style binding for
63
+ // @cloudflare/puppeteer; workers-types has no narrower interface.
64
+ const raw = WorkerEnvironment.useSync(
65
+ (env) => (env as Record<string, cf.Fetcher>)[browser.name]!,
66
+ );
67
+ return makeBrowserClient(raw);
68
+ });
69
+ }),
70
+ );
71
+
72
+ export class BrowserBindingPolicy extends Binding.Policy<
73
+ BrowserBindingPolicy,
74
+ (browser: BrowserLike) => Effect.Effect<void>
75
+ >()("Cloudflare.Browser.Binding") {}
76
+
77
+ export const BrowserBindingPolicyLive = BrowserBindingPolicy.layer.succeed(
78
+ Effect.fn(function* (host: ResourceLike, browser: BrowserLike) {
79
+ if (isWorker(host)) {
80
+ yield* host.bind(browser.name, {
81
+ bindings: [
82
+ {
83
+ type: "browser",
84
+ name: browser.name,
85
+ },
86
+ ],
87
+ });
88
+ } else {
89
+ return yield* Effect.die(
90
+ new Error(`BrowserBinding does not support runtime '${host.Type}'`),
91
+ );
92
+ }
93
+ }),
94
+ );
95
+
96
+ const tryPromise = <T>(fn: () => Promise<T>): Effect.Effect<T, BrowserError> =>
97
+ Effect.tryPromise({
98
+ try: fn,
99
+ catch: (error) =>
100
+ new BrowserError({
101
+ message:
102
+ error instanceof Error
103
+ ? error.message
104
+ : "Unknown Browser Rendering error",
105
+ cause: error,
106
+ }),
107
+ });
108
+
109
+ /** @internal */
110
+ export const makeBrowserClient = (
111
+ raw: Effect.Effect<cf.Fetcher, never, WorkerEnvironment>,
112
+ ): BrowserClient => {
113
+ const launch = <Browser extends BrowserHandle>(
114
+ puppeteer: BrowserPuppeteer<Browser>,
115
+ ) =>
116
+ raw.pipe(
117
+ Effect.flatMap((binding) => tryPromise(() => puppeteer.launch(binding))),
118
+ );
119
+
120
+ return {
121
+ raw,
122
+ launch,
123
+ withBrowser: (puppeteer, use) =>
124
+ Effect.acquireUseRelease(launch(puppeteer), use, (browser) =>
125
+ tryPromise(() => browser.close()),
126
+ ),
127
+ } satisfies BrowserClient;
128
+ };
@@ -0,0 +1,11 @@
1
+ export * from "./Browser.ts";
2
+ export {
3
+ BrowserBinding,
4
+ BrowserBindingLive,
5
+ BrowserBindingPolicy,
6
+ BrowserBindingPolicyLive,
7
+ BrowserError,
8
+ type BrowserClient,
9
+ type BrowserHandle,
10
+ type BrowserPuppeteer,
11
+ } from "./BrowserBinding.ts";
@@ -332,7 +332,10 @@ export const DatabaseProvider = () =>
332
332
  databaseId: db.uuid ?? output.databaseId,
333
333
  databaseName: db.name ?? output.databaseName,
334
334
  jurisdiction: output.jurisdiction,
335
- readReplication: db.readReplication ?? undefined,
335
+ // Distilled widened generated string enums to open unions.
336
+ readReplication: (db.readReplication ?? undefined) as
337
+ | { mode: "auto" | "disabled" }
338
+ | undefined,
336
339
  accountId: output.accountId,
337
340
  migrationsDir: output.migrationsDir,
338
341
  migrationsTable: output.migrationsTable,
@@ -375,7 +378,8 @@ export const DatabaseProvider = () =>
375
378
  | {
376
379
  uuid?: string | null;
377
380
  name?: string | null;
378
- readReplication?: { mode: "auto" | "disabled" } | null;
381
+ // Distilled widened generated string enums to open unions.
382
+ readReplication?: { mode: string } | null;
379
383
  }
380
384
  | undefined;
381
385
  if (output?.databaseId) {
@@ -5,7 +5,7 @@ import * as Provider from "../../Provider.ts";
5
5
  import { Resource } from "../../Resource.ts";
6
6
  import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
7
7
  import type { Providers } from "../Providers.ts";
8
- import { resolveZoneId, type ZoneReference } from "../Zone.ts";
8
+ import { resolveZoneId, type ZoneReference } from "../Zone/index.ts";
9
9
 
10
10
  export type EmailRoutingStatus =
11
11
  | "ready"
@@ -5,7 +5,7 @@ import * as Provider from "../../Provider.ts";
5
5
  import { Resource } from "../../Resource.ts";
6
6
  import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
7
7
  import type { Providers } from "../Providers.ts";
8
- import { resolveZoneId, type ZoneReference } from "../Zone.ts";
8
+ import { resolveZoneId, type ZoneReference } from "../Zone/index.ts";
9
9
 
10
10
  export type EmailMatcher =
11
11
  | { type: "all" }
@@ -88,16 +88,16 @@ const normalize = (
88
88
  name?: string | null;
89
89
  enabled?: boolean | null;
90
90
  priority?: number | null;
91
+ // Distilled widened generated string enums to open unions (`string & {}`);
92
+ // the runtime values are still the known variants, narrowed below.
91
93
  matchers?:
92
94
  | {
93
- type: "all" | "literal";
94
- field?: "to" | null;
95
+ type: string;
96
+ field?: string | null;
95
97
  value?: string | null;
96
98
  }[]
97
99
  | null;
98
- actions?:
99
- | { type: "drop" | "forward" | "worker"; value?: string[] | null }[]
100
- | null;
100
+ actions?: { type: string; value?: string[] | null }[] | null;
101
101
  },
102
102
  zoneId: string,
103
103
  ) => ({
@@ -1,32 +1,47 @@
1
- import * as Effect from "effect/Effect";
2
- import { ImagesBinding } from "./ImagesBinding.ts";
1
+ import type * as Effect from "effect/Effect";
2
+ import { SingleShotGen } from "effect/Utils";
3
+ import { ImagesBinding, type ImagesClient } from "./ImagesBinding.ts";
3
4
 
4
5
  type ImagesTypeId = typeof ImagesTypeId;
5
6
  const ImagesTypeId = "Cloudflare.Images" as const;
6
7
 
7
8
  export type ImagesProps = {
8
9
  /**
9
- * Binding name used when `Cloudflare.Images.bind(images)` attaches Images
10
- * from inside a Worker init phase. When Images is passed through
11
- * `Worker({ bindings: { ... } })`, the object key remains the binding name.
10
+ * Binding name used when `Images` is bound from inside a Worker init phase
11
+ * (`yield* Cloudflare.Images(...)`). When passed through
12
+ * `Worker({ env: { ... } })`, the object key remains the binding name.
12
13
  *
13
14
  * @default "IMAGES"
14
15
  */
15
16
  name?: string;
16
17
  };
17
18
 
19
+ /**
20
+ * The Effect yielded when an `Images` marker is used inside a Worker init
21
+ * phase: it attaches the `images` binding to the surrounding Worker and
22
+ * resolves to the runtime {@link ImagesClient}.
23
+ */
24
+ type BindEffect = Effect.Effect<ImagesClient, never, ImagesBinding>;
25
+
18
26
  /**
19
27
  * Marker for a Cloudflare Images binding.
20
28
  *
21
- * Images bindings are configured directly on Workers and do not have a
22
- * standalone provisioning API. The Worker provider sees this object in
23
- * `bindings: { ... }` and emits the corresponding `{ type: "images" }`
24
- * metadata binding to the script.
29
+ * It is a plain data structure (so it can be declared directly on a Worker's
30
+ * `env`) that is **also** yieldable inside an Effect-native Worker. Yielding it
31
+ * (`yield* Cloudflare.Images(...)`) attaches the binding to the surrounding
32
+ * Worker and returns the runtime {@link ImagesClient} — no separate `.bind(...)`
33
+ * step required.
34
+ *
35
+ * The divergence is achieved via `[Symbol.iterator]`: the object is not an
36
+ * `Effect` (so `InferEnv` resolves it to the native `ImagesBinding` in the
37
+ * `env` position), but it is iterable as one when `yield*`-ed.
25
38
  */
26
- export type Images = {
39
+ export interface Images {
27
40
  kind: ImagesTypeId;
28
41
  name: string;
29
- };
42
+ asEffect(): BindEffect;
43
+ [Symbol.iterator](): SingleShotGen<BindEffect, ImagesClient>;
44
+ }
30
45
 
31
46
  export const isImages = (value: unknown): value is Images =>
32
47
  typeof value === "object" &&
@@ -44,12 +59,6 @@ export const isImages = (value: unknown): value is Images =>
44
59
  * .draw(...).output(...)`. The runtime conversion to Cloudflare's
45
60
  * `ReadableStream` is handled internally.
46
61
  *
47
- * @section Declaring Images
48
- * @example
49
- * ```typescript
50
- * const Pipeline = yield* Cloudflare.Images({ name: "PIPELINE" });
51
- * ```
52
- *
53
62
  * @section Effect-style Worker (recommended)
54
63
  * @example Read image format and dimensions from the request body
55
64
  * ```typescript
@@ -57,8 +66,8 @@ export const isImages = (value: unknown): value is Images =>
57
66
  *
58
67
  * Cloudflare.Worker("ImageWorker", { main: import.meta.filename },
59
68
  * Effect.gen(function* () {
60
- * const pipeline = yield* Pipeline;
61
- * const images = yield* Cloudflare.Images.bind(pipeline);
69
+ * // Attaches the binding to this Worker AND returns the runtime client.
70
+ * const images = yield* Cloudflare.Images({ name: "PIPELINE" });
62
71
  * return {
63
72
  * fetch: Effect.gen(function* () {
64
73
  * const request = yield* HttpServerRequest;
@@ -85,7 +94,7 @@ export const isImages = (value: unknown): value is Images =>
85
94
  * ```typescript
86
95
  * export const Worker = Cloudflare.Worker("Worker", {
87
96
  * main: "./src/worker.ts",
88
- * bindings: { MEDIA: Pipeline },
97
+ * env: { MEDIA: Cloudflare.Images({ name: "PIPELINE" }) },
89
98
  * });
90
99
  *
91
100
  * export type WorkerEnv = Cloudflare.InferEnv<typeof Worker>;
@@ -101,19 +110,23 @@ export const isImages = (value: unknown): value is Images =>
101
110
  * @see https://developers.cloudflare.com/images/transform-images/bindings/
102
111
  */
103
112
  export const Images: {
104
- (props?: ImagesProps): Effect.Effect<Images>;
113
+ (props?: ImagesProps): Images;
105
114
  /**
106
- * Bind Cloudflare Images to the surrounding Worker, returning an
107
- * Effect-native client with access to the native Workers runtime binding.
115
+ * Bind an existing `Images` marker to the surrounding Worker, returning the
116
+ * Effect-native client. Equivalent to `yield* images` — prefer yielding the
117
+ * marker directly.
108
118
  */
109
119
  bind: typeof ImagesBinding.bind;
110
120
  } = Object.assign(
111
- Effect.fn(function* (props?: ImagesProps) {
112
- return {
121
+ (props?: ImagesProps): Images => {
122
+ const self: Images = {
113
123
  kind: ImagesTypeId,
114
124
  name: props?.name ?? "IMAGES",
115
- } satisfies Images;
116
- }),
125
+ asEffect: () => ImagesBinding.bind(self),
126
+ [Symbol.iterator]: () => new SingleShotGen(ImagesBinding.bind(self)),
127
+ };
128
+ return self;
129
+ },
117
130
  {
118
131
  bind: (...args: Parameters<typeof ImagesBinding.bind>) =>
119
132
  ImagesBinding.bind(...args),
@@ -18,7 +18,7 @@ import * as AnalyticsEngine from "./AnalyticsEngine/index.ts";
18
18
  import * as ApiToken from "./ApiToken/index.ts";
19
19
  import * as Artifacts from "./Artifacts/index.ts";
20
20
  import { CloudflareAuth } from "./Auth/AuthProvider.ts";
21
- import * as BrowserRendering from "./BrowserRendering/index.ts";
21
+ import * as Browser from "./Browser/index.ts";
22
22
  import * as CloudflareEnvironment from "./CloudflareEnvironment.ts";
23
23
  import * as Containers from "./Container/index.ts";
24
24
  import * as Credentials from "./Credentials.ts";
@@ -29,6 +29,7 @@ import * as Images from "./Images/index.ts";
29
29
  import * as KV from "./KV/index.ts";
30
30
  import * as Queue from "./Queue/index.ts";
31
31
  import * as R2 from "./R2/index.ts";
32
+ import * as RateLimit from "./RateLimit/index.ts";
32
33
  import * as SecretsStore from "./SecretsStore/index.ts";
33
34
  import * as Tunnel from "./Tunnel/index.ts";
34
35
  import * as Vectorize from "./Vectorize/index.ts";
@@ -36,6 +37,7 @@ import * as VpcService from "./VpcService/index.ts";
36
37
  import * as Workers from "./Workers/index.ts";
37
38
  import * as Workflows from "./Workers/Workflow.ts";
38
39
  import * as Zaraz from "./Zaraz/index.ts";
40
+ import * as Zone from "./Zone/index.ts";
39
41
 
40
42
  export { Credentials } from "@distilled.cloud/cloudflare/Credentials";
41
43
 
@@ -58,7 +60,7 @@ export const providers = () =>
58
60
  AiGateway.AiGatewayBindingPolicy,
59
61
  AnalyticsEngine.AnalyticsEngineDatasetBindingPolicy,
60
62
  Artifacts.ArtifactsBindingPolicy,
61
- BrowserRendering.BrowserRenderingBindingPolicy,
63
+ Browser.BrowserBindingPolicy,
62
64
  Command,
63
65
  Containers.Container,
64
66
  D1.D1ConnectionPolicy,
@@ -78,10 +80,14 @@ export const providers = () =>
78
80
  Queue.QueueEventSourcePolicy,
79
81
  R2.R2Bucket,
80
82
  R2.R2BucketBindingPolicy,
83
+ RateLimit.RateLimitBindingPolicy,
81
84
  SecretsStore.SecretBindingPolicy,
82
85
  SecretsStore.SecretsStore,
83
86
  SecretsStore.Secret,
84
87
  Tunnel.Tunnel,
88
+ Tunnel.TunnelReadPolicy,
89
+ Tunnel.TunnelReadWritePolicy,
90
+ Tunnel.TunnelWritePolicy,
85
91
  Vectorize.VectorizeIndexBindingPolicy,
86
92
  Vectorize.VectorizeIndex,
87
93
  Vectorize.VectorizeMetadataIndex,
@@ -94,6 +100,7 @@ export const providers = () =>
94
100
  Workers.Worker,
95
101
  Workflows.WorkflowResource,
96
102
  Zaraz.ZarazConfig,
103
+ Zone.Zone,
97
104
  ]),
98
105
  ).pipe(
99
106
  Layer.provide(
@@ -104,7 +111,7 @@ export const providers = () =>
104
111
  AiGateway.AiGatewayBindingPolicyLive,
105
112
  AnalyticsEngine.AnalyticsEngineDatasetBindingPolicyLive,
106
113
  Artifacts.ArtifactsBindingPolicyLive,
107
- BrowserRendering.BrowserRenderingBindingPolicyLive,
114
+ Browser.BrowserBindingPolicyLive,
108
115
  Containers.ContainerProvider(),
109
116
  D1.D1ConnectionPolicyLive,
110
117
  D1.DatabaseProvider(),
@@ -123,10 +130,14 @@ export const providers = () =>
123
130
  Queue.QueueConsumerProvider(),
124
131
  R2.R2BucketBindingPolicyLive,
125
132
  R2.R2BucketProvider(),
133
+ RateLimit.RateLimitBindingPolicyLive,
126
134
  SecretsStore.SecretBindingPolicyLive,
127
135
  SecretsStore.SecretsStoreProvider(),
128
136
  SecretsStore.StoreSecretProvider(),
129
137
  Tunnel.TunnelProvider(),
138
+ Tunnel.TunnelReadPolicyLive,
139
+ Tunnel.TunnelReadWritePolicyLive,
140
+ Tunnel.TunnelWritePolicyLive,
130
141
  Vectorize.VectorizeIndexBindingPolicyLive,
131
142
  Vectorize.VectorizeIndexProvider(),
132
143
  Vectorize.VectorizeMetadataIndexProvider(),
@@ -137,6 +148,7 @@ export const providers = () =>
137
148
  Workers.WorkerProvider(),
138
149
  Workflows.WorkflowProvider(),
139
150
  Zaraz.ZarazConfigProvider(),
151
+ Zone.ZoneProvider(),
140
152
  ),
141
153
  ),
142
154
  Layer.provideMerge(