File size: 9,665 Bytes
c592d77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "createInitialRouterState", {
    enumerable: true,
    get: function() {
        return createInitialRouterState;
    }
});
const _createhreffromurl = require("./create-href-from-url");
const _computechangedpath = require("./compute-changed-path");
const _flightdatahelpers = require("../../flight-data-helpers");
const _pprnavigations = require("./ppr-navigations");
const _cache = require("../segment-cache/cache");
const _types = require("../segment-cache/types");
const _bfcache = require("../segment-cache/bfcache");
const _fetchserverresponse = require("./fetch-server-response");
const _optimisticroutes = require("../segment-cache/optimistic-routes");
function createInitialRouterState({ navigatedAt, initialRSCPayload, initialFlightStreamForCache, location }) {
    const { c: initialCanonicalUrlParts, f: initialFlightData, q: initialRenderedSearch, i: initialCouldBeIntercepted, S: initialSupportsPerSegmentPrefetching, s: initialStaleTime, l: initialStaticStageByteLength, h: initialHeadVaryParams, p: initialRuntimePrefetchStream, d: initialDynamicStaleTimeSeconds } = initialRSCPayload;
    // When initialized on the server, the canonical URL is provided as an array of parts.
    // This is to ensure that when the RSC payload streamed to the client, crawlers don't interpret it
    // as a URL that should be crawled.
    const initialCanonicalUrl = initialCanonicalUrlParts.join('/');
    const normalizedFlightData = (0, _flightdatahelpers.getFlightDataPartsFromPath)(initialFlightData[0]);
    const { tree: initialTree, seedData: initialSeedData, head: initialHead } = normalizedFlightData;
    // For the SSR render, seed data should always be available (we only send back a `null` response
    // in the case of a `loading` segment, pre-PPR.)
    const canonicalUrl = // location.href is read as the initial value for canonicalUrl in the browser
    // This is safe to do as canonicalUrl can't be rendered, it's only used to control the history updates in the useEffect further down in this file.
    location ? (0, _createhreffromurl.createHrefFromUrl)(location) : initialCanonicalUrl;
    // Convert the initial FlightRouterState into the RouteTree type.
    // NOTE: The metadataVaryPath isn't used for anything currently because the
    // head is embedded into the CacheNode tree, but eventually we'll lift it out
    // and store it on the top-level state object.
    //
    // TODO: For statically-generated-at-build-time HTML pages, the
    // FlightRouterState baked into the initial RSC payload won't have the
    // correct segment inlining hints (ParentInlinedIntoSelf, InlinedIntoChild)
    // because those are computed after the pre-render. The client will need to
    // fetch the correct hints from the route tree prefetch (/_tree) response
    // before acting on inlining decisions.
    const acc = {
        metadataVaryPath: null
    };
    const initialRouteTree = (0, _cache.convertRootFlightRouterStateToRouteTree)(initialTree, initialRenderedSearch, acc);
    const metadataVaryPath = acc.metadataVaryPath;
    const initialTask = (0, _pprnavigations.createInitialCacheNodeForHydration)(navigatedAt, initialRouteTree, initialSeedData, initialHead, (0, _bfcache.computeDynamicStaleAt)(navigatedAt, initialDynamicStaleTimeSeconds ?? _bfcache.UnknownDynamicStaleTime));
    // The following only applies in the browser (location !== null) since neither
    // route learning nor segment cache state persists from SSR to client.
    if (location !== null && metadataVaryPath !== null) {
        // Learn the route pattern so we can predict it for future navigations.
        (0, _optimisticroutes.discoverKnownRoute)(Date.now(), location.pathname, null, null, initialRouteTree, metadataVaryPath, initialCouldBeIntercepted, canonicalUrl, initialSupportsPerSegmentPrefetching, false // hasDynamicRewrite
        );
        // Write the initial seed data into the segment cache so subsequent
        // navigations to the initial page can serve cached segments instantly.
        if (initialSeedData !== null && initialStaleTime !== undefined) {
            if (initialStaticStageByteLength !== undefined && initialFlightStreamForCache != null) {
                // Partially static page — truncate the cloned Flight stream at the
                // static stage byte boundary, decode, and cache the static subset.
                (0, _fetchserverresponse.decodeStaticStage)(initialFlightStreamForCache, initialStaticStageByteLength, undefined).then(async (staticStageResponse)=>{
                    const now = Date.now();
                    const staleAt = await (0, _cache.getStaleAt)(now, staticStageResponse.s);
                    (0, _cache.writeStaticStageResponseIntoCache)(now, staticStageResponse.f, undefined, staticStageResponse.h, staleAt, initialTree, initialRenderedSearch, true // isResponsePartial
                    );
                }).catch(()=>{
                // The static stage processing failed. Not fatal — the page
                // rendered normally, we just won't write into the cache.
                });
            } else {
                // Fully static page — cache the entire decoded seed data as-is. We're
                // not using the initial response here (which would allow us to combine
                // the two branches) to avoid unnecessary decoding of the Flight data,
                // since we can just take the seed data that we already decoded during
                // hydration and write it into the cache directly.
                const now = Date.now();
                (0, _cache.getStaleAt)(now, initialStaleTime).then((staleAt)=>{
                    (0, _cache.writeStaticStageResponseIntoCache)(now, initialFlightData, undefined, initialHeadVaryParams, staleAt, initialTree, initialRenderedSearch, false // isResponsePartial
                    );
                }).catch(()=>{
                // The static stage processing failed. Not fatal — the page
                // rendered normally, we just won't write into the cache.
                });
                // Cancel the stream clone — fully static path doesn't need it.
                initialFlightStreamForCache?.cancel();
            }
        } else {
            // No caching — cancel the unused stream clone.
            initialFlightStreamForCache?.cancel();
        }
        // If the initial RSC payload includes an embedded runtime prefetch stream,
        // decode it and write the runtime data into the segment cache. This allows
        // subsequent navigations to serve runtime-prefetchable content from cache
        // without a separate prefetch request.
        if (initialRuntimePrefetchStream != null) {
            (0, _cache.processRuntimePrefetchStream)(Date.now(), initialRuntimePrefetchStream, initialTree, initialRenderedSearch).then((processed)=>{
                if (processed !== null) {
                    (0, _cache.writeDynamicRenderResponseIntoCache)(Date.now(), _types.FetchStrategy.PPRRuntime, processed.flightDatas, processed.buildId, processed.isResponsePartial, processed.headVaryParams, processed.staleAt, processed.navigationSeed, null);
                }
            }).catch(()=>{
            // Runtime prefetch cache write failed. Not fatal — the page rendered
            // normally, we just won't cache runtime data.
            });
        }
    }
    // NOTE: We intentionally don't check if any data needs to be fetched from the
    // server. We assume the initial hydration payload is sufficient to render
    // the page.
    //
    // The completeness of the initial data is an important property that we rely
    // on as a last-ditch mechanism for recovering the app; we must always be able
    // to reload a fresh HTML document to get to a consistent state.
    //
    // In the future, there may be cases where the server intentionally sends
    // partial data and expects the client to fill in the rest, in which case this
    // logic may change. (There already is a similar case where the server sends
    // _no_ hydration data in the HTML document at all, and the client fetches it
    // separately, but that's different because we still end up hydrating with a
    // complete tree.)
    const initialState = {
        tree: initialTask.route,
        cache: initialTask.node,
        pushRef: {
            pendingPush: false,
            mpaNavigation: false,
            // First render needs to preserve the previous window.history.state
            // to avoid it being overwritten on navigation back/forward with MPA Navigation.
            preserveCustomHistoryState: true
        },
        focusAndScrollRef: {
            scrollRef: null,
            forceScroll: false,
            onlyHashChange: false,
            hashFragment: null
        },
        canonicalUrl,
        renderedSearch: initialRenderedSearch,
        // the || operator is intentional, the pathname can be an empty string
        nextUrl: ((0, _computechangedpath.extractPathFromFlightRouterState)(initialTree) || location?.pathname) ?? null,
        previousNextUrl: null,
        debugInfo: null
    };
    return initialState;
}

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=create-initial-router-state.js.map