/** * Cloudflare Workers entry - the app, cached in Workers KV so cached pages and on-demand purges hold * ACROSS instances. `ctx.waitUntil` keeps the worker alive while a stale page regenerates behind the * response. `bun run build` then `bunx wrangler dev` (a local KV is provided automatically). */ import { inProcessClient } from "@nifrajs/client" import { createWebApp, KVCacheStore, type KVNamespaceLike, revalidateEndpoint, withISR, } from "@nifrajs/web" import { reactAdapter } from "@nifrajs/web-react" import { backend } from "./backend" // Generated by build.ts (buildServer): the static-import route manifest + the baked client entry URL. import { clientEntry, manifest } from "./server-manifest" const app = createWebApp({ adapter: reactAdapter, manifest, clientEntry, api: inProcessClient(backend), title: "nifra + ISR", }) /** Workers bindings (wrangler.toml → [[kv_namespaces]] + [vars]). */ interface Env { readonly ISR_CACHE: KVNamespaceLike readonly REVALIDATE_SECRET?: string } /** Minimal shape of the Workers execution context - just `waitUntil`. */ interface ExecutionContext { waitUntil(promise: Promise): void } export default { async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise { const store = new KVCacheStore(env.ISR_CACHE, { expirationTtl: 86_400 }) // 1-day GC backstop // On-demand purge: POST /__nifra/revalidate?path=/ with the secret in x-nifra-revalidate-token. if (new URL(req.url).pathname === "/__nifra/revalidate") { return revalidateEndpoint({ store, secret: env.REVALIDATE_SECRET ?? "" })(req) } // Cache GET documents stale-while-revalidate. Default 60s; a route's `export const revalidate` // overrides it per-page. const isr = withISR(app, { store, revalidate: 60, now: () => Date.now() }) return isr(req, { env, waitUntil: (p) => ctx.waitUntil(p) }) }, }