2.0.0-beta.48 - Zones, Tunnels & Tokens
v2.0.0-beta.48 is a Cloudflare-heavy release: a managed
Zone resource, runtime Tunnel bindings, a RateLimit
binding, and a new create-token CLI command.
Cloudflare.Zone
Section titled “Cloudflare.Zone”Manage a Cloudflare DNS zone as a resource:
const zone = yield* Cloudflare.Zone("MyZone", { name: "example.com",});The full zone object is typed on the output — zone.zoneId,
zone.nameServers, zone.status, and the rest.
This creates the zone if it doesn’t exist, and by default does
not delete it on teardown. To import an existing zone, pipe
adopt():
import * as AdoptPolicy from "alchemy/AdoptPolicy";
yield* Cloudflare.Zone("MyZone", { name: "example.com" }).pipe( AdoptPolicy.adopt(),);To have it deleted on teardown, pipe destroy():
import * as RemovalPolicy from "alchemy/RemovalPolicy";
yield* Cloudflare.Zone("MyZone", { name: "example.com" }).pipe( RemovalPolicy.destroy(),);This also adds Resource(type, { defaultRemovalPolicy }) so a
resource type can flip the engine’s destroy-by-default (Zone
uses "retain"), and destroy() / retain() now default to
true when called with no argument.
(#493)
Tunnel bindings
Section titled “Tunnel bindings”Bind a typed Tunnel client into a Worker and manage Cloudflare Tunnels at runtime. Bind it once in Init, then call methods from your handler:
Effect.gen(function* () { const tunnels = yield* Cloudflare.TunnelReadWrite.bind();
return { fetch: Effect.gen(function* () { const tunnel = yield* tunnels.create({ name: "on-demand-tunnel" }); yield* tunnels.putConfiguration(tunnel.id!, { ingress: [ { hostname: "app.example.com", service: "http://localhost:3000" }, { service: "http_status:404" }, ], }); const token = yield* tunnels.getToken(tunnel.id!); return HttpServerResponse.json({ id: tunnel.id, token }); }), };}).pipe(Effect.provide(Cloudflare.TunnelReadWriteLive));Pick the surface you need:
Cloudflare.TunnelRead—get,list,getToken,getConfigurationCloudflare.TunnelWrite—create,update,delete,putConfigurationCloudflare.TunnelReadWrite— the full CRUD surface
RateLimit binding
Section titled “RateLimit binding”Cloudflare.RateLimit({...}) counts arbitrary keys inside a
Worker. The marker is dual-natured: a plain data structure in the
env position and yieldable inside an effect-native Worker.
In an effect-native Worker, yielding it attaches the binding and returns the runtime client in one step:
Effect.gen(function* () { const throttle = yield* Cloudflare.RateLimit({ namespaceId: 1001, simple: { limit: 2, period: 10 }, });
return { fetch: Effect.gen(function* () { const { success } = yield* throttle.limit({ key: "ip" }); return HttpServerResponse.text(success ? "ok" : "rate limited"); }), };}).pipe(Effect.provide(Cloudflare.RateLimitBindingLive));Or declare it on env for an async (non-Effect) Worker, where
InferEnv maps it to the native cf.RateLimit:
export const Worker = Cloudflare.Worker("Worker", { main: "./src/worker.ts", env: { THROTTLE: Cloudflare.RateLimit({ namespaceId: 1001, simple: { limit: 2, period: 10 }, }), },});Thanks Alex for the contribution. (#238)
alchemy cloudflare create-token
Section titled “alchemy cloudflare create-token”Mint a Cloudflare API token from the terminal — e.g. to produce a
CLOUDFLARE_API_TOKEN for CI or local deploys:
# curated permissions for typical Alchemy deploysalchemy cloudflare create-token --name ci-token
# full-access "superuser" token (all permission groups)alchemy cloudflare create-token --all-permissions --name adminIt’s standalone (no auth profile) and the token is never stored.
Permission groups are resolved live from the account so IDs
are always valid, and the minted token is verified via
/user/tokens/verify. Because Cloudflare only grants permissions
the authenticating credential holds, it authenticates with the
account’s Global API Key (CLOUDFLARE_API_KEY / CLOUDFLARE_EMAIL,
or prompted). See the
CLI guide for the full command.
(#496)
Also in this release
Section titled “Also in this release”Cloudflare.BrowserRenderingrenamed toCloudflare.Browser— along with the matching binding/client/error exports (#503).- Yieldable binding markers —
Browser,Images, andDynamicWorkerLoadercan beyield*ed directly inside an effect-native Worker to attach the binding and get the client in one step (#498, #501, #505). - Providers docs refocused — a new Bindings section and the Providers page now centers on the lifecycle interface (#502).
Zoneleans on error tags — distilled bumped to0.22.3for theZoneAlreadyExiststag, replacing brittle error-message matching; the same distilled bump widened generated enums to open unions, narrowed back at the API boundary across several providers (#507).