1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import type { Worker } from "../Workers/Worker.ts";
4import type { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
5import { type TunnelAuth } from "./TunnelBinding.ts";
6import { readClient, type ReadTunnelClient } from "./ReadTunnel.ts";
7import { writeClient, type WriteTunnelClient } from "./WriteTunnel.ts";
8
9/**
10 * Binding that lets a Worker perform the full Cloudflare Tunnel CRUD surface at
11 * runtime.
12 *
13 * Creates a scoped {@link AccountApiToken} with both the `Cloudflare Tunnel
14 * Read` and `Cloudflare Tunnel Write` permissions and binds its outputs into
15 * the Worker (the token value as a `secret_text` binding) so runtime code can
16 * authenticate.
17 *
18 * @remarks
19 *
20 * `ReadWriteTunnel` is a single identifier that is simultaneously the binding's
21 * Context tag, its type, and the callable —
22 * `yield* Cloudflare.Tunnel.ReadWriteTunnel()`.
23 *
24 * ### Managing tunnels at runtime
25 * **Example:** Create, configure, and delete a tunnel from a request handler
26 * ```typescript
27 * // init
28 * const tunnels = yield* Cloudflare.Tunnel.ReadWriteTunnel();
29 *
30 * return {
31 * fetch: Effect.gen(function* () {
32 * const tunnel = yield* tunnels.create({ name: "on-demand-tunnel" });
33 * yield* tunnels.putConfiguration(tunnel.id!, {
34 * ingress: [
35 * { hostname: "app.example.com", service: "http://localhost:3000" },
36 * { service: "http_status:404" },
37 * ],
38 * });
39 * const token = yield* tunnels.getToken(tunnel.id!);
40 * return HttpServerResponse.json({ id: tunnel.id, token });
41 * }),
42 * };
43 * ```
44 *
45 * ### Runtime Layer
46 * Provide {@link ReadWriteTunnelBinding} in the Worker's runtime layer.
47 * ```typescript
48 * Effect.provide(Cloudflare.Tunnel.ReadWriteTunnelBinding)
49 * ```
50 *
51 * @binding
52 * @product Tunnels
53 * @category Cloudflare One (Zero Trust)
54 */
55export interface ReadWriteTunnel extends Binding.Service<
56 ReadWriteTunnel,
57 "Cloudflare.Tunnel.ReadWriteTunnel",
58 () => Effect.Effect<
59 ReadWriteTunnelClient,
60 never,
61 Worker | CloudflareEnvironment
62 >
63> {}
64
65export const ReadWriteTunnel = Binding.Service<ReadWriteTunnel>(
66 "Cloudflare.Tunnel.ReadWriteTunnel",
67);
68
69/** Combined read + write tunnel operations. */
70export interface ReadWriteTunnelClient
71 extends ReadTunnelClient, WriteTunnelClient {}
72
73/** Build the combined read + write client over an injectable {@link TunnelAuth}. */
74export const readWriteClient = (auth: TunnelAuth): ReadWriteTunnelClient => ({
75 ...readClient(auth),
76 ...writeClient(auth),
77});