.phaze: Edge data (Cloudflare)
---platform and ---cloudflare are edge-data boundaries — a subsystem distinct from the four core boundaries (---page, ---data, ---state, ---props). The core four compile to exports, a loader, state, or params; these two compile to instance-scope edgeSignals whose values come from a built-in resolver reading Cloudflare’s per-request intelligence.
Unlike ---data (where you write name: value), these list bare field names — one per line — and the value comes from the resolver, not from you. ---platform answers “how is the user connecting?” (device + connection tech); ---cloudflare answers “who / where is the request from?” (geo / identity / bot).
What it exposes
Section titled “What it exposes”The resolvers read two Cloudflare-specific request sources during SSR:
-
request.cf— theIncomingRequestCfPropertiesobject Cloudflare attaches to every Worker request. Geo (country,city,timezone,regionCode,isEUCountry), connection (clientTcpRtt,clientQuicRtt,httpProtocol,edgeL4.deliveryRate), TLS (tlsVersion,tlsCipher), network (asn,asOrganization,colo), andbotManagementall live here. -
Cloudflare request headers — for the few fields not on
request.cf:Header Used for CF-Device-Typedevice(when thecache_by_device_typeCache Rule is on)CF-Connecting-IPconnectingIpTrue-Client-IPtrueClientIp(Enterprise)CF-RayrayCF-WorkerworkerCF-Visitorvisitor.scheme
---platformdeviceperf---cloudflarecountrycitytimezone---Compiles to
import { edgeSignal, __platform, __cloudflare } from '@madenowhere/phaze-cloudflare/runtime'
const device = edgeSignal(() => __platform().device)const perf = edgeSignal(() => __platform().perf)const country = edgeSignal(() => __cloudflare().country)const city = edgeSignal(() => __cloudflare().city)const timezone = edgeSignal(() => __cloudflare().timezone)The edgeSignal / __platform / __cloudflare imports are injected by phaze-compile’s babel pass (same merge-or-prepend as revalidate / @global) — you never write them.
Each field becomes an instance-scope edgeSignal — a real phaze Signal<T>. It resolves once on the edge during SSR (from request.cf + CF request headers), serializes into the page payload, and rehydrates on the client with no fetch — so it reads the same on both sides. Read it like any signal:
<p>{city} · {timezone}</p> {/* signal-as-child auto-binds */}watch(console.log(device(), city())) {/* or read explicitly */}---platform — how the user connects
Section titled “---platform — how the user connects”| Field | Type | Derived from |
|---|---|---|
device | 'mobile' | 'tablet' | 'desktop' | the CF-Device-Type header (set when the cache_by_device_type Cache Rule is on — it also partitions the edge cache per device), else a coarse User-Agent sniff. Not request.cf.deviceType — no such field exists. |
mobile | boolean | device !== 'desktop' — a convenience flag |
perf | { latency, protocol, throughput } | latency ← cf.clientTcpRtt ?? cf.clientQuicRtt; protocol ← cf.httpProtocol; throughput ← cf.edgeL4.deliveryRate |
tls | { version, cipher } | version ← cf.tlsVersion; cipher ← cf.tlsCipher |
network | { asn, asOrganization, colo } | cf.asn / cf.asOrganization / cf.colo |
---cloudflare — who / where the request is from
Section titled “---cloudflare — who / where the request is from”| Field | Type | Derived from |
|---|---|---|
country | string | undefined | cf.country (ISO 3166-1 alpha-2; same value as the CF-IPCountry header) |
eu | boolean | cf.isEUCountry === '1' |
region | string | undefined | cf.regionCode |
city | string | undefined | cf.city |
timezone | string | undefined | cf.timezone (IANA, e.g. America/New_York) |
connectingIp | string | null | the CF-Connecting-IP header |
trueClientIp | string | null | the True-Client-IP header (Enterprise) |
ray | string | null | the CF-Ray header |
worker | string | null | the CF-Worker header — the owning Worker zone’s hostname, stamped on Worker subrequests (loop detection) |
visitor | { scheme } | scheme ← the CF-Visitor header’s JSON (e.g. {"scheme":"https"}), parsed defensively — undefined if absent/malformed |
bot | { score, verified } | cf.botManagement.score / cf.botManagement.verifiedBot (requires Bot Management) |
Flat vs grouped. device / mobile and every ---cloudflare scalar carry a single value — device() is the string. perf / tls / network / visitor / bot carry an object — read a member off the signal: perf().latency, tls().version, visitor().scheme, bot().score. The compile shape is identical; only the resolver’s return type differs.
- Bare field names only — one identifier per line (same trailing-whitespace +
// commenttolerance as the other fences). Noname: value; the value is the resolver’s, not yours. - Unknown or duplicate fields surface a diagnostic and are skipped. The allowed sets are exactly the fields tabled above.
- Instance-scope, not module-scope —
edgeSignalmust run during render, so the declarations hoist into the component body (like Local---state), not above it. They’re in scope in the trailing region AND in every---mobile/---tablet/---desktopvariant body. - Initial state only — after hydration each is a regular signal; you may
.set()it, but the edge value is just the initial value (it never re-resolves client-side).