1import * as pqe from "@distilled.cloud/cloudflare/origin-post-quantum-encryption";
2import * as Effect from "effect/Effect";
3import * as Predicate from "effect/Predicate";
4
5import * as Provider from "../../Provider.ts";
6import { Resource } from "../../Resource.ts";
7import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
8import type { Providers } from "../Providers.ts";
9import { listAllZones } from "../Zone/lookup.ts";
10
11const TypeId =
12 "Cloudflare.OriginPostQuantumEncryption.OriginPostQuantumEncryption" as const;
13type TypeId = typeof TypeId;
14
15/**
16 * Value of the Origin Post-Quantum Encryption setting.
17 *
18 * - `"preferred"` — advertise post-quantum key agreement to the origin and
19 * use it whenever the origin supports it.
20 * - `"supported"` — accept post-quantum key agreement if the origin
21 * initiates it (Cloudflare's default).
22 * - `"off"` — never use post-quantum key agreement to the origin.
23 */
24export type Value = "preferred" | "supported" | "off";
25
26export type Props = {
27 /**
28 * Zone the Origin Post-Quantum Encryption setting belongs to. Stable —
29 * changing the zone triggers a replacement (the old zone's setting is
30 * restored to the value it had before Alchemy managed it).
31 */
32 zoneId: string;
33 /**
34 * Desired value of the setting. Mutable — updated in place.
35 *
36 * @default "supported"
37 */
38 value?: Value;
39};
40
41export type Attributes = {
42 /** Zone the setting belongs to. */
43 zoneId: string;
44 /** Resolved current value of the setting. */
45 value: Value;
46 /**
47 * Whether the setting can be modified on the zone's current plan.
48 */
49 editable: boolean;
50 /** When the setting was last modified, if Cloudflare reports it. */
51 modifiedOn: string | undefined;
52 /**
53 * The value the setting had before Alchemy first managed it. Restored
54 * on destroy, so deleting the resource puts the zone back the way it
55 * was found.
56 */
57 initialValue: Value;
58};
59
60export type OriginPostQuantumEncryption = Resource<
61 TypeId,
62 Props,
63 Attributes,
64 never,
65 Providers
66>;
67
68/**
69 * Origin Post-Quantum Encryption for a Cloudflare zone
70 * (`/zones/{zone_id}/cache/origin_post_quantum_encryption`).
71 *
72 * Controls whether Cloudflare uses post-quantum (PQ) key agreement on the
73 * TLS connections it makes to your origin. Despite living under the
74 * `/cache/` API path, this is an SSL/origin-connection setting, not a
75 * caching one.
76 *
77 * The setting is a singleton — it always exists on every zone with a
78 * Cloudflare default of `"supported"`, so this resource never creates or
79 * deletes anything physical. Reconcile updates the setting when the
80 * observed value differs from the desired one; destroy restores the value
81 * the setting had before Alchemy first managed it (captured as
82 * `initialValue`).
83 * ### Managing the setting
84 * **Example:** Prefer post-quantum key agreement to the origin
85 * ```typescript
86 * const zone = yield* Cloudflare.Zone.Zone("Site", { name: "example.com" });
87 *
88 * yield* Cloudflare.OriginPostQuantumEncryption.OriginPostQuantumEncryption("OriginPqe", {
89 * zoneId: zone.zoneId,
90 * value: "preferred",
91 * });
92 * ```
93 *
94 * **Example:** Disable post-quantum key agreement to the origin
95 * ```typescript
96 * yield* Cloudflare.OriginPostQuantumEncryption.OriginPostQuantumEncryption("OriginPqe", {
97 * zoneId: zone.zoneId,
98 * value: "off",
99 * });
100 * ```
101 *
102 * **Example:** Pin the Cloudflare default explicitly
103 * ```typescript
104 * yield* Cloudflare.OriginPostQuantumEncryption.OriginPostQuantumEncryption("OriginPqe", {
105 * zoneId: zone.zoneId,
106 * value: "supported",
107 * });
108 * ```
109 *
110 * @see https://developers.cloudflare.com/ssl/origin-configuration/pqc-to-origin/
111 *
112 * @resource
113 * @product Origin Post-Quantum Encryption
114 * @category SSL/TLS & Certificates
115 */
116export const OriginPostQuantumEncryption =
117 Resource<OriginPostQuantumEncryption>(TypeId, {
118 aliases: ["Cloudflare.OriginPostQuantumEncryption"],
119 });
120
121/**
122 * Returns true if the given value is an OriginPostQuantumEncryption resource.
123 */
124export const isOriginPostQuantumEncryption = (
125 value: unknown,
126): value is OriginPostQuantumEncryption =>
127 Predicate.hasProperty(value, "Type") && value.Type === TypeId;
128
129export const OriginPostQuantumEncryptionProvider = () =>
130 Provider.succeed(OriginPostQuantumEncryption, {
131 nuke: { singleton: true },
132 stables: ["zoneId", "initialValue"],
133
134 list: Effect.fn(function* () {
135 const { accountId } = yield* yield* CloudflareEnvironment;
136 // No account-wide API for this zone singleton — enumerate every
137 // zone in the account and read its setting (every zone has one).
138 const allZones = yield* listAllZones(accountId);
139 const rows = yield* Effect.forEach(
140 allZones.map((zone) => zone.id),
141 (zoneId) =>
142 pqe.getOriginPostQuantumEncryption({ zoneId }).pipe(
143 Effect.map((observed) =>
144 toAttributes(zoneId, observed, toValue(observed.value)),
145 ),
146 // Zone gone out-of-band / not resolvable — skip it. (Transient
147 // 403/429 "Authentication error" blips under concurrency are
148 // retried globally by the Cloudflare retry policy, so they never
149 // reach here as a real failure.)
150 Effect.catchTag("InvalidZoneIdentifier", () =>
151 Effect.succeed(undefined),
152 ),
153 ),
154 { concurrency: 10 },
155 );
156 return rows.filter((row): row is Attributes => row !== undefined);
157 }),
158
159 diff: Effect.fn(function* ({ olds = {}, news, output }) {
160 const o = olds as Props;
161 const n = news as Props;
162 // zoneId is Input<string>; compare only once both sides are concrete.
163 const oldZoneId =
164 output?.zoneId ?? (typeof o.zoneId === "string" ? o.zoneId : undefined);
165 if (
166 oldZoneId !== undefined &&
167 typeof n.zoneId === "string" &&
168 oldZoneId !== n.zoneId
169 ) {
170 return { action: "replace" } as const;
171 }
172 return undefined;
173 }),
174
175 read: Effect.fn(function* ({ output, olds }) {
176 const zoneId = output?.zoneId ?? (olds?.zoneId as string | undefined);
177 if (!zoneId) return undefined;
178 const observed = yield* pqe
179 .getOriginPostQuantumEncryption({ zoneId })
180 .pipe(
181 // Zone deleted out-of-band — the setting is gone with it.
182 Effect.catchTag("InvalidZoneIdentifier", () =>
183 Effect.succeed(undefined),
184 ),
185 );
186 if (observed === undefined) return undefined;
187 // The setting is a singleton that always exists with a Cloudflare
188 // default — there is nothing to "own", so a cold read adopts freely
189 // (never `Unowned`). The observed value at adoption time becomes the
190 // `initialValue` restored on destroy.
191 const initialValue =
192 output !== undefined ? output.initialValue : toValue(observed.value);
193 return toAttributes(zoneId, observed, initialValue);
194 }),
195
196 reconcile: Effect.fn(function* ({ news, output }) {
197 // Inputs have been resolved to concrete strings by Plan.
198 const zoneId = news.zoneId as string;
199 const desired: Value = news.value ?? "supported";
200
201 // 1. Observe — the setting always exists; read its live value.
202 const observed = yield* pqe.getOriginPostQuantumEncryption({ zoneId });
203
204 // 2. Capture — the pre-management value, restored on destroy.
205 // `output` (including an adoption read) already carries it;
206 // otherwise this is our first touch and the observed value is
207 // the zone's original.
208 const initialValue =
209 output !== undefined ? output.initialValue : toValue(observed.value);
210
211 // 3. Sync — update only when the observed value differs.
212 if (toValue(observed.value) === desired) {
213 return toAttributes(zoneId, observed, initialValue);
214 }
215 const updated = yield* pqe.putOriginPostQuantumEncryption({
216 zoneId,
217 value: desired,
218 });
219 return toAttributes(zoneId, updated, initialValue);
220 }),
221
222 delete: Effect.fn(function* ({ output }) {
223 const { zoneId, initialValue } = output;
224 // Observe — if the zone itself is gone, so is the setting.
225 const observed = yield* pqe
226 .getOriginPostQuantumEncryption({ zoneId })
227 .pipe(
228 Effect.catchTag("InvalidZoneIdentifier", () =>
229 Effect.succeed(undefined),
230 ),
231 );
232 if (observed === undefined) return;
233 // Restore the pre-management value; skip the call when it already
234 // matches (idempotent re-delete after a crashed run).
235 if (toValue(observed.value) === initialValue) return;
236 yield* pqe
237 .putOriginPostQuantumEncryption({ zoneId, value: initialValue })
238 .pipe(Effect.catchTag("InvalidZoneIdentifier", () => Effect.void));
239 }),
240 });
241
242/**
243 * Narrow the distilled response's open
244 * `"preferred" | "supported" | "off" | (string & {})` value to the closed
245 * triple — Cloudflare only ever returns the three literals for this
246 * setting; anything else collapses to the documented default.
247 */
248const toValue = (value: string): Value =>
249 value === "preferred" || value === "off" ? value : "supported";
250
251const toAttributes = (
252 zoneId: string,
253 setting:
254 | pqe.GetOriginPostQuantumEncryptionResponse
255 | pqe.PutOriginPostQuantumEncryptionResponse,
256 initialValue: Value,
257): Attributes => ({
258 zoneId,
259 value: toValue(setting.value),
260 editable: setting.editable,
261 modifiedOn: setting.modifiedOn ?? undefined,
262 initialValue,
263});