{"version":3,"sources":["../../../../src/server/app-render/vary-params.ts"],"sourcesContent":["import type { Params } from '../request/params'\nimport type { SearchParams } from '../request/search-params'\nimport {\n  getVaryParamsAccumulator,\n  workUnitAsyncStorage,\n} from './work-unit-async-storage.external'\n\n/**\n * Accumulates vary params for a single segment (or for metadata/rootParams).\n *\n * A VaryParamsAccumulator is an `AsyncIterable<string>` that can be serialized\n * by React Flight. As params are accessed during render, each newly-seen param\n * name is `add`ed, which yields it into the Flight stream immediately. After\n * rendering, call `close()` (via `finishAccumulatingVaryParams`) to end the\n * iteration.\n *\n * Because each access is flushed into the stream as it happens, the set of\n * accessed params is built up incrementally, with no step at the end of the\n * render that has to run for the client to read anything. If the prerender is\n * aborted by sync I/O, the params yielded before the abort are already in the\n * stream — and they're exactly the params the partial response depends on.\n * This mirrors how `StaleTimeIterable` works (see stale-time.ts).\n *\n * Each name is emitted at most once: `add` dedupes against the set of\n * already-yielded names, so the stream never contains a duplicate.\n *\n * NOTE: like `StaleTimeIterable`, this supports a single concurrent iteration\n * (Flight iterates it exactly once). The shared empty singleton below is the\n * only instance referenced by more than one segment, and it only ever yields\n * \"done\", so concurrent iteration of it is safe.\n */\nexport class VaryParamsAccumulator implements AsyncIterable<string> {\n  private _resolve: ((result: IteratorResult<string>) => void) | null = null\n  private _done = false\n  private _buffer: string[] = []\n  // The set of param names already yielded. Doubles as the dedupe guard so the\n  // same name is never emitted twice.\n  private _seen: Set<string> = new Set()\n\n  /**\n   * Records that a param was accessed. Yields the name into the stream the\n   * first time it's seen; subsequent accesses of the same name are no-ops.\n   */\n  add(paramName: string): void {\n    if (this._done || this._seen.has(paramName)) {\n      return\n    }\n    this._seen.add(paramName)\n    if (this._resolve !== null) {\n      this._resolve({ value: paramName, done: false })\n      this._resolve = null\n    } else {\n      this._buffer.push(paramName)\n    }\n  }\n\n  /** Ends the iteration. Best-effort: if skipped (e.g. on a sync-I/O abort),\n   * the consumer simply reads the params yielded so far. */\n  close(): void {\n    if (this._done) {\n      return\n    }\n    this._done = true\n    if (this._resolve !== null) {\n      this._resolve({ value: undefined, done: true })\n      this._resolve = null\n    }\n  }\n\n  [Symbol.asyncIterator](): AsyncIterator<string> {\n    return {\n      next: () => {\n        if (this._buffer.length > 0) {\n          return Promise.resolve({ value: this._buffer.shift()!, done: false })\n        }\n        if (this._done) {\n          return Promise.resolve({ value: undefined, done: true })\n        }\n        return new Promise<IteratorResult<string>>((resolve) => {\n          this._resolve = resolve\n        })\n      },\n    }\n  }\n}\n\n/**\n * A mutable data structure for accumulating per-segment vary params for an\n * entire server response. It's only used during prerenders. It describes\n * metadata about the response itself.\n */\nexport type ResponseVaryParamsAccumulator = {\n  /** Vary params accumulator for metadata/viewport (the \"head\" segment) */\n  head: VaryParamsAccumulator\n  /** Vary params accumulator for root params access */\n  rootParams: VaryParamsAccumulator\n  /** Vary params accumulators for each route segment */\n  segments: Set<VaryParamsAccumulator>\n}\n\n/**\n * A singleton accumulator that's already closed with no params. Use this for\n * segments where we know upfront that no params will be accessed, such as\n * client components or segments without user code.\n *\n * Benefits:\n * - No need to accumulate or close later\n * - Resilient: reads as an empty set even if other tracking fails\n * - Memory efficient: reuses the same object\n *\n * It's never added to `ResponseVaryParamsAccumulator.segments` (callers pass it\n * directly), so `finishAccumulatingVaryParams` doesn't touch it.\n */\nexport const emptyVaryParamsAccumulator: VaryParamsAccumulator =\n  new VaryParamsAccumulator()\nemptyVaryParamsAccumulator.close()\n\nexport function createResponseVaryParamsAccumulator(): ResponseVaryParamsAccumulator {\n  // Create the head and rootParams accumulators as top-level fields.\n  // Segment accumulators are added to the segments set as they are created.\n  const head = new VaryParamsAccumulator()\n  const rootParams = new VaryParamsAccumulator()\n  const segments = new Set<VaryParamsAccumulator>()\n\n  return {\n    head,\n    rootParams,\n    segments,\n  }\n}\n\n/**\n * Allocates a new VaryParamsAccumulator and adds it to the response accumulator\n * associated with the current WorkUnitStore.\n *\n * Returns an iterable that yields the segment's vary params as they're\n * accessed. The iterable can be passed directly to React Flight for\n * serialization.\n */\nexport function createVaryParamsAccumulator(): VaryParamsAccumulator | null {\n  const workUnitStore = workUnitAsyncStorage.getStore()\n  if (!workUnitStore) {\n    return null\n  }\n  const responseAccumulator = getVaryParamsAccumulator(workUnitStore)\n  if (!responseAccumulator) {\n    return null\n  }\n  const accumulator = new VaryParamsAccumulator()\n  responseAccumulator.segments.add(accumulator)\n  return accumulator\n}\n\nexport function getMetadataVaryParamsAccumulator(): VaryParamsAccumulator | null {\n  const workUnitStore = workUnitAsyncStorage.getStore()\n  if (!workUnitStore) {\n    return null\n  }\n  return getVaryParamsAccumulator(workUnitStore)?.head ?? null\n}\n\n// The metadata and viewport are always delivered in a single payload, so they\n// don't need to be tracked separately. This may change in the future, but for\n// now this is just an alias.\nexport const getViewportVaryParamsAccumulator = getMetadataVaryParamsAccumulator\n\n/**\n * Returns the response-level root params iterable for serialization. Root\n * params are emitted once at the top level (not folded into every segment);\n * the client unions them into each segment's set.\n */\nexport function getRootParamsVaryParamsAccumulator(): VaryParamsAccumulator | null {\n  const workUnitStore = workUnitAsyncStorage.getStore()\n  if (!workUnitStore) {\n    return null\n  }\n  return getVaryParamsAccumulator(workUnitStore)?.rootParams ?? null\n}\n\n/**\n * Records that a param was accessed. Adds the param name to the accumulator.\n */\nexport function accumulateVaryParam(\n  accumulator: VaryParamsAccumulator,\n  paramName: string\n): void {\n  accumulator.add(paramName)\n}\n\n/**\n * Records a root param access.\n */\nexport function accumulateRootVaryParam(paramName: string): void {\n  const rootParamsAccumulator = getRootParamsVaryParamsAccumulator()\n  if (rootParamsAccumulator !== null) {\n    accumulateVaryParam(rootParamsAccumulator, paramName)\n  }\n}\n\nexport function createVaryingParams(\n  accumulator: VaryParamsAccumulator,\n  originalParamsObject: Params,\n  optionalCatchAllParamName: string | null\n): Params {\n  if (optionalCatchAllParamName !== null) {\n    // When there's an optional catch-all param with no value (e.g.,\n    // [[...slug]] at /), the param doesn't exist as a property on the params\n    // object. Use a Proxy to track all param access — both existing params\n    // and the missing optional param — including enumeration patterns like\n    // Object.keys(), spread, for...in, and `in` checks.\n    return new Proxy(originalParamsObject, {\n      get(target, prop, receiver) {\n        if (typeof prop === 'string') {\n          if (\n            prop === optionalCatchAllParamName ||\n            Object.prototype.hasOwnProperty.call(target, prop)\n          ) {\n            accumulateVaryParam(accumulator, prop)\n          }\n        }\n        return Reflect.get(target, prop, receiver)\n      },\n      has(target, prop) {\n        if (prop === optionalCatchAllParamName) {\n          accumulateVaryParam(accumulator, optionalCatchAllParamName)\n        }\n        return Reflect.has(target, prop)\n      },\n      ownKeys(target) {\n        // Enumerating the params object means the user's code may depend on\n        // which params are present, so conservatively track the optional\n        // param as accessed.\n        accumulateVaryParam(accumulator, optionalCatchAllParamName)\n        return Reflect.ownKeys(target)\n      },\n    })\n  }\n\n  // When there's no optional catch-all, all params exist as properties on the\n  // object, so we can use defineProperty getters instead of a Proxy. This is\n  // faster because the engine can optimize property access on regular objects\n  // more aggressively than Proxy trap calls.\n  const underlyingParamsWithVarying: Params = {}\n  for (const paramName in originalParamsObject) {\n    Object.defineProperty(underlyingParamsWithVarying, paramName, {\n      get() {\n        accumulateVaryParam(accumulator, paramName)\n        return originalParamsObject[paramName]\n      },\n      enumerable: true,\n    })\n  }\n  return underlyingParamsWithVarying\n}\n\nexport function createVaryingSearchParams(\n  accumulator: VaryParamsAccumulator,\n  originalSearchParamsObject: SearchParams\n): SearchParams {\n  // Search params have no fixed schema, so any access — missing-key reads, `in`\n  // checks, or enumeration — must register as varying. A Proxy is required\n  // (rather than per-property getters) so that enumeration of an empty\n  // searchParams object still triggers a vary. All accesses bucket into the\n  // single sentinel '?'; the segment is keyed by the whole query string.\n  // TODO: Split into per-param tracking if the cache key evolves.\n  return new Proxy(originalSearchParamsObject, {\n    get(target, prop, receiver) {\n      if (typeof prop === 'string') {\n        accumulateVaryParam(accumulator, '?')\n      }\n      return Reflect.get(target, prop, receiver)\n    },\n    has(target, prop) {\n      if (typeof prop === 'string') {\n        accumulateVaryParam(accumulator, '?')\n      }\n      return Reflect.has(target, prop)\n    },\n    ownKeys(target) {\n      accumulateVaryParam(accumulator, '?')\n      return Reflect.ownKeys(target)\n    },\n  })\n}\n\n/**\n * Closes all accumulators in a ResponseVaryParamsAccumulator, ending their\n * iterables. Call this after rendering is complete.\n *\n * This does NOT merge root params into each segment — root params are\n * serialized separately (at the top level of the response) and unioned in by\n * the client. And it's best-effort: if it's skipped because the render was\n * aborted by sync I/O, the consumer just reads the params each iterable yielded\n * before the abort.\n *\n * If we can't track vary params (e.g., legacy prerender), simply don't call\n * this function - the client treats a missing iterable as \"unknown\" vary\n * params.\n */\nexport function finishAccumulatingVaryParams(\n  responseAccumulator: ResponseVaryParamsAccumulator\n): void {\n  responseAccumulator.head.close()\n  responseAccumulator.rootParams.close()\n  for (const segmentAccumulator of responseAccumulator.segments) {\n    segmentAccumulator.close()\n  }\n}\n"],"names":["getVaryParamsAccumulator","workUnitAsyncStorage","VaryParamsAccumulator","add","paramName","_done","_seen","has","_resolve","value","done","_buffer","push","close","undefined","Symbol","asyncIterator","next","length","Promise","resolve","shift","Set","emptyVaryParamsAccumulator","createResponseVaryParamsAccumulator","head","rootParams","segments","createVaryParamsAccumulator","workUnitStore","getStore","responseAccumulator","accumulator","getMetadataVaryParamsAccumulator","getViewportVaryParamsAccumulator","getRootParamsVaryParamsAccumulator","accumulateVaryParam","accumulateRootVaryParam","rootParamsAccumulator","createVaryingParams","originalParamsObject","optionalCatchAllParamName","Proxy","get","target","prop","receiver","Object","prototype","hasOwnProperty","call","Reflect","ownKeys","underlyingParamsWithVarying","defineProperty","enumerable","createVaryingSearchParams","originalSearchParamsObject","finishAccumulatingVaryParams","segmentAccumulator"],"mappings":"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","ignoreList":[0]}