UNPKG

next

Version:

The React Framework

116 lines (115 loc) 4.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "setupProbeScheduler", { enumerable: true, get: function() { return setupProbeScheduler; } }); const _usecacheprobeglobals = require("./use-cache-probe-globals"); const PROBE_THRESHOLD_MS = 10000; const MIN_PROBE_BUDGET_MS = 3000; function setupProbeScheduler(opts) { const { workStore, outerRequestStore, cacheContext, encodedArguments, fillDeadlineAt, stream, abortSignal, onProbeCompleted } = opts; if (fillDeadlineAt - performance.now() < PROBE_THRESHOLD_MS + MIN_PROBE_BUDGET_MS) { return stream; } const probe = (0, _usecacheprobeglobals.getUseCacheProbe)(); if (!probe) { return stream; } let lastChunkAt = performance.now(); let idleTimer; const startProbe = ()=>{ if (abortSignal.aborted) { return; } const probeStartedAtChunk = lastChunkAt; // Computed when the probe is about to run so it gets the actual remaining // budget, not a stale value baked in at scheduler setup. Reserves a 1s // buffer so the probe's internal timeout fires before the outer render // timeout. const probeInternalTimeoutMs = fillDeadlineAt - performance.now() - 1000; probe({ page: workStore.page, route: workStore.route, id: cacheContext.functionId, kind: cacheContext.handlerKind, encodedArguments, // Built lazily because most fills complete before the idle timer fires; // only worth assembling once we know the probe is running. request: { headers: Array.from(outerRequestStore.headers.entries()), cookieHeader: outerRequestStore.headers.get('cookie') ?? undefined, urlPathname: outerRequestStore.url.pathname, urlSearch: outerRequestStore.url.search, rootParams: outerRequestStore.rootParams ?? {}, isDraftMode: workStore.isDraftMode ?? false, isHmrRefresh: outerRequestStore.isHmrRefresh ?? false, hmrRefreshHash: outerRequestStore.hmrRefreshHash }, timeoutMs: probeInternalTimeoutMs }).then((completed)=>{ // Mid-probe recovery: chunks arrived while the probe was running, so // the main stream is making progress. Discard the probe's result rather // than reporting a deadlock that no longer holds. if (lastChunkAt > probeStartedAtChunk) { return; } if (completed && !abortSignal.aborted) { onProbeCompleted(); } }, ()=>{ // Probe failures are inconclusive; fall back to the regular cache-fill // timeout. }); }; const scheduleAfterIdle = ()=>{ if (idleTimer !== undefined || abortSignal.aborted) { return; } const now = performance.now(); const idleFor = now - lastChunkAt; const wait = Math.max(0, PROBE_THRESHOLD_MS - idleFor); // Skip scheduling if the outer fill timeout will fire before the probe // could even start running with at least a minimum useful budget. Without // this check, a chunk arriving late in the fill could reschedule a probe // that the outer timeout would then abort — wasted worker spawn for a probe // that can't meaningfully complete. if (fillDeadlineAt - now < wait + MIN_PROBE_BUDGET_MS) { return; } idleTimer = setTimeout(()=>{ idleTimer = undefined; if (abortSignal.aborted) { return; } const idleNow = performance.now() - lastChunkAt; if (idleNow < PROBE_THRESHOLD_MS) { // A chunk arrived since we set this timer; reschedule. scheduleAfterIdle(); return; } startProbe(); }, wait); }; abortSignal.addEventListener('abort', ()=>{ if (idleTimer !== undefined) { clearTimeout(idleTimer); idleTimer = undefined; } }, { once: true }); scheduleAfterIdle(); return stream.pipeThrough(new TransformStream({ transform (chunk, controller) { lastChunkAt = performance.now(); scheduleAfterIdle(); controller.enqueue(chunk); } })); } //# sourceMappingURL=use-cache-probe-scheduler.js.map