/** * Context passed to cache condition/key/tags functions. * * This is a subset of RequestContext that's guaranteed to be available * during cache key generation (before middleware runs). * * Note: While the full RequestContext is passed, middleware-set variables * read via `ctx.get()` may not be populated yet since cache lookup happens * before middleware execution. */ export type { RequestContext as CacheContext } from "../server/request-context.js"; /** * Cache configuration options for cache() DSL * * Controls how segments, layouts, and loaders are cached. * Cache configuration inherits down the route tree unless overridden. * * @example * ```typescript * // Basic caching with TTL * cache({ ttl: 60 }, () => [ * layout(), * route("post/:slug"), * ]) * * // With stale-while-revalidate * cache({ ttl: 60, swr: 300 }, () => [ * route("product/:id"), * ]) * * // Conditional caching * cache({ * ttl: 300, * condition: (ctx) => !ctx.request.headers.get('x-preview'), * }, () => [...]) * * // Custom cache key * cache({ * ttl: 300, * key: (ctx) => `product-${ctx.params.id}-${ctx.searchParams.get('variant')}`, * }, () => [...]) * * // With tags for invalidation * cache({ * ttl: 300, * tags: (ctx) => [`product:${ctx.params.id}`, 'products'], * }, () => [...]) * ``` */ export interface CacheOptions { /** * Time-to-live in seconds. * After this period, cached content is considered stale. */ ttl: number; /** * Stale-while-revalidate window in seconds (after TTL). * During this window, stale content is served immediately while * fresh content is fetched in the background via waitUntil. * * @example * // TTL: 60s, SWR: 300s * // 0-60s: FRESH (serve from cache) * // 60-360s: STALE (serve from cache, revalidate in background) * // 360s+: EXPIRED (cache miss, fetch fresh) */ swr?: number; /** * Override the cache store for this boundary. * When specified, this boundary and its children use this store * instead of the app-level store from handler config. * * Useful for: * - Different backends per route section (memory vs KV vs Redis) * - Loader-specific caching strategies * - Hot data in fast cache, cold data in larger/slower cache * * @example * ```typescript * const kvStore = new CloudflareKVStore(env.CACHE_KV); * const memoryStore = new MemorySegmentCacheStore({ defaults: { ttl: 10 } }); * * // Fast memory cache for hot data * cache({ store: memoryStore }, () => [ * route("dashboard"), * ]) * * // KV for larger, less frequently accessed data * cache({ store: kvStore, ttl: 3600 }, () => [ * route("archive/:year"), * ]) * ``` */ store?: import("../cache/types.js").SegmentCacheStore; /** * Conditional cache read function. * Return false to skip cache for this request (always fetch fresh). * * Has access to full RequestContext including env, request, params, cookies, etc. * Note: Middleware-set variables read via `ctx.get()` may not be populated yet. * * @example * ```typescript * condition: (ctx) => { * // Skip cache for preview mode * if (ctx.request.headers.get('x-preview')) return false; * // Skip cache for authenticated users * if (ctx.request.headers.has('authorization')) return false; * return true; * } * ``` */ condition?: ( ctx: import("../server/request-context.js").RequestContext, ) => boolean; /** * Custom cache key function - FULL OVERRIDE. * Bypasses default key generation AND store's keyGenerator. * * Has access to full RequestContext including env, request, params, cookies, etc. * Note: Middleware-set variables read via `ctx.get()` may not be populated yet. * * @example * ```typescript * // Include query params in cache key * key: (ctx) => `product-${ctx.params.id}-${ctx.searchParams.get('variant')}` * * // Include env bindings * key: (ctx) => `${ctx.env.REGION}:product:${ctx.params.id}` * * // Include cookies * key: (ctx) => `${cookies().get('locale')?.value ?? 'en'}:${ctx.pathname}` * ``` */ key?: ( ctx: import("../server/request-context.js").RequestContext, ) => string | Promise; /** * Tags for cache invalidation. * Can be a static array or a function that returns tags. * * Note: Tags are passed through to the store but built-in stores * (MemorySegmentCacheStore, CFCacheStore) do not yet index or * invalidate by tag. Effective tag-based invalidation requires a * custom store implementation with secondary indices. * * @example * ```typescript * // Static tags * tags: ['products', 'catalog'] * * // Dynamic tags * tags: (ctx) => [`product:${ctx.params.id}`, 'products'] * ``` */ tags?: | string[] | (( ctx: import("../server/request-context.js").RequestContext, ) => string[]); } /** * Partial cache options for cache() DSL. * * When `ttl` is not specified, it will use the default from cache config. * This allows cache() calls to inherit app-level defaults: * * @example * ```typescript * // App-level default (in handler config) * cache: { store: myStore, defaults: { ttl: 60 } } * * // Route-level (inherits ttl from defaults) * cache(() => [ * route("products"), * ]) * * // Override with explicit ttl * cache({ ttl: 300 }, () => [...]) * ``` */ export type PartialCacheOptions = Partial>; /** * Cache entry configuration stored in EntryData. * Represents the resolved cache config for a segment. */ export interface EntryCacheConfig { /** Cache options (false means caching disabled for this entry) - ttl is optional, uses defaults */ options: PartialCacheOptions | false; }