// GET /api/state: live counts for every wave and tier, the open wave, activation status and burn // amounts. Public and identical for everyone, so the edge keeps it for 5 seconds. import { getState, type CardState } from '../../server/card.ts' import { ConfigError, configFailure, contextFor, hostAllowed, type CardEnv } from '../../server/env.ts' import { failure, json } from '../../server/http.ts' const TTL = 5 // One chain read per isolate every TTL seconds, on top of the edge cache (which does nothing on // *.pages.dev). Only the finished value is shared, never a pending read: Workers cancel a request's // work when its client leaves, which would leave every other request waiting on that read hanging. // Its cost stays near one read per isolate every 5 s however busy it gets (simultaneous misses each // read once), so it needs no rate limit. let memo: { until: number; state: CardState } | null = null async function sharedState(env: CardEnv): Promise { if (memo && memo.until > Date.now()) return memo.state const state = await getState(await contextFor(env)) memo = { until: Date.now() + TTL * 1000, state } return state } export const onRequestGet: PagesFunction = async ({ request, env, waitUntil }) => { if (!hostAllowed(request, env)) return json({ error: 'Use the card app at its published address.', code: 'forbidden' }, 403) const cache = caches.default const key = new Request(new URL('/api/state', request.url).toString()) const hit = await cache.match(key) if (hit) return hit try { const res = json(await sharedState(env), 200, `public, max-age=${TTL}`) waitUntil(cache.put(key, res.clone())) return res } catch (err) { return err instanceof ConfigError ? configFailure(err) : failure(err, 'state') } }