1import * as argo from "@distilled.cloud/cloudflare/argo";
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.Argo.SmartRouting" as const;
12type TypeId = typeof TypeId;
13
14export type SmartRoutingProps = {
15 /**
16 * Zone the Argo Smart Routing setting belongs to. Stable — changing the
17 * zone triggers a replacement (the old zone's setting is restored to
18 * the value it had before Alchemy managed it).
19 */
20 zoneId: string;
21 /**
22 * Whether Argo Smart Routing is enabled on the zone. Mutable — patched
23 * in place.
24 *
25 * @default true
26 */
27 enabled?: boolean;
28};
29
30export type SmartRoutingAttributes = {
31 /** Zone the Argo Smart Routing setting belongs to. */
32 zoneId: string;
33 /** Resolved current value of the setting (`"on"` or `"off"`). */
34 value: "on" | "off";
35 /**
36 * Whether the setting can be modified on the zone's current plan.
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: "on" | "off";
47};
48
49export type SmartRouting = Resource<
50 TypeId,
51 SmartRoutingProps,
52 SmartRoutingAttributes,
53 never,
54 Providers
55>;
56
57/**
58 * Argo Smart Routing for a Cloudflare zone
59 * (`/zones/{zone_id}/argo/smart_routing`).
60 *
61 * Argo Smart Routing routes traffic across Cloudflare's network over the
62 * least-congested, most-reliable paths instead of standard BGP routes,
63 * reducing time to first byte for origin-bound requests.
64 *
65 * The setting is a singleton — it always exists on every zone with a
66 * Cloudflare default, so this resource never creates or deletes anything
67 * physical. Reconcile patches the setting when the observed value differs
68 * from the desired one; destroy restores the value the setting had before
69 * Alchemy first managed it (captured as `initialValue`).
70 *
71 * Argo Smart Routing is a paid, usage-billed add-on. On a zone without
72 * the Argo subscription every read or patch of this setting fails with
73 * the typed `NotAuthorized` error (Cloudflare code 1015) — purchase the
74 * add-on on the zone before managing this resource.
75 * ### Enabling Smart Routing
76 * **Example:** Enable Argo Smart Routing on a zone
77 * ```typescript
78 * const zone = yield* Cloudflare.Zone.Zone("Site", { name: "example.com" });
79 *
80 * yield* Cloudflare.Argo.SmartRouting("SmartRouting", {
81 * zoneId: zone.zoneId,
82 * });
83 * ```
84 *
85 * **Example:** Explicitly disable Argo Smart Routing
86 * ```typescript
87 * yield* Cloudflare.Argo.SmartRouting("SmartRouting", {
88 * zoneId: zone.zoneId,
89 * enabled: false,
90 * });
91 * ```
92 *
93 * @see https://developers.cloudflare.com/argo-smart-routing/
94 *
95 * @resource
96 * @product Argo
97 * @category Performance & Reliability
98 */
99export const SmartRouting = Resource<SmartRouting>(TypeId);
100
101/**
102 * Returns true if the given value is a SmartRouting resource.
103 */
104export const isSmartRouting = (value: unknown): value is SmartRouting =>
105 Predicate.hasProperty(value, "Type") && value.Type === TypeId;
106
107export const SmartRoutingProvider = () =>
108 Provider.succeed(SmartRouting, {
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.
115 const allZones = yield* listAllZones(accountId);
116 const rows = yield* Effect.forEach(
117 allZones.map((zone) => zone.id),
118 (zoneId) =>
119 argo.getSmartRouting({ zoneId }).pipe(
120 Effect.map((observed) =>
121 toAttributes(zoneId, observed, toValue(observed.value)),
122 ),
123 // Argo Smart Routing is a paid add-on — zones without the
124 // subscription reject every read with the typed entitlement
125 // tag (code 1015); skip them. Zones deleted out-of-band
126 // surface InvalidObjectIdentifier.
127 Effect.catchTag("NotAuthorized", () => Effect.succeed(undefined)),
128 Effect.catchTag("InvalidObjectIdentifier", () =>
129 Effect.succeed(undefined),
130 ),
131 ),
132 { concurrency: 10 },
133 );
134 return rows.filter(
135 (row): row is SmartRoutingAttributes => row !== undefined,
136 );
137 }),
138
139 diff: Effect.fn(function* ({ olds = {}, news, output }) {
140 const o = olds as SmartRoutingProps;
141 const n = news as SmartRoutingProps;
142 // zoneId is Input<string>; compare only once both sides are concrete.
143 const oldZoneId =
144 output?.zoneId ?? (typeof o.zoneId === "string" ? o.zoneId : undefined);
145 if (
146 oldZoneId !== undefined &&
147 typeof n.zoneId === "string" &&
148 oldZoneId !== n.zoneId
149 ) {
150 return { action: "replace" } as const;
151 }
152 return undefined;
153 }),
154
155 read: Effect.fn(function* ({ output, olds }) {
156 const zoneId = output?.zoneId ?? (olds?.zoneId as string | undefined);
157 if (!zoneId) return undefined;
158 const observed = yield* argo.getSmartRouting({ zoneId }).pipe(
159 // Zone deleted out-of-band — the setting is gone with it.
160 Effect.catchTag("InvalidObjectIdentifier", () =>
161 Effect.succeed(undefined),
162 ),
163 // Argo subscription removed out-of-band — the setting is no
164 // longer visible or manageable on the zone.
165 Effect.catchTag("NotAuthorized", () => Effect.succeed(undefined)),
166 );
167 if (observed === undefined) return undefined;
168 // The setting is a singleton that always exists with a Cloudflare
169 // default — there is nothing to "own", so a cold read adopts freely
170 // (never `Unowned`). The observed value at adoption time becomes the
171 // `initialValue` restored on destroy.
172 const initialValue =
173 output !== undefined ? output.initialValue : toValue(observed.value);
174 return toAttributes(zoneId, observed, initialValue);
175 }),
176
177 reconcile: Effect.fn(function* ({ news, output }) {
178 // Inputs have been resolved to concrete strings by Plan.
179 const zoneId = news.zoneId as string;
180 const desired: "on" | "off" = (news.enabled ?? true) ? "on" : "off";
181
182 // 1. Observe — the setting always exists (NotAuthorized propagates
183 // as a typed, clear "Argo subscription required" failure).
184 const observed = yield* argo.getSmartRouting({ zoneId });
185
186 // 2. Capture — the pre-management value, restored on destroy.
187 // `output` (including an adoption read) already carries it;
188 // otherwise this is our first touch and the observed value is
189 // the zone's original.
190 const initialValue =
191 output !== undefined ? output.initialValue : toValue(observed.value);
192
193 // 3. Sync — patch only when the observed value differs.
194 if (toValue(observed.value) === desired) {
195 return toAttributes(zoneId, observed, initialValue);
196 }
197 const patched = yield* argo.patchSmartRouting({
198 zoneId,
199 value: desired,
200 });
201 return toAttributes(zoneId, patched, initialValue);
202 }),
203
204 delete: Effect.fn(function* ({ output }) {
205 const { zoneId, initialValue } = output;
206 // Observe — if the zone is gone, or the Argo subscription was
207 // removed out-of-band, the setting is no longer ours to restore.
208 const observed = yield* argo.getSmartRouting({ zoneId }).pipe(
209 Effect.catchTag("InvalidObjectIdentifier", () =>
210 Effect.succeed(undefined),
211 ),
212 Effect.catchTag("NotAuthorized", () => Effect.succeed(undefined)),
213 );
214 if (observed === undefined) return;
215 // Restore the pre-management value; skip the call when it already
216 // matches (idempotent re-delete after a crashed run).
217 if (toValue(observed.value) === initialValue) return;
218 yield* argo.patchSmartRouting({ zoneId, value: initialValue }).pipe(
219 Effect.catchTag("InvalidObjectIdentifier", () => Effect.void),
220 Effect.catchTag("NotAuthorized", () => Effect.void),
221 );
222 }),
223 });
224
225/**
226 * Narrow the distilled response's open `"on" | "off" | (string & {})`
227 * value to the closed pair — Cloudflare only ever returns the two
228 * literals for this setting.
229 */
230const toValue = (value: string): "on" | "off" =>
231 value === "on" ? "on" : "off";
232
233const toAttributes = (
234 zoneId: string,
235 setting: argo.GetSmartRoutingResponse | argo.PatchSmartRoutingResponse,
236 initialValue: "on" | "off",
237): SmartRoutingAttributes => ({
238 zoneId,
239 value: toValue(setting.value),
240 editable: setting.editable,
241 modifiedOn: setting.modifiedOn ?? undefined,
242 initialValue,
243});