import { RedisClientOptions } from 'redis'; /** * Pluggable wire-format codec for Redis string values used by `RedisStringsHandler`. * * The serializer is the single point at which an in-memory `CacheEntry` becomes the * string written to Redis (and back). Plugging in a custom serializer lets you add * compression (gzip/brotli), encryption, or any other custom encoding without forking * this package or losing the existing dedup / batch / keyspace features. * * Both `serialize` and `deserialize` may return either a value directly or a `Promise`, * which enables non-blocking async codecs such as stream-based compression * (`zlib.brotliCompress`) or encryption (`crypto.subtle`). Synchronous implementations * continue to work unchanged - awaiting a plain value is a no-op. * * The default export {@link jsonCacheValueSerializer} is `JSON.stringify` / * `JSON.parse` paired with {@link bufferAndMapReplacer} / {@link bufferAndMapReviver}, * so native `Buffer` and `Map` values inside a `CacheEntry` round-trip transparently * (this is required for Next.js RSC payloads). * * Operational note: changing the serializer (or any of its parameters such as a * compression level or encryption key) makes existing Redis keys unreadable, because * the deserializer will fail or return `null` for entries written by the previous * format. Either flush the affected keys, bump `keyPrefix`, or migrate values * out-of-band before deploying a new serializer. */ type CacheValueSerializer = { /** * Encode an in-memory `CacheEntry` into the string written to Redis. * May return a `Promise` for async codecs (e.g. compression, encryption). */ serialize(value: CacheEntry): string | Promise; /** * Decode a string read from Redis back into a `CacheEntry`. * Returning `null` (or a `Promise`) signals "treat as cache miss" - * the handler will return `null` from `get()` without surfacing an error. * May return a `Promise` for async codecs. */ deserialize(stored: string): CacheEntry | null | Promise; }; /** * Default serializer used by `RedisStringsHandler` when no `valueSerializer` is * configured. Wraps `JSON.stringify` / `JSON.parse` with this package's * {@link bufferAndMapReplacer} / {@link bufferAndMapReviver} so native `Buffer` * and `Map` values inside a `CacheEntry` survive the round-trip. * * Exported as a singleton so consumers can compare against the default by * reference (e.g. to detect that no custom serializer was configured). */ declare const jsonCacheValueSerializer: CacheValueSerializer; type CacheEntry = { value: unknown; lastModified: number; tags: string[]; }; type CreateRedisStringsHandlerOptions = { /** Redis redisUrl to use. * @default process.env.REDIS_URL? process.env.REDIS_URL : process.env.REDISHOST ? `redis://${process.env.REDISHOST}:${process.env.REDISPORT}` : 'redis://localhost:6379' */ redisUrl?: string; /** Redis database number to use. Uses DB 0 for production, DB 1 otherwise * @default process.env.VERCEL_ENV === 'production' ? 0 : 1 */ database?: number; /** Prefix added to all Redis keys * @default process.env.KEY_PREFIX || process.env.VERCEL_URL || 'UNDEFINED_URL_' */ keyPrefix?: string; /** Timeout in milliseconds for time critical Redis operations (during cache get, which blocks site rendering). * If redis get is not fulfilled within this time, the cache handler will return null so site rendering will * not be blocked further and site can fallback to re-render/re-fetch the content. * @default 500 */ getTimeoutMs?: number; /** Number of entries to query in one batch during full sync of shared tags hash map * @default 250 */ revalidateTagQuerySize?: number; /** Key used to store shared tags hash map in Redis * @default '__sharedTags__' */ sharedTagsKey?: string; /** Average interval in milliseconds between tag map full re-syncs * @default 3600000 (1 hour) */ avgResyncIntervalMs?: number; /** Enable deduplication of Redis get requests via internal in-memory cache * @default true */ redisGetDeduplication?: boolean; /** Time in milliseconds to cache Redis get results in memory. Set this to 0 to disable in-memory caching completely * @default 10000 */ inMemoryCachingTime?: number; /** Default stale age in seconds for cached items * @default 1209600 (14 days) */ defaultStaleAge?: number; /** Function to calculate expire age (redis TTL value) from stale age * @default Production: staleAge * 2, Other: staleAge * 1.2 */ estimateExpireAge?: (staleAge: number) => number; /** Kill container on Redis client error if error threshold is reached * @default 0 (0 means no error threshold) */ killContainerOnErrorThreshold?: number; /** Additional Redis client socket options * @example { tls: true, rejectUnauthorized: false } */ socketOptions?: RedisClientOptions['socket']; /** Additional Redis client options to be passed directly to createClient * @example { username: 'user', password: 'pass' } */ clientOptions?: Omit; /** Pluggable wire-format codec for Redis string values. Lets you plug in * compression (gzip/brotli), encryption, or any other custom encoding without * forking this package or losing the existing dedup / batch / keyspace features. * * Both `serialize` and `deserialize` may return a `Promise`, enabling * non-blocking async codecs (e.g. `zlib.brotliCompress`) that don't block the * Node.js event loop. Synchronous implementations continue to work unchanged. * * Only the main cache-entry storage path is routed through the serializer. * The shared-tags map and the revalidated-tags map are not. The in-memory * deduplication cache stores the wire-format string verbatim - its contents * change with the serializer, but the cache itself is not re-encoded. * * Operational note: changing the serializer (or any of its parameters such as * a compression level or encryption key) makes existing Redis keys unreadable. * Either flush the affected keys or bump `keyPrefix` before deploying. * * @default jsonCacheValueSerializer (JSON.stringify with bufferAndMapReplacer * so native Buffer and Map values inside a CacheEntry round-trip transparently) */ valueSerializer?: CacheValueSerializer; }; declare class RedisStringsHandler { private client; private sharedTagsMap; private revalidatedTagsMap; private inMemoryDeduplicationCache; private getTimeoutMs; private redisGet; private redisDeduplicationHandler; private deduplicatedRedisGet; private keyPrefix; private redisGetDeduplication; private inMemoryCachingTime; private defaultStaleAge; private estimateExpireAge; private killContainerOnErrorThreshold; private valueSerializer; constructor({ redisUrl, database, keyPrefix, sharedTagsKey, getTimeoutMs, revalidateTagQuerySize, avgResyncIntervalMs, redisGetDeduplication, inMemoryCachingTime, defaultStaleAge, estimateExpireAge, killContainerOnErrorThreshold, socketOptions, clientOptions, valueSerializer, }: CreateRedisStringsHandlerOptions); resetRequestCache(): void; private clientReadyCalls; private assertClientIsReady; get(key: string, ctx: { kind: 'APP_ROUTE' | 'APP_PAGE'; isRoutePPREnabled: boolean; isFallback: boolean; } | { kind: 'FETCH'; revalidate: number; fetchUrl: string; fetchIdx: number; tags: string[]; softTags: string[]; isFallback: boolean; }): Promise; set(key: string, data: { kind: 'APP_PAGE'; status?: number; headers: { 'x-nextjs-stale-time': string; 'x-next-cache-tags': string; }; html: string; rscData: Buffer; segmentData: unknown; postboned: unknown; } | { kind: 'APP_ROUTE'; status: number; headers: { 'cache-control'?: string; 'x-nextjs-stale-time': string; 'x-next-cache-tags': string; }; body: Buffer; } | { kind: 'FETCH'; data: { headers: Record; body: string; status: number; url: string; }; revalidate: number | false; }, ctx: { isRoutePPREnabled: boolean; isFallback: boolean; tags?: string[]; revalidate?: number | false; cacheControl?: { revalidate: 5; expire: undefined; }; }): Promise; revalidateTag(tagOrTags: string | string[], ...rest: any[]): Promise; } type NextCacheHandlerOptions = CreateRedisStringsHandlerOptions & { serverDistDir?: string; }; declare class CachedHandler { constructor(options: NextCacheHandlerOptions); get(...args: Parameters): ReturnType; set(...args: Parameters): ReturnType; revalidateTag(...args: Parameters): ReturnType; resetRequestCache(...args: Parameters): ReturnType; } declare function bufferAndMapReviver(_: string, value: any): any; declare function bufferAndMapReplacer(_: string, value: any): any; interface CacheComponentsEntry { value: ReadableStream; tags: string[]; stale: number; timestamp: number; expire: number; revalidate: number; } interface CacheComponentsHandler { get(cacheKey: string, softTags: string[]): Promise; set(cacheKey: string, pendingEntry: Promise): Promise; refreshTags(): Promise; getExpiration(tags: string[]): Promise; updateTags(tags: string[], durations?: { expire?: number; }): Promise; } type CreateCacheComponentsHandlerOptions = CreateRedisStringsHandlerOptions & { serverDistDir?: string; }; declare function getRedisCacheComponentsHandler(options?: CreateCacheComponentsHandlerOptions): CacheComponentsHandler; declare const redisCacheHandler: CacheComponentsHandler; export { type CacheValueSerializer, type CreateRedisStringsHandlerOptions, RedisStringsHandler, bufferAndMapReplacer, bufferAndMapReviver, CachedHandler as default, getRedisCacheComponentsHandler, jsonCacheValueSerializer, redisCacheHandler };