next
Version:
The React Framework
76 lines (75 loc) • 3.53 kB
JavaScript
/**
* Vary Params Decoding
*
* This module is shared between server and client.
*/ /**
* Synchronously drains a vary params `AsyncIterable`, adding each yielded name
* to `target`.
*
* By the time this runs (on the client, or in collectSegmentData), the Flight
* stream has been fully buffered, so every yielded value is already
* materialized and can be read without awaiting. We force each iterator result
* to resolve synchronously using the same `.then(noop)` trick React uses
* internally, then read its `status`/`value` directly.
*
* We add "every param yielded up to the point the stream suspends": a
* normally-closed iterable drains fully, while one left hanging (a sync-I/O
* abort, or a `close()` whose row hasn't flushed yet) drains to the prefix
* already in the stream. Both are correct — a segment's param accesses are all
* flushed as they happen during its render, so the prefix is exactly the set
* the response depends on. We therefore never need the terminating `done` row
* to be present; it's only stream hygiene.
*/ function drainVaryParams(iterable, target) {
const iterator = iterable[Symbol.asyncIterator]();
while(true){
const chunk = iterator.next();
// Attach a no-op listener to force Flight to synchronously resolve the
// chunk. A freshly-arrived result may be in an intermediate
// 'resolved_model' state (data received but not unwrapped); calling
// .then() transitions it to 'fulfilled', making the value available
// synchronously. (A native Promise has no `status` and simply reads as
// not-fulfilled below, so this can never hang.)
chunk.then(noop, noop);
if (chunk.status !== 'fulfilled' || chunk.value === undefined) {
// The stream suspended here. Everything yielded before this point has
// already been added.
return;
}
const step = chunk.value;
if (step.done) {
return;
}
target.add(step.value);
}
}
/**
* Reads a segment's (or the head's) vary params, unioning in the response-level
* root params.
*
* Root params are emitted once at the top level rather than folded into every
* segment by the server, so every read recombines them here — building the
* merge into the read means a caller can't forget it, and it's done in a single
* pass with no intermediate set.
*
* Returns null ("unknown", key on all params) unless BOTH iterables are
* present. A null/absent `iterable` means the segment's own tracking wasn't
* enabled (e.g. not a prerender). A null/absent `rootIterable` means root
* params weren't tracked — and since a segment's own iterable never includes
* root params (those are accessed in layouts above it), narrowing on the
* segment set alone would wrongly assume no root params were accessed. In
* either case we stay conservative.
*
* When both are present each is authoritative even when it drains to the empty
* set — a tracked segment that read no params, with no root params accessed,
* can be shared across all param values.
*/ export function readVaryParams(iterable, rootIterable) {
if (iterable === null || iterable === undefined || rootIterable === null || rootIterable === undefined) {
return null;
}
const varyParams = new Set();
drainVaryParams(iterable, varyParams);
drainVaryParams(rootIterable, varyParams);
return varyParams;
}
const noop = ()=>{};
//# sourceMappingURL=vary-params-decoding.js.map