{"version":3,"sources":["../../../src/server/resume-data-cache/resume-data-cache.ts"],"sourcesContent":["import { InvariantError } from '../../shared/lib/invariant-error'\nimport {\n  type UseCacheCacheStore,\n  type FetchCacheStore,\n  type EncryptedBoundArgsCacheStore,\n  serializeUseCacheCacheStore,\n  parseUseCacheCacheStore,\n  type DecryptedBoundArgsCacheStore,\n  type UseCacheCacheStoreSerialized,\n} from './cache-store'\n\n/**\n * An immutable version of the resume data cache used during rendering.\n * This cache is read-only and cannot be modified once created.\n */\nexport interface RenderResumeDataCache {\n  /**\n   * A read-only Map store for values cached by the 'use cache' React hook.\n   * The 'set' operation is omitted to enforce immutability.\n   */\n  readonly cache: Omit<UseCacheCacheStore, 'set'>\n\n  /**\n   * A read-only Map store for cached fetch responses.\n   * The 'set' operation is omitted to enforce immutability.\n   */\n  readonly fetch: Omit<FetchCacheStore, 'set'>\n\n  /**\n   * A read-only Map store for encrypted bound args of inline server functions.\n   * The 'set' operation is omitted to enforce immutability.\n   */\n  readonly encryptedBoundArgs: Omit<EncryptedBoundArgsCacheStore, 'set'>\n\n  /**\n   * A read-only Map store for decrypted bound args of inline server functions.\n   * This is only intended for in-memory usage during pre-rendering, and must\n   * not be persisted in the resume store. The 'set' operation is omitted to\n   * enforce immutability.\n   */\n  readonly decryptedBoundArgs: Omit<DecryptedBoundArgsCacheStore, 'set'>\n}\n\n/**\n * A mutable version of the resume data cache used during pre-rendering.\n * This cache allows both reading and writing of cached values.\n */\nexport interface PrerenderResumeDataCache {\n  /**\n   * A mutable Map store for values cached by the 'use cache' React hook.\n   * Supports both 'get' and 'set' operations to build the cache during\n   * pre-rendering.\n   */\n  readonly cache: UseCacheCacheStore\n\n  /**\n   * A mutable Map store for cached fetch responses.\n   * Supports both 'get' and 'set' operations to build the cache during\n   * pre-rendering.\n   */\n  readonly fetch: FetchCacheStore\n\n  /**\n   * A mutable Map store for encrypted bound args of inline server functions.\n   * Supports both 'get' and 'set' operations to build the cache during\n   * pre-rendering.\n   */\n  readonly encryptedBoundArgs: EncryptedBoundArgsCacheStore\n\n  /**\n   * A mutable Map store for decrypted bound args of inline server functions.\n   * This is only intended for in-memory usage during pre-rendering, and must\n   * not be persisted in the resume store. Supports both 'get' and 'set'\n   * operations to build the cache during pre-rendering.\n   */\n  readonly decryptedBoundArgs: DecryptedBoundArgsCacheStore\n}\n\ntype ResumeStoreSerialized = {\n  store: {\n    cache: {\n      [key: string]: any\n    }\n    fetch: {\n      [key: string]: any\n    }\n    encryptedBoundArgs: {\n      [key: string]: string\n    }\n  }\n}\n\n/**\n * Serializes a resume data cache into a JSON string for storage or\n * transmission. Handles 'use cache' values, fetch responses, and encrypted\n * bound args for inline server functions.\n *\n * @param resumeDataCache - The immutable cache to serialize\n * @returns A Promise that resolves to the serialized cache as a JSON string, or\n * 'null' if empty\n */\nexport async function stringifyResumeDataCache(\n  resumeDataCache: RenderResumeDataCache | PrerenderResumeDataCache,\n  isCacheComponentsEnabled: boolean\n): Promise<string> {\n  if (process.env.NEXT_RUNTIME === 'edge') {\n    throw new InvariantError(\n      '`stringifyResumeDataCache` should not be called in edge runtime.'\n    )\n  } else {\n    if (resumeDataCache.fetch.size === 0 && resumeDataCache.cache.size === 0) {\n      return 'null'\n    }\n\n    const json: ResumeStoreSerialized = {\n      store: {\n        fetch: Object.fromEntries(Array.from(resumeDataCache.fetch.entries())),\n        cache: Object.fromEntries(\n          (\n            await serializeUseCacheCacheStore(\n              resumeDataCache.cache.entries(),\n              isCacheComponentsEnabled\n            )\n          ).filter(\n            (entry): entry is [string, UseCacheCacheStoreSerialized] =>\n              entry !== null\n          )\n        ),\n        encryptedBoundArgs: Object.fromEntries(\n          Array.from(resumeDataCache.encryptedBoundArgs.entries())\n        ),\n      },\n    }\n\n    // Compress the JSON string using zlib. As the data we already want to\n    // decompress is in memory, we use the synchronous deflateSync function.\n    const { deflateSync } = require('node:zlib') as typeof import('node:zlib')\n\n    return deflateSync(JSON.stringify(json)).toString('base64')\n  }\n}\n\n/**\n * Creates a new empty mutable resume data cache for pre-rendering.\n * Initializes fresh Map instances for both the 'use cache' and fetch caches.\n * Used at the start of pre-rendering to begin collecting cached values.\n *\n * @returns A new empty PrerenderResumeDataCache instance\n */\nexport function createPrerenderResumeDataCache(\n  source?: PrerenderResumeDataCache | RenderResumeDataCache\n): PrerenderResumeDataCache {\n  if (source) {\n    return {\n      cache: new Map(source.cache),\n      fetch: new Map(source.fetch),\n      encryptedBoundArgs: new Map(source.encryptedBoundArgs),\n      decryptedBoundArgs: new Map(source.decryptedBoundArgs),\n    }\n  } else {\n    return {\n      cache: new Map(),\n      fetch: new Map(),\n      encryptedBoundArgs: new Map(),\n      decryptedBoundArgs: new Map(),\n    }\n  }\n}\n\n/**\n * Creates an immutable render resume data cache from either:\n * 1. An existing prerender cache instance\n * 2. A serialized cache string\n *\n * @param renderResumeDataCache - A RenderResumeDataCache instance to be used directly\n * @param prerenderResumeDataCache - A PrerenderResumeDataCache instance to convert to immutable\n * @param persistedCache - A serialized cache string to parse\n * @param maxPostponedStateSizeBytes - The max compressed size limit in bytes (used to calculate 5x decompression limit)\n * @returns An immutable RenderResumeDataCache instance\n */\nexport function createRenderResumeDataCache(\n  renderResumeDataCache: RenderResumeDataCache\n): RenderResumeDataCache\nexport function createRenderResumeDataCache(\n  prerenderResumeDataCache: PrerenderResumeDataCache\n): RenderResumeDataCache\nexport function createRenderResumeDataCache(\n  persistedCache: string,\n  maxPostponedStateSizeBytes: number | undefined\n): RenderResumeDataCache\nexport function createRenderResumeDataCache(\n  resumeDataCacheOrPersistedCache:\n    | RenderResumeDataCache\n    | PrerenderResumeDataCache\n    | string,\n  maxPostponedStateSizeBytes?: number | undefined\n): RenderResumeDataCache {\n  if (process.env.NEXT_RUNTIME === 'edge') {\n    throw new InvariantError(\n      '`createRenderResumeDataCache` should not be called in edge runtime.'\n    )\n  } else {\n    if (typeof resumeDataCacheOrPersistedCache !== 'string') {\n      // If the cache is already a prerender or render cache, we can return it\n      // directly. For the former, we're just performing a type change.\n      return resumeDataCacheOrPersistedCache\n    }\n\n    if (resumeDataCacheOrPersistedCache === 'null') {\n      return {\n        cache: new Map(),\n        fetch: new Map(),\n        encryptedBoundArgs: new Map(),\n        decryptedBoundArgs: new Map(),\n      }\n    }\n\n    // This should be a compressed string. Let's decompress it using zlib.\n    // As the data we already want to decompress is in memory, we use the\n    // synchronous inflateSync function.\n    const { inflateSync } = require('node:zlib') as typeof import('node:zlib')\n\n    // Limit decompressed size to prevent zipbomb attacks. This is 5x the\n    // configured maxPostponedStateSize, allowing reasonable compression\n    // ratios while preventing extreme decompression bombs.\n    // Default is 500MB (5x the default 100MB compressed limit).\n    const maxDecompressedSize = maxPostponedStateSizeBytes\n      ? maxPostponedStateSizeBytes * 5\n      : 500 * 1024 * 1024\n\n    let json: ResumeStoreSerialized\n    try {\n      json = JSON.parse(\n        inflateSync(Buffer.from(resumeDataCacheOrPersistedCache, 'base64'), {\n          maxOutputLength: maxDecompressedSize,\n        }).toString('utf-8')\n      )\n    } catch (err: unknown) {\n      if (\n        err instanceof RangeError &&\n        (err as NodeJS.ErrnoException).code === 'ERR_BUFFER_TOO_LARGE'\n      ) {\n        throw new Error(\n          `Decompressed resume data cache exceeded ${maxDecompressedSize} byte limit`\n        )\n      }\n      throw err\n    }\n\n    return {\n      cache: parseUseCacheCacheStore(Object.entries(json.store.cache)),\n      fetch: new Map(Object.entries(json.store.fetch)),\n      encryptedBoundArgs: new Map(\n        Object.entries(json.store.encryptedBoundArgs)\n      ),\n      decryptedBoundArgs: new Map(),\n    }\n  }\n}\n"],"names":["createPrerenderResumeDataCache","createRenderResumeDataCache","stringifyResumeDataCache","resumeDataCache","isCacheComponentsEnabled","process","env","NEXT_RUNTIME","InvariantError","fetch","size","cache","json","store","Object","fromEntries","Array","from","entries","serializeUseCacheCacheStore","filter","entry","encryptedBoundArgs","deflateSync","require","JSON","stringify","toString","source","Map","decryptedBoundArgs","resumeDataCacheOrPersistedCache","maxPostponedStateSizeBytes","inflateSync","maxDecompressedSize","parse","Buffer","maxOutputLength","err","RangeError","code","Error","parseUseCacheCacheStore"],"mappings":"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","ignoreList":[0]}