1import type {
2 BatchRecordError,
3 BatchRecordRequest,
4 BatchRecordResponse,
5 CreateRecordError,
6 CreateRecordRequest,
7 CreateRecordResponse,
8 DeleteRecordError,
9 DeleteRecordResponse,
10 PatchRecordError,
11 PatchRecordRequest,
12 PatchRecordResponse,
13 UpdateRecordError,
14 UpdateRecordRequest,
15 UpdateRecordResponse,
16} from "@distilled.cloud/cloudflare/dns";
17import * as Effect from "effect/Effect";
18import * as Binding from "../../Binding.ts";
19import type { RuntimeContext } from "../../RuntimeContext.ts";
20import type { Zone } from "../Zone/Zone.ts";
21
22/**
23 * Binding that lets a Worker create, update, and delete Cloudflare DNS records
24 * at runtime.
25 *
26 * Creates a least-privilege {@link AccountApiToken} with only the `DNS Write`
27 * permission, scoped to the single zone passed to `bind`, and binds its value
28 * into the Worker so runtime code can authenticate.
29 *
30 *
31 * ### Mutating DNS records at runtime
32 * **Example:** Create, update, and delete records from inside a Worker
33 * Bind the client in the Worker's Init phase and provide {@link WriteDnsBinding}.
34 * The zone is fixed by `WriteDnsBinding(zone)` — the provisioned token only grants
35 * access to that zone, so calls take no `zoneId`. Pass the {@link Zone}
36 * resource directly (it's an `Effect`), or `yield* Zone` for a resolved value.
37 * ```typescript
38 * import * as Cloudflare from "alchemy/Cloudflare";
39 * import * as Effect from "effect/Effect";
40 * import * as HttpServerResponse from "effect/http/HttpServerResponse";
41 *
42 * const Zone = Cloudflare.Zone.Zone("MyZone", { name: "example.com" });
43 *
44 * export class WriteDnsrWorker extends Cloudflare.Worker<WriteDnsrWorker>()(
45 * "WriteDnsrWorker",
46 * { main: import.meta.url },
47 * Effect.gen(function* () {
48 * // Init phase — bind the write client scoped to the zone.
49 * const dns = yield* Cloudflare.DNS.WriteDns(Zone);
50 *
51 * return {
52 * fetch: Effect.gen(function* () {
53 * const { result } = yield* dns.createDnsRecord({
54 * type: "A",
55 * name: "app.example.com",
56 * content: "192.0.2.1",
57 * ttl: 1,
58 * proxied: true,
59 * });
60 * yield* dns.updateDnsRecord(result.id, {
61 * type: "A",
62 * name: "app.example.com",
63 * content: "192.0.2.2",
64 * ttl: 1,
65 * });
66 * yield* dns.deleteDnsRecord(result.id);
67 * return yield* HttpServerResponse.json({ id: result.id });
68 * }),
69 * };
70 * }).pipe(Effect.provide(Cloudflare.DNS.WriteDnsBinding)),
71 * ) {}
72 * ```
73 *
74 * **Example:** Apply a batch of changes atomically
75 * ```typescript
76 * yield* dns.batchDnsRecords({
77 * posts: [{ type: "A", name: "a.example.com", content: "192.0.2.1", ttl: 1 }],
78 * deletes: [{ id: oldRecordId }],
79 * });
80 * ```
81 *
82 * @binding
83 * @product DNS
84 * @category Domains & DNS
85 */
86export interface WriteDns extends Binding.Service<
87 WriteDns,
88 "Cloudflare.DNS.WriteDns",
89 (zone: Zone) => Effect.Effect<WriteDnsClient>
90> {}
91
92export const WriteDns = Binding.Service<WriteDns>("Cloudflare.DNS.WriteDns");
93
94/** Create-record request, minus the zone id (bound at `WriteDnsBinding(zone)` time). */
95export type CreateRecordRequestInput = Omit<CreateRecordRequest, "zoneId">;
96
97/** Update-record request, minus the zone id and record id. */
98export type UpdateRecordRequestInput = Omit<
99 UpdateRecordRequest,
100 "zoneId" | "dnsRecordId"
101>;
102
103/** Patch-record request, minus the zone id and record id. */
104export type PatchRecordRequestInput = Omit<
105 PatchRecordRequest,
106 "zoneId" | "dnsRecordId"
107>;
108
109/** Batch-records request, minus the zone id (bound at `WriteDnsBinding(zone)` time). */
110export type BatchRecordRequestInput = Omit<BatchRecordRequest, "zoneId">;
111
112/**
113 * Mutating DNS record operations. Backed by the `DNS Write` permission group.
114 * The zone is fixed when the client is bound, so no `zoneId` is passed per call.
115 */
116export interface WriteDnsClient {
117 /** Create a DNS record. */
118 createDnsRecord(
119 request: CreateRecordRequestInput,
120 ): Effect.Effect<CreateRecordResponse, CreateRecordError, RuntimeContext>;
121 /** Overwrite (PUT) a DNS record. */
122 updateDnsRecord(
123 dnsRecordId: string,
124 request: UpdateRecordRequestInput,
125 ): Effect.Effect<UpdateRecordResponse, UpdateRecordError, RuntimeContext>;
126 /** Partially update (PATCH) a DNS record. */
127 patchDnsRecord(
128 dnsRecordId: string,
129 request: PatchRecordRequestInput,
130 ): Effect.Effect<PatchRecordResponse, PatchRecordError, RuntimeContext>;
131 /** Delete a DNS record by id. */
132 deleteDnsRecord(
133 dnsRecordId: string,
134 ): Effect.Effect<DeleteRecordResponse, DeleteRecordError, RuntimeContext>;
135 /** Apply a batch of create / update / patch / delete operations atomically. */
136 batchDnsRecords(
137 request: BatchRecordRequestInput,
138 ): Effect.Effect<BatchRecordResponse, BatchRecordError, RuntimeContext>;
139}