UNPKG

next

Version:

The React Framework

254 lines (253 loc) • 12.8 kB
import { createDefaultCacheHandler } from '../lib/cache-handlers/default'; import { createTieredCacheHandler } from './tiered-cache-handler'; const debug = process.env.NEXT_PRIVATE_DEBUG_CACHE ? (message, ...args)=>{ console.log(`use-cache: ${message}`, ...args); } : undefined; const handlersSymbol = Symbol.for('@next/cache-handlers'); const handlersMapSymbol = Symbol.for('@next/cache-handlers-map'); const handlersSetSymbol = Symbol.for('@next/cache-handlers-set'); const privateHandlerSymbol = Symbol.for('@next/cache-handlers-private'); const devFrontHandlersSymbol = Symbol.for('@next/cache-handlers-dev-fronts'); const devTieredHandlersSymbol = Symbol.for('@next/cache-handlers-dev-tiered'); const memoryCacheDisabledSymbol = Symbol.for('@next/cache-handlers-memory-disabled'); /** * The in-memory size used for the dev-only built-in handlers (the private * handler, the size-0 replacement, and the per-kind front handlers for custom * kinds). Mirrors the framework default so `cacheMaxMemorySize: 0` does not * disable caching in development. */ const DEV_MEMORY_CACHE_SIZE = 50 * 1024 * 1024; /** * The reference to the cache handlers. We store the cache handlers on the * global object so that we can access the same instance across different * boundaries (such as different copies of the same module). */ const reference = globalThis; /** * Initialize the cache handlers. * @param cacheMaxMemorySize - The maximum memory size of the cache in bytes, if * not provided, the default memory size will be used. * @returns `true` if the cache handlers were initialized, `false` if they were already initialized. */ export function initializeCacheHandlers(cacheMaxMemorySize) { // If the cache handlers have already been initialized, don't do it again. if (reference[handlersMapSymbol]) { debug == null ? void 0 : debug('cache handlers already initialized'); return false; } debug == null ? void 0 : debug('initializing cache handlers'); const handlersMap = new Map(); reference[handlersMapSymbol] = handlersMap; // In development, `cacheMaxMemorySize: 0` would make the built-in default // handler a no-op, so every reload would miss. Use a real in-memory size // instead so dev stays fast; the "use cache" wrapper forces a dynamic cache // life for this case so it still behaves close to "no cache" (serve stale, // regenerate in the background). Production keeps the no-op handler. const builtInSize = process.env.__NEXT_DEV_SERVER && cacheMaxMemorySize === 0 ? DEV_MEMORY_CACHE_SIZE : cacheMaxMemorySize; // Initialize the cache from the symbol contents first. if (reference[handlersSymbol]) { let fallback; if (reference[handlersSymbol].DefaultCache) { debug == null ? void 0 : debug('setting "default" cache handler from symbol'); fallback = reference[handlersSymbol].DefaultCache; } else { debug == null ? void 0 : debug('setting "default" cache handler from default'); fallback = createDefaultCacheHandler(builtInSize); } handlersMap.set('default', fallback); if (reference[handlersSymbol].RemoteCache) { debug == null ? void 0 : debug('setting "remote" cache handler from symbol'); handlersMap.set('remote', reference[handlersSymbol].RemoteCache); } else { debug == null ? void 0 : debug('setting "remote" cache handler from default'); handlersMap.set('remote', fallback); } } else { const handler = createDefaultCacheHandler(builtInSize); debug == null ? void 0 : debug('setting "default" cache handler from default'); handlersMap.set('default', handler); debug == null ? void 0 : debug('setting "remote" cache handler from default'); handlersMap.set('remote', handler); } // Create a set of the cache handlers. reference[handlersSetSymbol] = new Set(handlersMap.values()); // In development we add dedicated built-in in-memory handlers so that reloads // are fast. These are always built-in handlers, never a user-configured one, // and are gated on the dev server so production behaves exactly as // configured. if (process.env.__NEXT_DEV_SERVER) { reference[memoryCacheDisabledSymbol] = cacheMaxMemorySize === 0; // Private caches are persisted here so reloads are fast. Private entries // can hold data specific to the incoming request (for example, derived from // its cookies or headers), so this is never the user-configured `default` // alias. Sized so it still caches under `cacheMaxMemorySize: 0` (otherwise // it would become the no-op stub and private reloads would miss). reference[privateHandlerSymbol] = createDefaultCacheHandler(DEV_MEMORY_CACHE_SIZE); // Built-in front handlers, one per custom kind, and the tiered handlers // that place a front in front of a (possibly slow or remote) backing // handler so cache hits resolve in a microtask, are both created per kind // in `setCacheHandler`. reference[devFrontHandlersSymbol] = new Map(); reference[devTieredHandlersSymbol] = new Map(); } return true; } /** * Get a cache handler by kind. * @param kind - The kind of cache handler to get. * @returns The cache handler, or `undefined` if it does not exist. * @throws If the cache handlers are not initialized. */ export function getCacheHandler(kind) { // This should never be called before initializeCacheHandlers. if (!reference[handlersMapSymbol]) { throw Object.defineProperty(new Error('Cache handlers not initialized'), "__NEXT_ERROR_CODE", { value: "E649", enumerable: false, configurable: true }); } return reference[handlersMapSymbol].get(kind); } /** * Get the dedicated in-memory cache handler that persists private caches in * development. Returns `undefined` outside the dev server, where private caches * must not be persisted. This is intentionally not part of the kind-keyed * handlers map so that it can never be replaced by a user-configured handler. */ export function getPrivateCacheHandler() { // This should never be called before initializeCacheHandlers. if (!reference[handlersMapSymbol]) { throw Object.defineProperty(new Error('Cache handlers not initialized'), "__NEXT_ERROR_CODE", { value: "E649", enumerable: false, configurable: true }); } return reference[privateHandlerSymbol]; } /** * Whether the in-memory cache is disabled (`cacheMaxMemorySize: 0`). Dev-only * signal used to force a dynamic cache life so that the size-0 case still * serves stale and regenerates in the background. Always `false` in production. */ export function isMemoryCacheDisabled() { return reference[memoryCacheDisabledSymbol] ?? false; } /** * Whether `kind` is backed by a real user-configured handler rather than the * built-in in-memory default. Such a handler may be slow or remote, so in * development a built-in front handler is placed in front of it (see * `getDevTieredCacheHandler`) to keep cache hits microtask-fast. The presence * of a dev front handler is the signal, since front handlers are created * exactly for user-registered kinds. Always `false` in production. */ export function isCustomCacheHandler(kind) { var _reference_devFrontHandlersSymbol; if (!process.env.__NEXT_DEV_SERVER) { return false; } return ((_reference_devFrontHandlersSymbol = reference[devFrontHandlersSymbol]) == null ? void 0 : _reference_devFrontHandlersSymbol.has(kind)) ?? false; } /** * Get the dev-only tiered cache handler for a custom `kind`: a fast built-in * in-memory front handler in front of the user-configured backing handler, so * cache hits resolve in a microtask. Returns `undefined` if there is none (a * built-in kind, or production). */ export function getDevTieredCacheHandler(kind) { var _reference_devTieredHandlersSymbol; if (!process.env.__NEXT_DEV_SERVER) { return undefined; } return (_reference_devTieredHandlersSymbol = reference[devTieredHandlersSymbol]) == null ? void 0 : _reference_devTieredHandlersSymbol.get(kind); } /** * Get an iterator over the cache handlers. * @returns An iterator over the cache handlers, or `undefined` if they are not * initialized. */ export function getCacheHandlers() { const handlersSet = reference[handlersSetSymbol]; if (!handlersSet) { return undefined; } if (process.env.__NEXT_DEV_SERVER) { return iterateCacheHandlersWithDevBuiltIns(handlersSet); } return handlersSet.values(); } /** * Yields the registered handlers plus the dev-only built-in handlers (the * private handler and the per-kind front handlers). The built-in handlers are * not part of the registered set, but tag operations must still reach them: * their `updateTags` writes the shared tags manifest that their `get` consults, * so `revalidateTag` can invalidate their entries. */ function* iterateCacheHandlersWithDevBuiltIns(handlersSet) { yield* handlersSet; const privateHandler = reference[privateHandlerSymbol]; if (privateHandler) { yield privateHandler; } const devFrontHandlers = reference[devFrontHandlersSymbol]; if (devFrontHandlers) { yield* devFrontHandlers.values(); } } /** * Get a map iterator over the cache handlers (keyed by kind). * @returns An iterator over the cache handler entries, or `undefined` if they * are not initialized. * @throws If the cache handlers are not initialized. */ export function getCacheHandlerEntries() { const handlersMap = reference[handlersMapSymbol]; if (!handlersMap) { return undefined; } if (process.env.__NEXT_DEV_SERVER) { return iterateCacheHandlerEntriesWithDevBuiltIns(handlersMap); } return handlersMap.entries(); } /** * Yields the registered entries plus the dev-only private handler under its * kind, so that per-kind tag operations (`refreshTags` / `getExpiration` for * implicit tags) apply to private entries. Custom-kind front handlers are * intentionally omitted: the backing handler (already in the map) is * authoritative for that kind's `getExpiration`, and a front entry is discarded * via that same value. */ function* iterateCacheHandlerEntriesWithDevBuiltIns(handlersMap) { yield* handlersMap.entries(); const privateHandler = reference[privateHandlerSymbol]; if (privateHandler) { const privateEntry = [ 'private', privateHandler ]; yield privateEntry; } } /** * Set a cache handler by kind. * @param kind - The kind of cache handler to set. * @param cacheHandler - The cache handler to set. */ export function setCacheHandler(kind, cacheHandler) { // This should never be called before initializeCacheHandlers. if (!reference[handlersMapSymbol] || !reference[handlersSetSymbol]) { throw Object.defineProperty(new Error('Cache handlers not initialized'), "__NEXT_ERROR_CODE", { value: "E649", enumerable: false, configurable: true }); } debug == null ? void 0 : debug('setting cache handler for "%s"', kind); reference[handlersMapSymbol].set(kind, cacheHandler); reference[handlersSetSymbol].add(cacheHandler); // A user-configured handler may be slow or remote. In development, give it a // dedicated built-in in-memory front handler so cache hits resolve in a // microtask, and pair the two into a tiered handler the wrapper reads // through. Both are created alongside registration so their lifecycle matches // the backing handler's, and the front handler's presence is the signal that // this kind is backed by a real handler (see `isCustomCacheHandler`). Being a // built-in default handler, the front inherits the dev minimum retention, so // a short-`expire` value still hits the front instead of falling through to // the slow backing on every read. if (process.env.__NEXT_DEV_SERVER) { var _reference_devFrontHandlersSymbol, _reference_devTieredHandlersSymbol; const frontHandler = createDefaultCacheHandler(DEV_MEMORY_CACHE_SIZE); (_reference_devFrontHandlersSymbol = reference[devFrontHandlersSymbol]) == null ? void 0 : _reference_devFrontHandlersSymbol.set(kind, frontHandler); (_reference_devTieredHandlersSymbol = reference[devTieredHandlersSymbol]) == null ? void 0 : _reference_devTieredHandlersSymbol.set(kind, createTieredCacheHandler(frontHandler, cacheHandler)); } } //# sourceMappingURL=handlers.js.map