declare global { interface CacheStorage { readonly default: Cache; } } /** * Cloudflare Edge Cache Store * * Production cache store using Cloudflare's Cache API. * Handles SWR atomically - get() checks staleness and marks REVALIDATING in one operation. * * Features: * - Extended TTL for SWR window (max-age = ttl + swr) * - Staleness via x-edge-cache-stale-at header * - Atomic REVALIDATING status for thundering herd prevention * - Non-blocking writes via waitUntil */ import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult } from "../types.js"; import { type RequestContext } from "../../server/request-context.js"; /** Header storing timestamp when entry becomes stale */ export declare const CACHE_STALE_AT_HEADER = "x-edge-cache-stale-at"; /** Header storing cache status: HIT | REVALIDATING */ export declare const CACHE_STATUS_HEADER = "x-edge-cache-status"; /** * Maximum age in seconds for REVALIDATING status before allowing new revalidation. * After this period, a stale entry in REVALIDATING status will trigger revalidation again. * @internal */ export declare const MAX_REVALIDATION_INTERVAL = 30; /** * Cloudflare Workers ExecutionContext (subset we need) */ export interface ExecutionContext { waitUntil(promise: Promise): void; passThroughOnException(): void; } export interface CFCacheStoreOptions { /** * Cache namespace. If not provided, uses caches.default (recommended). * Only set this if you need isolated cache storage. */ namespace?: string; /** * Base URL for cache keys. * * If not provided, derives from request hostname via requestContext: * - Production domains → uses `https://{hostname}/` * - Dev/preview (localhost, workers.dev, pages.dev) → uses internal fallback URL */ baseUrl?: string; /** Default cache options */ defaults?: CacheDefaults; /** * Cloudflare ExecutionContext for non-blocking cache writes. * Pass the `ctx` from your worker's fetch handler. * * @example * ```typescript * new CFCacheStore({ ctx: env.ctx }) * ``` */ ctx: ExecutionContext; /** * Cache version string override. When this changes, all cached entries are * effectively invalidated (new keys won't match old entries). * * Defaults to the auto-generated VERSION from `rsc-router:version` virtual module. * Only set this if you need a custom versioning strategy. */ version?: string; /** * Custom key generator applied to all cache operations. * Receives the full RequestContext (including env) and the default-generated key. * Return value becomes the final cache key (unless route overrides with `key` option). * * @example Using headers for user segmentation * ```typescript * keyGenerator: (ctx, defaultKey) => { * const segment = ctx.request.headers.get('x-user-segment') || 'default'; * return `${segment}:${defaultKey}`; * } * ``` * * @example Using env bindings for multi-region * ```typescript * keyGenerator: (ctx, defaultKey) => { * const region = ctx.env.REGION || 'us'; * return `${region}:${defaultKey}`; * } * ``` * * @example Using cookies for locale-aware caching * ```typescript * keyGenerator: (ctx, defaultKey) => { * const locale = ctx.cookie('locale') || 'en'; * return `${locale}:${defaultKey}`; * } * ``` */ keyGenerator?: (ctx: RequestContext, defaultKey: string) => string | Promise; } /** * Cache status values for the x-edge-cache-status header. * @internal */ export type CacheStatus = "HIT" | "REVALIDATING"; export declare class CFCacheStore implements SegmentCacheStore { readonly defaults?: CacheDefaults; readonly keyGenerator?: (ctx: RequestContext, defaultKey: string) => string | Promise; private readonly namespace?; private readonly baseUrl; private readonly waitUntil?; private readonly version?; constructor(options: CFCacheStoreOptions); /** * Derive base URL from request hostname via requestContext. * Uses internal fallback for dev/preview environments. * @internal */ private deriveBaseUrl; /** * Get the cache instance - uses caches.default unless namespace is specified. * @internal */ private getCache; /** * Get cached entry data by key. * * Handles SWR atomically: * - If stale and not already revalidating, marks as REVALIDATING and returns shouldRevalidate: true * - If already REVALIDATING (and recent), returns shouldRevalidate: false * - If fresh, returns shouldRevalidate: false * * The atomic mark prevents thundering herd - only first request triggers revalidation. */ get(key: string): Promise; /** * Store entry data with TTL and optional SWR window. * Uses waitUntil for non-blocking write when available. */ set(key: string, data: CachedEntryData, ttl: number, swr?: number): Promise; /** * Delete a cached entry */ delete(key: string): Promise; /** * Get a cached Response by key (for document-level caching). * Returns the response and whether it should be revalidated (SWR). */ getResponse(key: string): Promise<{ response: Response; shouldRevalidate: boolean; } | null>; /** * Store a Response with TTL and optional SWR window (for document-level caching). */ putResponse(key: string, response: Response, ttl: number, swr?: number): Promise; /** * Convert string key to Request object for CF Cache API. * Includes version in URL if specified (for cache invalidation on code changes). * @internal */ private keyToRequest; } //# sourceMappingURL=cf-cache-store.d.ts.map