next
Version:
The React Framework
91 lines (90 loc) • 4.18 kB
JavaScript
/**
* Vary Params Decoding
*
* This module is shared between server and client.
*/ import { readFulfilledValue } from '../rsc-transport';
/**
* The vary path id for search params. Path params are identified by their
* name. Search params don't have a fixed set of names, so any access to them
* is reported under this one id, and the segment is keyed by the whole search
* string (see createVaryingSearchParams in app-render/vary-params.ts).
*
* It's a number so it can't collide with a param name. `app/[?]/page.tsx` is a
* valid route.
*/ export const SEARCH_PARAMS_VARY_ID = 0;
/**
* 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: each iterator result is a
* Flight chunk whose settled state `readFulfilledValue` reads off the
* thenable's status.
*
* 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 step = readFulfilledValue(iterator.next(), undefined);
if (step === undefined || step.done) {
// Either the stream suspended here — everything yielded before this
// point has already been added — or the iterable finished cleanly.
return;
}
target.add(step.value);
}
}
/**
* Converts a segment's (or the head's) vary params off the wire, at the
* decode boundary, 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 decode recombines them here — building the
* merge into the decode 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 decodeVaryParams(iterable, rootIterable) {
if (iterable === null || iterable === undefined || rootIterable === null || rootIterable === undefined) {
return null;
}
const total = new Set();
drainVaryParams(iterable, total);
drainVaryParams(rootIterable, total);
return createVaryParams(total);
}
/**
* Wraps an already-known set as a vary params source. Shaped like a settled
* Flight promise so readVaryParams can read it off the thenable's status.
*/ export function createVaryParams(total) {
// TODO: Don't need to use a native promise. Just inline a thenable that
// immediately calls its listener.
const settled = Promise.resolve(total);
settled.status = 'fulfilled';
settled.value = total;
return settled;
}
/**
* Reads the set from a vary params source. Null when it is not available;
* the reader assumes every param varies.
*/ export function readVaryParams(varyParams) {
return readFulfilledValue(varyParams, null);
}
//# sourceMappingURL=vary-params-decoding.js.map