/** * Cache Store Types * * Generic caching interface supporting multiple value types. * Designed to be implemented by different backends: * - MemoryCacheStore (dev/testing) * - Cloudflare Cache API adapter * - Cloudflare KV adapter * - Redis adapter */ import type { ResolvedSegment } from "../types.js"; import type { RequestContext } from "../server/request-context.js"; /** * Result from cache get() including data and revalidation status */ export interface CacheGetResult { /** The cached entry data */ data: CachedEntryData; /** * Whether the caller should trigger background revalidation. * True when entry is stale AND not already being revalidated. * The store atomically marks the entry as REVALIDATING when returning true. */ shouldRevalidate: boolean; } /** * Low-level segment cache store interface. * * Implementations handle the actual storage (memory, KV, Redis, etc.). * The store deals with serialized data - RSC serialization is handled * by the cache provider layer. * * @typeParam TEnv - Platform bindings type (e.g., Cloudflare env) */ export interface SegmentCacheStore { /** * Default cache options for this store. * Used by cache() boundaries when ttl/swr are not explicitly specified. */ readonly defaults?: CacheDefaults; /** * Custom key generator applied to all cache operations using this store. * Receives the full RequestContext and the default-generated key. * Return value becomes the final cache key (unless route overrides with `key` option). * * Resolution priority: * 1. Route-level `key` function (full override) * 2. Store-level `keyGenerator` (modifies default key) * 3. Default key generation (prefix:pathname:params) * * @example Using headers for cache segmentation * ```typescript * keyGenerator: (ctx, defaultKey) => { * const segment = ctx.request.headers.get('x-user-segment') || 'default'; * return `${segment}:${defaultKey}`; * } * ``` * * @example Using env bindings (Cloudflare) * ```typescript * keyGenerator: (ctx, defaultKey) => { * const region = ctx.env.REGION || 'us'; * return `${region}:${defaultKey}`; * } * ``` * * @example Using cookies for locale * ```typescript * keyGenerator: (ctx, defaultKey) => { * const locale = ctx.cookie('locale') || 'en'; * return `${locale}:${defaultKey}`; * } * ``` */ readonly keyGenerator?: (ctx: RequestContext, defaultKey: string) => string | Promise; /** * Get cached entry data by key * @returns Cache result with data and staleness, or null if not found/expired */ get(key: string): Promise; /** * Store entry data with TTL * @param key - Cache key * @param data - Serialized entry data * @param ttl - Time-to-live in seconds * @param swr - Optional stale-while-revalidate window in seconds */ set(key: string, data: CachedEntryData, ttl: number, swr?: number): Promise; /** * Delete a cached entry * @returns true if deleted, false if not found */ delete(key: string): Promise; /** * Clear all cached entries (optional, for testing) */ clear?(): Promise; /** * Get a cached Response by key. * 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. * @param key - Cache key * @param response - Response to cache (will be cloned) * @param ttl - Time-to-live in seconds * @param swr - Optional stale-while-revalidate window in seconds */ putResponse?(key: string, response: Response, ttl: number, swr?: number): Promise; } /** * Serialized segment data stored in cache * Note: loading is preserved to ensure consistent tree structure between cached and fresh renders * * @internal This type is an implementation detail and may change without notice. */ export interface SerializedSegmentData { /** RSC-encoded component string */ encoded: string; /** RSC-encoded layout string (if present) */ encodedLayout?: string; /** RSC-encoded loading skeleton string (if present), or "null" for explicit null */ encodedLoading?: string; /** RSC-encoded loaderData (if present) */ encodedLoaderData?: string; /** RSC-encoded loaderDataPromise (if present) */ encodedLoaderDataPromise?: string; /** Segment metadata (everything except component, layout, loading, and loader data) */ metadata: Omit; } /** * Raw data stored in cache for an entry * * @internal This type is an implementation detail and may change without notice. */ export interface CachedEntryData { /** Serialized segments for this entry */ segments: SerializedSegmentData[]; /** Handle data keyed by segment ID */ handles: Record; /** Expiration timestamp (ms since epoch) */ expiresAt: number; } /** * Default cache options applied to all cache() boundaries. * Individual cache() calls can override any of these values. * * @example * ```ts * const store = new CFCacheStore({ * defaults: { ttl: 60, swr: 300 } * }); * ``` */ export interface CacheDefaults { /** * Default time-to-live in seconds. * After TTL expires, cached entry is considered stale. */ ttl?: number; /** * Default stale-while-revalidate window in seconds. * During SWR window, stale content is served while revalidating in background. */ swr?: number; } /** * Cache configuration for RSC handler */ export interface CacheConfig { /** Cache store implementation (includes defaults) */ store: SegmentCacheStore; /** Enable/disable caching (default: true) */ enabled?: boolean; } /** * Cache configuration - can be static or a function receiving env */ export type CacheConfigOrFactory = CacheConfig | ((env: TEnv) => CacheConfig); /** * Handle data for a single segment * Structure: { handleName: [values...] } */ export type SegmentHandleData = Record; /** * Result from cache get() including segments and their handle data * Each entry can produce multiple segments (main + parallels) */ export interface CachedEntryResult { /** All segments for this entry (main segment + parallels) */ segments: ResolvedSegment[]; /** Handle data keyed by segment ID */ handles: Record; } /** * Segment cache provider interface * * Used by router to check/store segment cache during matching. * Accessed via request context - if not present, caching is disabled. * * @internal Not currently implemented - CacheScope is used directly. * Reserved for future extensibility. */ export interface SegmentCacheProvider { /** Whether caching is enabled for this request */ readonly enabled: boolean; /** * Get cached segments and restore handles/loaders. * * Combines cache get with handle replay and loader data restoration. * Returns tuple of [segments, segmentIds] if cache hit, null if miss or disabled. * * @param cacheKey - Cache key to look up * @param params - Route params for cache key generation * @param loaderPromises - Map to restore loader data into * @returns Tuple of [segments, segmentIds] or null if miss */ restore(cacheKey: string, params: Record, loaderPromises: Map>): Promise<[ResolvedSegment[], string[]] | null>; /** * Cache entry with automatic handle collection (non-blocking). * * Schedules caching via waitUntil - handles are collected after they settle. * Validates segments have actual components before caching. * * @param cacheKey - The cache key to store under * @param segments - All resolved segments for this entry */ cacheEntry(cacheKey: string, segments: ResolvedSegment[]): void; } /** * Supported cache value types for the generic CacheStore interface. * @internal Reserved for future extensibility */ export type CacheValue = ReadableStream | Response | ArrayBuffer | string | unknown[] | Record; /** * Cache entry returned by match(). * @internal Reserved for future extensibility */ export interface CacheEntry { /** The cached value */ value: T; /** Optional metadata stored with the entry */ metadata?: CacheMetadata; } /** * Original value type for reconstruction. * @internal Reserved for future extensibility */ export type CacheValueType = "stream" | "response" | "arraybuffer" | "string" | "object"; /** * Metadata associated with a cache entry. * @internal Reserved for future extensibility */ export interface CacheMetadata { /** Timestamp when entry expires (ms since epoch) */ expiresAt?: number; /** Tags for bulk invalidation */ tags?: string[]; /** Original value type for reconstruction on read */ valueType?: CacheValueType; /** Response headers (preserved when caching Response) */ responseHeaders?: Record; /** Response status (preserved when caching Response) */ responseStatus?: number; /** Custom metadata */ [key: string]: unknown; } /** * Options for put(). * @internal Reserved for future extensibility */ export interface CachePutOptions { /** Time-to-live in seconds */ ttl?: number; /** Metadata to store with entry */ metadata?: Omit; } /** * Generic cache store interface for arbitrary value types. * * This interface is designed for future extensibility to support caching * responses, streams, and other values. Currently, segment caching uses * the SegmentCacheStore interface directly. * * Implementations must handle: * - Stream values (clone before storing, streams can only be read once) * - Promise values (await before storing) * - Expiration/TTL * * @internal Reserved for future extensibility */ export interface CacheStore { /** * Retrieve a cached entry by key. * @param key - Cache key * @returns The cached entry or undefined if not found/expired */ match(key: string): Promise | undefined>; /** * Store a value in the cache. * @param key - Cache key * @param value - Value to cache (stream, response, string, object, etc.) * @param options - TTL, metadata, etc. */ put(key: string, value: T, options?: CachePutOptions): Promise; /** * Delete a cached entry. * @param key - Cache key * @returns true if entry was deleted, false if not found */ delete(key: string): Promise; } //# sourceMappingURL=types.d.ts.map