{"version":3,"sources":["../../../../src/shared/lib/segment-cache/vary-params-decoding.ts"],"sourcesContent":["/**\n * Vary Params Decoding\n *\n * This module is shared between server and client.\n */\n\nexport type VaryParams = Set<string>\n\n/**\n * Vary params are serialized into the Flight stream as an\n * `AsyncIterable<string>` that yields each accessed param name exactly once\n * (the server dedupes before emitting). Because each access is flushed into the\n * stream as it happens, there's no step at the end of the render that has to\n * run for the client to read anything. If a prerender is aborted by sync I/O,\n * the params yielded before the abort are already in the stream, and they're\n * exactly the params the partial response actually depends on.\n *\n * Root params are NOT included in a segment's own iterable. They're emitted\n * once at the top level of the response (as a separate iterable) and unioned in\n * by `readVaryParams`, because root params can be accessed at any point during\n * the render — folding them into every segment would otherwise require a merge\n * once the whole render is complete.\n */\nexport type VaryParamsIterable = AsyncIterable<string>\n\n/**\n * A Flight \"chunk\": when an `AsyncIterable` arrives over a fully-buffered Flight\n * stream, each `iterator.next()` returns one of these instead of a native\n * Promise. It follows the React thenable protocol — once `status` is\n * `'fulfilled'`, `value` (the `IteratorResult`) can be read synchronously.\n */\ntype IteratorResultChunk = PromiseLike<IteratorResult<string>> & {\n  status?: 'pending' | 'resolved_model' | 'fulfilled' | 'rejected' | string\n  value?: IteratorResult<string>\n}\n\n/**\n * Synchronously drains a vary params `AsyncIterable`, adding each yielded name\n * to `target`.\n *\n * By the time this runs (on the client, or in collectSegmentData), the Flight\n * stream has been fully buffered, so every yielded value is already\n * materialized and can be read without awaiting. We force each iterator result\n * to resolve synchronously using the same `.then(noop)` trick React uses\n * internally, then read its `status`/`value` directly.\n *\n * We add \"every param yielded up to the point the stream suspends\": a\n * normally-closed iterable drains fully, while one left hanging (a sync-I/O\n * abort, or a `close()` whose row hasn't flushed yet) drains to the prefix\n * already in the stream. Both are correct — a segment's param accesses are all\n * flushed as they happen during its render, so the prefix is exactly the set\n * the response depends on. We therefore never need the terminating `done` row\n * to be present; it's only stream hygiene.\n */\nfunction drainVaryParams(\n  iterable: VaryParamsIterable,\n  target: VaryParams\n): void {\n  const iterator = iterable[Symbol.asyncIterator]()\n  while (true) {\n    const chunk = iterator.next() as IteratorResultChunk\n    // Attach a no-op listener to force Flight to synchronously resolve the\n    // chunk. A freshly-arrived result may be in an intermediate\n    // 'resolved_model' state (data received but not unwrapped); calling\n    // .then() transitions it to 'fulfilled', making the value available\n    // synchronously. (A native Promise has no `status` and simply reads as\n    // not-fulfilled below, so this can never hang.)\n    chunk.then(noop, noop)\n    if (chunk.status !== 'fulfilled' || chunk.value === undefined) {\n      // The stream suspended here. Everything yielded before this point has\n      // already been added.\n      return\n    }\n    const step = chunk.value\n    if (step.done) {\n      return\n    }\n    target.add(step.value)\n  }\n}\n\n/**\n * Reads a segment's (or the head's) vary params, unioning in the response-level\n * root params.\n *\n * Root params are emitted once at the top level rather than folded into every\n * segment by the server, so every read recombines them here — building the\n * merge into the read means a caller can't forget it, and it's done in a single\n * pass with no intermediate set.\n *\n * Returns null (\"unknown\", key on all params) unless BOTH iterables are\n * present. A null/absent `iterable` means the segment's own tracking wasn't\n * enabled (e.g. not a prerender). A null/absent `rootIterable` means root\n * params weren't tracked — and since a segment's own iterable never includes\n * root params (those are accessed in layouts above it), narrowing on the\n * segment set alone would wrongly assume no root params were accessed. In\n * either case we stay conservative.\n *\n * When both are present each is authoritative even when it drains to the empty\n * set — a tracked segment that read no params, with no root params accessed,\n * can be shared across all param values.\n */\nexport function readVaryParams(\n  iterable: VaryParamsIterable | null | undefined,\n  rootIterable: VaryParamsIterable | null | undefined\n): VaryParams | null {\n  if (\n    iterable === null ||\n    iterable === undefined ||\n    rootIterable === null ||\n    rootIterable === undefined\n  ) {\n    return null\n  }\n  const varyParams: VaryParams = new Set()\n  drainVaryParams(iterable, varyParams)\n  drainVaryParams(rootIterable, varyParams)\n  return varyParams\n}\n\nconst noop = () => {}\n"],"names":["readVaryParams","drainVaryParams","iterable","target","iterator","Symbol","asyncIterator","chunk","next","then","noop","status","value","undefined","step","done","add","rootIterable","varyParams","Set"],"mappings":"AAAA;;;;CAIC;;;;+BAkGeA;;;eAAAA;;;AAlEhB;;;;;;;;;;;;;;;;;CAiBC,GACD,SAASC,gBACPC,QAA4B,EAC5BC,MAAkB;IAElB,MAAMC,WAAWF,QAAQ,CAACG,OAAOC,aAAa,CAAC;IAC/C,MAAO,KAAM;QACX,MAAMC,QAAQH,SAASI,IAAI;QAC3B,uEAAuE;QACvE,4DAA4D;QAC5D,oEAAoE;QACpE,oEAAoE;QACpE,uEAAuE;QACvE,gDAAgD;QAChDD,MAAME,IAAI,CAACC,MAAMA;QACjB,IAAIH,MAAMI,MAAM,KAAK,eAAeJ,MAAMK,KAAK,KAAKC,WAAW;YAC7D,sEAAsE;YACtE,sBAAsB;YACtB;QACF;QACA,MAAMC,OAAOP,MAAMK,KAAK;QACxB,IAAIE,KAAKC,IAAI,EAAE;YACb;QACF;QACAZ,OAAOa,GAAG,CAACF,KAAKF,KAAK;IACvB;AACF;AAuBO,SAASZ,eACdE,QAA+C,EAC/Ce,YAAmD;IAEnD,IACEf,aAAa,QACbA,aAAaW,aACbI,iBAAiB,QACjBA,iBAAiBJ,WACjB;QACA,OAAO;IACT;IACA,MAAMK,aAAyB,IAAIC;IACnClB,gBAAgBC,UAAUgB;IAC1BjB,gBAAgBgB,cAAcC;IAC9B,OAAOA;AACT;AAEA,MAAMR,OAAO,KAAO","ignoreList":[0]}