1import * as cache from "@distilled.cloud/cloudflare/cache";
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 = "Cloudflare.Cache.SmartTieredCache" as const;
12type TypeId = typeof TypeId;
13
14export interface SmartTieredCacheProps {
15 /**
16 * Zone whose Smart Tiered Cache setting is managed. Stable — changing
17 * the zone triggers a replacement (the old zone's setting is restored
18 * to the value it had before Alchemy managed it).
19 */
20 zoneId: string;
21 /**
22 * Whether Smart Tiered Cache is enabled on the zone (`value: "on"`)
23 * or disabled (`value: "off"`). Mutable — patched in place.
24 * @default true
25 */
26 enabled?: boolean;
27}
28
29export interface SmartTieredCacheAttributes {
30 /** Zone the setting belongs to. */
31 zoneId: string;
32 /** Resolved current value of the setting (`"on"` or `"off"`). */
33 value: string;
34 /**
35 * Whether the setting can be modified on the zone's current plan
36 * (`false` means the setting is plan-gated).
37 */
38 editable: boolean;
39 /** When the setting was last modified, if Cloudflare reports it. */
40 modifiedOn: string | undefined;
41 /**
42 * The value the setting had before Alchemy first patched it. Restored
43 * on destroy, so deleting the resource puts the zone back the way it
44 * was found.
45 */
46 initialValue: string;
47}
48
49export type SmartTieredCache = Resource<
50 TypeId,
51 SmartTieredCacheProps,
52 SmartTieredCacheAttributes,
53 never,
54 Providers
55>;
56
57/**
58 * The Smart Tiered Cache setting of a Cloudflare zone
59 * (`/zones/{zone_id}/cache/tiered_cache_smart_topology_enable`).
60 *
61 * Smart Tiered Cache dynamically selects the single best upper-tier data
62 * center for each origin, reducing requests that reach the origin. The
63 * setting is a zone **singleton** — it always exists on every zone (default
64 * `off`), so this resource never creates or deletes anything physical.
65 * Reconcile patches the setting when the observed value differs from the
66 * desired one; destroy restores the value the setting had before Alchemy
67 * first managed it (captured as `initialValue`).
68 *
69 * Only one `SmartTieredCache` resource per zone makes sense — two instances
70 * managing the same zone would fight over the singleton.
71 * ### Managing Smart Tiered Cache
72 * **Example:** Enable Smart Tiered Cache on a zone
73 * ```typescript
74 * const zone = yield* Cloudflare.Zone.Zone("Site", { name: "example.com" });
75 *
76 * yield* Cloudflare.Cache.SmartTieredCache("SmartCache", {
77 * zoneId: zone.zoneId,
78 * });
79 * ```
80 *
81 * **Example:** Explicitly disable Smart Tiered Cache
82 * ```typescript
83 * yield* Cloudflare.Cache.SmartTieredCache("SmartCache", {
84 * zoneId: zone.zoneId,
85 * enabled: false,
86 * });
87 * ```
88 *
89 * @see https://developers.cloudflare.com/cache/how-to/tiered-cache/
90 *
91 * @resource
92 * @product Cache
93 * @category Performance & Reliability
94 */
95export const SmartTieredCache = Resource<SmartTieredCache>(TypeId);
96
97/**
98 * Returns true if the given value is a SmartTieredCache resource.
99 */
100export const isSmartTieredCache = (value: unknown): value is SmartTieredCache =>
101 Predicate.hasProperty(value, "Type") && value.Type === TypeId;
102
103const desiredValue = (props: SmartTieredCacheProps): "on" | "off" =>
104 (props.enabled ?? true) ? "on" : "off";
105
106export const SmartTieredCacheProvider = () =>
107 Provider.succeed(SmartTieredCache, {
108 nuke: { singleton: true },
109 stables: ["zoneId", "initialValue"],
110
111 list: Effect.fn(function* () {
112 const { accountId } = yield* yield* CloudflareEnvironment;
113 // No account-wide API for this zone singleton — enumerate every
114 // zone in the account and read its setting (every zone has one).
115 const allZones = yield* listAllZones(accountId);
116 const rows = yield* Effect.forEach(
117 allZones.map((zone) => zone.id),
118 (zoneId) =>
119 cache.getSmartTieredCache({ zoneId }).pipe(
120 Effect.map((observed) =>
121 toAttributes(zoneId, observed, observed.value),
122 ),
123 // Smart Tiered Cache is an Enterprise feature. When enumerating
124 // every zone in the account, zones that aren't entitled reject the
125 // read — as `InvalidRoute`, or as a persistent `Forbidden`/
126 // `Unauthorized` ("Access denied") on the gated route. These are
127 // not the transient code-10000 auth blips the global policy
128 // retries; the zone simply has no readable setting, so skip it.
129 Effect.catchTag(["InvalidRoute", "Forbidden", "Unauthorized"], () =>
130 Effect.succeed(undefined),
131 ),
132 ),
133 { concurrency: 10 },
134 );
135 return rows.filter(
136 (row): row is SmartTieredCacheAttributes => row !== undefined,
137 );
138 }),
139
140 diff: Effect.fn(function* ({ olds = {}, news, output }) {
141 const o = olds as SmartTieredCacheProps;
142 const n = news as SmartTieredCacheProps;
143 // zoneId is Input<string>; compare only once both sides are concrete.
144 const oldZoneId =
145 output?.zoneId ?? (typeof o.zoneId === "string" ? o.zoneId : undefined);
146 if (
147 oldZoneId !== undefined &&
148 typeof n.zoneId === "string" &&
149 oldZoneId !== n.zoneId
150 ) {
151 return { action: "replace" } as const;
152 }
153 return undefined;
154 }),
155
156 read: Effect.fn(function* ({ output, olds }) {
157 const zoneId = output?.zoneId ?? (olds?.zoneId as string | undefined);
158 if (!zoneId) return undefined;
159 const observed = yield* cache.getSmartTieredCache({ zoneId }).pipe(
160 // Zone deleted out-of-band — the setting is gone with it.
161 Effect.catchTag("InvalidRoute", () => Effect.succeed(undefined)),
162 );
163 if (observed === undefined) return undefined;
164 // The setting is a singleton that always exists with a Cloudflare
165 // default — there is nothing to "own", so a cold read adopts
166 // freely (never `Unowned`). The observed value at adoption time
167 // becomes the `initialValue` restored on destroy.
168 const initialValue =
169 output !== undefined ? output.initialValue : observed.value;
170 return toAttributes(zoneId, observed, initialValue);
171 }),
172
173 reconcile: Effect.fn(function* ({ news, output }) {
174 // Inputs have been resolved to concrete strings by Plan.
175 const zoneId = news.zoneId as string;
176
177 // 1. Observe — the setting always exists; read its live value.
178 const observed = yield* cache.getSmartTieredCache({ zoneId });
179
180 // 2. Capture — the pre-management value, restored on destroy.
181 // `output` (including an adoption read) already carries it;
182 // otherwise this is our first touch and the observed value is
183 // the zone's original.
184 const initialValue =
185 output !== undefined ? output.initialValue : observed.value;
186
187 // 3. Sync — patch only when the observed value differs.
188 const desired = desiredValue(news);
189 if (observed.value === desired) {
190 return toAttributes(zoneId, observed, initialValue);
191 }
192 const patched = yield* cache.patchSmartTieredCache({
193 zoneId,
194 value: desired,
195 });
196 return toAttributes(zoneId, patched, initialValue);
197 }),
198
199 delete: Effect.fn(function* ({ output }) {
200 const { zoneId, initialValue } = output;
201 // Observe — if the zone itself is gone, so is the setting.
202 const observed = yield* cache
203 .getSmartTieredCache({ zoneId })
204 .pipe(Effect.catchTag("InvalidRoute", () => Effect.succeed(undefined)));
205 if (observed === undefined) return;
206 // Restore the pre-management value; skip the call when it already
207 // matches (idempotent re-delete after a crashed run).
208 if (observed.value === initialValue) return;
209 yield* cache
210 .patchSmartTieredCache({ zoneId, value: initialValue })
211 .pipe(Effect.catchTag("InvalidRoute", () => Effect.void));
212 }),
213 });
214
215const toAttributes = (
216 zoneId: string,
217 setting:
218 | cache.GetSmartTieredCacheResponse
219 | cache.PatchSmartTieredCacheResponse,
220 initialValue: string,
221): SmartTieredCacheAttributes => ({
222 zoneId,
223 value: setting.value,
224 editable: setting.editable,
225 modifiedOn: setting.modifiedOn ?? undefined,
226 initialValue,
227});