'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
0 && (module.exports = {
    createFetch: null,
    createFromNextReadableStream: null,
    decodeBufferedStage: null,
    decodeStageUntilBoundary: null,
    fetchServerResponse: null,
    processFetch: null,
    resolveShellStageData: null,
    resolveStaticStageData: null
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: all[name]
    });
}
_export(exports, {
    createFetch: function() {
        return createFetch;
    },
    createFromNextReadableStream: function() {
        return createFromNextReadableStream;
    },
    decodeBufferedStage: function() {
        return decodeBufferedStage;
    },
    decodeStageUntilBoundary: function() {
        return decodeStageUntilBoundary;
    },
    fetchServerResponse: function() {
        return fetchServerResponse;
    },
    processFetch: function() {
        return processFetch;
    },
    resolveShellStageData: function() {
        return resolveShellStageData;
    },
    resolveStaticStageData: function() {
        return resolveStaticStageData;
    }
});
const _client = require("react-server-dom-webpack/client");
const _invarianterror = require("../../../shared/lib/invariant-error");
const _fetch = require("../segment-cache/fetch");
const _approuterheaders = require("../app-router-headers");
const _appcallserver = require("../../app-call-server");
const _appfindsourcemapurl = require("../../app-find-source-map-url");
const _flightdatahelpers = require("../../flight-data-helpers");
const _setcachebustingsearchparam = require("./set-cache-busting-search-param");
const _routeparams = require("../../route-params");
const _deploymentid = require("../../../shared/lib/deployment-id");
const _navigationbuildid = require("../../navigation-build-id");
const _constants = require("../../../lib/constants");
const _cache = require("../segment-cache/cache");
const _bfcache = require("../segment-cache/bfcache");
const createFromReadableStream = _client.createFromReadableStream;
const createFromFetch = _client.createFromFetch;
let createDebugChannel;
if (process.env.__NEXT_DEV_SERVER && process.env.__NEXT_REACT_DEBUG_CHANNEL) {
    createDebugChannel = require('../../dev/debug-channel').createDebugChannel;
}
function doMpaNavigation(url) {
    return (0, _routeparams.urlToUrlWithoutFlightMarker)(new URL(url, location.origin)).toString();
}
let isPageUnloading = false;
if (typeof window !== 'undefined') {
    // Track when the page is unloading, e.g. due to reloading the page or
    // performing hard navigations. This allows us to suppress error logging when
    // the browser cancels in-flight requests during page unload.
    window.addEventListener('pagehide', ()=>{
        isPageUnloading = true;
    });
    // Reset the flag on pageshow, e.g. when navigating back and the JavaScript
    // execution context is restored by the browser.
    window.addEventListener('pageshow', ()=>{
        isPageUnloading = false;
    });
}
async function fetchServerResponse(url, options) {
    const { flightRouterState, nextUrl } = options;
    const headers = {
        // Enable flight response
        [_approuterheaders.RSC_HEADER]: '1',
        // Provide the current router state
        [_approuterheaders.NEXT_ROUTER_STATE_TREE_HEADER]: (0, _flightdatahelpers.prepareFlightRouterStateForRequest)(flightRouterState, options.isHmrRefresh)
    };
    if (process.env.NODE_ENV === 'development' && options.isHmrRefresh) {
        headers[_approuterheaders.NEXT_HMR_REFRESH_HEADER] = '1';
    }
    if (nextUrl) {
        headers[_approuterheaders.NEXT_URL] = nextUrl;
    }
    // In static export mode, we need to modify the URL to request the .txt file,
    // but we should preserve the original URL for the canonical URL and error handling.
    const originalUrl = url;
    try {
        if (process.env.NODE_ENV === 'production') {
            if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
                // In "output: export" mode, we can't rely on headers to distinguish
                // between HTML and RSC requests. Instead, we append an extra prefix
                // to the request.
                url = new URL(url);
                if (url.pathname.endsWith('/')) {
                    url.pathname += 'index.txt';
                } else {
                    url.pathname += '.txt';
                }
            }
        }
        // Typically, during a navigation, we decode the response using Flight's
        // `createFromFetch` API, which accepts a `fetch` promise.
        // TODO: Remove this check once the old PPR flag is removed
        const isLegacyPPR = process.env.__NEXT_PPR && !process.env.__NEXT_CACHE_COMPONENTS;
        const shouldImmediatelyDecode = !isLegacyPPR;
        const res = await createFetch(url, headers, 'auto', shouldImmediatelyDecode, options.signal);
        // If the fetch succeeds while we're in the offline state, notify the
        // offline module so it can short-circuit the polling loop.
        if (process.env.__NEXT_USE_OFFLINE) {
            const { notifyOnline } = require('../offline');
            notifyOnline();
        }
        const responseUrl = (0, _routeparams.urlToUrlWithoutFlightMarker)(new URL(res.url));
        const canonicalUrl = res.redirected ? responseUrl : originalUrl;
        const contentType = res.headers.get('content-type') || '';
        const interception = !!res.headers.get('vary')?.includes(_approuterheaders.NEXT_URL);
        const postponed = !!res.headers.get(_approuterheaders.NEXT_DID_POSTPONE_HEADER);
        let isFlightResponse = contentType.startsWith(_approuterheaders.RSC_CONTENT_TYPE_HEADER);
        if (process.env.NODE_ENV === 'production') {
            if (process.env.__NEXT_CONFIG_OUTPUT === 'export') {
                if (!isFlightResponse) {
                    isFlightResponse = contentType.startsWith('text/plain');
                }
            }
        }
        // If fetch returns something different than flight response handle it like a mpa navigation
        // If the fetch was not 200, we also handle it like a mpa navigation
        if (!isFlightResponse || !res.ok || !res.body) {
            // in case the original URL came with a hash, preserve it before redirecting to the new URL
            if (url.hash) {
                responseUrl.hash = url.hash;
            }
            return doMpaNavigation(responseUrl.toString());
        }
        // We may navigate to a page that requires a different Webpack runtime.
        // In prod, every page will have the same Webpack runtime.
        // In dev, the Webpack runtime is minimal for each page.
        // We need to ensure the Webpack runtime is updated before executing client-side JS of the new page.
        // TODO: This needs to happen in the Flight Client.
        // Or Webpack needs to include the runtime update in the Flight response as
        // a blocking script.
        if (process.env.NODE_ENV !== 'production' && !process.env.TURBOPACK) {
            await require('../../dev/hot-reloader/app/hot-reloader-app').waitForWebpackRuntimeHotUpdate();
        }
        let flightResponsePromise = res.flightResponsePromise;
        if (flightResponsePromise === null) {
            // Typically, `createFetch` would have already started decoding the
            // Flight response. If it hasn't, though, we need to decode it now.
            // TODO: This should only be reachable if legacy PPR is enabled (i.e. PPR
            // without Cache Components). Remove this branch once legacy PPR
            // is deleted.
            flightResponsePromise = createFromNextReadableStream(res.body, headers, {
                allowPartialStream: postponed
            });
        }
        const [flightResponse, cacheData] = await Promise.all([
            flightResponsePromise,
            res.cacheData
        ]);
        if ((res.headers.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? flightResponse.b) !== (0, _navigationbuildid.getNavigationBuildId)()) {
            // The server build does not match the client build.
            return doMpaNavigation(res.url);
        }
        const normalizedFlightData = (0, _flightdatahelpers.normalizeFlightData)(flightResponse.f);
        if (typeof normalizedFlightData === 'string') {
            return doMpaNavigation(normalizedFlightData);
        }
        const staticStageData = cacheData !== null ? await resolveStaticStageData(cacheData, flightResponse, headers) : null;
        return {
            flightData: normalizedFlightData,
            canonicalUrl: canonicalUrl,
            // TODO: We should be able to read this from the rewrite header, not the
            // Flight response. Theoretically they should always agree, but there are
            // currently some cases where it's incorrect for interception routes. We
            // can always trust the value in the response body. However, per-segment
            // prefetch responses don't embed the value in the body; they rely on the
            // header alone. So we need to investigate why the header is sometimes
            // wrong for interception routes.
            renderedSearch: flightResponse.q,
            couldBeIntercepted: interception,
            supportsPerSegmentPrefetching: flightResponse.S,
            postponed,
            // The dynamicStaleTime is only present in the response body when
            // a page exports unstable_dynamicStaleTime and this is a dynamic render.
            // When absent (UnknownDynamicStaleTime), the client falls back to the
            // global DYNAMIC_STALETIME_MS. The value is in seconds.
            dynamicStaleTime: flightResponse.d ?? _bfcache.UnknownDynamicStaleTime,
            staticStageData,
            runtimePrefetchStream: flightResponse.p ?? null,
            responseHeaders: res.headers,
            debugInfo: flightResponsePromise._debugInfo ?? null,
            revealAfter: flightResponse._revealAfter ?? null
        };
    } catch (err) {
        if (options.signal?.aborted) {
            // A newer HMR refresh superseded this one and aborted its request.
            // Rethrow so the caller treats it as canceled, rather than logging a
            // failure or falling back to an MPA navigation.
            throw err;
        }
        // If the fetch rejected due to a network error, wait for connectivity
        // to be restored and then retry. checkOfflineError returns true for
        // network errors (and starts the polling loop); returns false for
        // intentional aborts/timeouts, which fall through to the MPA fallback.
        //
        // Note: when the user navigates multiple times while offline, each
        // navigation queues a separate retry here. Once connectivity returns,
        // all pending retries resume simultaneously. This is mitigated in PR 3
        // by reusing back-forward cache entries during offline navigation, which
        // avoids issuing new fetches in the first place.
        if (process.env.__NEXT_USE_OFFLINE && !isPageUnloading) {
            const { checkOfflineError, getOffline, waitForConnection } = require('../offline');
            if (checkOfflineError(err)) {
                const offline = getOffline();
                if (offline !== null) {
                    await waitForConnection(offline);
                }
                return fetchServerResponse(url, options);
            }
        }
        if (!isPageUnloading) {
            console.error(`Failed to fetch RSC payload for ${originalUrl}. Falling back to browser navigation.`, err);
        }
        // If fetch fails handle it like a mpa navigation
        // TODO-APP: Add a test for the case where a CORS request fails, e.g. external url redirect coming from the response.
        // See https://github.com/vercel/next.js/issues/43605#issuecomment-1451617521 for a reproduction.
        return originalUrl.toString();
    }
}
async function processFetch(response) {
    if (process.env.__NEXT_CACHE_COMPONENTS) {
        if (!response.body) {
            throw Object.defineProperty(new _invarianterror.InvariantError('Expected RSC navigation response to have a body'), "__NEXT_ERROR_CODE", {
                value: "E1088",
                enumerable: false,
                configurable: true
            });
        }
        const { stream, isPartial } = await (0, _cache.stripIsPartialByte)(response.body);
        let responseStream;
        let cacheData;
        if (process.env.__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS) {
            // Three readers needed: the main Flight decoder, the static-stage
            // extractor, and the shell-stage extractor. Tee twice.
            const [stream1, rest] = stream.tee();
            const [staticBodyClone, shellBodyClone] = rest.tee();
            responseStream = stream1;
            cacheData = {
                isResponsePartial: isPartial,
                staticBodyClone,
                shellBodyClone
            };
        } else {
            responseStream = stream;
            cacheData = {
                isResponsePartial: isPartial
            };
        }
        const strippedResponse = new Response(responseStream, {
            headers: response.headers,
            status: response.status,
            statusText: response.statusText
        });
        // The Response constructor doesn't preserve `url` or `redirected` from
        // the original. We need both: `url` for React DevTools and `redirected`
        // for the redirect replay logic below.
        Object.defineProperty(strippedResponse, 'url', {
            value: response.url
        });
        Object.defineProperty(strippedResponse, 'redirected', {
            value: response.redirected
        });
        return {
            response: strippedResponse,
            cacheData
        };
    }
    return {
        response,
        cacheData: null
    };
}
async function resolveStaticStageData(cacheData, flightResponse, headers) {
    const { isResponsePartial, staticBodyClone } = cacheData;
    if (staticBodyClone) {
        if (!isResponsePartial) {
            // Fully static — cache the entire decoded response as-is.
            staticBodyClone.cancel();
            return {
                response: flightResponse,
                isResponsePartial: false
            };
        }
        if (flightResponse.l !== undefined) {
            // Partially static — truncate the body clone at the byte boundary and
            // decode it.
            const staticStageByteLength = await flightResponse.l;
            const response = await decodeStageUntilBoundary(staticBodyClone, staticStageByteLength, headers);
            return {
                response,
                isResponsePartial: true
            };
        }
        // No caching — cancel the unused clone.
        staticBodyClone.cancel();
    }
    return null;
}
async function resolveShellStageData(cacheData, flightResponse, headers) {
    const { shellBodyClone } = cacheData;
    if (!shellBodyClone) {
        return null;
    }
    if (flightResponse.a === undefined) {
        shellBodyClone.cancel();
        return null;
    }
    const shellByteLength = await flightResponse.a;
    if (shellByteLength === null) {
        // Shell == main response — caller reuses the existing flightResponse.
        shellBodyClone.cancel();
        return null;
    }
    return decodeStageUntilBoundary(shellBodyClone, shellByteLength, headers);
}
async function decodeStageUntilBoundary(responseBodyClone, byteLength, headers) {
    const { buffer } = await (0, _cache.createNonTaskyPrefetchResponseStream)(responseBodyClone, byteLength);
    return decodeBufferedStage(buffer, headers);
}
function decodeBufferedStage(buffer, headers) {
    const stream = new ReadableStream({
        start (controller) {
            controller.enqueue(buffer);
            controller.close();
        }
    });
    return createFromNextReadableStream(stream, headers, {
        allowPartialStream: true
    });
}
// When an HMR refresh can be superseded, we decode its Flight response through
// a wrapper stream we can close on abort. Closing the stream (rather than
// letting the aborted fetch error it) makes React's Flight client mark
// unresolved rows as halted: they suspend during render instead of rejecting,
// so a superseded request never surfaces an error on an already-committed tree.
// Because the stream is closed, there's also no unclosed-stream GC-root leak
// (see #89610). The wrapper is created synchronously here so that the decode
// starts at the same point `createFromNextFetch` would, preserving the
// server-latency debug timing.
function createHaltingFlightResponse(fetchPromise, headers, signal) {
    let closed = false;
    let reader = null;
    const wrapper = new ReadableStream({
        start (controller) {
            const onAbort = ()=>{
                closed = true;
                try {
                    controller.close();
                } catch  {
                // The controller may already be closed; nothing to do.
                }
                if (reader !== null) {
                    reader.cancel().catch(()=>{});
                }
            };
            if (signal.aborted) {
                onAbort();
            } else {
                signal.addEventListener('abort', onAbort, {
                    once: true
                });
            }
        },
        async pull (controller) {
            if (closed) {
                return;
            }
            if (reader === null) {
                let response;
                try {
                    response = await fetchPromise;
                } catch (err) {
                    // We don't inspect `err`. If the request was superseded, `onAbort`
                    // already ran synchronously (abort listeners fire during
                    // `signal.abort()`, before this rejection microtask), so `closed` is
                    // true and the controller is already closed — erroring it would
                    // throw, and a superseded request's failure is moot regardless of its
                    // cause. Only a genuine, non-superseded failure reaches here with
                    // `closed` still false; that is the case we surface.
                    if (!closed) {
                        controller.error(err);
                    }
                    return;
                }
                if (closed) {
                    // Aborted while awaiting the response. The `fetch` abort tears down
                    // an in-flight request, but if it had already completed we still hold
                    // an unread body; release it so it isn't left dangling.
                    response.body?.cancel().catch(()=>{});
                    return;
                }
                const body = response.body;
                if (body === null) {
                    controller.close();
                    return;
                }
                reader = body.getReader();
            }
            try {
                const { done, value } = await reader.read();
                if (closed) {
                    return;
                }
                if (done) {
                    controller.close();
                } else {
                    controller.enqueue(value);
                }
            } catch (err) {
                // Same as the fetch catch above: once superseded (`closed`) the
                // controller is already closed and the outcome is moot, so we swallow
                // the rejection unconditionally; only a real, non-superseded read
                // failure (`closed` still false) is surfaced.
                if (!closed) {
                    controller.error(err);
                }
            }
        }
    });
    // React attaches `_debugInfo` to the returned promise at runtime.
    return createFromNextReadableStream(wrapper, headers, {
        allowPartialStream: true
    });
}
// Selects the Flight decode strategy: a halting wrapper for cancellable HMR
// refreshes, otherwise the standard fetch-based decode. Gated to the dev server
// (where HMR runs) so the wrapper is eliminated from production and
// `--debug-prerender` bundles regardless of the flag.
function decodeFlightResponse(fetchPromise, headers, signal) {
    if (process.env.__NEXT_DEV_SERVER && process.env.__NEXT_SERVER_COMPONENTS_HMR_CANCELLATION && signal) {
        return createHaltingFlightResponse(fetchPromise, headers, signal);
    }
    return createFromNextFetch(fetchPromise, headers);
}
async function createFetch(url, headers, fetchPriority, shouldImmediatelyDecode, signal) {
    // TODO: In output: "export" mode, the headers do nothing. Omit them (and the
    // cache busting search param) from the request so they're
    // maximally cacheable.
    if (process.env.__NEXT_TEST_MODE && fetchPriority !== null) {
        headers['Next-Test-Fetch-Priority'] = fetchPriority;
    }
    const deploymentId = (0, _deploymentid.getDeploymentId)();
    if (deploymentId) {
        headers['x-deployment-id'] = deploymentId;
    }
    if (process.env.__NEXT_DEV_SERVER) {
        if (self.__next_r) {
            headers[_approuterheaders.NEXT_HTML_REQUEST_ID_HEADER] = self.__next_r;
        }
        // Create a new request ID for the server action request. The server uses
        // this to tag debug information sent via WebSocket to the client, which
        // then routes those chunks to the debug channel associated with this ID.
        headers[_approuterheaders.NEXT_REQUEST_ID_HEADER] = crypto.getRandomValues(new Uint32Array(1))[0].toString(16);
    }
    const fetchOptions = {
        // Backwards compat for older browsers. `same-origin` is the default in modern browsers.
        credentials: 'same-origin',
        headers,
        priority: fetchPriority || undefined,
        signal
    };
    // `fetchUrl` is slightly different from `url` because we add a cache-busting
    // search param to it. This should not leak outside of this function, so we
    // track them separately.
    let fetchUrl = new URL(url);
    await (0, _setcachebustingsearchparam.setCacheBustingSearchParam)(fetchUrl, headers);
    let processed = (0, _fetch.fetch)(fetchUrl, fetchOptions).then(processFetch);
    let fetchPromise = processed.then(({ response })=>response);
    // Immediately pass the fetch promise to the Flight client so that the debug
    // info includes the latency from the client to the server. The internal timer
    // in React starts as soon as `createFromFetch` is called.
    //
    // The only case where we don't do this is during a prefetch, because a
    // top-level prefetch response never blocks a navigation; if it hasn't already
    // been written into the cache by the time the navigation happens, the router
    // will go straight to a dynamic request.
    let flightResponsePromise = shouldImmediatelyDecode ? decodeFlightResponse(fetchPromise, headers, signal) : null;
    let browserResponse = await fetchPromise;
    // If the server responds with a redirect (e.g. 307), and the redirected
    // location does not contain the cache busting search param set in the
    // original request, the response is likely invalid — when following the
    // redirect, the browser forwards the request headers, but since the cache
    // busting search param is missing, the server will reject the request due to
    // a mismatch.
    //
    // Ideally, we would be able to intercept the redirect response and perform it
    // manually, instead of letting the browser automatically follow it, but this
    // is not allowed by the fetch API.
    //
    // So instead, we must "replay" the redirect by fetching the new location
    // again, but this time we'll append the cache busting search param to prevent
    // a mismatch.
    //
    // TODO: We can optimize Next.js's built-in middleware APIs by returning a
    // custom status code, to prevent the browser from automatically following it.
    //
    // This does not affect Server Action-based redirects; those are encoded
    // differently, as part of the Flight body. It only affects redirects that
    // occur in a middleware or a third-party proxy.
    let redirected = browserResponse.redirected;
    if (process.env.__NEXT_CLIENT_VALIDATE_RSC_REQUEST_HEADERS) {
        // This is to prevent a redirect loop. Same limit used by Chrome.
        const MAX_REDIRECTS = 20;
        for(let n = 0; n < MAX_REDIRECTS; n++){
            if (!browserResponse.redirected) {
                break;
            }
            const responseUrl = new URL(browserResponse.url, fetchUrl);
            if (responseUrl.origin !== fetchUrl.origin) {
                break;
            }
            if (responseUrl.searchParams.get(_approuterheaders.NEXT_RSC_UNION_QUERY) === fetchUrl.searchParams.get(_approuterheaders.NEXT_RSC_UNION_QUERY)) {
                break;
            }
            // The RSC request was redirected. Assume the response is invalid.
            //
            // Append the cache busting search param to the redirected URL and
            // fetch again.
            // TODO: We should abort the previous request.
            fetchUrl = new URL(responseUrl);
            await (0, _setcachebustingsearchparam.setCacheBustingSearchParam)(fetchUrl, headers);
            processed = (0, _fetch.fetch)(fetchUrl, fetchOptions).then(processFetch);
            fetchPromise = processed.then(({ response })=>response);
            flightResponsePromise = shouldImmediatelyDecode ? decodeFlightResponse(fetchPromise, headers, signal) : null;
            browserResponse = await fetchPromise;
            // We just performed a manual redirect, so this is now true.
            redirected = true;
        }
    }
    // Remove the cache busting search param from the response URL, to prevent it
    // from leaking outside of this function.
    const responseUrl = new URL(browserResponse.url, fetchUrl);
    responseUrl.searchParams.delete(_approuterheaders.NEXT_RSC_UNION_QUERY);
    const rscResponse = {
        url: responseUrl.href,
        // This is true if any redirects occurred, either automatically by the
        // browser, or manually by us. So it's different from
        // `browserResponse.redirected`, which only tells us whether the browser
        // followed a redirect, and only for the last response in the chain.
        redirected,
        // These can be copied from the last browser response we received. We
        // intentionally only expose the subset of fields that are actually used
        // elsewhere in the codebase.
        ok: browserResponse.ok,
        headers: browserResponse.headers,
        body: browserResponse.body,
        status: browserResponse.status,
        // This is the exact promise returned by `createFromFetch`. It contains
        // debug information that we need to transfer to any derived promises that
        // are later rendered by React.
        flightResponsePromise: flightResponsePromise,
        cacheData: processed.then(({ cacheData })=>cacheData)
    };
    return rscResponse;
}
function createFromNextReadableStream(flightStream, requestHeaders, options) {
    return createFromReadableStream(flightStream, {
        callServer: _appcallserver.callServer,
        findSourceMapURL: _appfindsourcemapurl.findSourceMapURL,
        debugChannel: createDebugChannel && createDebugChannel(requestHeaders),
        unstable_allowPartialStream: options?.allowPartialStream
    });
}
function createFromNextFetch(promiseForResponse, requestHeaders) {
    return createFromFetch(promiseForResponse, {
        callServer: _appcallserver.callServer,
        findSourceMapURL: _appfindsourcemapurl.findSourceMapURL,
        debugChannel: createDebugChannel && createDebugChannel(requestHeaders)
    });
}

if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
  Object.defineProperty(exports.default, '__esModule', { value: true });
  Object.assign(exports.default, exports);
  module.exports = exports.default;
}

//# sourceMappingURL=fetch-server-response.js.map